@ai-agent-forge/plugin-memory 0.88.13 → 0.88.14
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/agent-forge.json +1 -1
- package/bin/agent-forge-memory-inference.js +0 -0
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +5 -1
- package/dist/entry.js.map +1 -1
- package/dist/inference-bin.bundle.js +2 -1
- package/dist/memory/enabling.d.ts.map +1 -1
- package/dist/memory/enabling.js +6 -0
- package/dist/memory/enabling.js.map +1 -1
- package/package.json +4 -3
- package/plugin.json +1 -1
package/agent-forge.json
CHANGED
|
File without changes
|
package/dist/entry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../src/entry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAmC,MAAM,yBAAyB,CAAC;AA0D9F;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACvC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAalE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,CAAC,OAAO,UAAU,uBAAuB,CAAC,GAAG,EAAE,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CA8F3F","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { CapabilityAPI, PluginConfig, PluginConfigValue } from \"@agent-forge/plugin-sdk\";\nimport {\n\tcreateMemoryCapability,\n\tMEMORY_CONSENT_DECLINED_VALUE,\n\tmemoryModeSourceIsDefault,\n\tresolveMemoryMode,\n} 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 * 记忆共享推理服务进程的档位解析 (Slice B1): bin 入口没有宿主插件配置面,\n * 解析链 = env `AGENT_FORGE_MEMORY_MODE` (resolver 内建) → agent-dir 配置文件\n * 的 `memorySettingsMode` → 默认 light — 与会话装配同一 resolver, 同一配置\n * 键。off (显式关闭/非法 fail-closed) 时服务不启动 (结构化 error)。\n */\nexport function resolveMemoryServiceMode(\n\tagentDir: string,\n\tenv: NodeJS.ProcessEnv,\n): { readonly mode: \"light\" | \"full\" } | { readonly error: string } {\n\tconst fileConfig = readAgentDirConfig(join(agentDir, CAPABILITY_CONFIG_RELATIVE_PATH));\n\tconst settingsMode = fileConfig === undefined ? undefined : stringConfigValue(fileConfig, \"memorySettingsMode\");\n\tif (settingsMode?.invalid !== undefined) {\n\t\treturn {\n\t\t\terror: \"memorySettingsMode config must be a string; the inference service refuses to start (fail-closed)\",\n\t\t};\n\t}\n\tconst resolution = resolveMemoryMode(undefined, env, settingsMode?.value);\n\tif (resolution.mode === \"off\") {\n\t\treturn { error: \"memory mode resolved to off — the inference service does not start\" };\n\t}\n\treturn { mode: resolution.mode };\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\t// Type-invalid config values fail closed to off as well: they never enter\n\t// the resolution chain, so without this guard they would fall through to\n\t// the default tier (light since the 2026-10-05 flip) and turn the\n\t// \"memory stays off (fail-closed)\" warn above into a lie.\n\tconst resolution =\n\t\tmode.invalid !== undefined || settingsMode.invalid !== undefined\n\t\t\t? { mode: \"off\" as const }\n\t\t\t: 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\t// 征询拒绝的持久化 (2026-10-05 用户裁决「拒绝即把记忆改为 off, 想用再显式\n\t// 改回来」): 拒绝标记写在本插件的 agent-dir 配置文件 (memoryConsent 键), 只\n\t// 抑制默认档——env / settings.json 的显式档位仍然生效, 用户日后显式启用时\n\t// 首次使用会再次走到征询门。\n\tconst consent = stringConfigValue(effectiveConfig, \"memoryConsent\");\n\tif (\n\t\tconsent.value === MEMORY_CONSENT_DECLINED_VALUE &&\n\t\tmemoryModeSourceIsDefault(mode.value, process.env, settingsMode.value)\n\t) {\n\t\tapi.logger?.debug(\"memory plugin idle (declined by user; set memory.mode in settings.json to re-enable)\");\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"]}
|
|
1
|
+
{"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../src/entry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAmC,MAAM,yBAAyB,CAAC;AA8D9F;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACvC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAalE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,CAAC,OAAO,UAAU,uBAAuB,CAAC,GAAG,EAAE,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CA8F3F","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { CapabilityAPI, PluginConfig, PluginConfigValue } from \"@agent-forge/plugin-sdk\";\nimport {\n\tcreateMemoryCapability,\n\tMEMORY_CONSENT_DECLINED_VALUE,\n\tmemoryModeSourceIsDefault,\n\tresolveMemoryMode,\n} 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\t// ENOTDIR 与 ENOENT 同义:路径上存在非目录组件,配置文件必然不存在\n\t\t// (例如 agentDir 指向一个文件);按未配置处理,不让配置读取失败阻断\n\t\t// 服务启动。\n\t\tconst code = (error as NodeJS.ErrnoException).code;\n\t\tif (code === \"ENOENT\" || code === \"ENOTDIR\") 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 * 记忆共享推理服务进程的档位解析 (Slice B1): bin 入口没有宿主插件配置面,\n * 解析链 = env `AGENT_FORGE_MEMORY_MODE` (resolver 内建) → agent-dir 配置文件\n * 的 `memorySettingsMode` → 默认 light — 与会话装配同一 resolver, 同一配置\n * 键。off (显式关闭/非法 fail-closed) 时服务不启动 (结构化 error)。\n */\nexport function resolveMemoryServiceMode(\n\tagentDir: string,\n\tenv: NodeJS.ProcessEnv,\n): { readonly mode: \"light\" | \"full\" } | { readonly error: string } {\n\tconst fileConfig = readAgentDirConfig(join(agentDir, CAPABILITY_CONFIG_RELATIVE_PATH));\n\tconst settingsMode = fileConfig === undefined ? undefined : stringConfigValue(fileConfig, \"memorySettingsMode\");\n\tif (settingsMode?.invalid !== undefined) {\n\t\treturn {\n\t\t\terror: \"memorySettingsMode config must be a string; the inference service refuses to start (fail-closed)\",\n\t\t};\n\t}\n\tconst resolution = resolveMemoryMode(undefined, env, settingsMode?.value);\n\tif (resolution.mode === \"off\") {\n\t\treturn { error: \"memory mode resolved to off — the inference service does not start\" };\n\t}\n\treturn { mode: resolution.mode };\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\t// Type-invalid config values fail closed to off as well: they never enter\n\t// the resolution chain, so without this guard they would fall through to\n\t// the default tier (light since the 2026-10-05 flip) and turn the\n\t// \"memory stays off (fail-closed)\" warn above into a lie.\n\tconst resolution =\n\t\tmode.invalid !== undefined || settingsMode.invalid !== undefined\n\t\t\t? { mode: \"off\" as const }\n\t\t\t: 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\t// 征询拒绝的持久化 (2026-10-05 用户裁决「拒绝即把记忆改为 off, 想用再显式\n\t// 改回来」): 拒绝标记写在本插件的 agent-dir 配置文件 (memoryConsent 键), 只\n\t// 抑制默认档——env / settings.json 的显式档位仍然生效, 用户日后显式启用时\n\t// 首次使用会再次走到征询门。\n\tconst consent = stringConfigValue(effectiveConfig, \"memoryConsent\");\n\tif (\n\t\tconsent.value === MEMORY_CONSENT_DECLINED_VALUE &&\n\t\tmemoryModeSourceIsDefault(mode.value, process.env, settingsMode.value)\n\t) {\n\t\tapi.logger?.debug(\"memory plugin idle (declined by user; set memory.mode in settings.json to re-enable)\");\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/entry.js
CHANGED
|
@@ -22,7 +22,11 @@ function readAgentDirConfig(configPath) {
|
|
|
22
22
|
raw = readFileSync(configPath, "utf8");
|
|
23
23
|
}
|
|
24
24
|
catch (error) {
|
|
25
|
-
|
|
25
|
+
// ENOTDIR 与 ENOENT 同义:路径上存在非目录组件,配置文件必然不存在
|
|
26
|
+
// (例如 agentDir 指向一个文件);按未配置处理,不让配置读取失败阻断
|
|
27
|
+
// 服务启动。
|
|
28
|
+
const code = error.code;
|
|
29
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
26
30
|
return undefined;
|
|
27
31
|
throw new Error(`Failed to read ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
28
32
|
}
|
package/dist/entry.js.map
CHANGED
|
@@ -1 +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 {\n\tcreateMemoryCapability,\n\tMEMORY_CONSENT_DECLINED_VALUE,\n\tmemoryModeSourceIsDefault,\n\tresolveMemoryMode,\n} 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 * 记忆共享推理服务进程的档位解析 (Slice B1): bin 入口没有宿主插件配置面,\n * 解析链 = env `AGENT_FORGE_MEMORY_MODE` (resolver 内建) → agent-dir 配置文件\n * 的 `memorySettingsMode` → 默认 light — 与会话装配同一 resolver, 同一配置\n * 键。off (显式关闭/非法 fail-closed) 时服务不启动 (结构化 error)。\n */\nexport function resolveMemoryServiceMode(\n\tagentDir: string,\n\tenv: NodeJS.ProcessEnv,\n): { readonly mode: \"light\" | \"full\" } | { readonly error: string } {\n\tconst fileConfig = readAgentDirConfig(join(agentDir, CAPABILITY_CONFIG_RELATIVE_PATH));\n\tconst settingsMode = fileConfig === undefined ? undefined : stringConfigValue(fileConfig, \"memorySettingsMode\");\n\tif (settingsMode?.invalid !== undefined) {\n\t\treturn {\n\t\t\terror: \"memorySettingsMode config must be a string; the inference service refuses to start (fail-closed)\",\n\t\t};\n\t}\n\tconst resolution = resolveMemoryMode(undefined, env, settingsMode?.value);\n\tif (resolution.mode === \"off\") {\n\t\treturn { error: \"memory mode resolved to off — the inference service does not start\" };\n\t}\n\treturn { mode: resolution.mode };\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\t// Type-invalid config values fail closed to off as well: they never enter\n\t// the resolution chain, so without this guard they would fall through to\n\t// the default tier (light since the 2026-10-05 flip) and turn the\n\t// \"memory stays off (fail-closed)\" warn above into a lie.\n\tconst resolution =\n\t\tmode.invalid !== undefined || settingsMode.invalid !== undefined\n\t\t\t? { mode: \"off\" as const }\n\t\t\t: 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\t// 征询拒绝的持久化 (2026-10-05 用户裁决「拒绝即把记忆改为 off, 想用再显式\n\t// 改回来」): 拒绝标记写在本插件的 agent-dir 配置文件 (memoryConsent 键), 只\n\t// 抑制默认档——env / settings.json 的显式档位仍然生效, 用户日后显式启用时\n\t// 首次使用会再次走到征询门。\n\tconst consent = stringConfigValue(effectiveConfig, \"memoryConsent\");\n\tif (\n\t\tconsent.value === MEMORY_CONSENT_DECLINED_VALUE &&\n\t\tmemoryModeSourceIsDefault(mode.value, process.env, settingsMode.value)\n\t) {\n\t\tapi.logger?.debug(\"memory plugin idle (declined by user; set memory.mode in settings.json to re-enable)\");\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"]}
|
|
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 {\n\tcreateMemoryCapability,\n\tMEMORY_CONSENT_DECLINED_VALUE,\n\tmemoryModeSourceIsDefault,\n\tresolveMemoryMode,\n} 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\t// ENOTDIR 与 ENOENT 同义:路径上存在非目录组件,配置文件必然不存在\n\t\t// (例如 agentDir 指向一个文件);按未配置处理,不让配置读取失败阻断\n\t\t// 服务启动。\n\t\tconst code = (error as NodeJS.ErrnoException).code;\n\t\tif (code === \"ENOENT\" || code === \"ENOTDIR\") 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 * 记忆共享推理服务进程的档位解析 (Slice B1): bin 入口没有宿主插件配置面,\n * 解析链 = env `AGENT_FORGE_MEMORY_MODE` (resolver 内建) → agent-dir 配置文件\n * 的 `memorySettingsMode` → 默认 light — 与会话装配同一 resolver, 同一配置\n * 键。off (显式关闭/非法 fail-closed) 时服务不启动 (结构化 error)。\n */\nexport function resolveMemoryServiceMode(\n\tagentDir: string,\n\tenv: NodeJS.ProcessEnv,\n): { readonly mode: \"light\" | \"full\" } | { readonly error: string } {\n\tconst fileConfig = readAgentDirConfig(join(agentDir, CAPABILITY_CONFIG_RELATIVE_PATH));\n\tconst settingsMode = fileConfig === undefined ? undefined : stringConfigValue(fileConfig, \"memorySettingsMode\");\n\tif (settingsMode?.invalid !== undefined) {\n\t\treturn {\n\t\t\terror: \"memorySettingsMode config must be a string; the inference service refuses to start (fail-closed)\",\n\t\t};\n\t}\n\tconst resolution = resolveMemoryMode(undefined, env, settingsMode?.value);\n\tif (resolution.mode === \"off\") {\n\t\treturn { error: \"memory mode resolved to off — the inference service does not start\" };\n\t}\n\treturn { mode: resolution.mode };\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\t// Type-invalid config values fail closed to off as well: they never enter\n\t// the resolution chain, so without this guard they would fall through to\n\t// the default tier (light since the 2026-10-05 flip) and turn the\n\t// \"memory stays off (fail-closed)\" warn above into a lie.\n\tconst resolution =\n\t\tmode.invalid !== undefined || settingsMode.invalid !== undefined\n\t\t\t? { mode: \"off\" as const }\n\t\t\t: 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\t// 征询拒绝的持久化 (2026-10-05 用户裁决「拒绝即把记忆改为 off, 想用再显式\n\t// 改回来」): 拒绝标记写在本插件的 agent-dir 配置文件 (memoryConsent 键), 只\n\t// 抑制默认档——env / settings.json 的显式档位仍然生效, 用户日后显式启用时\n\t// 首次使用会再次走到征询门。\n\tconst consent = stringConfigValue(effectiveConfig, \"memoryConsent\");\n\tif (\n\t\tconsent.value === MEMORY_CONSENT_DECLINED_VALUE &&\n\t\tmemoryModeSourceIsDefault(mode.value, process.env, settingsMode.value)\n\t) {\n\t\tapi.logger?.debug(\"memory plugin idle (declined by user; set memory.mode in settings.json to re-enable)\");\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"]}
|
|
@@ -11566,7 +11566,8 @@ function readAgentDirConfig(configPath) {
|
|
|
11566
11566
|
try {
|
|
11567
11567
|
raw = readFileSync2(configPath, "utf8");
|
|
11568
11568
|
} catch (error) {
|
|
11569
|
-
|
|
11569
|
+
const code = error.code;
|
|
11570
|
+
if (code === "ENOENT" || code === "ENOTDIR") return void 0;
|
|
11570
11571
|
throw new Error(`Failed to read ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
11571
11572
|
}
|
|
11572
11573
|
let parsed;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enabling.d.ts","sourceRoot":"","sources":["../../src/memory/enabling.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG5D,4FAA+D;AAC/D,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,CAG1F,CAAC;AACF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,CAGzF,CAAC;AACF,eAAO,MAAM,yBAAyB,QAAmB,CAAC;AAI1D,+GAA0D;AAC1D,UAAU,oBAAoB;IAC7B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,0GAAoD;IACpD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC7B;AAED,eAAO,MAAM,mBAAmB,EAAE,oBAIjC,CAAC;AACF,eAAO,MAAM,kBAAkB,EAAE,oBAIhC,CAAC;AAIF,wFAA0C;AAC1C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,GAAG,oBAAoB,CAE5F;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,IAAI,SAAS,MAAM,EAAE,CAO1D;AAsBD,wBAAgB,iBAAiB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAkC5E;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CASzE;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC5C,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,EACpD,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAC1C,MAAM,EAAE,CAeV;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAoB7G;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAyB3G;AAED,6EAA0C;AAC1C,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,GAAG,SAAS,MAAM,EAAE,CAUtG;AAQD,+EAAiD;AACjD,eAAO,MAAM,6BAA6B,aAAa,CAAC;AACxD,yFAA6D;AAC7D,eAAO,MAAM,qCAAqC,QAA8C,CAAC;AAgCjG,0FAAoD;AACpD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAGrF;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMjE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAKtF","sourcesContent":["/**\n * enabling 前置检查、档位预设与镜像源(capability 拆分战役 slice 1):纯函数,\n * 无闭包依赖。\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\";\nimport { statfs } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type { MemoryModeV1 } from \"@agent-forge/plugin-sdk\";\nimport { purgeZeroByteCacheArtifacts } from \"./model-cache-hygiene.ts\";\n\n/** 开启前磁盘余量下限 (设计 §3.2 checking): light ≥ 500MB, full ≥ 2GB。 */\nexport const DISK_HEADROOM_MIN_BYTES: Readonly<Record<Exclude<MemoryModeV1, \"off\">, number>> = {\n\tlight: 500 * 1024 * 1024,\n\tfull: 2 * 1024 * 1024 * 1024,\n};\n/**\n * 模型文件大小下限 (防截断/空文件的 sanity 下限, 非精确体积): 高档 embedding\n * (bge-m3 q8) 数百 MB → 下限 256MB, 精排器 (bge-reranker-base q8) ~280MB →\n * 下限 64MB, 低档 (bge-small-zh q8) ~24MB → 下限 8MB。真实校验由加载冒烟承担。\n */\nexport const MODEL_MIN_WEIGHT_BYTES: Readonly<Record<Exclude<MemoryModeV1, \"off\">, number>> = {\n\tlight: 8 * 1024 * 1024,\n\tfull: 256 * 1024 * 1024,\n};\nexport const RERANKER_MIN_WEIGHT_BYTES = 64 * 1024 * 1024;\n/** 自定义镜像源环境变量 (失败行动清单第一项指向它)。 */\nconst HF_MIRRORS_ENV = \"AGENT_FORGE_HF_MIRRORS\";\n\n/** 两档预设 (实施计划 §2): light = 纯中文强项低配档, full = 跨语言大规模默认档。 */\ninterface MemoryVectorPresetV1 {\n\treadonly embeddingModelId: string;\n\treadonly dimensions: number;\n\t/** 查询侧指令前缀 (bge-small-zh 系查询侧指令由调用方负责添加; 写入侧不加)。 */\n\treadonly queryPrefix: string;\n}\n\nexport const VECTOR_PRESET_LIGHT: MemoryVectorPresetV1 = {\n\tembeddingModelId: \"Xenova/bge-small-zh-v1.5\",\n\tdimensions: 512,\n\tqueryPrefix: \"为这个句子生成表示以用于检索相关文章:\",\n};\nexport const VECTOR_PRESET_FULL: MemoryVectorPresetV1 = {\n\tembeddingModelId: \"Xenova/bge-m3\",\n\tdimensions: 1024,\n\tqueryPrefix: \"\",\n};\n/** 镜像序列 (实施计划 §7): 直连 huggingface.co 超时时回落 hf-mirror.com。 */\nconst VECTOR_REMOTE_HOSTS: readonly string[] = [\"https://huggingface.co\", \"https://hf-mirror.com\"];\n\n/** 档位预设: mode → 模型组合。off 在进入装配前已被门控拦截。 */\nexport function vectorPresetForMode(mode: Exclude<MemoryModeV1, \"off\">): MemoryVectorPresetV1 {\n\treturn mode === \"light\" ? VECTOR_PRESET_LIGHT : VECTOR_PRESET_FULL;\n}\n\n/**\n * agentDir 身份归一化 (2026-10-06 串线修复): 绝对路径解析抹平分隔符与相对段,\n * win32 再 casefold —— 同一目录在 Windows 下的大小写差异 (盘符/路径段) 不得\n * 误判为跨 agent-dir 串线。仅用于身份比对, 不触碰文件系统语义。\n */\nexport function normalizeMemoryAgentDirIdentity(agentDir: string): string {\n\tconst resolvedPath = resolve(agentDir);\n\treturn process.platform === \"win32\" ? resolvedPath.toLowerCase() : resolvedPath;\n}\n\n/**\n * 自定义镜像源 (失败行动清单第一项指向它): env `AGENT_FORGE_HF_MIRRORS` 逗号\n * 分隔 host 列表, 排在默认镜像序列之前优先尝试; 默认序列直连 huggingface.co\n * 超时时回落 hf-mirror.com。\n *\n * env 条目归一化 (2026-10-06 live E2E 修复): 裸主机名补 `https://` — 用户\n * 按行动清单写 `AGENT_FORGE_HF_MIRRORS=hf-mirror.com` 时, 无 scheme 的\n * remoteHost 会让 transformers 的文件请求按相对 URL 解析并在模块内留下\n * 毒化状态, 连后续带 scheme 的镜像重试也崩; 行动清单推荐的自救手段本身\n * 不能是断的。已带 scheme 的条目原样保留, 尾部 `/` 去除。\n */\nexport function remoteHostsForEnabling(): readonly string[] {\n\tconst custom = process.env[HF_MIRRORS_ENV]\n\t\t?.split(\",\")\n\t\t.map((host) => host.trim().replace(/\\/+$/, \"\"))\n\t\t.map((host) => (host === \"\" || host.includes(\"://\") ? host : `https://${host}`))\n\t\t.filter((host) => host !== \"\");\n\treturn custom === undefined || custom.length === 0 ? VECTOR_REMOTE_HOSTS : [...custom, ...VECTOR_REMOTE_HOSTS];\n}\n\n/**\n * 本地检查 1: native .node sidecar 可解析 (设计 §3.2 checking)。布局:\n * onnxruntime-node/bin 下的 napi-vN/<platform>/<arch> 原生模块 — 缺失 = 安装\n * 损坏。容错: 不钉死 napi 版本号, 逐目录扫描。\n *\n * 解析基点双通道: jiti(安装店加载缝)注入的 `require` 走宿主 alias 表\n * (宿主 node_modules 副本, 店内无依赖树); 纯 ESM(源码/工作区直载)下裸\n * `require` 未定义(typeof 探测不抛 ReferenceError), 回落原生 createRequire。\n */\nfunction resolveOnnxRuntimeEntryPath(): string {\n\tif (typeof require === \"function\") {\n\t\ttry {\n\t\t\treturn require.resolve(\"onnxruntime-node\");\n\t\t} catch {\n\t\t\t// jiti 解析失败(alias 未覆盖等)→ 继续走原生解析再报结构化错误。\n\t\t}\n\t}\n\treturn createRequire(import.meta.url).resolve(\"onnxruntime-node\");\n}\n\nexport function nativeBinaryCheck(hostEntryPath?: string): string | undefined {\n\t// 两种失败要区分 (D-075 安装店路径修复后前者成为真实可达路径):\n\t// (a) 宿主未注入入口路径且插件位置自解析不到 — 不是安装损坏, 行动是宿主侧\n\t// 安装重依赖后重启会话, 不是重装插件;\n\t// (b) 路径存在但原生二进制缺失/损坏 — 保留损坏类文案。\n\tlet entryPath: string;\n\tif (hostEntryPath === undefined) {\n\t\ttry {\n\t\t\tentryPath = resolveOnnxRuntimeEntryPath();\n\t\t} catch (error) {\n\t\t\tconst detail = error instanceof Error ? error.message : String(error);\n\t\t\treturn `onnxruntime-node is not provided by the host installation and could not be resolved from the plugin location (${detail}); to enable memory features, install onnxruntime-node in the host project (npm i onnxruntime-node) and restart the session`;\n\t\t}\n\t} else {\n\t\tentryPath = hostEntryPath;\n\t}\n\ttry {\n\t\tlet packageDir = dirname(entryPath);\n\t\twhile (packageDir !== dirname(packageDir) && !existsSync(join(packageDir, \"package.json\"))) {\n\t\t\tpackageDir = dirname(packageDir);\n\t\t}\n\t\tconst binDir = join(packageDir, \"bin\");\n\t\tif (!existsSync(binDir)) return `onnxruntime native binary directory not found: ${binDir}`;\n\t\tfor (const napiEntry of readdirSync(binDir, { withFileTypes: true })) {\n\t\t\tif (!napiEntry.isDirectory() || !napiEntry.name.startsWith(\"napi-v\")) continue;\n\t\t\tconst platformDir = join(binDir, napiEntry.name, process.platform, process.arch);\n\t\t\tif (!existsSync(platformDir)) continue;\n\t\t\tconst binaries = readdirSync(platformDir).filter((file) => file.endsWith(\".node\"));\n\t\t\tif (binaries.length > 0) return undefined;\n\t\t}\n\t\treturn `onnxruntime native binary for ${process.platform}/${process.arch} not found under ${binDir}`;\n\t} catch (error) {\n\t\treturn `onnxruntime native binary could not be resolved (${error instanceof Error ? error.message : String(error)}); the installation looks damaged — reinstall the package`;\n\t}\n}\n\n/**\n * 重依赖自解析默认实现 (missingHeavyDependencyModules 生产接线): 双通道与\n * {@link resolveOnnxRuntimeEntryPath} 一致 — jiti(安装店加载缝)注入的\n * `require` 走宿主 alias 表; 纯 ESM(源码/工作区直载)下回落原生 createRequire。\n */\nexport function resolveHeavyDependencyEntryPath(specifier: string): string {\n\tif (typeof require === \"function\") {\n\t\ttry {\n\t\t\treturn require.resolve(specifier);\n\t\t} catch {\n\t\t\t// jiti 解析失败(alias 未覆盖等)→ 继续走原生解析。\n\t\t}\n\t}\n\treturn createRequire(import.meta.url).resolve(specifier);\n}\n\n/**\n * 本地检查 1b: 重依赖供给 (设计 §3.2 checking)。`@lancedb/lancedb` 与\n * `@huggingface/transformers` 逐个判定 — entries 提供宿主入口路径 = 可用\n * (不触自解析); 未提供时 resolveModule 自解析成功 = 可用, 抛错 = 计入缺失。\n * 返回缺失 specifier 列表 (空 = 通过)。纯函数零 IO; 导出仅供测试 (与宿主侧\n * buildHostDependencyAliases 同一模式), 生产接线传插件位置自解析 — 把加载步\n * 的 \"Cannot find module\" 泛化包装前置成精准缺供文案, 省掉模型下载等前置工作。\n */\nexport function missingHeavyDependencyModules(\n\tentries: { lancedb?: string; transformers?: string },\n\tresolveModule: (specifier: string) => string,\n): string[] {\n\tconst missing: string[] = [];\n\tconst supplies: readonly (readonly [specifier: string, entryPath: string | undefined])[] = [\n\t\t[\"@lancedb/lancedb\", entries.lancedb],\n\t\t[\"@huggingface/transformers\", entries.transformers],\n\t];\n\tfor (const [specifier, entryPath] of supplies) {\n\t\tif (entryPath !== undefined) continue;\n\t\ttry {\n\t\t\tresolveModule(specifier);\n\t\t} catch {\n\t\t\tmissing.push(specifier);\n\t\t}\n\t}\n\treturn missing;\n}\n\n/**\n * transformers.js 缓存布局的容错存在性检查 (设计 §3.2 手动导入路径):\n * `<cacheDir>/<modelId>/` 下需有非空 tokenizer 文件与 `onnx/` 权重文件。\n * 返回 undefined = 检查通过; \"missing\" = 目录/文件缺失或仅剩 0 字节残骸\n * (失败下载产物, 清理后进入下载阶段, 见 model-cache-hygiene); 其他字符串 =\n * 文件存在但可疑 (截断/非空过小), 属失败。\n */\nexport function modelFilesCheck(cacheDir: string, modelId: string, minWeightBytes: number): string | undefined {\n\tconst modelDir = join(cacheDir, modelId);\n\tif (!existsSync(modelDir)) return \"missing\";\n\t// tokenizer 只剩 0 字节文件 = 失败下载残骸 (真 tokenizer.json 不可能为空):\n\t// 清掉按缺失处理可重下; 非空文件不动 (手动导入布局不受影响)。\n\tconst tokenizerNames = [\"tokenizer.json\", \"tokenizer.model\"].filter((name) => existsSync(join(modelDir, name)));\n\tconst hasTokenizer = tokenizerNames.some((name) => statSync(join(modelDir, name)).size > 0);\n\tif (!hasTokenizer) {\n\t\tif (tokenizerNames.length > 0) purgeZeroByteCacheArtifacts(modelDir);\n\t\treturn \"missing\";\n\t}\n\tconst onnxDir = join(modelDir, \"onnx\");\n\tif (!existsSync(onnxDir)) return `onnx weight directory missing under ${modelDir}`;\n\tconst weights = readdirSync(onnxDir).filter((name) => name.includes(\".onnx\"));\n\tif (weights.length === 0) return `no onnx weight file under ${onnxDir}`;\n\tconst largest = Math.max(...weights.map((name) => statSync(join(onnxDir, name)).size));\n\tif (largest < minWeightBytes) {\n\t\treturn `largest onnx weight under ${onnxDir} is ${largest} bytes, below the expected minimum ${minWeightBytes} — the download looks truncated`;\n\t}\n\treturn undefined;\n}\n\n/**\n * 本地检查 3: 磁盘余量 (设计 §3.2 checking, node:fs statfs, 零新依赖)。\n * 以 agentDir 所在卷为准 (cacheDir 同卷创建)。agentDir 可能尚不存在 (全新\n * 机器首启, 下载前无人创建过它 — 2026-10-06 live E2E 实测踩中) — statfs\n * ENOENT 时逐级上溯最近存在的祖先目录, 同卷即同余量; 上溯到根仍失败才按\n * 无法判定报告。\n */\nexport async function diskHeadroomCheck(agentDir: string, minimumBytes: number): Promise<string | undefined> {\n\tlet freeBytes: number | undefined;\n\tlet probe: string | undefined = agentDir;\n\tfor (let attempt = 0; probe !== undefined && attempt < 16; attempt++) {\n\t\ttry {\n\t\t\tconst stats = await statfs(probe);\n\t\t\tfreeBytes = stats.bfree * stats.bsize;\n\t\t\tbreak;\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof Error) || !String(error.message).includes(\"ENOENT\")) {\n\t\t\t\treturn `disk free space could not be determined for ${agentDir} (probing ${probe}: ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t})`;\n\t\t\t}\n\t\t\tconst parent = dirname(probe);\n\t\t\tprobe = parent === probe ? undefined : parent;\n\t\t}\n\t}\n\tif (freeBytes === undefined) {\n\t\treturn `disk free space could not be determined for ${agentDir} (no existing ancestor directory to statfs)`;\n\t}\n\tif (freeBytes < minimumBytes) {\n\t\treturn `disk free space is ${freeBytes} bytes, below the required ${minimumBytes}`;\n\t}\n\treturn undefined;\n}\n\n/** enabling 失败的五选一行动清单 (设计 §3.2 硬性要求)。 */\nexport function enablingActionList(modelCacheDir: string, currentMode: MemoryModeV1): readonly string[] {\n\treturn [\n\t\t`Switch to a reachable mirror: set ${HF_MIRRORS_ENV} to a comma-separated host list (tried before the defaults)`,\n\t\t`Install the heavy dependencies in the host project (npm i onnxruntime-node @lancedb/lancedb @huggingface/transformers) so the host can supply them via hostDependencyPaths, then restart the session`,\n\t\t`Download the models manually and place them under ${modelCacheDir} (layout: <modelId>/tokenizer.json + <modelId>/onnx/model*.onnx)`,\n\t\tcurrentMode === \"full\"\n\t\t\t? 'Downgrade to memory.mode \"light\" (much smaller models)'\n\t\t\t: 'Keep memory.mode \"light\" and retry with a reachable mirror',\n\t\t'Give up: leave memory.mode \"off\" (the default) so the memory family stays unregistered',\n\t];\n}\n\n// ---------------------------------------------------------------------------\n// 征询持久化 (capability 拆分战役 slice 3b 随迁, 征询域): agent-dir 配置文件\n// 的读写与征询拒绝/档位落盘。纯函数, 路径域显式传入; capability.ts 经\n// re-export 维持历史导出面, 向量通道 (vector-channel.ts) 直接 import。\n// ---------------------------------------------------------------------------\n\n/** agent-dir 配置文件里的征询拒绝标记值 (memoryConsent 键)。 */\nexport const MEMORY_CONSENT_DECLINED_VALUE = \"declined\";\n/** memory 插件 agent-dir 配置通道 (entry 读、capability 写, 同一路径)。 */\nexport const MEMORY_AGENT_DIR_CONFIG_RELATIVE_PATH = join(\"capabilities\", \"builtin.memory.json\");\n\nfunction readMemoryAgentDirConfig(agentDir: string): Record<string, unknown> {\n\tconst configPath = join(agentDir, MEMORY_AGENT_DIR_CONFIG_RELATIVE_PATH);\n\tif (existsSync(configPath)) {\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(readFileSync(configPath, \"utf8\"));\n\t\t\tif (parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\t\treturn parsed as Record<string, unknown>;\n\t\t\t}\n\t\t} catch {\n\t\t\t// 既有文件非法时以本次写入为准 (持久化是显式用户动作, 不被脏文件阻塞)。\n\t\t}\n\t}\n\treturn {};\n}\n\n/**\n * 写 agent-dir 配置的原子替换 (2026-10-06 复审 P1-2): temp 文件 + rename —\n * 自动升档引入了无人值守写者, 与其它进程的 /memory off (会删档位键) 读改写\n * 交错时, 直接 writeFileSync 可能留下半截 JSON 让并发读者拿到空配置 (整块\n * 丢键: 抹掉并发 decline 或升档)。rename 在同一目录内原子替换, 读者要么见\n * 旧文件要么见新文件。\n */\nfunction writeMemoryAgentDirConfig(agentDir: string, config: Record<string, unknown>): void {\n\tconst configPath = join(agentDir, MEMORY_AGENT_DIR_CONFIG_RELATIVE_PATH);\n\tmkdirSync(dirname(configPath), { recursive: true });\n\tconst tempPath = `${configPath}.${process.pid}.tmp`;\n\twriteFileSync(tempPath, `${JSON.stringify(config, null, \"\\t\")}\\n`, \"utf8\");\n\trenameSync(tempPath, configPath);\n}\n\n/** agent-dir 配置里用户显式持久化的档位键 (无 / 该键即 undefined)。 */\nexport function readMemorySettingsMode(agentDir: string): \"light\" | \"full\" | undefined {\n\tconst value = readMemoryAgentDirConfig(agentDir).memorySettingsMode;\n\treturn value === \"light\" || value === \"full\" ? value : undefined;\n}\n\n/**\n * 拒绝/跳过的持久化 (2026-10-05 用户裁决): 写入插件自己的 agent-dir 配置文件,\n * 不改写用户 settings.json。语义是「用户对默认档说了不」——仅抑制默认档;\n * 用户日后显式设置 settings.json `memory.mode` (light/full) 时显式值仍然生效。\n * 同时移除 /memory light|full 持久化的档位键, 保证\"先升 full 再 off\"不会留下\n * 一个压过 settings 的陈旧档位。\n */\nexport function writeMemoryConsentDeclined(agentDir: string): void {\n\tconst config = readMemoryAgentDirConfig(agentDir);\n\tconfig.memoryConsent = MEMORY_CONSENT_DECLINED_VALUE;\n\tdelete config.memorySettingsMode;\n\tdelete config.memoryMode;\n\twriteMemoryAgentDirConfig(agentDir, config);\n}\n\n/**\n * 档位升级/降级的持久化 (/memory light|full): 写入插件 agent-dir 配置的\n * `memorySettingsMode` 键——与 settings.json `memory.mode` 同一解析层 (持久\n * 配置面, env 仍可临时覆盖)。插件无 settings.json 写通道 (HostCapabilities\n * 无该面), 本文件键在其上, 即命令是比 settings 更近一次的显式表达。同时清\n * 除拒绝标记: 显式升/降档是最新的用户意图, 压过此前的\"对默认档说不\"。\n */\nexport function writeMemorySettingsMode(agentDir: string, mode: \"light\" | \"full\"): void {\n\tconst config = readMemoryAgentDirConfig(agentDir);\n\tconfig.memorySettingsMode = mode;\n\tdelete config.memoryConsent;\n\twriteMemoryAgentDirConfig(agentDir, config);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"enabling.d.ts","sourceRoot":"","sources":["../../src/memory/enabling.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG5D,4FAA+D;AAC/D,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,CAG1F,CAAC;AACF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,CAGzF,CAAC;AACF,eAAO,MAAM,yBAAyB,QAAmB,CAAC;AAI1D,+GAA0D;AAC1D,UAAU,oBAAoB;IAC7B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,0GAAoD;IACpD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC7B;AAED,eAAO,MAAM,mBAAmB,EAAE,oBAIjC,CAAC;AACF,eAAO,MAAM,kBAAkB,EAAE,oBAIhC,CAAC;AAIF,wFAA0C;AAC1C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,GAAG,oBAAoB,CAE5F;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,IAAI,SAAS,MAAM,EAAE,CAO1D;AAsBD,wBAAgB,iBAAiB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAwC5E;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CASzE;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC5C,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,EACpD,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAC1C,MAAM,EAAE,CAeV;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAoB7G;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAyB3G;AAED,6EAA0C;AAC1C,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,GAAG,SAAS,MAAM,EAAE,CAUtG;AAQD,+EAAiD;AACjD,eAAO,MAAM,6BAA6B,aAAa,CAAC;AACxD,yFAA6D;AAC7D,eAAO,MAAM,qCAAqC,QAA8C,CAAC;AAgCjG,0FAAoD;AACpD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAGrF;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMjE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAKtF","sourcesContent":["/**\n * enabling 前置检查、档位预设与镜像源(capability 拆分战役 slice 1):纯函数,\n * 无闭包依赖。\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\";\nimport { statfs } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type { MemoryModeV1 } from \"@agent-forge/plugin-sdk\";\nimport { purgeZeroByteCacheArtifacts } from \"./model-cache-hygiene.ts\";\n\n/** 开启前磁盘余量下限 (设计 §3.2 checking): light ≥ 500MB, full ≥ 2GB。 */\nexport const DISK_HEADROOM_MIN_BYTES: Readonly<Record<Exclude<MemoryModeV1, \"off\">, number>> = {\n\tlight: 500 * 1024 * 1024,\n\tfull: 2 * 1024 * 1024 * 1024,\n};\n/**\n * 模型文件大小下限 (防截断/空文件的 sanity 下限, 非精确体积): 高档 embedding\n * (bge-m3 q8) 数百 MB → 下限 256MB, 精排器 (bge-reranker-base q8) ~280MB →\n * 下限 64MB, 低档 (bge-small-zh q8) ~24MB → 下限 8MB。真实校验由加载冒烟承担。\n */\nexport const MODEL_MIN_WEIGHT_BYTES: Readonly<Record<Exclude<MemoryModeV1, \"off\">, number>> = {\n\tlight: 8 * 1024 * 1024,\n\tfull: 256 * 1024 * 1024,\n};\nexport const RERANKER_MIN_WEIGHT_BYTES = 64 * 1024 * 1024;\n/** 自定义镜像源环境变量 (失败行动清单第一项指向它)。 */\nconst HF_MIRRORS_ENV = \"AGENT_FORGE_HF_MIRRORS\";\n\n/** 两档预设 (实施计划 §2): light = 纯中文强项低配档, full = 跨语言大规模默认档。 */\ninterface MemoryVectorPresetV1 {\n\treadonly embeddingModelId: string;\n\treadonly dimensions: number;\n\t/** 查询侧指令前缀 (bge-small-zh 系查询侧指令由调用方负责添加; 写入侧不加)。 */\n\treadonly queryPrefix: string;\n}\n\nexport const VECTOR_PRESET_LIGHT: MemoryVectorPresetV1 = {\n\tembeddingModelId: \"Xenova/bge-small-zh-v1.5\",\n\tdimensions: 512,\n\tqueryPrefix: \"为这个句子生成表示以用于检索相关文章:\",\n};\nexport const VECTOR_PRESET_FULL: MemoryVectorPresetV1 = {\n\tembeddingModelId: \"Xenova/bge-m3\",\n\tdimensions: 1024,\n\tqueryPrefix: \"\",\n};\n/** 镜像序列 (实施计划 §7): 直连 huggingface.co 超时时回落 hf-mirror.com。 */\nconst VECTOR_REMOTE_HOSTS: readonly string[] = [\"https://huggingface.co\", \"https://hf-mirror.com\"];\n\n/** 档位预设: mode → 模型组合。off 在进入装配前已被门控拦截。 */\nexport function vectorPresetForMode(mode: Exclude<MemoryModeV1, \"off\">): MemoryVectorPresetV1 {\n\treturn mode === \"light\" ? VECTOR_PRESET_LIGHT : VECTOR_PRESET_FULL;\n}\n\n/**\n * agentDir 身份归一化 (2026-10-06 串线修复): 绝对路径解析抹平分隔符与相对段,\n * win32 再 casefold —— 同一目录在 Windows 下的大小写差异 (盘符/路径段) 不得\n * 误判为跨 agent-dir 串线。仅用于身份比对, 不触碰文件系统语义。\n */\nexport function normalizeMemoryAgentDirIdentity(agentDir: string): string {\n\tconst resolvedPath = resolve(agentDir);\n\treturn process.platform === \"win32\" ? resolvedPath.toLowerCase() : resolvedPath;\n}\n\n/**\n * 自定义镜像源 (失败行动清单第一项指向它): env `AGENT_FORGE_HF_MIRRORS` 逗号\n * 分隔 host 列表, 排在默认镜像序列之前优先尝试; 默认序列直连 huggingface.co\n * 超时时回落 hf-mirror.com。\n *\n * env 条目归一化 (2026-10-06 live E2E 修复): 裸主机名补 `https://` — 用户\n * 按行动清单写 `AGENT_FORGE_HF_MIRRORS=hf-mirror.com` 时, 无 scheme 的\n * remoteHost 会让 transformers 的文件请求按相对 URL 解析并在模块内留下\n * 毒化状态, 连后续带 scheme 的镜像重试也崩; 行动清单推荐的自救手段本身\n * 不能是断的。已带 scheme 的条目原样保留, 尾部 `/` 去除。\n */\nexport function remoteHostsForEnabling(): readonly string[] {\n\tconst custom = process.env[HF_MIRRORS_ENV]\n\t\t?.split(\",\")\n\t\t.map((host) => host.trim().replace(/\\/+$/, \"\"))\n\t\t.map((host) => (host === \"\" || host.includes(\"://\") ? host : `https://${host}`))\n\t\t.filter((host) => host !== \"\");\n\treturn custom === undefined || custom.length === 0 ? VECTOR_REMOTE_HOSTS : [...custom, ...VECTOR_REMOTE_HOSTS];\n}\n\n/**\n * 本地检查 1: native .node sidecar 可解析 (设计 §3.2 checking)。布局:\n * onnxruntime-node/bin 下的 napi-vN/<platform>/<arch> 原生模块 — 缺失 = 安装\n * 损坏。容错: 不钉死 napi 版本号, 逐目录扫描。\n *\n * 解析基点双通道: jiti(安装店加载缝)注入的 `require` 走宿主 alias 表\n * (宿主 node_modules 副本, 店内无依赖树); 纯 ESM(源码/工作区直载)下裸\n * `require` 未定义(typeof 探测不抛 ReferenceError), 回落原生 createRequire。\n */\nfunction resolveOnnxRuntimeEntryPath(): string {\n\tif (typeof require === \"function\") {\n\t\ttry {\n\t\t\treturn require.resolve(\"onnxruntime-node\");\n\t\t} catch {\n\t\t\t// jiti 解析失败(alias 未覆盖等)→ 继续走原生解析再报结构化错误。\n\t\t}\n\t}\n\treturn createRequire(import.meta.url).resolve(\"onnxruntime-node\");\n}\n\nexport function nativeBinaryCheck(hostEntryPath?: string): string | undefined {\n\t// 两种失败要区分 (D-075 安装店路径修复后前者成为真实可达路径):\n\t// (a) 宿主未注入入口路径且插件位置自解析不到 — 不是安装损坏, 行动是宿主侧\n\t// 安装重依赖后重启会话, 不是重装插件;\n\t// (b) 路径存在但原生二进制缺失/损坏 — 保留损坏类文案。\n\tlet entryPath: string;\n\tif (hostEntryPath === undefined) {\n\t\ttry {\n\t\t\tentryPath = resolveOnnxRuntimeEntryPath();\n\t\t} catch (error) {\n\t\t\tconst detail = error instanceof Error ? error.message : String(error);\n\t\t\treturn `onnxruntime-node is not provided by the host installation and could not be resolved from the plugin location (${detail}); to enable memory features, install onnxruntime-node in the host project (npm i onnxruntime-node) and restart the session`;\n\t\t}\n\t} else {\n\t\tentryPath = hostEntryPath;\n\t}\n\ttry {\n\t\tlet packageDir = dirname(entryPath);\n\t\twhile (packageDir !== dirname(packageDir) && !existsSync(join(packageDir, \"package.json\"))) {\n\t\t\tpackageDir = dirname(packageDir);\n\t\t}\n\t\tconst binDir = join(packageDir, \"bin\");\n\t\t// 走到文件系统根仍未找到 package.json:这是损坏/伪造的安装树,不能\n\t\t// 拿根目录的 bin(POSIX 上 /bin 真实存在)继续探测,否则会把损坏误报成\n\t\t// 平台二进制缺失。\n\t\tif (!existsSync(join(packageDir, \"package.json\"))) {\n\t\t\treturn `onnxruntime native binary directory not found: ${binDir}`;\n\t\t}\n\t\tif (!existsSync(binDir)) return `onnxruntime native binary directory not found: ${binDir}`;\n\t\tfor (const napiEntry of readdirSync(binDir, { withFileTypes: true })) {\n\t\t\tif (!napiEntry.isDirectory() || !napiEntry.name.startsWith(\"napi-v\")) continue;\n\t\t\tconst platformDir = join(binDir, napiEntry.name, process.platform, process.arch);\n\t\t\tif (!existsSync(platformDir)) continue;\n\t\t\tconst binaries = readdirSync(platformDir).filter((file) => file.endsWith(\".node\"));\n\t\t\tif (binaries.length > 0) return undefined;\n\t\t}\n\t\treturn `onnxruntime native binary for ${process.platform}/${process.arch} not found under ${binDir}`;\n\t} catch (error) {\n\t\treturn `onnxruntime native binary could not be resolved (${error instanceof Error ? error.message : String(error)}); the installation looks damaged — reinstall the package`;\n\t}\n}\n\n/**\n * 重依赖自解析默认实现 (missingHeavyDependencyModules 生产接线): 双通道与\n * {@link resolveOnnxRuntimeEntryPath} 一致 — jiti(安装店加载缝)注入的\n * `require` 走宿主 alias 表; 纯 ESM(源码/工作区直载)下回落原生 createRequire。\n */\nexport function resolveHeavyDependencyEntryPath(specifier: string): string {\n\tif (typeof require === \"function\") {\n\t\ttry {\n\t\t\treturn require.resolve(specifier);\n\t\t} catch {\n\t\t\t// jiti 解析失败(alias 未覆盖等)→ 继续走原生解析。\n\t\t}\n\t}\n\treturn createRequire(import.meta.url).resolve(specifier);\n}\n\n/**\n * 本地检查 1b: 重依赖供给 (设计 §3.2 checking)。`@lancedb/lancedb` 与\n * `@huggingface/transformers` 逐个判定 — entries 提供宿主入口路径 = 可用\n * (不触自解析); 未提供时 resolveModule 自解析成功 = 可用, 抛错 = 计入缺失。\n * 返回缺失 specifier 列表 (空 = 通过)。纯函数零 IO; 导出仅供测试 (与宿主侧\n * buildHostDependencyAliases 同一模式), 生产接线传插件位置自解析 — 把加载步\n * 的 \"Cannot find module\" 泛化包装前置成精准缺供文案, 省掉模型下载等前置工作。\n */\nexport function missingHeavyDependencyModules(\n\tentries: { lancedb?: string; transformers?: string },\n\tresolveModule: (specifier: string) => string,\n): string[] {\n\tconst missing: string[] = [];\n\tconst supplies: readonly (readonly [specifier: string, entryPath: string | undefined])[] = [\n\t\t[\"@lancedb/lancedb\", entries.lancedb],\n\t\t[\"@huggingface/transformers\", entries.transformers],\n\t];\n\tfor (const [specifier, entryPath] of supplies) {\n\t\tif (entryPath !== undefined) continue;\n\t\ttry {\n\t\t\tresolveModule(specifier);\n\t\t} catch {\n\t\t\tmissing.push(specifier);\n\t\t}\n\t}\n\treturn missing;\n}\n\n/**\n * transformers.js 缓存布局的容错存在性检查 (设计 §3.2 手动导入路径):\n * `<cacheDir>/<modelId>/` 下需有非空 tokenizer 文件与 `onnx/` 权重文件。\n * 返回 undefined = 检查通过; \"missing\" = 目录/文件缺失或仅剩 0 字节残骸\n * (失败下载产物, 清理后进入下载阶段, 见 model-cache-hygiene); 其他字符串 =\n * 文件存在但可疑 (截断/非空过小), 属失败。\n */\nexport function modelFilesCheck(cacheDir: string, modelId: string, minWeightBytes: number): string | undefined {\n\tconst modelDir = join(cacheDir, modelId);\n\tif (!existsSync(modelDir)) return \"missing\";\n\t// tokenizer 只剩 0 字节文件 = 失败下载残骸 (真 tokenizer.json 不可能为空):\n\t// 清掉按缺失处理可重下; 非空文件不动 (手动导入布局不受影响)。\n\tconst tokenizerNames = [\"tokenizer.json\", \"tokenizer.model\"].filter((name) => existsSync(join(modelDir, name)));\n\tconst hasTokenizer = tokenizerNames.some((name) => statSync(join(modelDir, name)).size > 0);\n\tif (!hasTokenizer) {\n\t\tif (tokenizerNames.length > 0) purgeZeroByteCacheArtifacts(modelDir);\n\t\treturn \"missing\";\n\t}\n\tconst onnxDir = join(modelDir, \"onnx\");\n\tif (!existsSync(onnxDir)) return `onnx weight directory missing under ${modelDir}`;\n\tconst weights = readdirSync(onnxDir).filter((name) => name.includes(\".onnx\"));\n\tif (weights.length === 0) return `no onnx weight file under ${onnxDir}`;\n\tconst largest = Math.max(...weights.map((name) => statSync(join(onnxDir, name)).size));\n\tif (largest < minWeightBytes) {\n\t\treturn `largest onnx weight under ${onnxDir} is ${largest} bytes, below the expected minimum ${minWeightBytes} — the download looks truncated`;\n\t}\n\treturn undefined;\n}\n\n/**\n * 本地检查 3: 磁盘余量 (设计 §3.2 checking, node:fs statfs, 零新依赖)。\n * 以 agentDir 所在卷为准 (cacheDir 同卷创建)。agentDir 可能尚不存在 (全新\n * 机器首启, 下载前无人创建过它 — 2026-10-06 live E2E 实测踩中) — statfs\n * ENOENT 时逐级上溯最近存在的祖先目录, 同卷即同余量; 上溯到根仍失败才按\n * 无法判定报告。\n */\nexport async function diskHeadroomCheck(agentDir: string, minimumBytes: number): Promise<string | undefined> {\n\tlet freeBytes: number | undefined;\n\tlet probe: string | undefined = agentDir;\n\tfor (let attempt = 0; probe !== undefined && attempt < 16; attempt++) {\n\t\ttry {\n\t\t\tconst stats = await statfs(probe);\n\t\t\tfreeBytes = stats.bfree * stats.bsize;\n\t\t\tbreak;\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof Error) || !String(error.message).includes(\"ENOENT\")) {\n\t\t\t\treturn `disk free space could not be determined for ${agentDir} (probing ${probe}: ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t})`;\n\t\t\t}\n\t\t\tconst parent = dirname(probe);\n\t\t\tprobe = parent === probe ? undefined : parent;\n\t\t}\n\t}\n\tif (freeBytes === undefined) {\n\t\treturn `disk free space could not be determined for ${agentDir} (no existing ancestor directory to statfs)`;\n\t}\n\tif (freeBytes < minimumBytes) {\n\t\treturn `disk free space is ${freeBytes} bytes, below the required ${minimumBytes}`;\n\t}\n\treturn undefined;\n}\n\n/** enabling 失败的五选一行动清单 (设计 §3.2 硬性要求)。 */\nexport function enablingActionList(modelCacheDir: string, currentMode: MemoryModeV1): readonly string[] {\n\treturn [\n\t\t`Switch to a reachable mirror: set ${HF_MIRRORS_ENV} to a comma-separated host list (tried before the defaults)`,\n\t\t`Install the heavy dependencies in the host project (npm i onnxruntime-node @lancedb/lancedb @huggingface/transformers) so the host can supply them via hostDependencyPaths, then restart the session`,\n\t\t`Download the models manually and place them under ${modelCacheDir} (layout: <modelId>/tokenizer.json + <modelId>/onnx/model*.onnx)`,\n\t\tcurrentMode === \"full\"\n\t\t\t? 'Downgrade to memory.mode \"light\" (much smaller models)'\n\t\t\t: 'Keep memory.mode \"light\" and retry with a reachable mirror',\n\t\t'Give up: leave memory.mode \"off\" (the default) so the memory family stays unregistered',\n\t];\n}\n\n// ---------------------------------------------------------------------------\n// 征询持久化 (capability 拆分战役 slice 3b 随迁, 征询域): agent-dir 配置文件\n// 的读写与征询拒绝/档位落盘。纯函数, 路径域显式传入; capability.ts 经\n// re-export 维持历史导出面, 向量通道 (vector-channel.ts) 直接 import。\n// ---------------------------------------------------------------------------\n\n/** agent-dir 配置文件里的征询拒绝标记值 (memoryConsent 键)。 */\nexport const MEMORY_CONSENT_DECLINED_VALUE = \"declined\";\n/** memory 插件 agent-dir 配置通道 (entry 读、capability 写, 同一路径)。 */\nexport const MEMORY_AGENT_DIR_CONFIG_RELATIVE_PATH = join(\"capabilities\", \"builtin.memory.json\");\n\nfunction readMemoryAgentDirConfig(agentDir: string): Record<string, unknown> {\n\tconst configPath = join(agentDir, MEMORY_AGENT_DIR_CONFIG_RELATIVE_PATH);\n\tif (existsSync(configPath)) {\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(readFileSync(configPath, \"utf8\"));\n\t\t\tif (parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\t\treturn parsed as Record<string, unknown>;\n\t\t\t}\n\t\t} catch {\n\t\t\t// 既有文件非法时以本次写入为准 (持久化是显式用户动作, 不被脏文件阻塞)。\n\t\t}\n\t}\n\treturn {};\n}\n\n/**\n * 写 agent-dir 配置的原子替换 (2026-10-06 复审 P1-2): temp 文件 + rename —\n * 自动升档引入了无人值守写者, 与其它进程的 /memory off (会删档位键) 读改写\n * 交错时, 直接 writeFileSync 可能留下半截 JSON 让并发读者拿到空配置 (整块\n * 丢键: 抹掉并发 decline 或升档)。rename 在同一目录内原子替换, 读者要么见\n * 旧文件要么见新文件。\n */\nfunction writeMemoryAgentDirConfig(agentDir: string, config: Record<string, unknown>): void {\n\tconst configPath = join(agentDir, MEMORY_AGENT_DIR_CONFIG_RELATIVE_PATH);\n\tmkdirSync(dirname(configPath), { recursive: true });\n\tconst tempPath = `${configPath}.${process.pid}.tmp`;\n\twriteFileSync(tempPath, `${JSON.stringify(config, null, \"\\t\")}\\n`, \"utf8\");\n\trenameSync(tempPath, configPath);\n}\n\n/** agent-dir 配置里用户显式持久化的档位键 (无 / 该键即 undefined)。 */\nexport function readMemorySettingsMode(agentDir: string): \"light\" | \"full\" | undefined {\n\tconst value = readMemoryAgentDirConfig(agentDir).memorySettingsMode;\n\treturn value === \"light\" || value === \"full\" ? value : undefined;\n}\n\n/**\n * 拒绝/跳过的持久化 (2026-10-05 用户裁决): 写入插件自己的 agent-dir 配置文件,\n * 不改写用户 settings.json。语义是「用户对默认档说了不」——仅抑制默认档;\n * 用户日后显式设置 settings.json `memory.mode` (light/full) 时显式值仍然生效。\n * 同时移除 /memory light|full 持久化的档位键, 保证\"先升 full 再 off\"不会留下\n * 一个压过 settings 的陈旧档位。\n */\nexport function writeMemoryConsentDeclined(agentDir: string): void {\n\tconst config = readMemoryAgentDirConfig(agentDir);\n\tconfig.memoryConsent = MEMORY_CONSENT_DECLINED_VALUE;\n\tdelete config.memorySettingsMode;\n\tdelete config.memoryMode;\n\twriteMemoryAgentDirConfig(agentDir, config);\n}\n\n/**\n * 档位升级/降级的持久化 (/memory light|full): 写入插件 agent-dir 配置的\n * `memorySettingsMode` 键——与 settings.json `memory.mode` 同一解析层 (持久\n * 配置面, env 仍可临时覆盖)。插件无 settings.json 写通道 (HostCapabilities\n * 无该面), 本文件键在其上, 即命令是比 settings 更近一次的显式表达。同时清\n * 除拒绝标记: 显式升/降档是最新的用户意图, 压过此前的\"对默认档说不\"。\n */\nexport function writeMemorySettingsMode(agentDir: string, mode: \"light\" | \"full\"): void {\n\tconst config = readMemoryAgentDirConfig(agentDir);\n\tconfig.memorySettingsMode = mode;\n\tdelete config.memoryConsent;\n\twriteMemoryAgentDirConfig(agentDir, config);\n}\n"]}
|
package/dist/memory/enabling.js
CHANGED
|
@@ -112,6 +112,12 @@ export function nativeBinaryCheck(hostEntryPath) {
|
|
|
112
112
|
packageDir = dirname(packageDir);
|
|
113
113
|
}
|
|
114
114
|
const binDir = join(packageDir, "bin");
|
|
115
|
+
// 走到文件系统根仍未找到 package.json:这是损坏/伪造的安装树,不能
|
|
116
|
+
// 拿根目录的 bin(POSIX 上 /bin 真实存在)继续探测,否则会把损坏误报成
|
|
117
|
+
// 平台二进制缺失。
|
|
118
|
+
if (!existsSync(join(packageDir, "package.json"))) {
|
|
119
|
+
return `onnxruntime native binary directory not found: ${binDir}`;
|
|
120
|
+
}
|
|
115
121
|
if (!existsSync(binDir))
|
|
116
122
|
return `onnxruntime native binary directory not found: ${binDir}`;
|
|
117
123
|
for (const napiEntry of readdirSync(binDir, { withFileTypes: true })) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enabling.js","sourceRoot":"","sources":["../../src/memory/enabling.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * enabling 前置检查、档位预设与镜像源(capability 拆分战役 slice 1):纯函数,\n * 无闭包依赖。\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\";\nimport { statfs } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type { MemoryModeV1 } from \"@agent-forge/plugin-sdk\";\nimport { purgeZeroByteCacheArtifacts } from \"./model-cache-hygiene.ts\";\n\n/** 开启前磁盘余量下限 (设计 §3.2 checking): light ≥ 500MB, full ≥ 2GB。 */\nexport const DISK_HEADROOM_MIN_BYTES: Readonly<Record<Exclude<MemoryModeV1, \"off\">, number>> = {\n\tlight: 500 * 1024 * 1024,\n\tfull: 2 * 1024 * 1024 * 1024,\n};\n/**\n * 模型文件大小下限 (防截断/空文件的 sanity 下限, 非精确体积): 高档 embedding\n * (bge-m3 q8) 数百 MB → 下限 256MB, 精排器 (bge-reranker-base q8) ~280MB →\n * 下限 64MB, 低档 (bge-small-zh q8) ~24MB → 下限 8MB。真实校验由加载冒烟承担。\n */\nexport const MODEL_MIN_WEIGHT_BYTES: Readonly<Record<Exclude<MemoryModeV1, \"off\">, number>> = {\n\tlight: 8 * 1024 * 1024,\n\tfull: 256 * 1024 * 1024,\n};\nexport const RERANKER_MIN_WEIGHT_BYTES = 64 * 1024 * 1024;\n/** 自定义镜像源环境变量 (失败行动清单第一项指向它)。 */\nconst HF_MIRRORS_ENV = \"AGENT_FORGE_HF_MIRRORS\";\n\n/** 两档预设 (实施计划 §2): light = 纯中文强项低配档, full = 跨语言大规模默认档。 */\ninterface MemoryVectorPresetV1 {\n\treadonly embeddingModelId: string;\n\treadonly dimensions: number;\n\t/** 查询侧指令前缀 (bge-small-zh 系查询侧指令由调用方负责添加; 写入侧不加)。 */\n\treadonly queryPrefix: string;\n}\n\nexport const VECTOR_PRESET_LIGHT: MemoryVectorPresetV1 = {\n\tembeddingModelId: \"Xenova/bge-small-zh-v1.5\",\n\tdimensions: 512,\n\tqueryPrefix: \"为这个句子生成表示以用于检索相关文章:\",\n};\nexport const VECTOR_PRESET_FULL: MemoryVectorPresetV1 = {\n\tembeddingModelId: \"Xenova/bge-m3\",\n\tdimensions: 1024,\n\tqueryPrefix: \"\",\n};\n/** 镜像序列 (实施计划 §7): 直连 huggingface.co 超时时回落 hf-mirror.com。 */\nconst VECTOR_REMOTE_HOSTS: readonly string[] = [\"https://huggingface.co\", \"https://hf-mirror.com\"];\n\n/** 档位预设: mode → 模型组合。off 在进入装配前已被门控拦截。 */\nexport function vectorPresetForMode(mode: Exclude<MemoryModeV1, \"off\">): MemoryVectorPresetV1 {\n\treturn mode === \"light\" ? VECTOR_PRESET_LIGHT : VECTOR_PRESET_FULL;\n}\n\n/**\n * agentDir 身份归一化 (2026-10-06 串线修复): 绝对路径解析抹平分隔符与相对段,\n * win32 再 casefold —— 同一目录在 Windows 下的大小写差异 (盘符/路径段) 不得\n * 误判为跨 agent-dir 串线。仅用于身份比对, 不触碰文件系统语义。\n */\nexport function normalizeMemoryAgentDirIdentity(agentDir: string): string {\n\tconst resolvedPath = resolve(agentDir);\n\treturn process.platform === \"win32\" ? resolvedPath.toLowerCase() : resolvedPath;\n}\n\n/**\n * 自定义镜像源 (失败行动清单第一项指向它): env `AGENT_FORGE_HF_MIRRORS` 逗号\n * 分隔 host 列表, 排在默认镜像序列之前优先尝试; 默认序列直连 huggingface.co\n * 超时时回落 hf-mirror.com。\n *\n * env 条目归一化 (2026-10-06 live E2E 修复): 裸主机名补 `https://` — 用户\n * 按行动清单写 `AGENT_FORGE_HF_MIRRORS=hf-mirror.com` 时, 无 scheme 的\n * remoteHost 会让 transformers 的文件请求按相对 URL 解析并在模块内留下\n * 毒化状态, 连后续带 scheme 的镜像重试也崩; 行动清单推荐的自救手段本身\n * 不能是断的。已带 scheme 的条目原样保留, 尾部 `/` 去除。\n */\nexport function remoteHostsForEnabling(): readonly string[] {\n\tconst custom = process.env[HF_MIRRORS_ENV]\n\t\t?.split(\",\")\n\t\t.map((host) => host.trim().replace(/\\/+$/, \"\"))\n\t\t.map((host) => (host === \"\" || host.includes(\"://\") ? host : `https://${host}`))\n\t\t.filter((host) => host !== \"\");\n\treturn custom === undefined || custom.length === 0 ? VECTOR_REMOTE_HOSTS : [...custom, ...VECTOR_REMOTE_HOSTS];\n}\n\n/**\n * 本地检查 1: native .node sidecar 可解析 (设计 §3.2 checking)。布局:\n * onnxruntime-node/bin 下的 napi-vN/<platform>/<arch> 原生模块 — 缺失 = 安装\n * 损坏。容错: 不钉死 napi 版本号, 逐目录扫描。\n *\n * 解析基点双通道: jiti(安装店加载缝)注入的 `require` 走宿主 alias 表\n * (宿主 node_modules 副本, 店内无依赖树); 纯 ESM(源码/工作区直载)下裸\n * `require` 未定义(typeof 探测不抛 ReferenceError), 回落原生 createRequire。\n */\nfunction resolveOnnxRuntimeEntryPath(): string {\n\tif (typeof require === \"function\") {\n\t\ttry {\n\t\t\treturn require.resolve(\"onnxruntime-node\");\n\t\t} catch {\n\t\t\t// jiti 解析失败(alias 未覆盖等)→ 继续走原生解析再报结构化错误。\n\t\t}\n\t}\n\treturn createRequire(import.meta.url).resolve(\"onnxruntime-node\");\n}\n\nexport function nativeBinaryCheck(hostEntryPath?: string): string | undefined {\n\t// 两种失败要区分 (D-075 安装店路径修复后前者成为真实可达路径):\n\t// (a) 宿主未注入入口路径且插件位置自解析不到 — 不是安装损坏, 行动是宿主侧\n\t// 安装重依赖后重启会话, 不是重装插件;\n\t// (b) 路径存在但原生二进制缺失/损坏 — 保留损坏类文案。\n\tlet entryPath: string;\n\tif (hostEntryPath === undefined) {\n\t\ttry {\n\t\t\tentryPath = resolveOnnxRuntimeEntryPath();\n\t\t} catch (error) {\n\t\t\tconst detail = error instanceof Error ? error.message : String(error);\n\t\t\treturn `onnxruntime-node is not provided by the host installation and could not be resolved from the plugin location (${detail}); to enable memory features, install onnxruntime-node in the host project (npm i onnxruntime-node) and restart the session`;\n\t\t}\n\t} else {\n\t\tentryPath = hostEntryPath;\n\t}\n\ttry {\n\t\tlet packageDir = dirname(entryPath);\n\t\twhile (packageDir !== dirname(packageDir) && !existsSync(join(packageDir, \"package.json\"))) {\n\t\t\tpackageDir = dirname(packageDir);\n\t\t}\n\t\tconst binDir = join(packageDir, \"bin\");\n\t\tif (!existsSync(binDir)) return `onnxruntime native binary directory not found: ${binDir}`;\n\t\tfor (const napiEntry of readdirSync(binDir, { withFileTypes: true })) {\n\t\t\tif (!napiEntry.isDirectory() || !napiEntry.name.startsWith(\"napi-v\")) continue;\n\t\t\tconst platformDir = join(binDir, napiEntry.name, process.platform, process.arch);\n\t\t\tif (!existsSync(platformDir)) continue;\n\t\t\tconst binaries = readdirSync(platformDir).filter((file) => file.endsWith(\".node\"));\n\t\t\tif (binaries.length > 0) return undefined;\n\t\t}\n\t\treturn `onnxruntime native binary for ${process.platform}/${process.arch} not found under ${binDir}`;\n\t} catch (error) {\n\t\treturn `onnxruntime native binary could not be resolved (${error instanceof Error ? error.message : String(error)}); the installation looks damaged — reinstall the package`;\n\t}\n}\n\n/**\n * 重依赖自解析默认实现 (missingHeavyDependencyModules 生产接线): 双通道与\n * {@link resolveOnnxRuntimeEntryPath} 一致 — jiti(安装店加载缝)注入的\n * `require` 走宿主 alias 表; 纯 ESM(源码/工作区直载)下回落原生 createRequire。\n */\nexport function resolveHeavyDependencyEntryPath(specifier: string): string {\n\tif (typeof require === \"function\") {\n\t\ttry {\n\t\t\treturn require.resolve(specifier);\n\t\t} catch {\n\t\t\t// jiti 解析失败(alias 未覆盖等)→ 继续走原生解析。\n\t\t}\n\t}\n\treturn createRequire(import.meta.url).resolve(specifier);\n}\n\n/**\n * 本地检查 1b: 重依赖供给 (设计 §3.2 checking)。`@lancedb/lancedb` 与\n * `@huggingface/transformers` 逐个判定 — entries 提供宿主入口路径 = 可用\n * (不触自解析); 未提供时 resolveModule 自解析成功 = 可用, 抛错 = 计入缺失。\n * 返回缺失 specifier 列表 (空 = 通过)。纯函数零 IO; 导出仅供测试 (与宿主侧\n * buildHostDependencyAliases 同一模式), 生产接线传插件位置自解析 — 把加载步\n * 的 \"Cannot find module\" 泛化包装前置成精准缺供文案, 省掉模型下载等前置工作。\n */\nexport function missingHeavyDependencyModules(\n\tentries: { lancedb?: string; transformers?: string },\n\tresolveModule: (specifier: string) => string,\n): string[] {\n\tconst missing: string[] = [];\n\tconst supplies: readonly (readonly [specifier: string, entryPath: string | undefined])[] = [\n\t\t[\"@lancedb/lancedb\", entries.lancedb],\n\t\t[\"@huggingface/transformers\", entries.transformers],\n\t];\n\tfor (const [specifier, entryPath] of supplies) {\n\t\tif (entryPath !== undefined) continue;\n\t\ttry {\n\t\t\tresolveModule(specifier);\n\t\t} catch {\n\t\t\tmissing.push(specifier);\n\t\t}\n\t}\n\treturn missing;\n}\n\n/**\n * transformers.js 缓存布局的容错存在性检查 (设计 §3.2 手动导入路径):\n * `<cacheDir>/<modelId>/` 下需有非空 tokenizer 文件与 `onnx/` 权重文件。\n * 返回 undefined = 检查通过; \"missing\" = 目录/文件缺失或仅剩 0 字节残骸\n * (失败下载产物, 清理后进入下载阶段, 见 model-cache-hygiene); 其他字符串 =\n * 文件存在但可疑 (截断/非空过小), 属失败。\n */\nexport function modelFilesCheck(cacheDir: string, modelId: string, minWeightBytes: number): string | undefined {\n\tconst modelDir = join(cacheDir, modelId);\n\tif (!existsSync(modelDir)) return \"missing\";\n\t// tokenizer 只剩 0 字节文件 = 失败下载残骸 (真 tokenizer.json 不可能为空):\n\t// 清掉按缺失处理可重下; 非空文件不动 (手动导入布局不受影响)。\n\tconst tokenizerNames = [\"tokenizer.json\", \"tokenizer.model\"].filter((name) => existsSync(join(modelDir, name)));\n\tconst hasTokenizer = tokenizerNames.some((name) => statSync(join(modelDir, name)).size > 0);\n\tif (!hasTokenizer) {\n\t\tif (tokenizerNames.length > 0) purgeZeroByteCacheArtifacts(modelDir);\n\t\treturn \"missing\";\n\t}\n\tconst onnxDir = join(modelDir, \"onnx\");\n\tif (!existsSync(onnxDir)) return `onnx weight directory missing under ${modelDir}`;\n\tconst weights = readdirSync(onnxDir).filter((name) => name.includes(\".onnx\"));\n\tif (weights.length === 0) return `no onnx weight file under ${onnxDir}`;\n\tconst largest = Math.max(...weights.map((name) => statSync(join(onnxDir, name)).size));\n\tif (largest < minWeightBytes) {\n\t\treturn `largest onnx weight under ${onnxDir} is ${largest} bytes, below the expected minimum ${minWeightBytes} — the download looks truncated`;\n\t}\n\treturn undefined;\n}\n\n/**\n * 本地检查 3: 磁盘余量 (设计 §3.2 checking, node:fs statfs, 零新依赖)。\n * 以 agentDir 所在卷为准 (cacheDir 同卷创建)。agentDir 可能尚不存在 (全新\n * 机器首启, 下载前无人创建过它 — 2026-10-06 live E2E 实测踩中) — statfs\n * ENOENT 时逐级上溯最近存在的祖先目录, 同卷即同余量; 上溯到根仍失败才按\n * 无法判定报告。\n */\nexport async function diskHeadroomCheck(agentDir: string, minimumBytes: number): Promise<string | undefined> {\n\tlet freeBytes: number | undefined;\n\tlet probe: string | undefined = agentDir;\n\tfor (let attempt = 0; probe !== undefined && attempt < 16; attempt++) {\n\t\ttry {\n\t\t\tconst stats = await statfs(probe);\n\t\t\tfreeBytes = stats.bfree * stats.bsize;\n\t\t\tbreak;\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof Error) || !String(error.message).includes(\"ENOENT\")) {\n\t\t\t\treturn `disk free space could not be determined for ${agentDir} (probing ${probe}: ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t})`;\n\t\t\t}\n\t\t\tconst parent = dirname(probe);\n\t\t\tprobe = parent === probe ? undefined : parent;\n\t\t}\n\t}\n\tif (freeBytes === undefined) {\n\t\treturn `disk free space could not be determined for ${agentDir} (no existing ancestor directory to statfs)`;\n\t}\n\tif (freeBytes < minimumBytes) {\n\t\treturn `disk free space is ${freeBytes} bytes, below the required ${minimumBytes}`;\n\t}\n\treturn undefined;\n}\n\n/** enabling 失败的五选一行动清单 (设计 §3.2 硬性要求)。 */\nexport function enablingActionList(modelCacheDir: string, currentMode: MemoryModeV1): readonly string[] {\n\treturn [\n\t\t`Switch to a reachable mirror: set ${HF_MIRRORS_ENV} to a comma-separated host list (tried before the defaults)`,\n\t\t`Install the heavy dependencies in the host project (npm i onnxruntime-node @lancedb/lancedb @huggingface/transformers) so the host can supply them via hostDependencyPaths, then restart the session`,\n\t\t`Download the models manually and place them under ${modelCacheDir} (layout: <modelId>/tokenizer.json + <modelId>/onnx/model*.onnx)`,\n\t\tcurrentMode === \"full\"\n\t\t\t? 'Downgrade to memory.mode \"light\" (much smaller models)'\n\t\t\t: 'Keep memory.mode \"light\" and retry with a reachable mirror',\n\t\t'Give up: leave memory.mode \"off\" (the default) so the memory family stays unregistered',\n\t];\n}\n\n// ---------------------------------------------------------------------------\n// 征询持久化 (capability 拆分战役 slice 3b 随迁, 征询域): agent-dir 配置文件\n// 的读写与征询拒绝/档位落盘。纯函数, 路径域显式传入; capability.ts 经\n// re-export 维持历史导出面, 向量通道 (vector-channel.ts) 直接 import。\n// ---------------------------------------------------------------------------\n\n/** agent-dir 配置文件里的征询拒绝标记值 (memoryConsent 键)。 */\nexport const MEMORY_CONSENT_DECLINED_VALUE = \"declined\";\n/** memory 插件 agent-dir 配置通道 (entry 读、capability 写, 同一路径)。 */\nexport const MEMORY_AGENT_DIR_CONFIG_RELATIVE_PATH = join(\"capabilities\", \"builtin.memory.json\");\n\nfunction readMemoryAgentDirConfig(agentDir: string): Record<string, unknown> {\n\tconst configPath = join(agentDir, MEMORY_AGENT_DIR_CONFIG_RELATIVE_PATH);\n\tif (existsSync(configPath)) {\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(readFileSync(configPath, \"utf8\"));\n\t\t\tif (parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\t\treturn parsed as Record<string, unknown>;\n\t\t\t}\n\t\t} catch {\n\t\t\t// 既有文件非法时以本次写入为准 (持久化是显式用户动作, 不被脏文件阻塞)。\n\t\t}\n\t}\n\treturn {};\n}\n\n/**\n * 写 agent-dir 配置的原子替换 (2026-10-06 复审 P1-2): temp 文件 + rename —\n * 自动升档引入了无人值守写者, 与其它进程的 /memory off (会删档位键) 读改写\n * 交错时, 直接 writeFileSync 可能留下半截 JSON 让并发读者拿到空配置 (整块\n * 丢键: 抹掉并发 decline 或升档)。rename 在同一目录内原子替换, 读者要么见\n * 旧文件要么见新文件。\n */\nfunction writeMemoryAgentDirConfig(agentDir: string, config: Record<string, unknown>): void {\n\tconst configPath = join(agentDir, MEMORY_AGENT_DIR_CONFIG_RELATIVE_PATH);\n\tmkdirSync(dirname(configPath), { recursive: true });\n\tconst tempPath = `${configPath}.${process.pid}.tmp`;\n\twriteFileSync(tempPath, `${JSON.stringify(config, null, \"\\t\")}\\n`, \"utf8\");\n\trenameSync(tempPath, configPath);\n}\n\n/** agent-dir 配置里用户显式持久化的档位键 (无 / 该键即 undefined)。 */\nexport function readMemorySettingsMode(agentDir: string): \"light\" | \"full\" | undefined {\n\tconst value = readMemoryAgentDirConfig(agentDir).memorySettingsMode;\n\treturn value === \"light\" || value === \"full\" ? value : undefined;\n}\n\n/**\n * 拒绝/跳过的持久化 (2026-10-05 用户裁决): 写入插件自己的 agent-dir 配置文件,\n * 不改写用户 settings.json。语义是「用户对默认档说了不」——仅抑制默认档;\n * 用户日后显式设置 settings.json `memory.mode` (light/full) 时显式值仍然生效。\n * 同时移除 /memory light|full 持久化的档位键, 保证\"先升 full 再 off\"不会留下\n * 一个压过 settings 的陈旧档位。\n */\nexport function writeMemoryConsentDeclined(agentDir: string): void {\n\tconst config = readMemoryAgentDirConfig(agentDir);\n\tconfig.memoryConsent = MEMORY_CONSENT_DECLINED_VALUE;\n\tdelete config.memorySettingsMode;\n\tdelete config.memoryMode;\n\twriteMemoryAgentDirConfig(agentDir, config);\n}\n\n/**\n * 档位升级/降级的持久化 (/memory light|full): 写入插件 agent-dir 配置的\n * `memorySettingsMode` 键——与 settings.json `memory.mode` 同一解析层 (持久\n * 配置面, env 仍可临时覆盖)。插件无 settings.json 写通道 (HostCapabilities\n * 无该面), 本文件键在其上, 即命令是比 settings 更近一次的显式表达。同时清\n * 除拒绝标记: 显式升/降档是最新的用户意图, 压过此前的\"对默认档说不\"。\n */\nexport function writeMemorySettingsMode(agentDir: string, mode: \"light\" | \"full\"): void {\n\tconst config = readMemoryAgentDirConfig(agentDir);\n\tconfig.memorySettingsMode = mode;\n\tdelete config.memoryConsent;\n\twriteMemoryAgentDirConfig(agentDir, config);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"enabling.js","sourceRoot":"","sources":["../../src/memory/enabling.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * enabling 前置检查、档位预设与镜像源(capability 拆分战役 slice 1):纯函数,\n * 无闭包依赖。\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\";\nimport { statfs } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type { MemoryModeV1 } from \"@agent-forge/plugin-sdk\";\nimport { purgeZeroByteCacheArtifacts } from \"./model-cache-hygiene.ts\";\n\n/** 开启前磁盘余量下限 (设计 §3.2 checking): light ≥ 500MB, full ≥ 2GB。 */\nexport const DISK_HEADROOM_MIN_BYTES: Readonly<Record<Exclude<MemoryModeV1, \"off\">, number>> = {\n\tlight: 500 * 1024 * 1024,\n\tfull: 2 * 1024 * 1024 * 1024,\n};\n/**\n * 模型文件大小下限 (防截断/空文件的 sanity 下限, 非精确体积): 高档 embedding\n * (bge-m3 q8) 数百 MB → 下限 256MB, 精排器 (bge-reranker-base q8) ~280MB →\n * 下限 64MB, 低档 (bge-small-zh q8) ~24MB → 下限 8MB。真实校验由加载冒烟承担。\n */\nexport const MODEL_MIN_WEIGHT_BYTES: Readonly<Record<Exclude<MemoryModeV1, \"off\">, number>> = {\n\tlight: 8 * 1024 * 1024,\n\tfull: 256 * 1024 * 1024,\n};\nexport const RERANKER_MIN_WEIGHT_BYTES = 64 * 1024 * 1024;\n/** 自定义镜像源环境变量 (失败行动清单第一项指向它)。 */\nconst HF_MIRRORS_ENV = \"AGENT_FORGE_HF_MIRRORS\";\n\n/** 两档预设 (实施计划 §2): light = 纯中文强项低配档, full = 跨语言大规模默认档。 */\ninterface MemoryVectorPresetV1 {\n\treadonly embeddingModelId: string;\n\treadonly dimensions: number;\n\t/** 查询侧指令前缀 (bge-small-zh 系查询侧指令由调用方负责添加; 写入侧不加)。 */\n\treadonly queryPrefix: string;\n}\n\nexport const VECTOR_PRESET_LIGHT: MemoryVectorPresetV1 = {\n\tembeddingModelId: \"Xenova/bge-small-zh-v1.5\",\n\tdimensions: 512,\n\tqueryPrefix: \"为这个句子生成表示以用于检索相关文章:\",\n};\nexport const VECTOR_PRESET_FULL: MemoryVectorPresetV1 = {\n\tembeddingModelId: \"Xenova/bge-m3\",\n\tdimensions: 1024,\n\tqueryPrefix: \"\",\n};\n/** 镜像序列 (实施计划 §7): 直连 huggingface.co 超时时回落 hf-mirror.com。 */\nconst VECTOR_REMOTE_HOSTS: readonly string[] = [\"https://huggingface.co\", \"https://hf-mirror.com\"];\n\n/** 档位预设: mode → 模型组合。off 在进入装配前已被门控拦截。 */\nexport function vectorPresetForMode(mode: Exclude<MemoryModeV1, \"off\">): MemoryVectorPresetV1 {\n\treturn mode === \"light\" ? VECTOR_PRESET_LIGHT : VECTOR_PRESET_FULL;\n}\n\n/**\n * agentDir 身份归一化 (2026-10-06 串线修复): 绝对路径解析抹平分隔符与相对段,\n * win32 再 casefold —— 同一目录在 Windows 下的大小写差异 (盘符/路径段) 不得\n * 误判为跨 agent-dir 串线。仅用于身份比对, 不触碰文件系统语义。\n */\nexport function normalizeMemoryAgentDirIdentity(agentDir: string): string {\n\tconst resolvedPath = resolve(agentDir);\n\treturn process.platform === \"win32\" ? resolvedPath.toLowerCase() : resolvedPath;\n}\n\n/**\n * 自定义镜像源 (失败行动清单第一项指向它): env `AGENT_FORGE_HF_MIRRORS` 逗号\n * 分隔 host 列表, 排在默认镜像序列之前优先尝试; 默认序列直连 huggingface.co\n * 超时时回落 hf-mirror.com。\n *\n * env 条目归一化 (2026-10-06 live E2E 修复): 裸主机名补 `https://` — 用户\n * 按行动清单写 `AGENT_FORGE_HF_MIRRORS=hf-mirror.com` 时, 无 scheme 的\n * remoteHost 会让 transformers 的文件请求按相对 URL 解析并在模块内留下\n * 毒化状态, 连后续带 scheme 的镜像重试也崩; 行动清单推荐的自救手段本身\n * 不能是断的。已带 scheme 的条目原样保留, 尾部 `/` 去除。\n */\nexport function remoteHostsForEnabling(): readonly string[] {\n\tconst custom = process.env[HF_MIRRORS_ENV]\n\t\t?.split(\",\")\n\t\t.map((host) => host.trim().replace(/\\/+$/, \"\"))\n\t\t.map((host) => (host === \"\" || host.includes(\"://\") ? host : `https://${host}`))\n\t\t.filter((host) => host !== \"\");\n\treturn custom === undefined || custom.length === 0 ? VECTOR_REMOTE_HOSTS : [...custom, ...VECTOR_REMOTE_HOSTS];\n}\n\n/**\n * 本地检查 1: native .node sidecar 可解析 (设计 §3.2 checking)。布局:\n * onnxruntime-node/bin 下的 napi-vN/<platform>/<arch> 原生模块 — 缺失 = 安装\n * 损坏。容错: 不钉死 napi 版本号, 逐目录扫描。\n *\n * 解析基点双通道: jiti(安装店加载缝)注入的 `require` 走宿主 alias 表\n * (宿主 node_modules 副本, 店内无依赖树); 纯 ESM(源码/工作区直载)下裸\n * `require` 未定义(typeof 探测不抛 ReferenceError), 回落原生 createRequire。\n */\nfunction resolveOnnxRuntimeEntryPath(): string {\n\tif (typeof require === \"function\") {\n\t\ttry {\n\t\t\treturn require.resolve(\"onnxruntime-node\");\n\t\t} catch {\n\t\t\t// jiti 解析失败(alias 未覆盖等)→ 继续走原生解析再报结构化错误。\n\t\t}\n\t}\n\treturn createRequire(import.meta.url).resolve(\"onnxruntime-node\");\n}\n\nexport function nativeBinaryCheck(hostEntryPath?: string): string | undefined {\n\t// 两种失败要区分 (D-075 安装店路径修复后前者成为真实可达路径):\n\t// (a) 宿主未注入入口路径且插件位置自解析不到 — 不是安装损坏, 行动是宿主侧\n\t// 安装重依赖后重启会话, 不是重装插件;\n\t// (b) 路径存在但原生二进制缺失/损坏 — 保留损坏类文案。\n\tlet entryPath: string;\n\tif (hostEntryPath === undefined) {\n\t\ttry {\n\t\t\tentryPath = resolveOnnxRuntimeEntryPath();\n\t\t} catch (error) {\n\t\t\tconst detail = error instanceof Error ? error.message : String(error);\n\t\t\treturn `onnxruntime-node is not provided by the host installation and could not be resolved from the plugin location (${detail}); to enable memory features, install onnxruntime-node in the host project (npm i onnxruntime-node) and restart the session`;\n\t\t}\n\t} else {\n\t\tentryPath = hostEntryPath;\n\t}\n\ttry {\n\t\tlet packageDir = dirname(entryPath);\n\t\twhile (packageDir !== dirname(packageDir) && !existsSync(join(packageDir, \"package.json\"))) {\n\t\t\tpackageDir = dirname(packageDir);\n\t\t}\n\t\tconst binDir = join(packageDir, \"bin\");\n\t\t// 走到文件系统根仍未找到 package.json:这是损坏/伪造的安装树,不能\n\t\t// 拿根目录的 bin(POSIX 上 /bin 真实存在)继续探测,否则会把损坏误报成\n\t\t// 平台二进制缺失。\n\t\tif (!existsSync(join(packageDir, \"package.json\"))) {\n\t\t\treturn `onnxruntime native binary directory not found: ${binDir}`;\n\t\t}\n\t\tif (!existsSync(binDir)) return `onnxruntime native binary directory not found: ${binDir}`;\n\t\tfor (const napiEntry of readdirSync(binDir, { withFileTypes: true })) {\n\t\t\tif (!napiEntry.isDirectory() || !napiEntry.name.startsWith(\"napi-v\")) continue;\n\t\t\tconst platformDir = join(binDir, napiEntry.name, process.platform, process.arch);\n\t\t\tif (!existsSync(platformDir)) continue;\n\t\t\tconst binaries = readdirSync(platformDir).filter((file) => file.endsWith(\".node\"));\n\t\t\tif (binaries.length > 0) return undefined;\n\t\t}\n\t\treturn `onnxruntime native binary for ${process.platform}/${process.arch} not found under ${binDir}`;\n\t} catch (error) {\n\t\treturn `onnxruntime native binary could not be resolved (${error instanceof Error ? error.message : String(error)}); the installation looks damaged — reinstall the package`;\n\t}\n}\n\n/**\n * 重依赖自解析默认实现 (missingHeavyDependencyModules 生产接线): 双通道与\n * {@link resolveOnnxRuntimeEntryPath} 一致 — jiti(安装店加载缝)注入的\n * `require` 走宿主 alias 表; 纯 ESM(源码/工作区直载)下回落原生 createRequire。\n */\nexport function resolveHeavyDependencyEntryPath(specifier: string): string {\n\tif (typeof require === \"function\") {\n\t\ttry {\n\t\t\treturn require.resolve(specifier);\n\t\t} catch {\n\t\t\t// jiti 解析失败(alias 未覆盖等)→ 继续走原生解析。\n\t\t}\n\t}\n\treturn createRequire(import.meta.url).resolve(specifier);\n}\n\n/**\n * 本地检查 1b: 重依赖供给 (设计 §3.2 checking)。`@lancedb/lancedb` 与\n * `@huggingface/transformers` 逐个判定 — entries 提供宿主入口路径 = 可用\n * (不触自解析); 未提供时 resolveModule 自解析成功 = 可用, 抛错 = 计入缺失。\n * 返回缺失 specifier 列表 (空 = 通过)。纯函数零 IO; 导出仅供测试 (与宿主侧\n * buildHostDependencyAliases 同一模式), 生产接线传插件位置自解析 — 把加载步\n * 的 \"Cannot find module\" 泛化包装前置成精准缺供文案, 省掉模型下载等前置工作。\n */\nexport function missingHeavyDependencyModules(\n\tentries: { lancedb?: string; transformers?: string },\n\tresolveModule: (specifier: string) => string,\n): string[] {\n\tconst missing: string[] = [];\n\tconst supplies: readonly (readonly [specifier: string, entryPath: string | undefined])[] = [\n\t\t[\"@lancedb/lancedb\", entries.lancedb],\n\t\t[\"@huggingface/transformers\", entries.transformers],\n\t];\n\tfor (const [specifier, entryPath] of supplies) {\n\t\tif (entryPath !== undefined) continue;\n\t\ttry {\n\t\t\tresolveModule(specifier);\n\t\t} catch {\n\t\t\tmissing.push(specifier);\n\t\t}\n\t}\n\treturn missing;\n}\n\n/**\n * transformers.js 缓存布局的容错存在性检查 (设计 §3.2 手动导入路径):\n * `<cacheDir>/<modelId>/` 下需有非空 tokenizer 文件与 `onnx/` 权重文件。\n * 返回 undefined = 检查通过; \"missing\" = 目录/文件缺失或仅剩 0 字节残骸\n * (失败下载产物, 清理后进入下载阶段, 见 model-cache-hygiene); 其他字符串 =\n * 文件存在但可疑 (截断/非空过小), 属失败。\n */\nexport function modelFilesCheck(cacheDir: string, modelId: string, minWeightBytes: number): string | undefined {\n\tconst modelDir = join(cacheDir, modelId);\n\tif (!existsSync(modelDir)) return \"missing\";\n\t// tokenizer 只剩 0 字节文件 = 失败下载残骸 (真 tokenizer.json 不可能为空):\n\t// 清掉按缺失处理可重下; 非空文件不动 (手动导入布局不受影响)。\n\tconst tokenizerNames = [\"tokenizer.json\", \"tokenizer.model\"].filter((name) => existsSync(join(modelDir, name)));\n\tconst hasTokenizer = tokenizerNames.some((name) => statSync(join(modelDir, name)).size > 0);\n\tif (!hasTokenizer) {\n\t\tif (tokenizerNames.length > 0) purgeZeroByteCacheArtifacts(modelDir);\n\t\treturn \"missing\";\n\t}\n\tconst onnxDir = join(modelDir, \"onnx\");\n\tif (!existsSync(onnxDir)) return `onnx weight directory missing under ${modelDir}`;\n\tconst weights = readdirSync(onnxDir).filter((name) => name.includes(\".onnx\"));\n\tif (weights.length === 0) return `no onnx weight file under ${onnxDir}`;\n\tconst largest = Math.max(...weights.map((name) => statSync(join(onnxDir, name)).size));\n\tif (largest < minWeightBytes) {\n\t\treturn `largest onnx weight under ${onnxDir} is ${largest} bytes, below the expected minimum ${minWeightBytes} — the download looks truncated`;\n\t}\n\treturn undefined;\n}\n\n/**\n * 本地检查 3: 磁盘余量 (设计 §3.2 checking, node:fs statfs, 零新依赖)。\n * 以 agentDir 所在卷为准 (cacheDir 同卷创建)。agentDir 可能尚不存在 (全新\n * 机器首启, 下载前无人创建过它 — 2026-10-06 live E2E 实测踩中) — statfs\n * ENOENT 时逐级上溯最近存在的祖先目录, 同卷即同余量; 上溯到根仍失败才按\n * 无法判定报告。\n */\nexport async function diskHeadroomCheck(agentDir: string, minimumBytes: number): Promise<string | undefined> {\n\tlet freeBytes: number | undefined;\n\tlet probe: string | undefined = agentDir;\n\tfor (let attempt = 0; probe !== undefined && attempt < 16; attempt++) {\n\t\ttry {\n\t\t\tconst stats = await statfs(probe);\n\t\t\tfreeBytes = stats.bfree * stats.bsize;\n\t\t\tbreak;\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof Error) || !String(error.message).includes(\"ENOENT\")) {\n\t\t\t\treturn `disk free space could not be determined for ${agentDir} (probing ${probe}: ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t})`;\n\t\t\t}\n\t\t\tconst parent = dirname(probe);\n\t\t\tprobe = parent === probe ? undefined : parent;\n\t\t}\n\t}\n\tif (freeBytes === undefined) {\n\t\treturn `disk free space could not be determined for ${agentDir} (no existing ancestor directory to statfs)`;\n\t}\n\tif (freeBytes < minimumBytes) {\n\t\treturn `disk free space is ${freeBytes} bytes, below the required ${minimumBytes}`;\n\t}\n\treturn undefined;\n}\n\n/** enabling 失败的五选一行动清单 (设计 §3.2 硬性要求)。 */\nexport function enablingActionList(modelCacheDir: string, currentMode: MemoryModeV1): readonly string[] {\n\treturn [\n\t\t`Switch to a reachable mirror: set ${HF_MIRRORS_ENV} to a comma-separated host list (tried before the defaults)`,\n\t\t`Install the heavy dependencies in the host project (npm i onnxruntime-node @lancedb/lancedb @huggingface/transformers) so the host can supply them via hostDependencyPaths, then restart the session`,\n\t\t`Download the models manually and place them under ${modelCacheDir} (layout: <modelId>/tokenizer.json + <modelId>/onnx/model*.onnx)`,\n\t\tcurrentMode === \"full\"\n\t\t\t? 'Downgrade to memory.mode \"light\" (much smaller models)'\n\t\t\t: 'Keep memory.mode \"light\" and retry with a reachable mirror',\n\t\t'Give up: leave memory.mode \"off\" (the default) so the memory family stays unregistered',\n\t];\n}\n\n// ---------------------------------------------------------------------------\n// 征询持久化 (capability 拆分战役 slice 3b 随迁, 征询域): agent-dir 配置文件\n// 的读写与征询拒绝/档位落盘。纯函数, 路径域显式传入; capability.ts 经\n// re-export 维持历史导出面, 向量通道 (vector-channel.ts) 直接 import。\n// ---------------------------------------------------------------------------\n\n/** agent-dir 配置文件里的征询拒绝标记值 (memoryConsent 键)。 */\nexport const MEMORY_CONSENT_DECLINED_VALUE = \"declined\";\n/** memory 插件 agent-dir 配置通道 (entry 读、capability 写, 同一路径)。 */\nexport const MEMORY_AGENT_DIR_CONFIG_RELATIVE_PATH = join(\"capabilities\", \"builtin.memory.json\");\n\nfunction readMemoryAgentDirConfig(agentDir: string): Record<string, unknown> {\n\tconst configPath = join(agentDir, MEMORY_AGENT_DIR_CONFIG_RELATIVE_PATH);\n\tif (existsSync(configPath)) {\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(readFileSync(configPath, \"utf8\"));\n\t\t\tif (parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\t\treturn parsed as Record<string, unknown>;\n\t\t\t}\n\t\t} catch {\n\t\t\t// 既有文件非法时以本次写入为准 (持久化是显式用户动作, 不被脏文件阻塞)。\n\t\t}\n\t}\n\treturn {};\n}\n\n/**\n * 写 agent-dir 配置的原子替换 (2026-10-06 复审 P1-2): temp 文件 + rename —\n * 自动升档引入了无人值守写者, 与其它进程的 /memory off (会删档位键) 读改写\n * 交错时, 直接 writeFileSync 可能留下半截 JSON 让并发读者拿到空配置 (整块\n * 丢键: 抹掉并发 decline 或升档)。rename 在同一目录内原子替换, 读者要么见\n * 旧文件要么见新文件。\n */\nfunction writeMemoryAgentDirConfig(agentDir: string, config: Record<string, unknown>): void {\n\tconst configPath = join(agentDir, MEMORY_AGENT_DIR_CONFIG_RELATIVE_PATH);\n\tmkdirSync(dirname(configPath), { recursive: true });\n\tconst tempPath = `${configPath}.${process.pid}.tmp`;\n\twriteFileSync(tempPath, `${JSON.stringify(config, null, \"\\t\")}\\n`, \"utf8\");\n\trenameSync(tempPath, configPath);\n}\n\n/** agent-dir 配置里用户显式持久化的档位键 (无 / 该键即 undefined)。 */\nexport function readMemorySettingsMode(agentDir: string): \"light\" | \"full\" | undefined {\n\tconst value = readMemoryAgentDirConfig(agentDir).memorySettingsMode;\n\treturn value === \"light\" || value === \"full\" ? value : undefined;\n}\n\n/**\n * 拒绝/跳过的持久化 (2026-10-05 用户裁决): 写入插件自己的 agent-dir 配置文件,\n * 不改写用户 settings.json。语义是「用户对默认档说了不」——仅抑制默认档;\n * 用户日后显式设置 settings.json `memory.mode` (light/full) 时显式值仍然生效。\n * 同时移除 /memory light|full 持久化的档位键, 保证\"先升 full 再 off\"不会留下\n * 一个压过 settings 的陈旧档位。\n */\nexport function writeMemoryConsentDeclined(agentDir: string): void {\n\tconst config = readMemoryAgentDirConfig(agentDir);\n\tconfig.memoryConsent = MEMORY_CONSENT_DECLINED_VALUE;\n\tdelete config.memorySettingsMode;\n\tdelete config.memoryMode;\n\twriteMemoryAgentDirConfig(agentDir, config);\n}\n\n/**\n * 档位升级/降级的持久化 (/memory light|full): 写入插件 agent-dir 配置的\n * `memorySettingsMode` 键——与 settings.json `memory.mode` 同一解析层 (持久\n * 配置面, env 仍可临时覆盖)。插件无 settings.json 写通道 (HostCapabilities\n * 无该面), 本文件键在其上, 即命令是比 settings 更近一次的显式表达。同时清\n * 除拒绝标记: 显式升/降档是最新的用户意图, 压过此前的\"对默认档说不\"。\n */\nexport function writeMemorySettingsMode(agentDir: string, mode: \"light\" | \"full\"): void {\n\tconst config = readMemoryAgentDirConfig(agentDir);\n\tconfig.memorySettingsMode = mode;\n\tdelete config.memoryConsent;\n\twriteMemoryAgentDirConfig(agentDir, config);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-agent-forge/plugin-memory",
|
|
3
|
-
"version": "0.88.
|
|
3
|
+
"version": "0.88.14",
|
|
4
4
|
"description": "First-party memory capability plugin for Agent Forge (memory_write/recall/list/forget over the M5 Memory Foundation)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -48,11 +48,12 @@
|
|
|
48
48
|
"@huggingface/transformers": "4.3.0",
|
|
49
49
|
"@lancedb/lancedb": "0.39.0",
|
|
50
50
|
"typebox": "1.3.27",
|
|
51
|
-
"@ai-agent-forge/plugin-sdk": "^0.88.
|
|
51
|
+
"@ai-agent-forge/plugin-sdk": "^0.88.14"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"shx": "0.3.4",
|
|
55
|
-
"vitest": "4.1.11"
|
|
55
|
+
"vitest": "4.1.11",
|
|
56
|
+
"@ai-agent-forge/agent-forge": "^0.88.14"
|
|
56
57
|
},
|
|
57
58
|
"bin": {
|
|
58
59
|
"agent-forge-memory-inference": "./bin/agent-forge-memory-inference.js"
|