@ai-agent-forge/plugin-memory 0.85.0
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 +65 -0
- package/agent-forge.json +11 -0
- package/dist/capability.d.ts +182 -0
- package/dist/capability.d.ts.map +1 -0
- package/dist/capability.js +2565 -0
- package/dist/capability.js.map +1 -0
- package/dist/entry.d.ts +36 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +154 -0
- package/dist/entry.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/assistant-card.d.ts +31 -0
- package/dist/memory/assistant-card.d.ts.map +1 -0
- package/dist/memory/assistant-card.js +108 -0
- package/dist/memory/assistant-card.js.map +1 -0
- package/dist/memory/candidates.d.ts +65 -0
- package/dist/memory/candidates.d.ts.map +1 -0
- package/dist/memory/candidates.js +100 -0
- package/dist/memory/candidates.js.map +1 -0
- package/dist/memory/code-memory.d.ts +89 -0
- package/dist/memory/code-memory.d.ts.map +1 -0
- package/dist/memory/code-memory.js +104 -0
- package/dist/memory/code-memory.js.map +1 -0
- package/dist/memory/compaction-sequencer.d.ts +63 -0
- package/dist/memory/compaction-sequencer.d.ts.map +1 -0
- package/dist/memory/compaction-sequencer.js +129 -0
- package/dist/memory/compaction-sequencer.js.map +1 -0
- package/dist/memory/continuation.d.ts +44 -0
- package/dist/memory/continuation.d.ts.map +1 -0
- package/dist/memory/continuation.js +49 -0
- package/dist/memory/continuation.js.map +1 -0
- package/dist/memory/curation.d.ts +58 -0
- package/dist/memory/curation.d.ts.map +1 -0
- package/dist/memory/curation.js +68 -0
- package/dist/memory/curation.js.map +1 -0
- package/dist/memory/egress-policy.d.ts +50 -0
- package/dist/memory/egress-policy.d.ts.map +1 -0
- package/dist/memory/egress-policy.js +71 -0
- package/dist/memory/egress-policy.js.map +1 -0
- package/dist/memory/embedding-provider.d.ts +70 -0
- package/dist/memory/embedding-provider.d.ts.map +1 -0
- package/dist/memory/embedding-provider.js +164 -0
- package/dist/memory/embedding-provider.js.map +1 -0
- package/dist/memory/embedding-reranker.d.ts +56 -0
- package/dist/memory/embedding-reranker.d.ts.map +1 -0
- package/dist/memory/embedding-reranker.js +109 -0
- package/dist/memory/embedding-reranker.js.map +1 -0
- package/dist/memory/foundation.d.ts +168 -0
- package/dist/memory/foundation.d.ts.map +1 -0
- package/dist/memory/foundation.js +487 -0
- package/dist/memory/foundation.js.map +1 -0
- package/dist/memory/host-module-import.d.ts +25 -0
- package/dist/memory/host-module-import.d.ts.map +1 -0
- package/dist/memory/host-module-import.js +41 -0
- package/dist/memory/host-module-import.js.map +1 -0
- package/dist/memory/ledger.d.ts +58 -0
- package/dist/memory/ledger.d.ts.map +1 -0
- package/dist/memory/ledger.js +315 -0
- package/dist/memory/ledger.js.map +1 -0
- package/dist/memory/lifecycle.d.ts +124 -0
- package/dist/memory/lifecycle.d.ts.map +1 -0
- package/dist/memory/lifecycle.js +201 -0
- package/dist/memory/lifecycle.js.map +1 -0
- package/dist/memory/memory-network.d.ts +55 -0
- package/dist/memory/memory-network.d.ts.map +1 -0
- package/dist/memory/memory-network.js +70 -0
- package/dist/memory/memory-network.js.map +1 -0
- package/dist/memory/model-cache-hygiene.d.ts +18 -0
- package/dist/memory/model-cache-hygiene.d.ts.map +1 -0
- package/dist/memory/model-cache-hygiene.js +38 -0
- package/dist/memory/model-cache-hygiene.js.map +1 -0
- package/dist/memory/preference-disambiguator.d.ts +43 -0
- package/dist/memory/preference-disambiguator.d.ts.map +1 -0
- package/dist/memory/preference-disambiguator.js +81 -0
- package/dist/memory/preference-disambiguator.js.map +1 -0
- package/dist/memory/preference-lifecycle.d.ts +66 -0
- package/dist/memory/preference-lifecycle.d.ts.map +1 -0
- package/dist/memory/preference-lifecycle.js +129 -0
- package/dist/memory/preference-lifecycle.js.map +1 -0
- package/dist/memory/preference-promotion.d.ts +87 -0
- package/dist/memory/preference-promotion.d.ts.map +1 -0
- package/dist/memory/preference-promotion.js +102 -0
- package/dist/memory/preference-promotion.js.map +1 -0
- package/dist/memory/preference-resolver.d.ts +44 -0
- package/dist/memory/preference-resolver.d.ts.map +1 -0
- package/dist/memory/preference-resolver.js +107 -0
- package/dist/memory/preference-resolver.js.map +1 -0
- package/dist/memory/purge-journal.d.ts +76 -0
- package/dist/memory/purge-journal.d.ts.map +1 -0
- package/dist/memory/purge-journal.js +130 -0
- package/dist/memory/purge-journal.js.map +1 -0
- package/dist/memory/purge.d.ts +90 -0
- package/dist/memory/purge.d.ts.map +1 -0
- package/dist/memory/purge.js +138 -0
- package/dist/memory/purge.js.map +1 -0
- package/dist/memory/recall-agent.d.ts +84 -0
- package/dist/memory/recall-agent.d.ts.map +1 -0
- package/dist/memory/recall-agent.js +199 -0
- package/dist/memory/recall-agent.js.map +1 -0
- package/dist/memory/recall-index.d.ts +87 -0
- package/dist/memory/recall-index.d.ts.map +1 -0
- package/dist/memory/recall-index.js +222 -0
- package/dist/memory/recall-index.js.map +1 -0
- package/dist/memory/recall-packet.d.ts +121 -0
- package/dist/memory/recall-packet.d.ts.map +1 -0
- package/dist/memory/recall-packet.js +156 -0
- package/dist/memory/recall-packet.js.map +1 -0
- package/dist/memory/scheduler-api.d.ts +99 -0
- package/dist/memory/scheduler-api.d.ts.map +1 -0
- package/dist/memory/scheduler-api.js +93 -0
- package/dist/memory/scheduler-api.js.map +1 -0
- package/dist/memory/scheduler.d.ts +55 -0
- package/dist/memory/scheduler.d.ts.map +1 -0
- package/dist/memory/scheduler.js +91 -0
- package/dist/memory/scheduler.js.map +1 -0
- package/dist/memory/store.d.ts +107 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +208 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/suite-memory.d.ts +208 -0
- package/dist/memory/suite-memory.d.ts.map +1 -0
- package/dist/memory/suite-memory.js +288 -0
- package/dist/memory/suite-memory.js.map +1 -0
- package/dist/memory/transfer.d.ts +142 -0
- package/dist/memory/transfer.d.ts.map +1 -0
- package/dist/memory/transfer.js +210 -0
- package/dist/memory/transfer.js.map +1 -0
- package/dist/memory/vector-index.d.ts +39 -0
- package/dist/memory/vector-index.d.ts.map +1 -0
- package/dist/memory/vector-index.js +136 -0
- package/dist/memory/vector-index.js.map +1 -0
- package/dist/memory/write-budget.d.ts +33 -0
- package/dist/memory/write-budget.d.ts.map +1 -0
- package/dist/memory/write-budget.js +45 -0
- package/dist/memory/write-budget.js.map +1 -0
- package/dist/testing/memory-testkit.d.ts +149 -0
- package/dist/testing/memory-testkit.d.ts.map +1 -0
- package/dist/testing/memory-testkit.js +438 -0
- package/dist/testing/memory-testkit.js.map +1 -0
- package/dist/utils/sync-sleep.d.ts +2 -0
- package/dist/utils/sync-sleep.d.ts.map +1 -0
- package/dist/utils/sync-sleep.js +11 -0
- package/dist/utils/sync-sleep.js.map +1 -0
- package/package.json +56 -0
- package/plugin.json +10 -0
package/dist/entry.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { createMemoryCapability, resolveMemoryMode } from "./capability.js";
|
|
4
|
+
/**
|
|
5
|
+
* Agent-dir config file channel (D-075 S4 fourth batch): the same path the
|
|
6
|
+
* host's pre-split `builtinCapabilityConfigPath(agentDir, "builtin.memory")`
|
|
7
|
+
* resolved for the embedded capability. Hosts that declare `api.host.agentDir`
|
|
8
|
+
* have this file read here and merged over the manifest config — the exact
|
|
9
|
+
* merge-over-defaults semantics the embedded `withCapabilityConfig` channel
|
|
10
|
+
* had. A missing file is the normal unconfigured state; an unreadable file,
|
|
11
|
+
* invalid JSON, or a non-object payload fails the plugin load explicitly
|
|
12
|
+
* (D-028: this plugin only, never the session). A host without `agentDir`
|
|
13
|
+
* loads the plugin with the manifest config alone.
|
|
14
|
+
*/
|
|
15
|
+
const CAPABILITY_CONFIG_RELATIVE_PATH = join("capabilities", "builtin.memory.json");
|
|
16
|
+
/** Reads the agent-dir config channel; `undefined` means "no file" (normal unconfigured state). */
|
|
17
|
+
function readAgentDirConfig(configPath) {
|
|
18
|
+
if (configPath === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
let raw;
|
|
21
|
+
try {
|
|
22
|
+
raw = readFileSync(configPath, "utf8");
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error.code === "ENOENT")
|
|
26
|
+
return undefined;
|
|
27
|
+
throw new Error(`Failed to read ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
28
|
+
}
|
|
29
|
+
let parsed;
|
|
30
|
+
try {
|
|
31
|
+
parsed = JSON.parse(raw);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
throw new Error(`${configPath} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
35
|
+
}
|
|
36
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
37
|
+
throw new Error(`${configPath} must contain a JSON object`);
|
|
38
|
+
}
|
|
39
|
+
return parsed;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The string-valued config key or `undefined`. A present-but-non-string value
|
|
43
|
+
* (only possible through the raw JSON channels) is reported by the caller —
|
|
44
|
+
* never silently coerced.
|
|
45
|
+
*/
|
|
46
|
+
function stringConfigValue(config, key) {
|
|
47
|
+
const raw = config[key];
|
|
48
|
+
if (raw === undefined)
|
|
49
|
+
return {};
|
|
50
|
+
if (typeof raw === "string")
|
|
51
|
+
return { value: raw };
|
|
52
|
+
return { invalid: raw };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Default-export factory consumed by the host's `loadCapabilityPluginFromManifest`.
|
|
56
|
+
*
|
|
57
|
+
* Service mapping (D-075 S4 fourth batch): the host facts the embedded
|
|
58
|
+
* capability received as constructor context are assembled here from
|
|
59
|
+
* `api.host` and the merged config —
|
|
60
|
+
* - `api.host.agentDir` roots the durable ledger (`<agentDir>/memory/`) and
|
|
61
|
+
* opens the agent-dir config channel;
|
|
62
|
+
* - `api.host.memoryStorage` → the A1/A2 storage injection face
|
|
63
|
+
* (`MemoryStorageComponentsV1`: store/ledger factories plus optional
|
|
64
|
+
* vector components); absent = the plugin's built-in local implementation;
|
|
65
|
+
* - mode resolution reuses the migrated `resolveMemoryMode` with the same
|
|
66
|
+
* precedence the host assembler had: explicit config `memoryMode` (host
|
|
67
|
+
* configOverride injects it, wiring lands with the host cutover batch) →
|
|
68
|
+
* env `AGENT_FORGE_MEMORY_MODE` (read here via the resolver's process.env
|
|
69
|
+
* default) → config `memorySettingsMode` (the persistent settings face) →
|
|
70
|
+
* legacy alias → off.
|
|
71
|
+
*
|
|
72
|
+
* A resolved mode of "off", or a host without `agentDir`, registers NOTHING
|
|
73
|
+
* (zero tools, zero commands, zero events) with one debug log line — the same
|
|
74
|
+
* tool-face-absent semantics the embedded capability had for mode=off; the
|
|
75
|
+
* factory never throws for a disabled configuration.
|
|
76
|
+
*
|
|
77
|
+
* `vectorComponents` (the test-only seam) deliberately does NOT ride the
|
|
78
|
+
* entry: it stays on the library face (`createCapabilityWithVectorComponents`)
|
|
79
|
+
* for embedders and tests, per the deterministic-mock gate.
|
|
80
|
+
*
|
|
81
|
+
* The factory returns its registrations as an array; the wrapper disposes them
|
|
82
|
+
* in reverse order on plugin teardown — the same disposal semantics as the
|
|
83
|
+
* host's embedded `syncRegistrationFactory`.
|
|
84
|
+
*/
|
|
85
|
+
export default function createMemoryPluginEntry(api) {
|
|
86
|
+
const agentDir = api.host.agentDir;
|
|
87
|
+
if (agentDir === undefined) {
|
|
88
|
+
// No agent dir = no durable memory location; the tool face stays absent
|
|
89
|
+
// instead of failing the plugin (the host opted out of disk state).
|
|
90
|
+
api.logger?.debug("memory plugin idle: host declares no agentDir");
|
|
91
|
+
return { dispose: () => { } };
|
|
92
|
+
}
|
|
93
|
+
const configPath = join(agentDir, CAPABILITY_CONFIG_RELATIVE_PATH);
|
|
94
|
+
const fileConfig = readAgentDirConfig(configPath);
|
|
95
|
+
const effectiveConfig = fileConfig === undefined ? api.config : { ...api.config, ...fileConfig };
|
|
96
|
+
const mode = stringConfigValue(effectiveConfig, "memoryMode");
|
|
97
|
+
if (mode.invalid !== undefined) {
|
|
98
|
+
api.logger?.warn(`memoryMode config must be a string; memory stays off (fail-closed)`);
|
|
99
|
+
}
|
|
100
|
+
const settingsMode = stringConfigValue(effectiveConfig, "memorySettingsMode");
|
|
101
|
+
if (settingsMode.invalid !== undefined) {
|
|
102
|
+
api.logger?.warn(`memorySettingsMode config must be a string; memory stays off (fail-closed)`);
|
|
103
|
+
}
|
|
104
|
+
// resolveMemoryMode's env parameter defaults to process.env; passing the
|
|
105
|
+
// default explicitly keeps the entry's "env read here" contract visible.
|
|
106
|
+
const resolution = resolveMemoryMode(mode.value, process.env, settingsMode.value);
|
|
107
|
+
if (resolution.invalidEnvValue !== undefined) {
|
|
108
|
+
api.logger?.warn(`invalid AGENT_FORGE_MEMORY_MODE value "${resolution.invalidEnvValue}"; memory stays off (fail-closed)`);
|
|
109
|
+
}
|
|
110
|
+
if (resolution.invalidSettingsValue !== undefined) {
|
|
111
|
+
api.logger?.warn(`invalid memory.mode setting "${resolution.invalidSettingsValue}"; memory stays off (fail-closed)`);
|
|
112
|
+
}
|
|
113
|
+
if (resolution.mode === "off") {
|
|
114
|
+
api.logger?.debug("memory plugin idle (mode=off)");
|
|
115
|
+
return { dispose: () => { } };
|
|
116
|
+
}
|
|
117
|
+
const memoryStorage = api.host.memoryStorage;
|
|
118
|
+
// 宿主重依赖路径注入 (安装店可见性): 装载器把 hostDependencyPaths 并进
|
|
119
|
+
// manifest config (specifier → 宿主 node_modules 入口绝对路径)。onnxruntime
|
|
120
|
+
// 供 native 预检; lancedb/transformers 供运行时按绝对路径原生 import (安装店
|
|
121
|
+
// 副本的裸 import 无法解析)。非字符串值忽略 (fail-closed 由消费侧兜底)。
|
|
122
|
+
const dependencyPaths = effectiveConfig.hostDependencyPaths;
|
|
123
|
+
const dependencyPathMap = Array.isArray(dependencyPaths) || typeof dependencyPaths !== "object" || dependencyPaths === null
|
|
124
|
+
? undefined
|
|
125
|
+
: dependencyPaths;
|
|
126
|
+
const stringDependencyPath = (key) => {
|
|
127
|
+
const value = dependencyPathMap?.[key];
|
|
128
|
+
return typeof value === "string" ? value : undefined;
|
|
129
|
+
};
|
|
130
|
+
const hostOnnxRuntimeEntryPath = stringDependencyPath("onnxruntimeNode");
|
|
131
|
+
const hostLancedbEntryPath = stringDependencyPath("lancedb");
|
|
132
|
+
const hostTransformersEntryPath = stringDependencyPath("transformers");
|
|
133
|
+
const registrations = createMemoryCapability(api, {
|
|
134
|
+
agentDir,
|
|
135
|
+
...(hostOnnxRuntimeEntryPath === undefined ? {} : { hostOnnxRuntimeEntryPath }),
|
|
136
|
+
...(hostLancedbEntryPath === undefined ? {} : { hostLancedbEntryPath }),
|
|
137
|
+
...(hostTransformersEntryPath === undefined ? {} : { hostTransformersEntryPath }),
|
|
138
|
+
memory: {
|
|
139
|
+
mode: resolution.mode,
|
|
140
|
+
...(settingsMode.value === undefined ? {} : { settingsMode: settingsMode.value }),
|
|
141
|
+
},
|
|
142
|
+
...(memoryStorage === undefined ? {} : { storage: memoryStorage }),
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
dispose: () => {
|
|
146
|
+
for (const registration of [...registrations].reverse()) {
|
|
147
|
+
// Registration disposal in the capability runtime is synchronous;
|
|
148
|
+
// void consumes the declared Promise possibility of the contract.
|
|
149
|
+
void registration.dispose();
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../src/entry.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { CapabilityAPI, PluginConfig, PluginConfigValue } from \"@agent-forge/plugin-sdk\";\nimport { createMemoryCapability, resolveMemoryMode } from \"./capability.ts\";\n\n/**\n * Agent-dir config file channel (D-075 S4 fourth batch): the same path the\n * host's pre-split `builtinCapabilityConfigPath(agentDir, \"builtin.memory\")`\n * resolved for the embedded capability. Hosts that declare `api.host.agentDir`\n * have this file read here and merged over the manifest config — the exact\n * merge-over-defaults semantics the embedded `withCapabilityConfig` channel\n * had. A missing file is the normal unconfigured state; an unreadable file,\n * invalid JSON, or a non-object payload fails the plugin load explicitly\n * (D-028: this plugin only, never the session). A host without `agentDir`\n * loads the plugin with the manifest config alone.\n */\nconst CAPABILITY_CONFIG_RELATIVE_PATH = join(\"capabilities\", \"builtin.memory.json\");\n\n/** Reads the agent-dir config channel; `undefined` means \"no file\" (normal unconfigured state). */\nfunction readAgentDirConfig(configPath: string | undefined): PluginConfig | undefined {\n\tif (configPath === undefined) return undefined;\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(configPath, \"utf8\");\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n\t\tthrow new Error(`Failed to read ${configPath}: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (error) {\n\t\tthrow new Error(`${configPath} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\tthrow new Error(`${configPath} must contain a JSON object`);\n\t}\n\treturn parsed as PluginConfig;\n}\n\n/**\n * The string-valued config key or `undefined`. A present-but-non-string value\n * (only possible through the raw JSON channels) is reported by the caller —\n * never silently coerced.\n */\nfunction stringConfigValue(\n\tconfig: PluginConfig,\n\tkey: string,\n): { readonly value?: string; readonly invalid?: PluginConfigValue } {\n\tconst raw = config[key];\n\tif (raw === undefined) return {};\n\tif (typeof raw === \"string\") return { value: raw };\n\treturn { invalid: raw };\n}\n\n/**\n * Default-export factory consumed by the host's `loadCapabilityPluginFromManifest`.\n *\n * Service mapping (D-075 S4 fourth batch): the host facts the embedded\n * capability received as constructor context are assembled here from\n * `api.host` and the merged config —\n * - `api.host.agentDir` roots the durable ledger (`<agentDir>/memory/`) and\n * opens the agent-dir config channel;\n * - `api.host.memoryStorage` → the A1/A2 storage injection face\n * (`MemoryStorageComponentsV1`: store/ledger factories plus optional\n * vector components); absent = the plugin's built-in local implementation;\n * - mode resolution reuses the migrated `resolveMemoryMode` with the same\n * precedence the host assembler had: explicit config `memoryMode` (host\n * configOverride injects it, wiring lands with the host cutover batch) →\n * env `AGENT_FORGE_MEMORY_MODE` (read here via the resolver's process.env\n * default) → config `memorySettingsMode` (the persistent settings face) →\n * legacy alias → off.\n *\n * A resolved mode of \"off\", or a host without `agentDir`, registers NOTHING\n * (zero tools, zero commands, zero events) with one debug log line — the same\n * tool-face-absent semantics the embedded capability had for mode=off; the\n * factory never throws for a disabled configuration.\n *\n * `vectorComponents` (the test-only seam) deliberately does NOT ride the\n * entry: it stays on the library face (`createCapabilityWithVectorComponents`)\n * for embedders and tests, per the deterministic-mock gate.\n *\n * The factory returns its registrations as an array; the wrapper disposes them\n * in reverse order on plugin teardown — the same disposal semantics as the\n * host's embedded `syncRegistrationFactory`.\n */\nexport default function createMemoryPluginEntry(api: CapabilityAPI): { dispose: () => void } {\n\tconst agentDir = api.host.agentDir;\n\tif (agentDir === undefined) {\n\t\t// No agent dir = no durable memory location; the tool face stays absent\n\t\t// instead of failing the plugin (the host opted out of disk state).\n\t\tapi.logger?.debug(\"memory plugin idle: host declares no agentDir\");\n\t\treturn { dispose: () => {} };\n\t}\n\tconst configPath = join(agentDir, CAPABILITY_CONFIG_RELATIVE_PATH);\n\tconst fileConfig = readAgentDirConfig(configPath);\n\tconst effectiveConfig: PluginConfig = fileConfig === undefined ? api.config : { ...api.config, ...fileConfig };\n\n\tconst mode = stringConfigValue(effectiveConfig, \"memoryMode\");\n\tif (mode.invalid !== undefined) {\n\t\tapi.logger?.warn(`memoryMode config must be a string; memory stays off (fail-closed)`);\n\t}\n\tconst settingsMode = stringConfigValue(effectiveConfig, \"memorySettingsMode\");\n\tif (settingsMode.invalid !== undefined) {\n\t\tapi.logger?.warn(`memorySettingsMode config must be a string; memory stays off (fail-closed)`);\n\t}\n\t// resolveMemoryMode's env parameter defaults to process.env; passing the\n\t// default explicitly keeps the entry's \"env read here\" contract visible.\n\tconst resolution = resolveMemoryMode(mode.value, process.env, settingsMode.value);\n\tif (resolution.invalidEnvValue !== undefined) {\n\t\tapi.logger?.warn(\n\t\t\t`invalid AGENT_FORGE_MEMORY_MODE value \"${resolution.invalidEnvValue}\"; memory stays off (fail-closed)`,\n\t\t);\n\t}\n\tif (resolution.invalidSettingsValue !== undefined) {\n\t\tapi.logger?.warn(\n\t\t\t`invalid memory.mode setting \"${resolution.invalidSettingsValue}\"; memory stays off (fail-closed)`,\n\t\t);\n\t}\n\tif (resolution.mode === \"off\") {\n\t\tapi.logger?.debug(\"memory plugin idle (mode=off)\");\n\t\treturn { dispose: () => {} };\n\t}\n\n\tconst memoryStorage = api.host.memoryStorage;\n\t// 宿主重依赖路径注入 (安装店可见性): 装载器把 hostDependencyPaths 并进\n\t// manifest config (specifier → 宿主 node_modules 入口绝对路径)。onnxruntime\n\t// 供 native 预检; lancedb/transformers 供运行时按绝对路径原生 import (安装店\n\t// 副本的裸 import 无法解析)。非字符串值忽略 (fail-closed 由消费侧兜底)。\n\tconst dependencyPaths = effectiveConfig.hostDependencyPaths;\n\tconst dependencyPathMap: Record<string, PluginConfigValue> | undefined =\n\t\tArray.isArray(dependencyPaths) || typeof dependencyPaths !== \"object\" || dependencyPaths === null\n\t\t\t? undefined\n\t\t\t: (dependencyPaths as Record<string, PluginConfigValue>);\n\tconst stringDependencyPath = (key: string): string | undefined => {\n\t\tconst value = dependencyPathMap?.[key];\n\t\treturn typeof value === \"string\" ? value : undefined;\n\t};\n\tconst hostOnnxRuntimeEntryPath = stringDependencyPath(\"onnxruntimeNode\");\n\tconst hostLancedbEntryPath = stringDependencyPath(\"lancedb\");\n\tconst hostTransformersEntryPath = stringDependencyPath(\"transformers\");\n\tconst registrations = createMemoryCapability(api, {\n\t\tagentDir,\n\t\t...(hostOnnxRuntimeEntryPath === undefined ? {} : { hostOnnxRuntimeEntryPath }),\n\t\t...(hostLancedbEntryPath === undefined ? {} : { hostLancedbEntryPath }),\n\t\t...(hostTransformersEntryPath === undefined ? {} : { hostTransformersEntryPath }),\n\t\tmemory: {\n\t\t\tmode: resolution.mode,\n\t\t\t...(settingsMode.value === undefined ? {} : { settingsMode: settingsMode.value }),\n\t\t},\n\t\t...(memoryStorage === undefined ? {} : { storage: memoryStorage }),\n\t});\n\treturn {\n\t\tdispose: () => {\n\t\t\tfor (const registration of [...registrations].reverse()) {\n\t\t\t\t// Registration disposal in the capability runtime is synchronous;\n\t\t\t\t// void consumes the declared Promise possibility of the contract.\n\t\t\t\tvoid registration.dispose();\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-party memory capability plugin: the model-facing
|
|
3
|
+
* memory_write / memory_recall / memory_list / memory_forget surface over the
|
|
4
|
+
* M5 Memory Foundation, extracted from the host's embedded builtin capability
|
|
5
|
+
* `agent-forge.builtin.memory` (D-075 S4, fourth batch).
|
|
6
|
+
*
|
|
7
|
+
* Library face (`createMemoryCapability(api, context, override?)`) keeps the
|
|
8
|
+
* explicit-context signature for embedders and tests; the plugin entry
|
|
9
|
+
* (`./entry.ts`, manifest-loaded) assembles the same context from the host
|
|
10
|
+
* services (`api.host.agentDir` / `api.host.memoryStorage`) plus the
|
|
11
|
+
* agent-dir config channel. The 29 Foundation/semantic modules and the
|
|
12
|
+
* deterministic memory testkit migrate with the capability and are
|
|
13
|
+
* re-exported here so consumers keep one import face. The memory public
|
|
14
|
+
* contracts (canonical atom family, store/ledger/vector seams,
|
|
15
|
+
* `MemoryStorageComponentsV1`, `MEMORY_RECALL_TOOL_NAME`) live in
|
|
16
|
+
* `@agent-forge/plugin-sdk` and are re-exported through `./capability.ts`.
|
|
17
|
+
*/
|
|
18
|
+
export * from "./capability.ts";
|
|
19
|
+
export * from "./memory/assistant-card.ts";
|
|
20
|
+
export * from "./memory/candidates.ts";
|
|
21
|
+
export * from "./memory/code-memory.ts";
|
|
22
|
+
export * from "./memory/compaction-sequencer.ts";
|
|
23
|
+
export * from "./memory/continuation.ts";
|
|
24
|
+
export * from "./memory/curation.ts";
|
|
25
|
+
export * from "./memory/egress-policy.ts";
|
|
26
|
+
export * from "./memory/embedding-provider.ts";
|
|
27
|
+
export * from "./memory/embedding-reranker.ts";
|
|
28
|
+
export * from "./memory/foundation.ts";
|
|
29
|
+
export * from "./memory/ledger.ts";
|
|
30
|
+
export * from "./memory/lifecycle.ts";
|
|
31
|
+
export * from "./memory/memory-network.ts";
|
|
32
|
+
export * from "./memory/preference-disambiguator.ts";
|
|
33
|
+
export * from "./memory/preference-lifecycle.ts";
|
|
34
|
+
export * from "./memory/preference-promotion.ts";
|
|
35
|
+
export * from "./memory/preference-resolver.ts";
|
|
36
|
+
export * from "./memory/purge.ts";
|
|
37
|
+
export * from "./memory/purge-journal.ts";
|
|
38
|
+
export * from "./memory/recall-agent.ts";
|
|
39
|
+
export * from "./memory/recall-index.ts";
|
|
40
|
+
export * from "./memory/recall-packet.ts";
|
|
41
|
+
export * from "./memory/scheduler.ts";
|
|
42
|
+
export * from "./memory/scheduler-api.ts";
|
|
43
|
+
export * from "./memory/store.ts";
|
|
44
|
+
export * from "./memory/suite-memory.ts";
|
|
45
|
+
export * from "./memory/transfer.ts";
|
|
46
|
+
export * from "./memory/vector-index.ts";
|
|
47
|
+
export * from "./memory/write-budget.ts";
|
|
48
|
+
export * from "./testing/memory-testkit.ts";
|
|
49
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC","sourcesContent":["/**\n * First-party memory capability plugin: the model-facing\n * memory_write / memory_recall / memory_list / memory_forget surface over the\n * M5 Memory Foundation, extracted from the host's embedded builtin capability\n * `agent-forge.builtin.memory` (D-075 S4, fourth batch).\n *\n * Library face (`createMemoryCapability(api, context, override?)`) keeps the\n * explicit-context signature for embedders and tests; the plugin entry\n * (`./entry.ts`, manifest-loaded) assembles the same context from the host\n * services (`api.host.agentDir` / `api.host.memoryStorage`) plus the\n * agent-dir config channel. The 29 Foundation/semantic modules and the\n * deterministic memory testkit migrate with the capability and are\n * re-exported here so consumers keep one import face. The memory public\n * contracts (canonical atom family, store/ledger/vector seams,\n * `MemoryStorageComponentsV1`, `MEMORY_RECALL_TOOL_NAME`) live in\n * `@agent-forge/plugin-sdk` and are re-exported through `./capability.ts`.\n */\n\nexport * from \"./capability.ts\";\nexport * from \"./memory/assistant-card.ts\";\nexport * from \"./memory/candidates.ts\";\nexport * from \"./memory/code-memory.ts\";\nexport * from \"./memory/compaction-sequencer.ts\";\nexport * from \"./memory/continuation.ts\";\nexport * from \"./memory/curation.ts\";\nexport * from \"./memory/egress-policy.ts\";\nexport * from \"./memory/embedding-provider.ts\";\nexport * from \"./memory/embedding-reranker.ts\";\nexport * from \"./memory/foundation.ts\";\nexport * from \"./memory/ledger.ts\";\nexport * from \"./memory/lifecycle.ts\";\nexport * from \"./memory/memory-network.ts\";\nexport * from \"./memory/preference-disambiguator.ts\";\nexport * from \"./memory/preference-lifecycle.ts\";\nexport * from \"./memory/preference-promotion.ts\";\nexport * from \"./memory/preference-resolver.ts\";\nexport * from \"./memory/purge.ts\";\nexport * from \"./memory/purge-journal.ts\";\nexport * from \"./memory/recall-agent.ts\";\nexport * from \"./memory/recall-index.ts\";\nexport * from \"./memory/recall-packet.ts\";\nexport * from \"./memory/scheduler.ts\";\nexport * from \"./memory/scheduler-api.ts\";\nexport * from \"./memory/store.ts\";\nexport * from \"./memory/suite-memory.ts\";\nexport * from \"./memory/transfer.ts\";\nexport * from \"./memory/vector-index.ts\";\nexport * from \"./memory/write-budget.ts\";\nexport * from \"./testing/memory-testkit.ts\";\n"]}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-party memory capability plugin: the model-facing
|
|
3
|
+
* memory_write / memory_recall / memory_list / memory_forget surface over the
|
|
4
|
+
* M5 Memory Foundation, extracted from the host's embedded builtin capability
|
|
5
|
+
* `agent-forge.builtin.memory` (D-075 S4, fourth batch).
|
|
6
|
+
*
|
|
7
|
+
* Library face (`createMemoryCapability(api, context, override?)`) keeps the
|
|
8
|
+
* explicit-context signature for embedders and tests; the plugin entry
|
|
9
|
+
* (`./entry.ts`, manifest-loaded) assembles the same context from the host
|
|
10
|
+
* services (`api.host.agentDir` / `api.host.memoryStorage`) plus the
|
|
11
|
+
* agent-dir config channel. The 29 Foundation/semantic modules and the
|
|
12
|
+
* deterministic memory testkit migrate with the capability and are
|
|
13
|
+
* re-exported here so consumers keep one import face. The memory public
|
|
14
|
+
* contracts (canonical atom family, store/ledger/vector seams,
|
|
15
|
+
* `MemoryStorageComponentsV1`, `MEMORY_RECALL_TOOL_NAME`) live in
|
|
16
|
+
* `@agent-forge/plugin-sdk` and are re-exported through `./capability.ts`.
|
|
17
|
+
*/
|
|
18
|
+
export * from "./capability.js";
|
|
19
|
+
export * from "./memory/assistant-card.js";
|
|
20
|
+
export * from "./memory/candidates.js";
|
|
21
|
+
export * from "./memory/code-memory.js";
|
|
22
|
+
export * from "./memory/compaction-sequencer.js";
|
|
23
|
+
export * from "./memory/continuation.js";
|
|
24
|
+
export * from "./memory/curation.js";
|
|
25
|
+
export * from "./memory/egress-policy.js";
|
|
26
|
+
export * from "./memory/embedding-provider.js";
|
|
27
|
+
export * from "./memory/embedding-reranker.js";
|
|
28
|
+
export * from "./memory/foundation.js";
|
|
29
|
+
export * from "./memory/ledger.js";
|
|
30
|
+
export * from "./memory/lifecycle.js";
|
|
31
|
+
export * from "./memory/memory-network.js";
|
|
32
|
+
export * from "./memory/preference-disambiguator.js";
|
|
33
|
+
export * from "./memory/preference-lifecycle.js";
|
|
34
|
+
export * from "./memory/preference-promotion.js";
|
|
35
|
+
export * from "./memory/preference-resolver.js";
|
|
36
|
+
export * from "./memory/purge.js";
|
|
37
|
+
export * from "./memory/purge-journal.js";
|
|
38
|
+
export * from "./memory/recall-agent.js";
|
|
39
|
+
export * from "./memory/recall-index.js";
|
|
40
|
+
export * from "./memory/recall-packet.js";
|
|
41
|
+
export * from "./memory/scheduler.js";
|
|
42
|
+
export * from "./memory/scheduler-api.js";
|
|
43
|
+
export * from "./memory/store.js";
|
|
44
|
+
export * from "./memory/suite-memory.js";
|
|
45
|
+
export * from "./memory/transfer.js";
|
|
46
|
+
export * from "./memory/vector-index.js";
|
|
47
|
+
export * from "./memory/write-budget.js";
|
|
48
|
+
export * from "./testing/memory-testkit.js";
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC","sourcesContent":["/**\n * First-party memory capability plugin: the model-facing\n * memory_write / memory_recall / memory_list / memory_forget surface over the\n * M5 Memory Foundation, extracted from the host's embedded builtin capability\n * `agent-forge.builtin.memory` (D-075 S4, fourth batch).\n *\n * Library face (`createMemoryCapability(api, context, override?)`) keeps the\n * explicit-context signature for embedders and tests; the plugin entry\n * (`./entry.ts`, manifest-loaded) assembles the same context from the host\n * services (`api.host.agentDir` / `api.host.memoryStorage`) plus the\n * agent-dir config channel. The 29 Foundation/semantic modules and the\n * deterministic memory testkit migrate with the capability and are\n * re-exported here so consumers keep one import face. The memory public\n * contracts (canonical atom family, store/ledger/vector seams,\n * `MemoryStorageComponentsV1`, `MEMORY_RECALL_TOOL_NAME`) live in\n * `@agent-forge/plugin-sdk` and are re-exported through `./capability.ts`.\n */\n\nexport * from \"./capability.ts\";\nexport * from \"./memory/assistant-card.ts\";\nexport * from \"./memory/candidates.ts\";\nexport * from \"./memory/code-memory.ts\";\nexport * from \"./memory/compaction-sequencer.ts\";\nexport * from \"./memory/continuation.ts\";\nexport * from \"./memory/curation.ts\";\nexport * from \"./memory/egress-policy.ts\";\nexport * from \"./memory/embedding-provider.ts\";\nexport * from \"./memory/embedding-reranker.ts\";\nexport * from \"./memory/foundation.ts\";\nexport * from \"./memory/ledger.ts\";\nexport * from \"./memory/lifecycle.ts\";\nexport * from \"./memory/memory-network.ts\";\nexport * from \"./memory/preference-disambiguator.ts\";\nexport * from \"./memory/preference-lifecycle.ts\";\nexport * from \"./memory/preference-promotion.ts\";\nexport * from \"./memory/preference-resolver.ts\";\nexport * from \"./memory/purge.ts\";\nexport * from \"./memory/purge-journal.ts\";\nexport * from \"./memory/recall-agent.ts\";\nexport * from \"./memory/recall-index.ts\";\nexport * from \"./memory/recall-packet.ts\";\nexport * from \"./memory/scheduler.ts\";\nexport * from \"./memory/scheduler-api.ts\";\nexport * from \"./memory/store.ts\";\nexport * from \"./memory/suite-memory.ts\";\nexport * from \"./memory/transfer.ts\";\nexport * from \"./memory/vector-index.ts\";\nexport * from \"./memory/write-budget.ts\";\nexport * from \"./testing/memory-testkit.ts\";\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MemoryAtomV1 } from "./foundation.ts";
|
|
2
|
+
/** Maximum preference lines on one card (轻量注入, never unbounded). */
|
|
3
|
+
export declare const ASSISTANT_PREFERENCE_CARD_MAX_ENTRIES = 8;
|
|
4
|
+
/**
|
|
5
|
+
* Renders the preference atoms as a one-line-per-preference card. Non-
|
|
6
|
+
* preference atoms are ignored; preference atoms without an envelope are
|
|
7
|
+
* impossible (Foundation validation) and are ignored defensively. Promoted
|
|
8
|
+
* user-default preferences carry a ⭐ marker (#4). Deterministic: first
|
|
9
|
+
* `ASSISTANT_PREFERENCE_CARD_MAX_ENTRIES` preferences in input order.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildAssistantPreferenceCard(atoms: readonly MemoryAtomV1[]): string | undefined;
|
|
12
|
+
export interface LoadAssistantPreferenceCardOptions {
|
|
13
|
+
/** Agent home directory root of the durable ledgers (`<agentDir>/memory/`). */
|
|
14
|
+
readonly agentDir: string;
|
|
15
|
+
/** Foundation owner (isolation principal) the memory capability wrote under. */
|
|
16
|
+
readonly owner: string;
|
|
17
|
+
/** The active suite; the card covers its own plus promoted user-default preferences. */
|
|
18
|
+
readonly suiteId: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Host-side injection entry (统一修复轮 A 交付3 接线通道): reads every durable
|
|
22
|
+
* ledger of the owner under `<agentDir>/memory/<owner>/ledger-*.jsonl`, replays
|
|
23
|
+
* the atoms in memory, applies the suite read boundary
|
|
24
|
+
* ({@link memorySuiteVisibility} — the suite's own preferences plus explicitly
|
|
25
|
+
* promoted user-default preferences), and renders the preference card. A
|
|
26
|
+
* missing memory directory (nothing written yet) yields `undefined`; every
|
|
27
|
+
* other IO error propagates. Malformed ledger lines are skipped by the ledger
|
|
28
|
+
* loader itself (损坏容错), never failing the session start.
|
|
29
|
+
*/
|
|
30
|
+
export declare function loadAssistantPreferenceCard(options: LoadAssistantPreferenceCardOptions): string | undefined;
|
|
31
|
+
//# sourceMappingURL=assistant-card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistant-card.d.ts","sourceRoot":"","sources":["../../src/memory/assistant-card.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAIpD,4EAAoE;AACpE,eAAO,MAAM,qCAAqC,IAAI,CAAC;AAkCvD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,GAAG,SAAS,CAU/F;AAED,MAAM,WAAW,kCAAkC;IAClD,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,kCAAkC,GAAG,MAAM,GAAG,SAAS,CAqB3G","sourcesContent":["/**\n * Assistant preference card (统一修复轮 A, 方案系统设计 §6.1) — the lightweight\n * user-profile card injected into the assistant suite's context at session\n * start (召回形态: \"会话开始的轻量注入(用户画像卡)\").\n *\n * Pure formatter: the caller passes the atoms already scoped by the suite read\n * boundary (the suite's own preferences plus explicitly promoted user-default\n * preferences, e.g. `store.listForSuite(...)` results); this module only picks\n * preference atoms and renders at most one line per preference. No content →\n * `undefined`, so hosts can skip the injection entirely instead of emitting an\n * empty section. The output is deterministic: input order, capped entries.\n * The card closes with a priority note (#6): the card is the pre-loaded\n * summary injected at session start, while memory_recall is the live query —\n * the model is told to consult the latter for additional context or recent\n * updates. Promoted user-default preferences (explicitly confirmed, the one\n * cross-suite class) are starred (#4) so the model can tell them apart from\n * suite-local preferences.\n */\nimport { readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { JsonValue } from \"@agent-forge/plugin-sdk\";\nimport type { MemoryAtomV1 } from \"./foundation.ts\";\nimport { memorySuiteVisibility } from \"./foundation.ts\";\nimport { createDurableMemoryLedger } from \"./ledger.ts\";\n\n/** Maximum preference lines on one card (轻量注入, never unbounded). */\nexport const ASSISTANT_PREFERENCE_CARD_MAX_ENTRIES = 8;\n\nconst CARD_HEADER = \"User preference card (from memory):\";\n/**\n * Priority note appended to every card (#6): pre-loaded summary vs live\n * recall. Pinned verbatim by test/memory-assistant-card.test.ts.\n */\nconst CARD_PRELOADED_NOTE =\n\t\"(These preferences are pre-loaded. Use memory_recall to check for additional context or recent updates.)\";\nconst PROMOTED_PREFERENCE_MARKER = \"⭐\";\nconst LEDGER_DIR_NAME = \"memory\";\nconst LEDGER_FILE_PREFIX = \"ledger-\";\nconst LEDGER_FILE_SUFFIX = \".jsonl\";\n\nfunction preferenceValueText(value: JsonValue): string {\n\treturn typeof value === \"string\" ? value : JSON.stringify(value);\n}\n\n/**\n * The explicitly promoted user-default preference marker (#4): the same\n * predicate {@link memorySuiteVisibility} uses for its cross-suite promoted\n * arm — scope.level \"user-default\" WITH `confirmedAt`, set only by the\n * explicit promotion channel. Declared here because the card formatter must\n * distinguish promoted entries without widening the foundation module's\n * exports.\n */\nfunction isPromotedUserDefaultPreference(atom: MemoryAtomV1): boolean {\n\treturn (\n\t\tatom.memoryKind === \"preference\" &&\n\t\tatom.preference?.scope.level === \"user-default\" &&\n\t\tatom.preference.confirmedAt !== undefined\n\t);\n}\n\n/**\n * Renders the preference atoms as a one-line-per-preference card. Non-\n * preference atoms are ignored; preference atoms without an envelope are\n * impossible (Foundation validation) and are ignored defensively. Promoted\n * user-default preferences carry a ⭐ marker (#4). Deterministic: first\n * `ASSISTANT_PREFERENCE_CARD_MAX_ENTRIES` preferences in input order.\n */\nexport function buildAssistantPreferenceCard(atoms: readonly MemoryAtomV1[]): string | undefined {\n\tconst lines: string[] = [];\n\tfor (const atom of atoms) {\n\t\tif (lines.length >= ASSISTANT_PREFERENCE_CARD_MAX_ENTRIES) break;\n\t\tif (atom.memoryKind !== \"preference\" || atom.preference === undefined) continue;\n\t\tconst marker = isPromotedUserDefaultPreference(atom) ? `${PROMOTED_PREFERENCE_MARKER} ` : \"\";\n\t\tlines.push(`- ${marker}[${atom.preference.subject}] ${preferenceValueText(atom.preference.preferredValue)}`);\n\t}\n\tif (lines.length === 0) return undefined;\n\treturn [CARD_HEADER, ...lines, CARD_PRELOADED_NOTE].join(\"\\n\");\n}\n\nexport interface LoadAssistantPreferenceCardOptions {\n\t/** Agent home directory root of the durable ledgers (`<agentDir>/memory/`). */\n\treadonly agentDir: string;\n\t/** Foundation owner (isolation principal) the memory capability wrote under. */\n\treadonly owner: string;\n\t/** The active suite; the card covers its own plus promoted user-default preferences. */\n\treadonly suiteId: string;\n}\n\n/**\n * Host-side injection entry (统一修复轮 A 交付3 接线通道): reads every durable\n * ledger of the owner under `<agentDir>/memory/<owner>/ledger-*.jsonl`, replays\n * the atoms in memory, applies the suite read boundary\n * ({@link memorySuiteVisibility} — the suite's own preferences plus explicitly\n * promoted user-default preferences), and renders the preference card. A\n * missing memory directory (nothing written yet) yields `undefined`; every\n * other IO error propagates. Malformed ledger lines are skipped by the ledger\n * loader itself (损坏容错), never failing the session start.\n */\nexport function loadAssistantPreferenceCard(options: LoadAssistantPreferenceCardOptions): string | undefined {\n\tif (options.owner.trim() === \"\") throw new Error(\"loadAssistantPreferenceCard owner must be a non-empty string\");\n\tif (options.suiteId.trim() === \"\") throw new Error(\"loadAssistantPreferenceCard suiteId must be a non-empty string\");\n\tconst ledgerDir = join(options.agentDir, LEDGER_DIR_NAME, options.owner);\n\tlet files: string[];\n\ttry {\n\t\tfiles = readdirSync(ledgerDir).filter(\n\t\t\t(name) => name.startsWith(LEDGER_FILE_PREFIX) && name.endsWith(LEDGER_FILE_SUFFIX),\n\t\t);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n\t\tthrow error;\n\t}\n\tconst visible: MemoryAtomV1[] = [];\n\tfor (const file of files) {\n\t\tfor (const atom of createDurableMemoryLedger({ path: join(ledgerDir, file) }).load().atoms) {\n\t\t\tif (atom.memoryKind !== \"preference\" || atom.preference === undefined) continue;\n\t\t\tif (memorySuiteVisibility(atom, options.suiteId) === \"visible\") visible.push(atom);\n\t\t}\n\t}\n\treturn buildAssistantPreferenceCard(visible);\n}\n"]}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assistant preference card (统一修复轮 A, 方案系统设计 §6.1) — the lightweight
|
|
3
|
+
* user-profile card injected into the assistant suite's context at session
|
|
4
|
+
* start (召回形态: "会话开始的轻量注入(用户画像卡)").
|
|
5
|
+
*
|
|
6
|
+
* Pure formatter: the caller passes the atoms already scoped by the suite read
|
|
7
|
+
* boundary (the suite's own preferences plus explicitly promoted user-default
|
|
8
|
+
* preferences, e.g. `store.listForSuite(...)` results); this module only picks
|
|
9
|
+
* preference atoms and renders at most one line per preference. No content →
|
|
10
|
+
* `undefined`, so hosts can skip the injection entirely instead of emitting an
|
|
11
|
+
* empty section. The output is deterministic: input order, capped entries.
|
|
12
|
+
* The card closes with a priority note (#6): the card is the pre-loaded
|
|
13
|
+
* summary injected at session start, while memory_recall is the live query —
|
|
14
|
+
* the model is told to consult the latter for additional context or recent
|
|
15
|
+
* updates. Promoted user-default preferences (explicitly confirmed, the one
|
|
16
|
+
* cross-suite class) are starred (#4) so the model can tell them apart from
|
|
17
|
+
* suite-local preferences.
|
|
18
|
+
*/
|
|
19
|
+
import { readdirSync } from "node:fs";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import { memorySuiteVisibility } from "./foundation.js";
|
|
22
|
+
import { createDurableMemoryLedger } from "./ledger.js";
|
|
23
|
+
/** Maximum preference lines on one card (轻量注入, never unbounded). */
|
|
24
|
+
export const ASSISTANT_PREFERENCE_CARD_MAX_ENTRIES = 8;
|
|
25
|
+
const CARD_HEADER = "User preference card (from memory):";
|
|
26
|
+
/**
|
|
27
|
+
* Priority note appended to every card (#6): pre-loaded summary vs live
|
|
28
|
+
* recall. Pinned verbatim by test/memory-assistant-card.test.ts.
|
|
29
|
+
*/
|
|
30
|
+
const CARD_PRELOADED_NOTE = "(These preferences are pre-loaded. Use memory_recall to check for additional context or recent updates.)";
|
|
31
|
+
const PROMOTED_PREFERENCE_MARKER = "⭐";
|
|
32
|
+
const LEDGER_DIR_NAME = "memory";
|
|
33
|
+
const LEDGER_FILE_PREFIX = "ledger-";
|
|
34
|
+
const LEDGER_FILE_SUFFIX = ".jsonl";
|
|
35
|
+
function preferenceValueText(value) {
|
|
36
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The explicitly promoted user-default preference marker (#4): the same
|
|
40
|
+
* predicate {@link memorySuiteVisibility} uses for its cross-suite promoted
|
|
41
|
+
* arm — scope.level "user-default" WITH `confirmedAt`, set only by the
|
|
42
|
+
* explicit promotion channel. Declared here because the card formatter must
|
|
43
|
+
* distinguish promoted entries without widening the foundation module's
|
|
44
|
+
* exports.
|
|
45
|
+
*/
|
|
46
|
+
function isPromotedUserDefaultPreference(atom) {
|
|
47
|
+
return (atom.memoryKind === "preference" &&
|
|
48
|
+
atom.preference?.scope.level === "user-default" &&
|
|
49
|
+
atom.preference.confirmedAt !== undefined);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Renders the preference atoms as a one-line-per-preference card. Non-
|
|
53
|
+
* preference atoms are ignored; preference atoms without an envelope are
|
|
54
|
+
* impossible (Foundation validation) and are ignored defensively. Promoted
|
|
55
|
+
* user-default preferences carry a ⭐ marker (#4). Deterministic: first
|
|
56
|
+
* `ASSISTANT_PREFERENCE_CARD_MAX_ENTRIES` preferences in input order.
|
|
57
|
+
*/
|
|
58
|
+
export function buildAssistantPreferenceCard(atoms) {
|
|
59
|
+
const lines = [];
|
|
60
|
+
for (const atom of atoms) {
|
|
61
|
+
if (lines.length >= ASSISTANT_PREFERENCE_CARD_MAX_ENTRIES)
|
|
62
|
+
break;
|
|
63
|
+
if (atom.memoryKind !== "preference" || atom.preference === undefined)
|
|
64
|
+
continue;
|
|
65
|
+
const marker = isPromotedUserDefaultPreference(atom) ? `${PROMOTED_PREFERENCE_MARKER} ` : "";
|
|
66
|
+
lines.push(`- ${marker}[${atom.preference.subject}] ${preferenceValueText(atom.preference.preferredValue)}`);
|
|
67
|
+
}
|
|
68
|
+
if (lines.length === 0)
|
|
69
|
+
return undefined;
|
|
70
|
+
return [CARD_HEADER, ...lines, CARD_PRELOADED_NOTE].join("\n");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Host-side injection entry (统一修复轮 A 交付3 接线通道): reads every durable
|
|
74
|
+
* ledger of the owner under `<agentDir>/memory/<owner>/ledger-*.jsonl`, replays
|
|
75
|
+
* the atoms in memory, applies the suite read boundary
|
|
76
|
+
* ({@link memorySuiteVisibility} — the suite's own preferences plus explicitly
|
|
77
|
+
* promoted user-default preferences), and renders the preference card. A
|
|
78
|
+
* missing memory directory (nothing written yet) yields `undefined`; every
|
|
79
|
+
* other IO error propagates. Malformed ledger lines are skipped by the ledger
|
|
80
|
+
* loader itself (损坏容错), never failing the session start.
|
|
81
|
+
*/
|
|
82
|
+
export function loadAssistantPreferenceCard(options) {
|
|
83
|
+
if (options.owner.trim() === "")
|
|
84
|
+
throw new Error("loadAssistantPreferenceCard owner must be a non-empty string");
|
|
85
|
+
if (options.suiteId.trim() === "")
|
|
86
|
+
throw new Error("loadAssistantPreferenceCard suiteId must be a non-empty string");
|
|
87
|
+
const ledgerDir = join(options.agentDir, LEDGER_DIR_NAME, options.owner);
|
|
88
|
+
let files;
|
|
89
|
+
try {
|
|
90
|
+
files = readdirSync(ledgerDir).filter((name) => name.startsWith(LEDGER_FILE_PREFIX) && name.endsWith(LEDGER_FILE_SUFFIX));
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (error.code === "ENOENT")
|
|
94
|
+
return undefined;
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
const visible = [];
|
|
98
|
+
for (const file of files) {
|
|
99
|
+
for (const atom of createDurableMemoryLedger({ path: join(ledgerDir, file) }).load().atoms) {
|
|
100
|
+
if (atom.memoryKind !== "preference" || atom.preference === undefined)
|
|
101
|
+
continue;
|
|
102
|
+
if (memorySuiteVisibility(atom, options.suiteId) === "visible")
|
|
103
|
+
visible.push(atom);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return buildAssistantPreferenceCard(visible);
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=assistant-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistant-card.js","sourceRoot":"","sources":["../../src/memory/assistant-card.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Assistant preference card (统一修复轮 A, 方案系统设计 §6.1) — the lightweight\n * user-profile card injected into the assistant suite's context at session\n * start (召回形态: \"会话开始的轻量注入(用户画像卡)\").\n *\n * Pure formatter: the caller passes the atoms already scoped by the suite read\n * boundary (the suite's own preferences plus explicitly promoted user-default\n * preferences, e.g. `store.listForSuite(...)` results); this module only picks\n * preference atoms and renders at most one line per preference. No content →\n * `undefined`, so hosts can skip the injection entirely instead of emitting an\n * empty section. The output is deterministic: input order, capped entries.\n * The card closes with a priority note (#6): the card is the pre-loaded\n * summary injected at session start, while memory_recall is the live query —\n * the model is told to consult the latter for additional context or recent\n * updates. Promoted user-default preferences (explicitly confirmed, the one\n * cross-suite class) are starred (#4) so the model can tell them apart from\n * suite-local preferences.\n */\nimport { readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { JsonValue } from \"@agent-forge/plugin-sdk\";\nimport type { MemoryAtomV1 } from \"./foundation.ts\";\nimport { memorySuiteVisibility } from \"./foundation.ts\";\nimport { createDurableMemoryLedger } from \"./ledger.ts\";\n\n/** Maximum preference lines on one card (轻量注入, never unbounded). */\nexport const ASSISTANT_PREFERENCE_CARD_MAX_ENTRIES = 8;\n\nconst CARD_HEADER = \"User preference card (from memory):\";\n/**\n * Priority note appended to every card (#6): pre-loaded summary vs live\n * recall. Pinned verbatim by test/memory-assistant-card.test.ts.\n */\nconst CARD_PRELOADED_NOTE =\n\t\"(These preferences are pre-loaded. Use memory_recall to check for additional context or recent updates.)\";\nconst PROMOTED_PREFERENCE_MARKER = \"⭐\";\nconst LEDGER_DIR_NAME = \"memory\";\nconst LEDGER_FILE_PREFIX = \"ledger-\";\nconst LEDGER_FILE_SUFFIX = \".jsonl\";\n\nfunction preferenceValueText(value: JsonValue): string {\n\treturn typeof value === \"string\" ? value : JSON.stringify(value);\n}\n\n/**\n * The explicitly promoted user-default preference marker (#4): the same\n * predicate {@link memorySuiteVisibility} uses for its cross-suite promoted\n * arm — scope.level \"user-default\" WITH `confirmedAt`, set only by the\n * explicit promotion channel. Declared here because the card formatter must\n * distinguish promoted entries without widening the foundation module's\n * exports.\n */\nfunction isPromotedUserDefaultPreference(atom: MemoryAtomV1): boolean {\n\treturn (\n\t\tatom.memoryKind === \"preference\" &&\n\t\tatom.preference?.scope.level === \"user-default\" &&\n\t\tatom.preference.confirmedAt !== undefined\n\t);\n}\n\n/**\n * Renders the preference atoms as a one-line-per-preference card. Non-\n * preference atoms are ignored; preference atoms without an envelope are\n * impossible (Foundation validation) and are ignored defensively. Promoted\n * user-default preferences carry a ⭐ marker (#4). Deterministic: first\n * `ASSISTANT_PREFERENCE_CARD_MAX_ENTRIES` preferences in input order.\n */\nexport function buildAssistantPreferenceCard(atoms: readonly MemoryAtomV1[]): string | undefined {\n\tconst lines: string[] = [];\n\tfor (const atom of atoms) {\n\t\tif (lines.length >= ASSISTANT_PREFERENCE_CARD_MAX_ENTRIES) break;\n\t\tif (atom.memoryKind !== \"preference\" || atom.preference === undefined) continue;\n\t\tconst marker = isPromotedUserDefaultPreference(atom) ? `${PROMOTED_PREFERENCE_MARKER} ` : \"\";\n\t\tlines.push(`- ${marker}[${atom.preference.subject}] ${preferenceValueText(atom.preference.preferredValue)}`);\n\t}\n\tif (lines.length === 0) return undefined;\n\treturn [CARD_HEADER, ...lines, CARD_PRELOADED_NOTE].join(\"\\n\");\n}\n\nexport interface LoadAssistantPreferenceCardOptions {\n\t/** Agent home directory root of the durable ledgers (`<agentDir>/memory/`). */\n\treadonly agentDir: string;\n\t/** Foundation owner (isolation principal) the memory capability wrote under. */\n\treadonly owner: string;\n\t/** The active suite; the card covers its own plus promoted user-default preferences. */\n\treadonly suiteId: string;\n}\n\n/**\n * Host-side injection entry (统一修复轮 A 交付3 接线通道): reads every durable\n * ledger of the owner under `<agentDir>/memory/<owner>/ledger-*.jsonl`, replays\n * the atoms in memory, applies the suite read boundary\n * ({@link memorySuiteVisibility} — the suite's own preferences plus explicitly\n * promoted user-default preferences), and renders the preference card. A\n * missing memory directory (nothing written yet) yields `undefined`; every\n * other IO error propagates. Malformed ledger lines are skipped by the ledger\n * loader itself (损坏容错), never failing the session start.\n */\nexport function loadAssistantPreferenceCard(options: LoadAssistantPreferenceCardOptions): string | undefined {\n\tif (options.owner.trim() === \"\") throw new Error(\"loadAssistantPreferenceCard owner must be a non-empty string\");\n\tif (options.suiteId.trim() === \"\") throw new Error(\"loadAssistantPreferenceCard suiteId must be a non-empty string\");\n\tconst ledgerDir = join(options.agentDir, LEDGER_DIR_NAME, options.owner);\n\tlet files: string[];\n\ttry {\n\t\tfiles = readdirSync(ledgerDir).filter(\n\t\t\t(name) => name.startsWith(LEDGER_FILE_PREFIX) && name.endsWith(LEDGER_FILE_SUFFIX),\n\t\t);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n\t\tthrow error;\n\t}\n\tconst visible: MemoryAtomV1[] = [];\n\tfor (const file of files) {\n\t\tfor (const atom of createDurableMemoryLedger({ path: join(ledgerDir, file) }).load().atoms) {\n\t\t\tif (atom.memoryKind !== \"preference\" || atom.preference === undefined) continue;\n\t\t\tif (memorySuiteVisibility(atom, options.suiteId) === \"visible\") visible.push(atom);\n\t\t}\n\t}\n\treturn buildAssistantPreferenceCard(visible);\n}\n"]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Foundation — minimal observation/candidate/commit state machine
|
|
3
|
+
* (1C.2).
|
|
4
|
+
*
|
|
5
|
+
* The Foundation validates sources, revisions, idempotency, and structural
|
|
6
|
+
* state only. Accept/reject are public state transitions recorded in an
|
|
7
|
+
* immutable transition log; nothing is silently dropped. Idempotency identity
|
|
8
|
+
* is `(owner, observationId)`: re-observing the same key returns the existing
|
|
9
|
+
* record and never duplicates a commit.
|
|
10
|
+
*
|
|
11
|
+
* States: observed → candidate → committed, or → rejected (terminal, with
|
|
12
|
+
* reason). Lifecycle beyond commit (decay/archive/purge) belongs to the
|
|
13
|
+
* lifecycle manager slices.
|
|
14
|
+
*/
|
|
15
|
+
import type { JsonValue } from "@agent-forge/plugin-sdk";
|
|
16
|
+
import type { MemoryAtomV1, MemoryScopeV1 } from "./foundation.ts";
|
|
17
|
+
import type { CommittedMemoryV1, MemoryStoreV1 } from "./store.ts";
|
|
18
|
+
export type MemoryCandidateStateV1 = "observed" | "candidate" | "committed" | "rejected";
|
|
19
|
+
export interface MemoryObservationInputV1<TPayload extends JsonValue = JsonValue> {
|
|
20
|
+
readonly owner: string;
|
|
21
|
+
readonly observationId: string;
|
|
22
|
+
/** The unvalidated draft atom; its `observationId` field must match the observation. */
|
|
23
|
+
readonly draft: MemoryAtomV1<TPayload>;
|
|
24
|
+
}
|
|
25
|
+
export interface MemoryStateTransitionV1 {
|
|
26
|
+
readonly from: MemoryCandidateStateV1 | null;
|
|
27
|
+
readonly to: MemoryCandidateStateV1;
|
|
28
|
+
readonly at: number;
|
|
29
|
+
readonly reason?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface MemoryCandidateRecordV1<TPayload extends JsonValue = JsonValue> {
|
|
32
|
+
readonly key: string;
|
|
33
|
+
readonly owner: string;
|
|
34
|
+
readonly observationId: string;
|
|
35
|
+
readonly state: MemoryCandidateStateV1;
|
|
36
|
+
/** Set from `candidate` onward: the validated, frozen draft. */
|
|
37
|
+
readonly atom?: MemoryAtomV1<TPayload>;
|
|
38
|
+
/** Set once committed: the store-assigned record. */
|
|
39
|
+
readonly committed?: CommittedMemoryV1<TPayload>;
|
|
40
|
+
/** Set when rejected: the structural reason the draft failed. */
|
|
41
|
+
readonly rejectedReason?: string;
|
|
42
|
+
readonly transitions: readonly MemoryStateTransitionV1[];
|
|
43
|
+
}
|
|
44
|
+
export interface MemoryObserveResultV1<TPayload extends JsonValue = JsonValue> {
|
|
45
|
+
readonly record: MemoryCandidateRecordV1<TPayload>;
|
|
46
|
+
/** True when the (owner, observationId) identity already existed. */
|
|
47
|
+
readonly deduped: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface MemoryCandidateMachineV1 {
|
|
50
|
+
observe<TPayload extends JsonValue = JsonValue>(input: MemoryObservationInputV1<TPayload>): MemoryObserveResultV1<TPayload>;
|
|
51
|
+
/** Promotes a candidate into the Foundation ledger (1C.1b store). */
|
|
52
|
+
accept(observationId: string, owner: string, options?: {
|
|
53
|
+
readonly retentionModeId?: string;
|
|
54
|
+
}): CommittedMemoryV1;
|
|
55
|
+
/** Explicitly rejects a candidate; the reason becomes part of the audit log. */
|
|
56
|
+
reject(observationId: string, owner: string, reason: string): MemoryCandidateRecordV1;
|
|
57
|
+
stateOf(observationId: string, owner: string): MemoryCandidateRecordV1 | undefined;
|
|
58
|
+
}
|
|
59
|
+
/** Creates the deterministic candidate state machine over a 1C.1b store. */
|
|
60
|
+
export declare function createMemoryCandidateMachine(options: {
|
|
61
|
+
readonly store: MemoryStoreV1;
|
|
62
|
+
readonly now?: () => number;
|
|
63
|
+
}): MemoryCandidateMachineV1;
|
|
64
|
+
export type { MemoryScopeV1 };
|
|
65
|
+
//# sourceMappingURL=candidates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"candidates.d.ts","sourceRoot":"","sources":["../../src/memory/candidates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEnE,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEnE,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAC;AAEzF,MAAM,WAAW,wBAAwB,CAAC,QAAQ,SAAS,SAAS,GAAG,SAAS;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,wFAAwF;IACxF,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,IAAI,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC7C,QAAQ,CAAC,EAAE,EAAE,sBAAsB,CAAC;IACpC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,uBAAuB,CAAC,QAAQ,SAAS,SAAS,GAAG,SAAS;IAC9E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAC;IACvC,gEAAgE;IAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IACvC,qDAAqD;IACrD,QAAQ,CAAC,SAAS,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,SAAS,uBAAuB,EAAE,CAAC;CACzD;AAED,MAAM,WAAW,qBAAqB,CAAC,QAAQ,SAAS,SAAS,GAAG,SAAS;IAC5E,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IACnD,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACxC,OAAO,CAAC,QAAQ,SAAS,SAAS,GAAG,SAAS,EAC7C,KAAK,EAAE,wBAAwB,CAAC,QAAQ,CAAC,GACvC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IACnC,qEAAqE;IACrE,MAAM,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,iBAAiB,CAAC;IACjH,gFAAgF;IAChF,MAAM,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,uBAAuB,CAAC;IACtF,OAAO,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CAAC;CACnF;AAED,4EAA4E;AAC5E,wBAAgB,4BAA4B,CAAC,OAAO,EAAE;IACrD,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC5B,GAAG,wBAAwB,CA2G3B;AAMD,YAAY,EAAE,aAAa,EAAE,CAAC","sourcesContent":["/**\n * Memory Foundation — minimal observation/candidate/commit state machine\n * (1C.2).\n *\n * The Foundation validates sources, revisions, idempotency, and structural\n * state only. Accept/reject are public state transitions recorded in an\n * immutable transition log; nothing is silently dropped. Idempotency identity\n * is `(owner, observationId)`: re-observing the same key returns the existing\n * record and never duplicates a commit.\n *\n * States: observed → candidate → committed, or → rejected (terminal, with\n * reason). Lifecycle beyond commit (decay/archive/purge) belongs to the\n * lifecycle manager slices.\n */\nimport type { JsonValue } from \"@agent-forge/plugin-sdk\";\nimport type { MemoryAtomV1, MemoryScopeV1 } from \"./foundation.ts\";\nimport { memoryObservationKey, validateMemoryAtomV1 } from \"./foundation.ts\";\nimport type { CommittedMemoryV1, MemoryStoreV1 } from \"./store.ts\";\n\nexport type MemoryCandidateStateV1 = \"observed\" | \"candidate\" | \"committed\" | \"rejected\";\n\nexport interface MemoryObservationInputV1<TPayload extends JsonValue = JsonValue> {\n\treadonly owner: string;\n\treadonly observationId: string;\n\t/** The unvalidated draft atom; its `observationId` field must match the observation. */\n\treadonly draft: MemoryAtomV1<TPayload>;\n}\n\nexport interface MemoryStateTransitionV1 {\n\treadonly from: MemoryCandidateStateV1 | null;\n\treadonly to: MemoryCandidateStateV1;\n\treadonly at: number;\n\treadonly reason?: string;\n}\n\nexport interface MemoryCandidateRecordV1<TPayload extends JsonValue = JsonValue> {\n\treadonly key: string;\n\treadonly owner: string;\n\treadonly observationId: string;\n\treadonly state: MemoryCandidateStateV1;\n\t/** Set from `candidate` onward: the validated, frozen draft. */\n\treadonly atom?: MemoryAtomV1<TPayload>;\n\t/** Set once committed: the store-assigned record. */\n\treadonly committed?: CommittedMemoryV1<TPayload>;\n\t/** Set when rejected: the structural reason the draft failed. */\n\treadonly rejectedReason?: string;\n\treadonly transitions: readonly MemoryStateTransitionV1[];\n}\n\nexport interface MemoryObserveResultV1<TPayload extends JsonValue = JsonValue> {\n\treadonly record: MemoryCandidateRecordV1<TPayload>;\n\t/** True when the (owner, observationId) identity already existed. */\n\treadonly deduped: boolean;\n}\n\nexport interface MemoryCandidateMachineV1 {\n\tobserve<TPayload extends JsonValue = JsonValue>(\n\t\tinput: MemoryObservationInputV1<TPayload>,\n\t): MemoryObserveResultV1<TPayload>;\n\t/** Promotes a candidate into the Foundation ledger (1C.1b store). */\n\taccept(observationId: string, owner: string, options?: { readonly retentionModeId?: string }): CommittedMemoryV1;\n\t/** Explicitly rejects a candidate; the reason becomes part of the audit log. */\n\treject(observationId: string, owner: string, reason: string): MemoryCandidateRecordV1;\n\tstateOf(observationId: string, owner: string): MemoryCandidateRecordV1 | undefined;\n}\n\n/** Creates the deterministic candidate state machine over a 1C.1b store. */\nexport function createMemoryCandidateMachine(options: {\n\treadonly store: MemoryStoreV1;\n\treadonly now?: () => number;\n}): MemoryCandidateMachineV1 {\n\tconst store = options.store;\n\tconst now = options.now ?? (() => Date.now());\n\tconst records = new Map<string, MemoryCandidateRecordV1<JsonValue>>();\n\n\tconst keyOf = (owner: string, observationId: string): string => memoryObservationKey(owner, observationId);\n\n\tconst appendTransition = (\n\t\trecord: MemoryCandidateRecordV1<JsonValue>,\n\t\tto: MemoryCandidateStateV1,\n\t\tpatch: Partial<MemoryCandidateRecordV1<JsonValue>> = {},\n\t\treason?: string,\n\t): MemoryCandidateRecordV1<JsonValue> => {\n\t\tconst transition: MemoryStateTransitionV1 = Object.freeze({\n\t\t\tfrom: record.state,\n\t\t\tto,\n\t\t\tat: now(),\n\t\t\t...(reason === undefined ? {} : { reason }),\n\t\t});\n\t\tconst next: MemoryCandidateRecordV1<JsonValue> = Object.freeze({\n\t\t\t...record,\n\t\t\t...patch,\n\t\t\tstate: to,\n\t\t\ttransitions: Object.freeze([...record.transitions, transition]),\n\t\t});\n\t\trecords.set(record.key, next);\n\t\treturn next;\n\t};\n\n\treturn {\n\t\tobserve<TPayload extends JsonValue = JsonValue>(\n\t\t\tinput: MemoryObservationInputV1<TPayload>,\n\t\t): MemoryObserveResultV1<TPayload> {\n\t\t\tassertNonEmptyString(input.owner, \"observation owner\");\n\t\t\tassertNonEmptyString(input.observationId, \"observation observationId\");\n\t\t\tconst key = keyOf(input.owner, input.observationId);\n\t\t\tconst existing = records.get(key);\n\t\t\tif (existing) return { record: existing as MemoryCandidateRecordV1<TPayload>, deduped: true };\n\n\t\t\tconst at = now();\n\t\t\tconst initial: MemoryCandidateRecordV1<TPayload> = Object.freeze({\n\t\t\t\tkey,\n\t\t\t\towner: input.owner,\n\t\t\t\tobservationId: input.observationId,\n\t\t\t\tstate: \"observed\" as const,\n\t\t\t\ttransitions: Object.freeze([Object.freeze({ from: null, to: \"observed\" as const, at })]),\n\t\t\t});\n\n\t\t\t// Structural gate 1 — identity consistency: the draft must claim the\n\t\t\t// same observationId it is submitted under.\n\t\t\tif (input.draft.observationId !== input.observationId) {\n\t\t\t\tconst rejected = appendTransition(\n\t\t\t\t\tinitial,\n\t\t\t\t\t\"rejected\",\n\t\t\t\t\t{ rejectedReason: \"Draft atom observationId does not match the observation identity\" },\n\t\t\t\t\t\"observation identity mismatch\",\n\t\t\t\t);\n\t\t\t\treturn { record: rejected as MemoryCandidateRecordV1<TPayload>, deduped: false };\n\t\t\t}\n\n\t\t\t// Structural gate 2 — full canonical atom validation (schema, sources,\n\t\t\t// revisions, enums, JSON safety). Failures are explicit rejections.\n\t\t\tlet atom: MemoryAtomV1<TPayload>;\n\t\t\ttry {\n\t\t\t\tatom = validateMemoryAtomV1<TPayload>(input.draft);\n\t\t\t\tif (atom.owner !== input.owner) throw new Error(\"Draft atom owner does not match the observation owner\");\n\t\t\t} catch (error) {\n\t\t\t\t// v8 ignore next -- 校验器只抛 Error 对象\n\t\t\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\t\t\tconst rejected = appendTransition(initial, \"rejected\", { rejectedReason: reason }, reason);\n\t\t\t\treturn { record: rejected as MemoryCandidateRecordV1<TPayload>, deduped: false };\n\t\t\t}\n\n\t\t\tconst candidate = appendTransition(initial, \"candidate\", { atom });\n\t\t\treturn { record: candidate as MemoryCandidateRecordV1<TPayload>, deduped: false };\n\t\t},\n\n\t\taccept(observationId, owner, acceptOptions) {\n\t\t\tconst key = keyOf(owner, observationId);\n\t\t\tconst record = records.get(key);\n\t\t\tif (!record) throw new Error(`Unknown observation: ${observationId}`);\n\t\t\tif (record.state === \"committed\" && record.committed) {\n\t\t\t\t// Idempotent accept: the store commit is never repeated.\n\t\t\t\treturn record.committed;\n\t\t\t}\n\t\t\tif (record.state !== \"candidate\" || !record.atom) {\n\t\t\t\tthrow new Error(`Observation ${observationId} is ${record.state} and cannot be accepted`);\n\t\t\t}\n\t\t\tconst committed = store.commit(record.atom, acceptOptions);\n\t\t\tappendTransition(record, \"committed\", { committed });\n\t\t\treturn committed;\n\t\t},\n\n\t\treject(observationId, owner, reason) {\n\t\t\tassertNonEmptyString(reason, \"reject reason\");\n\t\t\tconst key = keyOf(owner, observationId);\n\t\t\tconst record = records.get(key);\n\t\t\tif (!record) throw new Error(`Unknown observation: ${observationId}`);\n\t\t\tif (record.state === \"committed\") throw new Error(\"A committed memory cannot be rejected\");\n\t\t\tif (record.state === \"rejected\") return record;\n\t\t\treturn appendTransition(record, \"rejected\", { rejectedReason: reason }, reason);\n\t\t},\n\n\t\tstateOf(observationId, owner) {\n\t\t\treturn records.get(keyOf(owner, observationId)) as MemoryCandidateRecordV1 | undefined;\n\t\t},\n\t};\n}\n\nfunction assertNonEmptyString(value: unknown, label: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.trim().length === 0) throw new Error(`${label} must be a non-empty string`);\n}\n\nexport type { MemoryScopeV1 };\n"]}
|