@aigne/agent-runtime 1.12.0-beta.5
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/LICENSE.md +26 -0
- package/README.md +79 -0
- package/dist/agent-runners/ai-runner.cjs +133 -0
- package/dist/agent-runners/ai-runner.d.cts +33 -0
- package/dist/agent-runners/ai-runner.d.cts.map +1 -0
- package/dist/agent-runners/ai-runner.d.mts +33 -0
- package/dist/agent-runners/ai-runner.d.mts.map +1 -0
- package/dist/agent-runners/ai-runner.mjs +134 -0
- package/dist/agent-runners/ai-runner.mjs.map +1 -0
- package/dist/agent-runners/ash-runner.cjs +272 -0
- package/dist/agent-runners/ash-runner.d.cts +30 -0
- package/dist/agent-runners/ash-runner.d.cts.map +1 -0
- package/dist/agent-runners/ash-runner.d.mts +30 -0
- package/dist/agent-runners/ash-runner.d.mts.map +1 -0
- package/dist/agent-runners/ash-runner.mjs +273 -0
- package/dist/agent-runners/ash-runner.mjs.map +1 -0
- package/dist/agent-runners/budget.cjs +71 -0
- package/dist/agent-runners/budget.d.cts +40 -0
- package/dist/agent-runners/budget.d.cts.map +1 -0
- package/dist/agent-runners/budget.d.mts +40 -0
- package/dist/agent-runners/budget.d.mts.map +1 -0
- package/dist/agent-runners/budget.mjs +67 -0
- package/dist/agent-runners/budget.mjs.map +1 -0
- package/dist/agent-runners/code-runner.cjs +61 -0
- package/dist/agent-runners/code-runner.mjs +61 -0
- package/dist/agent-runners/code-runner.mjs.map +1 -0
- package/dist/agent-runners/env-whitelist.cjs +36 -0
- package/dist/agent-runners/env-whitelist.d.cts +5 -0
- package/dist/agent-runners/env-whitelist.d.cts.map +1 -0
- package/dist/agent-runners/env-whitelist.d.mts +5 -0
- package/dist/agent-runners/env-whitelist.d.mts.map +1 -0
- package/dist/agent-runners/env-whitelist.mjs +36 -0
- package/dist/agent-runners/env-whitelist.mjs.map +1 -0
- package/dist/agent-runners/guide-rails.cjs +47 -0
- package/dist/agent-runners/guide-rails.d.cts +16 -0
- package/dist/agent-runners/guide-rails.d.cts.map +1 -0
- package/dist/agent-runners/guide-rails.d.mts +16 -0
- package/dist/agent-runners/guide-rails.d.mts.map +1 -0
- package/dist/agent-runners/guide-rails.mjs +48 -0
- package/dist/agent-runners/guide-rails.mjs.map +1 -0
- package/dist/agent-runners/hook-channel.cjs +55 -0
- package/dist/agent-runners/hook-channel.d.cts +38 -0
- package/dist/agent-runners/hook-channel.d.cts.map +1 -0
- package/dist/agent-runners/hook-channel.d.mts +38 -0
- package/dist/agent-runners/hook-channel.d.mts.map +1 -0
- package/dist/agent-runners/hook-channel.mjs +55 -0
- package/dist/agent-runners/hook-channel.mjs.map +1 -0
- package/dist/agent-runners/hooks.cjs +107 -0
- package/dist/agent-runners/hooks.d.cts +41 -0
- package/dist/agent-runners/hooks.d.cts.map +1 -0
- package/dist/agent-runners/hooks.d.mts +41 -0
- package/dist/agent-runners/hooks.d.mts.map +1 -0
- package/dist/agent-runners/hooks.mjs +107 -0
- package/dist/agent-runners/hooks.mjs.map +1 -0
- package/dist/agent-runners/memory-afs.cjs +342 -0
- package/dist/agent-runners/memory-afs.d.cts +221 -0
- package/dist/agent-runners/memory-afs.d.cts.map +1 -0
- package/dist/agent-runners/memory-afs.d.mts +221 -0
- package/dist/agent-runners/memory-afs.d.mts.map +1 -0
- package/dist/agent-runners/memory-afs.mjs +326 -0
- package/dist/agent-runners/memory-afs.mjs.map +1 -0
- package/dist/agent-runners/memory-archive.cjs +263 -0
- package/dist/agent-runners/memory-archive.d.cts +47 -0
- package/dist/agent-runners/memory-archive.d.cts.map +1 -0
- package/dist/agent-runners/memory-archive.d.mts +47 -0
- package/dist/agent-runners/memory-archive.d.mts.map +1 -0
- package/dist/agent-runners/memory-archive.mjs +257 -0
- package/dist/agent-runners/memory-archive.mjs.map +1 -0
- package/dist/agent-runners/orchestrator-runner.cjs +302 -0
- package/dist/agent-runners/orchestrator-runner.d.cts +23 -0
- package/dist/agent-runners/orchestrator-runner.d.cts.map +1 -0
- package/dist/agent-runners/orchestrator-runner.d.mts +23 -0
- package/dist/agent-runners/orchestrator-runner.d.mts.map +1 -0
- package/dist/agent-runners/orchestrator-runner.mjs +303 -0
- package/dist/agent-runners/orchestrator-runner.mjs.map +1 -0
- package/dist/agent-runners/orchestrator-state.cjs +68 -0
- package/dist/agent-runners/orchestrator-state.d.cts +23 -0
- package/dist/agent-runners/orchestrator-state.d.cts.map +1 -0
- package/dist/agent-runners/orchestrator-state.d.mts +23 -0
- package/dist/agent-runners/orchestrator-state.d.mts.map +1 -0
- package/dist/agent-runners/orchestrator-state.mjs +65 -0
- package/dist/agent-runners/orchestrator-state.mjs.map +1 -0
- package/dist/agent-runners/retry.cjs +30 -0
- package/dist/agent-runners/retry.d.cts +13 -0
- package/dist/agent-runners/retry.d.cts.map +1 -0
- package/dist/agent-runners/retry.d.mts +13 -0
- package/dist/agent-runners/retry.d.mts.map +1 -0
- package/dist/agent-runners/retry.mjs +30 -0
- package/dist/agent-runners/retry.mjs.map +1 -0
- package/dist/agent-runners/scratchpad-afs.cjs +276 -0
- package/dist/agent-runners/scratchpad-afs.d.cts +103 -0
- package/dist/agent-runners/scratchpad-afs.d.cts.map +1 -0
- package/dist/agent-runners/scratchpad-afs.d.mts +103 -0
- package/dist/agent-runners/scratchpad-afs.d.mts.map +1 -0
- package/dist/agent-runners/scratchpad-afs.mjs +271 -0
- package/dist/agent-runners/scratchpad-afs.mjs.map +1 -0
- package/dist/agent-runners/stream-router.cjs +72 -0
- package/dist/agent-runners/stream-router.d.cts +22 -0
- package/dist/agent-runners/stream-router.d.cts.map +1 -0
- package/dist/agent-runners/stream-router.d.mts +22 -0
- package/dist/agent-runners/stream-router.d.mts.map +1 -0
- package/dist/agent-runners/stream-router.mjs +72 -0
- package/dist/agent-runners/stream-router.mjs.map +1 -0
- package/dist/agent-runners/system-prompt.cjs +35 -0
- package/dist/agent-runners/system-prompt.d.cts +17 -0
- package/dist/agent-runners/system-prompt.d.cts.map +1 -0
- package/dist/agent-runners/system-prompt.d.mts +17 -0
- package/dist/agent-runners/system-prompt.d.mts.map +1 -0
- package/dist/agent-runners/system-prompt.mjs +34 -0
- package/dist/agent-runners/system-prompt.mjs.map +1 -0
- package/dist/agent-runners/team-modes/aggregates.cjs +36 -0
- package/dist/agent-runners/team-modes/aggregates.mjs +35 -0
- package/dist/agent-runners/team-modes/aggregates.mjs.map +1 -0
- package/dist/agent-runners/team-modes/iterate.cjs +46 -0
- package/dist/agent-runners/team-modes/iterate.mjs +47 -0
- package/dist/agent-runners/team-modes/iterate.mjs.map +1 -0
- package/dist/agent-runners/team-modes/parallel.cjs +59 -0
- package/dist/agent-runners/team-modes/parallel.mjs +60 -0
- package/dist/agent-runners/team-modes/parallel.mjs.map +1 -0
- package/dist/agent-runners/team-modes/reflection.cjs +155 -0
- package/dist/agent-runners/team-modes/reflection.mjs +156 -0
- package/dist/agent-runners/team-modes/reflection.mjs.map +1 -0
- package/dist/agent-runners/team-modes/sequential.cjs +63 -0
- package/dist/agent-runners/team-modes/sequential.mjs +64 -0
- package/dist/agent-runners/team-modes/sequential.mjs.map +1 -0
- package/dist/agent-runners/team-modes/utils.cjs +39 -0
- package/dist/agent-runners/team-modes/utils.mjs +36 -0
- package/dist/agent-runners/team-modes/utils.mjs.map +1 -0
- package/dist/agent-runners/team-runner.cjs +277 -0
- package/dist/agent-runners/team-runner.d.cts +34 -0
- package/dist/agent-runners/team-runner.d.cts.map +1 -0
- package/dist/agent-runners/team-runner.d.mts +34 -0
- package/dist/agent-runners/team-runner.d.mts.map +1 -0
- package/dist/agent-runners/team-runner.mjs +278 -0
- package/dist/agent-runners/team-runner.mjs.map +1 -0
- package/dist/agent-runners/template-context.cjs +32 -0
- package/dist/agent-runners/template-context.d.cts +43 -0
- package/dist/agent-runners/template-context.d.cts.map +1 -0
- package/dist/agent-runners/template-context.d.mts +43 -0
- package/dist/agent-runners/template-context.d.mts.map +1 -0
- package/dist/agent-runners/template-context.mjs +33 -0
- package/dist/agent-runners/template-context.mjs.map +1 -0
- package/dist/index.cjs +123 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.mts +29 -0
- package/dist/index.mjs +29 -0
- package/dist/program/agent-action.cjs +496 -0
- package/dist/program/agent-action.d.cts +42 -0
- package/dist/program/agent-action.d.cts.map +1 -0
- package/dist/program/agent-action.d.mts +42 -0
- package/dist/program/agent-action.d.mts.map +1 -0
- package/dist/program/agent-action.mjs +497 -0
- package/dist/program/agent-action.mjs.map +1 -0
- package/dist/program/agent-messages-tools.cjs +137 -0
- package/dist/program/agent-messages-tools.d.cts +59 -0
- package/dist/program/agent-messages-tools.d.cts.map +1 -0
- package/dist/program/agent-messages-tools.d.mts +59 -0
- package/dist/program/agent-messages-tools.d.mts.map +1 -0
- package/dist/program/agent-messages-tools.mjs +130 -0
- package/dist/program/agent-messages-tools.mjs.map +1 -0
- package/dist/program/agent-runner-registry.cjs +314 -0
- package/dist/program/agent-runner-registry.d.cts +52 -0
- package/dist/program/agent-runner-registry.d.cts.map +1 -0
- package/dist/program/agent-runner-registry.d.mts +52 -0
- package/dist/program/agent-runner-registry.d.mts.map +1 -0
- package/dist/program/agent-runner-registry.mjs +313 -0
- package/dist/program/agent-runner-registry.mjs.map +1 -0
- package/dist/program/agent-subscribe.cjs +72 -0
- package/dist/program/agent-subscribe.d.cts +18 -0
- package/dist/program/agent-subscribe.d.cts.map +1 -0
- package/dist/program/agent-subscribe.d.mts +18 -0
- package/dist/program/agent-subscribe.d.mts.map +1 -0
- package/dist/program/agent-subscribe.mjs +73 -0
- package/dist/program/agent-subscribe.mjs.map +1 -0
- package/dist/program/agent-tools.cjs +135 -0
- package/dist/program/agent-tools.d.cts +35 -0
- package/dist/program/agent-tools.d.cts.map +1 -0
- package/dist/program/agent-tools.d.mts +35 -0
- package/dist/program/agent-tools.d.mts.map +1 -0
- package/dist/program/agent-tools.mjs +136 -0
- package/dist/program/agent-tools.mjs.map +1 -0
- package/dist/program/manifest-nesting.cjs +57 -0
- package/dist/program/manifest-nesting.d.cts +13 -0
- package/dist/program/manifest-nesting.d.cts.map +1 -0
- package/dist/program/manifest-nesting.d.mts +13 -0
- package/dist/program/manifest-nesting.d.mts.map +1 -0
- package/dist/program/manifest-nesting.mjs +57 -0
- package/dist/program/manifest-nesting.mjs.map +1 -0
- package/dist/program/result-contract.cjs +55 -0
- package/dist/program/result-contract.d.cts +23 -0
- package/dist/program/result-contract.d.cts.map +1 -0
- package/dist/program/result-contract.d.mts +23 -0
- package/dist/program/result-contract.d.mts.map +1 -0
- package/dist/program/result-contract.mjs +51 -0
- package/dist/program/result-contract.mjs.map +1 -0
- package/dist/program/runner-error.cjs +71 -0
- package/dist/program/runner-error.d.cts +25 -0
- package/dist/program/runner-error.d.cts.map +1 -0
- package/dist/program/runner-error.d.mts +25 -0
- package/dist/program/runner-error.d.mts.map +1 -0
- package/dist/program/runner-error.mjs +72 -0
- package/dist/program/runner-error.mjs.map +1 -0
- package/dist/program/scoped-afs.cjs +538 -0
- package/dist/program/scoped-afs.d.cts +245 -0
- package/dist/program/scoped-afs.d.cts.map +1 -0
- package/dist/program/scoped-afs.d.mts +245 -0
- package/dist/program/scoped-afs.d.mts.map +1 -0
- package/dist/program/scoped-afs.mjs +531 -0
- package/dist/program/scoped-afs.mjs.map +1 -0
- package/dist/program/session-hierarchy.cjs +1 -0
- package/dist/program/session-hierarchy.d.cts +2 -0
- package/dist/program/session-hierarchy.d.mts +2 -0
- package/dist/program/session-hierarchy.mjs +3 -0
- package/dist/program/tool-discovery/ash-frontmatter.cjs +83 -0
- package/dist/program/tool-discovery/ash-frontmatter.d.cts +21 -0
- package/dist/program/tool-discovery/ash-frontmatter.d.cts.map +1 -0
- package/dist/program/tool-discovery/ash-frontmatter.d.mts +21 -0
- package/dist/program/tool-discovery/ash-frontmatter.d.mts.map +1 -0
- package/dist/program/tool-discovery/ash-frontmatter.mjs +84 -0
- package/dist/program/tool-discovery/ash-frontmatter.mjs.map +1 -0
- package/dist/program/tool-discovery/dot-path-resolver.cjs +60 -0
- package/dist/program/tool-discovery/dot-path-resolver.mjs +61 -0
- package/dist/program/tool-discovery/dot-path-resolver.mjs.map +1 -0
- package/dist/program/tool-discovery/skill-gate.cjs +36 -0
- package/dist/program/tool-discovery/skill-gate.d.cts +10 -0
- package/dist/program/tool-discovery/skill-gate.d.cts.map +1 -0
- package/dist/program/tool-discovery/skill-gate.d.mts +10 -0
- package/dist/program/tool-discovery/skill-gate.d.mts.map +1 -0
- package/dist/program/tool-discovery/skill-gate.mjs +35 -0
- package/dist/program/tool-discovery/skill-gate.mjs.map +1 -0
- package/dist/providers/basic/ash/dist/index.cjs +1809 -0
- package/dist/providers/basic/ash/dist/index.mjs +1807 -0
- package/dist/providers/basic/ash/dist/index.mjs.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { AFSRoot } from "@aigne/afs";
|
|
2
|
+
import { AFSMemory, MemoryStorage } from "@aigne/afs-memory";
|
|
3
|
+
|
|
4
|
+
//#region src/agent-runners/memory-afs.d.ts
|
|
5
|
+
declare const MEMORY_SCOPE_ROOT = "/memory";
|
|
6
|
+
/** Paths that must route to the scope-local memory provider. */
|
|
7
|
+
declare function isMemoryPath(path: string): boolean;
|
|
8
|
+
interface CreateMemoryProviderOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Logical tenant. Baked into the provider as `defaultDomain`; any action
|
|
11
|
+
* that omits `domain` in its args falls back to this value. The overlay
|
|
12
|
+
* treats `domain` as fixed-at-construction — runtime callers cannot
|
|
13
|
+
* widen it by passing a different arg, because storage is already
|
|
14
|
+
* tenant-scoped.
|
|
15
|
+
*/
|
|
16
|
+
readonly domain: string;
|
|
17
|
+
/**
|
|
18
|
+
* Optional instance name (shows up in descriptions / traces). Defaults
|
|
19
|
+
* to `memory:<domain>`.
|
|
20
|
+
*/
|
|
21
|
+
readonly name?: string;
|
|
22
|
+
/**
|
|
23
|
+
* When set, persist observations to the local filesystem under this
|
|
24
|
+
* directory via `FSStorage`. When unset, falls back to the
|
|
25
|
+
* process-wide `agentMemoryRoot` (set via `setAgentMemoryRoot()`); when
|
|
26
|
+
* neither is set, falls back to `InMemoryStorage` (legacy behavior).
|
|
27
|
+
*
|
|
28
|
+
* The provider keyed by `(domain, resolvedStoragePath)` is cached
|
|
29
|
+
* process-wide so multiple scopes for the same tenant share the same
|
|
30
|
+
* `AFSMemory` instance — without that, two scopes pointing at the same
|
|
31
|
+
* directory would each get their own `FSStorage`, doubling on-disk
|
|
32
|
+
* fan-out and confusing the `consolidate`/`aging` background jobs that
|
|
33
|
+
* AFSMemory schedules at construction time.
|
|
34
|
+
*/
|
|
35
|
+
readonly storagePath?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Explicit storage backend. When provided, skips path-based storage
|
|
38
|
+
* selection (storagePath / agentMemoryRoot) and uses this directly.
|
|
39
|
+
* Used when the session AFS is the backing store (DID Space persistence).
|
|
40
|
+
* Providers created with explicit storage are NOT cached (each caller
|
|
41
|
+
* has a distinct backing AFS).
|
|
42
|
+
*/
|
|
43
|
+
readonly storage?: MemoryStorage;
|
|
44
|
+
/**
|
|
45
|
+
* Test-only escape hatch: bypass the provider cache and always return a
|
|
46
|
+
* fresh instance. Used by the persistence test to prove storage is at
|
|
47
|
+
* the FS layer (not at the JS-object layer).
|
|
48
|
+
*/
|
|
49
|
+
readonly _bypassCache?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Explicit AFS root for index dispatch (indexEntry on remember/archive,
|
|
52
|
+
* queryIndex on recall). When set, overrides the process-global
|
|
53
|
+
* `agentIndexAfs` for THIS provider — used by CF per-caller memory, where
|
|
54
|
+
* the caller's session view (holding `/user/index`) is the correct index
|
|
55
|
+
* root, not a process global (which would be shared across concurrent
|
|
56
|
+
* callers). Node leaves this unset → keeps the global `/modules/index`.
|
|
57
|
+
*/
|
|
58
|
+
readonly afsRoot?: {
|
|
59
|
+
exec?: (...args: unknown[]) => unknown;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Override the default index query path (`/modules/index/.actions/query`).
|
|
63
|
+
* Set to `/user/index/.actions/query` for per-caller user-axis indexing.
|
|
64
|
+
*/
|
|
65
|
+
readonly indexQueryPath?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Override the default index write path (`/modules/index/.actions/index`).
|
|
68
|
+
* Set to `/user/index/.actions/index` for per-caller user-axis indexing.
|
|
69
|
+
*/
|
|
70
|
+
readonly indexWritePath?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Daemon wire-up companion to `setAgentMemoryRoot`: attach a global AFS
|
|
74
|
+
* reference that the per-scope memory provider can use to dispatch
|
|
75
|
+
* `/modules/index/.actions/index` calls. Without this, `AFSMemory`'s
|
|
76
|
+
* built-in `indexEntry` step (every observation goes through the
|
|
77
|
+
* configured index) silently no-ops because the scope-local provider
|
|
78
|
+
* has no `afsRoot` set (it's attached via Object.create overlay, not
|
|
79
|
+
* `afs.mount()`).
|
|
80
|
+
*/
|
|
81
|
+
declare function setAgentIndexAfs(afs: {
|
|
82
|
+
exec?: (...args: any[]) => any;
|
|
83
|
+
} | undefined): void;
|
|
84
|
+
/** Test-only — used by `afterEach` to drop the configured index AFS. */
|
|
85
|
+
declare function clearAgentIndexAfsForTesting(): void;
|
|
86
|
+
/**
|
|
87
|
+
* Daemon wire-up: configure a process-wide root directory for agent
|
|
88
|
+
* memory. When set, `createMemoryProvider({ domain })` (no `storagePath`)
|
|
89
|
+
* resolves storage to `<root>/<domain>/` via `FSStorage`. Production
|
|
90
|
+
* daemons set this once at startup so cross-session recall works
|
|
91
|
+
* without each call site threading the path.
|
|
92
|
+
*
|
|
93
|
+
* Tests should clear via `clearAgentMemoryRootForTesting()` between
|
|
94
|
+
* cases to keep the cache clean.
|
|
95
|
+
*/
|
|
96
|
+
declare function setAgentMemoryRoot(root: string | undefined): void;
|
|
97
|
+
/** Test-only — used by `afterEach` to reset between cases. */
|
|
98
|
+
declare function clearAgentMemoryRootForTesting(): void;
|
|
99
|
+
/**
|
|
100
|
+
* Symbol key used to attach per-session memory storage to the session AFS.
|
|
101
|
+
* Propagates through `Object.create(sessionAfs)` prototype chains, so all
|
|
102
|
+
* scope-local AFS objects derived from the session AFS inherit it without
|
|
103
|
+
* explicit threading.
|
|
104
|
+
*
|
|
105
|
+
* Set via `attachSessionMemoryStorage()` in `buildCfSessionView` (CF) and
|
|
106
|
+
* `buildSessionView` (Node) for authenticated callers. Anonymous callers
|
|
107
|
+
* or sessions without a DID Space get no storage attached (undefined →
|
|
108
|
+
* falls through to FSStorage / InMemoryStorage legacy path).
|
|
109
|
+
*/
|
|
110
|
+
declare const SESSION_MEMORY_STORAGE_KEY: unique symbol;
|
|
111
|
+
/**
|
|
112
|
+
* Attach an AFS-backed memory storage to the session AFS so that all
|
|
113
|
+
* scope-local AFS objects derived from it (via `Object.create`) inherit
|
|
114
|
+
* the storage via prototype-chain lookup.
|
|
115
|
+
*
|
|
116
|
+
* Call this in `buildCfSessionView` / `buildSessionView` for authenticated
|
|
117
|
+
* callers: `attachSessionMemoryStorage(sessionView, new AFSBackedStorage(sessionView, "/user/memory"))`.
|
|
118
|
+
*/
|
|
119
|
+
declare function attachSessionMemoryStorage(afs: AFSRoot, storage: MemoryStorage): void;
|
|
120
|
+
/**
|
|
121
|
+
* Build memory storage for the given scope AFS using the session-attached
|
|
122
|
+
* storage (set via `attachSessionMemoryStorage`). Returns undefined when no
|
|
123
|
+
* storage is attached (anonymous callers, tests without wiring).
|
|
124
|
+
*/
|
|
125
|
+
declare function buildMemoryStorageForAfs(afs: AFSRoot): MemoryStorage | undefined;
|
|
126
|
+
/** Per-session index dispatch config, attached in `buildCfSessionView`. */
|
|
127
|
+
interface SessionIndexConfig {
|
|
128
|
+
/** AFS whose `indexQueryPath`/`indexWritePath` resolve to the caller's index. */
|
|
129
|
+
readonly afsRoot: {
|
|
130
|
+
exec?: (...args: unknown[]) => unknown;
|
|
131
|
+
};
|
|
132
|
+
readonly indexQueryPath: string;
|
|
133
|
+
readonly indexWritePath: string;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Point per-caller memory at a per-caller index. Companion to
|
|
137
|
+
* `attachSessionMemoryStorage`: when set, `createMemoryProvider` dispatches
|
|
138
|
+
* `indexEntry`/`queryIndex` to `config.afsRoot` at the given paths instead of
|
|
139
|
+
* the process-global `agentIndexAfs` + `/modules/index`. Propagates through
|
|
140
|
+
* `Object.create(sessionAfs)` prototype chains. CF calls this in
|
|
141
|
+
* `buildCfSessionView`; Node leaves it unset (keeps the global index).
|
|
142
|
+
*/
|
|
143
|
+
declare function attachSessionIndex(afs: AFSRoot, config: SessionIndexConfig): void;
|
|
144
|
+
/** Read the session-attached index config (via prototype chain), or undefined. */
|
|
145
|
+
declare function readSessionIndex(afs: AFSRoot): SessionIndexConfig | undefined;
|
|
146
|
+
/**
|
|
147
|
+
* Spread-ready index opts for `createMemoryProvider`, derived from the session's
|
|
148
|
+
* attached index config. Empty object when none is attached (Node / anonymous)
|
|
149
|
+
* → the provider keeps the process-global index root + default `/modules/index`.
|
|
150
|
+
*/
|
|
151
|
+
declare function buildMemoryIndexOptsForAfs(afs: AFSRoot): Pick<CreateMemoryProviderOptions, "afsRoot" | "indexQueryPath" | "indexWritePath">;
|
|
152
|
+
/** Test-only — used by `afterEach` to drop cached instances. */
|
|
153
|
+
declare function clearMemoryProviderCacheForTesting(): void;
|
|
154
|
+
/**
|
|
155
|
+
* Instantiate an `AFSMemory` suitable for a scope-local `/memory/` mount.
|
|
156
|
+
*
|
|
157
|
+
* Storage selection follows this order:
|
|
158
|
+
*
|
|
159
|
+
* 1. Explicit `opts.storage` — used as-is, not cached (caller controls lifecycle).
|
|
160
|
+
* 2. Explicit `opts.storagePath` (typically test fixtures).
|
|
161
|
+
* 3. Process-wide `agentMemoryRoot` set by the daemon — yields
|
|
162
|
+
* `<root>/<domain>` via `FSStorage`. Cross-session recall lives here.
|
|
163
|
+
* 4. Default `InMemoryStorage` — used when no persistence is configured
|
|
164
|
+
* (legacy + most tests).
|
|
165
|
+
*
|
|
166
|
+
* Provider instances are cached by `(domain, resolvedPath)` for cases 2–4.
|
|
167
|
+
* Case 1 (explicit storage) is not cached because the storage is per-session.
|
|
168
|
+
*/
|
|
169
|
+
declare function createMemoryProvider(opts: CreateMemoryProviderOptions): AFSMemory;
|
|
170
|
+
/**
|
|
171
|
+
* Overlay `/memory/*` onto `afs`, routing to `provider`. Delegates via
|
|
172
|
+
* `Object.create(afs)` so siblings / other mounts on the same scope still
|
|
173
|
+
* resolve through the parent's prototype chain.
|
|
174
|
+
*
|
|
175
|
+
* We forward to `provider.<op>(providerPath(path), …)` rather than to an
|
|
176
|
+
* internal AFS + `afs.mount(provider, "/memory")` — the provider's
|
|
177
|
+
* `AFSBaseProvider` already takes absolute-from-root paths, so one layer
|
|
178
|
+
* of routing is enough. Skipping the extra AFS means we don't have to
|
|
179
|
+
* await a `mount()` during scope projection (projectForChild is sync).
|
|
180
|
+
*
|
|
181
|
+
* Unlike the sibling `attachScratchMount` (which asserts valid scratch
|
|
182
|
+
* segments at the overlay), we defer path-traversal / null-byte /
|
|
183
|
+
* separator checks to `AFSMemory`'s own `AFSBaseProvider`. The provider
|
|
184
|
+
* enforces the memory segment grammar at every action entry point, so a
|
|
185
|
+
* parallel check here would be redundant. See
|
|
186
|
+
* `providers/core/memory/src/provider.ts` and its action handlers.
|
|
187
|
+
*/
|
|
188
|
+
declare function attachMemoryMount<T extends AFSRoot>(afs: T, provider: AFSMemory): T;
|
|
189
|
+
/**
|
|
190
|
+
* Resolve the domain used to instantiate a scope's AFSMemory.
|
|
191
|
+
*
|
|
192
|
+
* Precedence (design doc §3.3):
|
|
193
|
+
* 1. Explicit `manifest.memory.domain`
|
|
194
|
+
* 2. Parent scope's current memory domain (if already memory-backed)
|
|
195
|
+
* 3. The child's member name, derived from memberPath (final non-empty segment)
|
|
196
|
+
*
|
|
197
|
+
* The resulting string is validated against the memory provider's segment
|
|
198
|
+
* grammar; path traversal, separators, and empty strings all fail loudly.
|
|
199
|
+
*/
|
|
200
|
+
declare function resolveMemoryDomain(opts: {
|
|
201
|
+
manifestMemory: unknown;
|
|
202
|
+
parentDomain?: string;
|
|
203
|
+
memberPath: string;
|
|
204
|
+
}): string;
|
|
205
|
+
/**
|
|
206
|
+
* Unified surface helper — mount /memory/ when a manifest declares memory.
|
|
207
|
+
*
|
|
208
|
+
* Consolidates the 3-step pattern (resolveMemoryDomain → createMemoryProvider →
|
|
209
|
+
* attachMemoryMount) that previously appeared independently in each surface
|
|
210
|
+
* handler (chat: agent-handler.ts, terminal: agent-runner-registry.ts).
|
|
211
|
+
* All surfaces call this instead of replicating the logic.
|
|
212
|
+
*
|
|
213
|
+
* Safe to call unconditionally:
|
|
214
|
+
* - skips when manifestMemory is falsy
|
|
215
|
+
* - skips when the caller's AFS already carries a memory domain in scope
|
|
216
|
+
* (inherits parent mount rather than layering a fresh provider)
|
|
217
|
+
*/
|
|
218
|
+
declare function mountManifestMemory<T extends AFSRoot>(afs: T, manifestMemory: unknown, memberPath: string): T;
|
|
219
|
+
//#endregion
|
|
220
|
+
export { CreateMemoryProviderOptions, MEMORY_SCOPE_ROOT, SESSION_MEMORY_STORAGE_KEY, SessionIndexConfig, attachMemoryMount, attachSessionIndex, attachSessionMemoryStorage, buildMemoryIndexOptsForAfs, buildMemoryStorageForAfs, clearAgentIndexAfsForTesting, clearAgentMemoryRootForTesting, clearMemoryProviderCacheForTesting, createMemoryProvider, isMemoryPath, mountManifestMemory, readSessionIndex, resolveMemoryDomain, setAgentIndexAfs, setAgentMemoryRoot };
|
|
221
|
+
//# sourceMappingURL=memory-afs.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-afs.d.mts","names":[],"sources":["../../src/agent-runners/memory-afs.ts"],"mappings":";;;;cAmCa,iBAAA;;iBAIG,YAAA,CAAa,IAAA;AAAA,UAcZ,2BAAA;EAkDM;;;;;;AAwBvB;EAxBuB,SA1CZ,MAAA;;;;;WAKA,IAAA;EA6DyE;;AAKpF;;;;;AAgBA;;;;;AAKA;EA1BoF,SA/CzE,WAAA;;;;AAwFX;;;;WAhFW,OAAA,GAAU,aAAA;EA0FL;;;;;EAAA,SApFL,YAAA;EAoFuD;;;;AAclE;;;;EAdkE,SA3EvD,OAAA;IAAY,IAAA,OAAW,IAAA;EAAA;EAyFmC;;AAKrE;;EALqE,SApF1D,cAAA;EAyFwB;;;;EAAA,SApFxB,cAAA;AAAA;;;AAqGX;;;;;;;iBAvFgB,gBAAA,CAAiB,GAAA;EAAO,IAAA,OAAW,IAAA;AAAA;AAiGnD;AAAA,iBA5FgB,4BAAA,CAAA;;;;;;;;AAqGhB;;;iBArFgB,kBAAA,CAAmB,IAAA;;iBAKnB,8BAAA,CAAA;;;;;;;;;AA+FhB;;;cAhFa,0BAAA;;AAmHb;;;;;;;iBAzGgB,0BAAA,CAA2B,GAAA,EAAK,OAAA,EAAS,OAAA,EAAS,aAAA;;AA6LlE;;;;iBA/KgB,wBAAA,CAAyB,GAAA,EAAK,OAAA,GAAU,aAAA;;UAKvC,kBAAA;EA0KmE;EAAA,SAxKzE,OAAA;IAAW,IAAA,OAAW,IAAA;EAAA;EAAA,SACtB,cAAA;EAAA,SACA,cAAA;AAAA;;;;;AA+PX;;;;iBAlPgB,kBAAA,CAAmB,GAAA,EAAK,OAAA,EAAS,MAAA,EAAQ,kBAAA;;iBAUzC,gBAAA,CAAiB,GAAA,EAAK,OAAA,GAAU,kBAAA;;;;AA8QhD;;iBArQgB,0BAAA,CACd,GAAA,EAAK,OAAA,GACJ,IAAA,CAAK,2BAAA;;iBAaQ,kCAAA,CAAA;;;;;;;;;;;;;;;;iBAmCA,oBAAA,CAAqB,IAAA,EAAM,2BAAA,GAA8B,SAAA;;;;;;;;;;;;;;;;;;;iBAoFzD,iBAAA,WAA4B,OAAA,CAAA,CAAS,GAAA,EAAK,CAAA,EAAG,QAAA,EAAU,SAAA,GAAY,CAAA;;;;;;;;;;;;iBAyFnE,mBAAA,CAAoB,IAAA;EAClC,cAAA;EACA,YAAA;EACA,UAAA;AAAA;;;;;;;;;;;;;;iBAmCc,mBAAA,WAA8B,OAAA,CAAA,CAC5C,GAAA,EAAK,CAAA,EACL,cAAA,WACA,UAAA,WACC,CAAA"}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { AFSMemory, FSStorage, InMemoryStorage } from "@aigne/afs-memory";
|
|
2
|
+
|
|
3
|
+
//#region src/agent-runners/memory-afs.ts
|
|
4
|
+
const MEMORY_SCOPE_ROOT = "/memory";
|
|
5
|
+
const MEMORY_PREFIX = `${MEMORY_SCOPE_ROOT}/`;
|
|
6
|
+
/** Paths that must route to the scope-local memory provider. */
|
|
7
|
+
function isMemoryPath(path) {
|
|
8
|
+
return path === MEMORY_SCOPE_ROOT || path.startsWith(MEMORY_PREFIX);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Strip the `/memory` mount prefix, yielding the provider-relative path
|
|
12
|
+
* (`/memory/.actions/remember` → `/.actions/remember`, `/memory` → `/`).
|
|
13
|
+
*/
|
|
14
|
+
function providerPath(path) {
|
|
15
|
+
if (path === MEMORY_SCOPE_ROOT) return "/";
|
|
16
|
+
const rest = path.slice(7);
|
|
17
|
+
return rest.startsWith("/") ? rest : `/${rest}`;
|
|
18
|
+
}
|
|
19
|
+
let agentIndexAfs;
|
|
20
|
+
/**
|
|
21
|
+
* Daemon wire-up companion to `setAgentMemoryRoot`: attach a global AFS
|
|
22
|
+
* reference that the per-scope memory provider can use to dispatch
|
|
23
|
+
* `/modules/index/.actions/index` calls. Without this, `AFSMemory`'s
|
|
24
|
+
* built-in `indexEntry` step (every observation goes through the
|
|
25
|
+
* configured index) silently no-ops because the scope-local provider
|
|
26
|
+
* has no `afsRoot` set (it's attached via Object.create overlay, not
|
|
27
|
+
* `afs.mount()`).
|
|
28
|
+
*/
|
|
29
|
+
function setAgentIndexAfs(afs) {
|
|
30
|
+
agentIndexAfs = afs;
|
|
31
|
+
}
|
|
32
|
+
/** Test-only — used by `afterEach` to drop the configured index AFS. */
|
|
33
|
+
function clearAgentIndexAfsForTesting() {
|
|
34
|
+
agentIndexAfs = void 0;
|
|
35
|
+
}
|
|
36
|
+
let agentMemoryRoot;
|
|
37
|
+
/**
|
|
38
|
+
* Daemon wire-up: configure a process-wide root directory for agent
|
|
39
|
+
* memory. When set, `createMemoryProvider({ domain })` (no `storagePath`)
|
|
40
|
+
* resolves storage to `<root>/<domain>/` via `FSStorage`. Production
|
|
41
|
+
* daemons set this once at startup so cross-session recall works
|
|
42
|
+
* without each call site threading the path.
|
|
43
|
+
*
|
|
44
|
+
* Tests should clear via `clearAgentMemoryRootForTesting()` between
|
|
45
|
+
* cases to keep the cache clean.
|
|
46
|
+
*/
|
|
47
|
+
function setAgentMemoryRoot(root) {
|
|
48
|
+
agentMemoryRoot = root;
|
|
49
|
+
}
|
|
50
|
+
/** Test-only — used by `afterEach` to reset between cases. */
|
|
51
|
+
function clearAgentMemoryRootForTesting() {
|
|
52
|
+
agentMemoryRoot = void 0;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Symbol key used to attach per-session memory storage to the session AFS.
|
|
56
|
+
* Propagates through `Object.create(sessionAfs)` prototype chains, so all
|
|
57
|
+
* scope-local AFS objects derived from the session AFS inherit it without
|
|
58
|
+
* explicit threading.
|
|
59
|
+
*
|
|
60
|
+
* Set via `attachSessionMemoryStorage()` in `buildCfSessionView` (CF) and
|
|
61
|
+
* `buildSessionView` (Node) for authenticated callers. Anonymous callers
|
|
62
|
+
* or sessions without a DID Space get no storage attached (undefined →
|
|
63
|
+
* falls through to FSStorage / InMemoryStorage legacy path).
|
|
64
|
+
*/
|
|
65
|
+
const SESSION_MEMORY_STORAGE_KEY = Symbol("sessionMemoryStorage");
|
|
66
|
+
/**
|
|
67
|
+
* Attach an AFS-backed memory storage to the session AFS so that all
|
|
68
|
+
* scope-local AFS objects derived from it (via `Object.create`) inherit
|
|
69
|
+
* the storage via prototype-chain lookup.
|
|
70
|
+
*
|
|
71
|
+
* Call this in `buildCfSessionView` / `buildSessionView` for authenticated
|
|
72
|
+
* callers: `attachSessionMemoryStorage(sessionView, new AFSBackedStorage(sessionView, "/user/memory"))`.
|
|
73
|
+
*/
|
|
74
|
+
function attachSessionMemoryStorage(afs, storage) {
|
|
75
|
+
Object.defineProperty(afs, SESSION_MEMORY_STORAGE_KEY, {
|
|
76
|
+
value: storage,
|
|
77
|
+
writable: false,
|
|
78
|
+
configurable: false,
|
|
79
|
+
enumerable: false
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Build memory storage for the given scope AFS using the session-attached
|
|
84
|
+
* storage (set via `attachSessionMemoryStorage`). Returns undefined when no
|
|
85
|
+
* storage is attached (anonymous callers, tests without wiring).
|
|
86
|
+
*/
|
|
87
|
+
function buildMemoryStorageForAfs(afs) {
|
|
88
|
+
return afs[SESSION_MEMORY_STORAGE_KEY];
|
|
89
|
+
}
|
|
90
|
+
const SESSION_INDEX_KEY = Symbol("sessionIndexConfig");
|
|
91
|
+
/**
|
|
92
|
+
* Point per-caller memory at a per-caller index. Companion to
|
|
93
|
+
* `attachSessionMemoryStorage`: when set, `createMemoryProvider` dispatches
|
|
94
|
+
* `indexEntry`/`queryIndex` to `config.afsRoot` at the given paths instead of
|
|
95
|
+
* the process-global `agentIndexAfs` + `/modules/index`. Propagates through
|
|
96
|
+
* `Object.create(sessionAfs)` prototype chains. CF calls this in
|
|
97
|
+
* `buildCfSessionView`; Node leaves it unset (keeps the global index).
|
|
98
|
+
*/
|
|
99
|
+
function attachSessionIndex(afs, config) {
|
|
100
|
+
Object.defineProperty(afs, SESSION_INDEX_KEY, {
|
|
101
|
+
value: config,
|
|
102
|
+
writable: false,
|
|
103
|
+
configurable: false,
|
|
104
|
+
enumerable: false
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/** Read the session-attached index config (via prototype chain), or undefined. */
|
|
108
|
+
function readSessionIndex(afs) {
|
|
109
|
+
return afs[SESSION_INDEX_KEY];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Spread-ready index opts for `createMemoryProvider`, derived from the session's
|
|
113
|
+
* attached index config. Empty object when none is attached (Node / anonymous)
|
|
114
|
+
* → the provider keeps the process-global index root + default `/modules/index`.
|
|
115
|
+
*/
|
|
116
|
+
function buildMemoryIndexOptsForAfs(afs) {
|
|
117
|
+
const cfg = readSessionIndex(afs);
|
|
118
|
+
if (!cfg) return {};
|
|
119
|
+
return {
|
|
120
|
+
afsRoot: cfg.afsRoot,
|
|
121
|
+
indexQueryPath: cfg.indexQueryPath,
|
|
122
|
+
indexWritePath: cfg.indexWritePath
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const providerCache = /* @__PURE__ */ new Map();
|
|
126
|
+
/** Test-only — used by `afterEach` to drop cached instances. */
|
|
127
|
+
function clearMemoryProviderCacheForTesting() {
|
|
128
|
+
providerCache.clear();
|
|
129
|
+
}
|
|
130
|
+
function resolveStoragePath(opts) {
|
|
131
|
+
if (typeof opts.storagePath === "string" && opts.storagePath.length > 0) return opts.storagePath;
|
|
132
|
+
if (typeof agentMemoryRoot === "string" && agentMemoryRoot.length > 0) return agentMemoryRoot;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Instantiate an `AFSMemory` suitable for a scope-local `/memory/` mount.
|
|
136
|
+
*
|
|
137
|
+
* Storage selection follows this order:
|
|
138
|
+
*
|
|
139
|
+
* 1. Explicit `opts.storage` — used as-is, not cached (caller controls lifecycle).
|
|
140
|
+
* 2. Explicit `opts.storagePath` (typically test fixtures).
|
|
141
|
+
* 3. Process-wide `agentMemoryRoot` set by the daemon — yields
|
|
142
|
+
* `<root>/<domain>` via `FSStorage`. Cross-session recall lives here.
|
|
143
|
+
* 4. Default `InMemoryStorage` — used when no persistence is configured
|
|
144
|
+
* (legacy + most tests).
|
|
145
|
+
*
|
|
146
|
+
* Provider instances are cached by `(domain, resolvedPath)` for cases 2–4.
|
|
147
|
+
* Case 1 (explicit storage) is not cached because the storage is per-session.
|
|
148
|
+
*/
|
|
149
|
+
function createMemoryProvider(opts) {
|
|
150
|
+
validateDomain(opts.domain);
|
|
151
|
+
if (opts.storage) {
|
|
152
|
+
const provider$1 = new AFSMemory({
|
|
153
|
+
name: opts.name ?? `memory:${opts.domain}`,
|
|
154
|
+
defaultDomain: opts.domain,
|
|
155
|
+
enforceDefaultDomain: true,
|
|
156
|
+
storage: opts.storage,
|
|
157
|
+
indexQueryPath: opts.indexQueryPath,
|
|
158
|
+
indexWritePath: opts.indexWritePath
|
|
159
|
+
});
|
|
160
|
+
const indexAfs = opts.afsRoot ?? agentIndexAfs;
|
|
161
|
+
if (indexAfs) provider$1.afsRoot = indexAfs;
|
|
162
|
+
return provider$1;
|
|
163
|
+
}
|
|
164
|
+
const storagePath = resolveStoragePath(opts);
|
|
165
|
+
const cacheKey = `${opts.domain}::${storagePath ?? ":memory:"}`;
|
|
166
|
+
if (!opts._bypassCache) {
|
|
167
|
+
const cached = providerCache.get(cacheKey);
|
|
168
|
+
if (cached) return cached;
|
|
169
|
+
}
|
|
170
|
+
const storage = storagePath ? new FSStorage(storagePath) : new InMemoryStorage();
|
|
171
|
+
const provider = new AFSMemory({
|
|
172
|
+
name: opts.name ?? `memory:${opts.domain}`,
|
|
173
|
+
defaultDomain: opts.domain,
|
|
174
|
+
enforceDefaultDomain: true,
|
|
175
|
+
storage
|
|
176
|
+
});
|
|
177
|
+
if (agentIndexAfs) provider.afsRoot = agentIndexAfs;
|
|
178
|
+
if (!opts._bypassCache) providerCache.set(cacheKey, provider);
|
|
179
|
+
return provider;
|
|
180
|
+
}
|
|
181
|
+
function validateDomain(domain) {
|
|
182
|
+
if (typeof domain !== "string" || domain.length === 0) throw new Error(`MEMORY_DOMAIN_INVALID: domain must be a non-empty string`);
|
|
183
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(domain)) throw new Error(`MEMORY_DOMAIN_INVALID: domain "${domain}" must match [a-zA-Z0-9_-]+ — segments, null bytes, and path traversal are rejected`);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Overlay `/memory/*` onto `afs`, routing to `provider`. Delegates via
|
|
187
|
+
* `Object.create(afs)` so siblings / other mounts on the same scope still
|
|
188
|
+
* resolve through the parent's prototype chain.
|
|
189
|
+
*
|
|
190
|
+
* We forward to `provider.<op>(providerPath(path), …)` rather than to an
|
|
191
|
+
* internal AFS + `afs.mount(provider, "/memory")` — the provider's
|
|
192
|
+
* `AFSBaseProvider` already takes absolute-from-root paths, so one layer
|
|
193
|
+
* of routing is enough. Skipping the extra AFS means we don't have to
|
|
194
|
+
* await a `mount()` during scope projection (projectForChild is sync).
|
|
195
|
+
*
|
|
196
|
+
* Unlike the sibling `attachScratchMount` (which asserts valid scratch
|
|
197
|
+
* segments at the overlay), we defer path-traversal / null-byte /
|
|
198
|
+
* separator checks to `AFSMemory`'s own `AFSBaseProvider`. The provider
|
|
199
|
+
* enforces the memory segment grammar at every action entry point, so a
|
|
200
|
+
* parallel check here would be redundant. See
|
|
201
|
+
* `providers/core/memory/src/provider.ts` and its action handlers.
|
|
202
|
+
*/
|
|
203
|
+
function attachMemoryMount(afs, provider) {
|
|
204
|
+
const child = Object.create(afs);
|
|
205
|
+
Object.defineProperty(child, "read", {
|
|
206
|
+
value: async (path, options) => {
|
|
207
|
+
if (isMemoryPath(path)) return provider.read(providerPath(path), options);
|
|
208
|
+
return afs.read.call(afs, path, options);
|
|
209
|
+
},
|
|
210
|
+
enumerable: false,
|
|
211
|
+
writable: false,
|
|
212
|
+
configurable: false
|
|
213
|
+
});
|
|
214
|
+
Object.defineProperty(child, "list", {
|
|
215
|
+
value: async (path, options) => {
|
|
216
|
+
if (isMemoryPath(path)) return provider.list(providerPath(path), options);
|
|
217
|
+
return afs.list.call(afs, path, options);
|
|
218
|
+
},
|
|
219
|
+
enumerable: false,
|
|
220
|
+
writable: false,
|
|
221
|
+
configurable: false
|
|
222
|
+
});
|
|
223
|
+
Object.defineProperty(child, "exec", {
|
|
224
|
+
value: async (path, args, options) => {
|
|
225
|
+
if (isMemoryPath(path)) return provider.exec(providerPath(path), args, options);
|
|
226
|
+
return afs.exec.call(afs, path, args, options);
|
|
227
|
+
},
|
|
228
|
+
enumerable: false,
|
|
229
|
+
writable: false,
|
|
230
|
+
configurable: false
|
|
231
|
+
});
|
|
232
|
+
Object.defineProperty(child, "write", {
|
|
233
|
+
value: async (path, payload, options) => {
|
|
234
|
+
if (isMemoryPath(path)) return provider.write(providerPath(path), payload, options);
|
|
235
|
+
if (typeof afs.write !== "function") throw new Error(`MEMORY_MOUNT_NO_UNDERLYING_WRITE: afs.write not available for ${path}`);
|
|
236
|
+
return afs.write.call(afs, path, payload, options);
|
|
237
|
+
},
|
|
238
|
+
enumerable: false,
|
|
239
|
+
writable: false,
|
|
240
|
+
configurable: false
|
|
241
|
+
});
|
|
242
|
+
Object.defineProperty(child, "stat", {
|
|
243
|
+
value: async (path, options) => {
|
|
244
|
+
if (isMemoryPath(path)) return provider.stat(providerPath(path), options);
|
|
245
|
+
return afs.stat.call(afs, path, options);
|
|
246
|
+
},
|
|
247
|
+
enumerable: false,
|
|
248
|
+
writable: false,
|
|
249
|
+
configurable: false
|
|
250
|
+
});
|
|
251
|
+
Object.defineProperty(child, "delete", {
|
|
252
|
+
value: async (path, options) => {
|
|
253
|
+
if (isMemoryPath(path)) return provider.delete(providerPath(path), options);
|
|
254
|
+
if (typeof afs.delete !== "function") return { success: false };
|
|
255
|
+
return afs.delete.call(afs, path, options);
|
|
256
|
+
},
|
|
257
|
+
enumerable: false,
|
|
258
|
+
writable: false,
|
|
259
|
+
configurable: false
|
|
260
|
+
});
|
|
261
|
+
return child;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Resolve the domain used to instantiate a scope's AFSMemory.
|
|
265
|
+
*
|
|
266
|
+
* Precedence (design doc §3.3):
|
|
267
|
+
* 1. Explicit `manifest.memory.domain`
|
|
268
|
+
* 2. Parent scope's current memory domain (if already memory-backed)
|
|
269
|
+
* 3. The child's member name, derived from memberPath (final non-empty segment)
|
|
270
|
+
*
|
|
271
|
+
* The resulting string is validated against the memory provider's segment
|
|
272
|
+
* grammar; path traversal, separators, and empty strings all fail loudly.
|
|
273
|
+
*/
|
|
274
|
+
function resolveMemoryDomain(opts) {
|
|
275
|
+
const fromManifest = extractManifestDomain(opts.manifestMemory);
|
|
276
|
+
if (fromManifest) {
|
|
277
|
+
validateDomain(fromManifest);
|
|
278
|
+
return fromManifest;
|
|
279
|
+
}
|
|
280
|
+
if (opts.parentDomain) {
|
|
281
|
+
validateDomain(opts.parentDomain);
|
|
282
|
+
return opts.parentDomain;
|
|
283
|
+
}
|
|
284
|
+
const memberName = deriveMemberId(opts.memberPath);
|
|
285
|
+
validateDomain(memberName);
|
|
286
|
+
return memberName;
|
|
287
|
+
}
|
|
288
|
+
function extractManifestDomain(manifestMemory) {
|
|
289
|
+
if (typeof manifestMemory !== "object" || manifestMemory === null) return void 0;
|
|
290
|
+
const domain = manifestMemory.domain;
|
|
291
|
+
return typeof domain === "string" && domain.length > 0 ? domain : void 0;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Unified surface helper — mount /memory/ when a manifest declares memory.
|
|
295
|
+
*
|
|
296
|
+
* Consolidates the 3-step pattern (resolveMemoryDomain → createMemoryProvider →
|
|
297
|
+
* attachMemoryMount) that previously appeared independently in each surface
|
|
298
|
+
* handler (chat: agent-handler.ts, terminal: agent-runner-registry.ts).
|
|
299
|
+
* All surfaces call this instead of replicating the logic.
|
|
300
|
+
*
|
|
301
|
+
* Safe to call unconditionally:
|
|
302
|
+
* - skips when manifestMemory is falsy
|
|
303
|
+
* - skips when the caller's AFS already carries a memory domain in scope
|
|
304
|
+
* (inherits parent mount rather than layering a fresh provider)
|
|
305
|
+
*/
|
|
306
|
+
function mountManifestMemory(afs, manifestMemory, memberPath) {
|
|
307
|
+
if (!manifestMemory || typeof manifestMemory !== "object" && manifestMemory !== true) return afs;
|
|
308
|
+
if (afs.scope?.memoryDomain) return afs;
|
|
309
|
+
return attachMemoryMount(afs, createMemoryProvider({
|
|
310
|
+
domain: resolveMemoryDomain({
|
|
311
|
+
manifestMemory,
|
|
312
|
+
memberPath
|
|
313
|
+
}),
|
|
314
|
+
storage: buildMemoryStorageForAfs(afs),
|
|
315
|
+
...buildMemoryIndexOptsForAfs(afs)
|
|
316
|
+
}));
|
|
317
|
+
}
|
|
318
|
+
function deriveMemberId(memberPath) {
|
|
319
|
+
const trimmed = memberPath.replace(/\/+$/, "");
|
|
320
|
+
const cleaned = trimmed.slice(trimmed.lastIndexOf("/") + 1).replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
321
|
+
return cleaned.length > 0 ? cleaned : "scope";
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
//#endregion
|
|
325
|
+
export { MEMORY_SCOPE_ROOT, SESSION_MEMORY_STORAGE_KEY, attachMemoryMount, attachSessionIndex, attachSessionMemoryStorage, buildMemoryIndexOptsForAfs, buildMemoryStorageForAfs, clearAgentIndexAfsForTesting, clearAgentMemoryRootForTesting, clearMemoryProviderCacheForTesting, createMemoryProvider, extractManifestDomain, isMemoryPath, mountManifestMemory, readSessionIndex, resolveMemoryDomain, setAgentIndexAfs, setAgentMemoryRoot };
|
|
326
|
+
//# sourceMappingURL=memory-afs.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-afs.mjs","names":["provider"],"sources":["../../src/agent-runners/memory-afs.ts"],"sourcesContent":["/**\n * Memory Projection — per-scope `/memory/*` overlay backed by a fresh\n * `AFSMemory` provider instance.\n *\n * Pre-Phase-3 each blocklet shared the runtime-level `/modules/memory/`\n * mount and separated tenants via a `domain` arg. Phase 3 replaces that\n * with physical isolation: every scope that declares `memory: true`\n * owns its own `AFSMemory` + `InMemoryStorage` (swap-in FSStorage later),\n * and the overlay here routes reads/writes/lists/execs on `/memory/*`\n * straight to that instance. Other paths fall through to the parent AFS\n * via the prototype chain.\n *\n * Why per-scope rather than one shared instance: see\n * `docs/architecture/agent-memory.md` §1.3 — domain strings are a\n * semantic tag, not a security boundary. Physical isolation at the\n * provider instance level is what keeps one blocklet's memory from\n * being reachable by another, regardless of what arg the LLM sends.\n */\n\nimport type {\n AFSExecOptions,\n AFSExecResult,\n AFSListOptions,\n AFSListResult,\n AFSReadOptions,\n AFSReadResult,\n AFSRoot,\n AFSStatOptions,\n AFSStatResult,\n AFSWriteOptions,\n AFSWriteResult,\n} from \"@aigne/afs\";\nimport type { MemoryStorage } from \"@aigne/afs-memory\";\nimport { AFSMemory, FSStorage, InMemoryStorage } from \"@aigne/afs-memory\";\n\nexport const MEMORY_SCOPE_ROOT = \"/memory\";\nconst MEMORY_PREFIX = `${MEMORY_SCOPE_ROOT}/`;\n\n/** Paths that must route to the scope-local memory provider. */\nexport function isMemoryPath(path: string): boolean {\n return path === MEMORY_SCOPE_ROOT || path.startsWith(MEMORY_PREFIX);\n}\n\n/**\n * Strip the `/memory` mount prefix, yielding the provider-relative path\n * (`/memory/.actions/remember` → `/.actions/remember`, `/memory` → `/`).\n */\nfunction providerPath(path: string): string {\n if (path === MEMORY_SCOPE_ROOT) return \"/\";\n const rest = path.slice(MEMORY_SCOPE_ROOT.length);\n return rest.startsWith(\"/\") ? rest : `/${rest}`;\n}\n\nexport interface CreateMemoryProviderOptions {\n /**\n * Logical tenant. Baked into the provider as `defaultDomain`; any action\n * that omits `domain` in its args falls back to this value. The overlay\n * treats `domain` as fixed-at-construction — runtime callers cannot\n * widen it by passing a different arg, because storage is already\n * tenant-scoped.\n */\n readonly domain: string;\n /**\n * Optional instance name (shows up in descriptions / traces). Defaults\n * to `memory:<domain>`.\n */\n readonly name?: string;\n /**\n * When set, persist observations to the local filesystem under this\n * directory via `FSStorage`. When unset, falls back to the\n * process-wide `agentMemoryRoot` (set via `setAgentMemoryRoot()`); when\n * neither is set, falls back to `InMemoryStorage` (legacy behavior).\n *\n * The provider keyed by `(domain, resolvedStoragePath)` is cached\n * process-wide so multiple scopes for the same tenant share the same\n * `AFSMemory` instance — without that, two scopes pointing at the same\n * directory would each get their own `FSStorage`, doubling on-disk\n * fan-out and confusing the `consolidate`/`aging` background jobs that\n * AFSMemory schedules at construction time.\n */\n readonly storagePath?: string;\n /**\n * Explicit storage backend. When provided, skips path-based storage\n * selection (storagePath / agentMemoryRoot) and uses this directly.\n * Used when the session AFS is the backing store (DID Space persistence).\n * Providers created with explicit storage are NOT cached (each caller\n * has a distinct backing AFS).\n */\n readonly storage?: MemoryStorage;\n /**\n * Test-only escape hatch: bypass the provider cache and always return a\n * fresh instance. Used by the persistence test to prove storage is at\n * the FS layer (not at the JS-object layer).\n */\n readonly _bypassCache?: boolean;\n /**\n * Explicit AFS root for index dispatch (indexEntry on remember/archive,\n * queryIndex on recall). When set, overrides the process-global\n * `agentIndexAfs` for THIS provider — used by CF per-caller memory, where\n * the caller's session view (holding `/user/index`) is the correct index\n * root, not a process global (which would be shared across concurrent\n * callers). Node leaves this unset → keeps the global `/modules/index`.\n */\n readonly afsRoot?: { exec?: (...args: unknown[]) => unknown };\n /**\n * Override the default index query path (`/modules/index/.actions/query`).\n * Set to `/user/index/.actions/query` for per-caller user-axis indexing.\n */\n readonly indexQueryPath?: string;\n /**\n * Override the default index write path (`/modules/index/.actions/index`).\n * Set to `/user/index/.actions/index` for per-caller user-axis indexing.\n */\n readonly indexWritePath?: string;\n}\n\nlet agentIndexAfs: { exec?: (...args: any[]) => any } | undefined;\n\n/**\n * Daemon wire-up companion to `setAgentMemoryRoot`: attach a global AFS\n * reference that the per-scope memory provider can use to dispatch\n * `/modules/index/.actions/index` calls. Without this, `AFSMemory`'s\n * built-in `indexEntry` step (every observation goes through the\n * configured index) silently no-ops because the scope-local provider\n * has no `afsRoot` set (it's attached via Object.create overlay, not\n * `afs.mount()`).\n */\nexport function setAgentIndexAfs(afs: { exec?: (...args: any[]) => any } | undefined): void {\n agentIndexAfs = afs;\n}\n\n/** Test-only — used by `afterEach` to drop the configured index AFS. */\nexport function clearAgentIndexAfsForTesting(): void {\n agentIndexAfs = undefined;\n}\n\nlet agentMemoryRoot: string | undefined;\n\n/**\n * Daemon wire-up: configure a process-wide root directory for agent\n * memory. When set, `createMemoryProvider({ domain })` (no `storagePath`)\n * resolves storage to `<root>/<domain>/` via `FSStorage`. Production\n * daemons set this once at startup so cross-session recall works\n * without each call site threading the path.\n *\n * Tests should clear via `clearAgentMemoryRootForTesting()` between\n * cases to keep the cache clean.\n */\nexport function setAgentMemoryRoot(root: string | undefined): void {\n agentMemoryRoot = root;\n}\n\n/** Test-only — used by `afterEach` to reset between cases. */\nexport function clearAgentMemoryRootForTesting(): void {\n agentMemoryRoot = undefined;\n}\n\n/**\n * Symbol key used to attach per-session memory storage to the session AFS.\n * Propagates through `Object.create(sessionAfs)` prototype chains, so all\n * scope-local AFS objects derived from the session AFS inherit it without\n * explicit threading.\n *\n * Set via `attachSessionMemoryStorage()` in `buildCfSessionView` (CF) and\n * `buildSessionView` (Node) for authenticated callers. Anonymous callers\n * or sessions without a DID Space get no storage attached (undefined →\n * falls through to FSStorage / InMemoryStorage legacy path).\n */\nexport const SESSION_MEMORY_STORAGE_KEY: unique symbol = Symbol(\"sessionMemoryStorage\");\n\n/**\n * Attach an AFS-backed memory storage to the session AFS so that all\n * scope-local AFS objects derived from it (via `Object.create`) inherit\n * the storage via prototype-chain lookup.\n *\n * Call this in `buildCfSessionView` / `buildSessionView` for authenticated\n * callers: `attachSessionMemoryStorage(sessionView, new AFSBackedStorage(sessionView, \"/user/memory\"))`.\n */\nexport function attachSessionMemoryStorage(afs: AFSRoot, storage: MemoryStorage): void {\n Object.defineProperty(afs, SESSION_MEMORY_STORAGE_KEY, {\n value: storage,\n writable: false,\n configurable: false,\n enumerable: false,\n });\n}\n\n/**\n * Build memory storage for the given scope AFS using the session-attached\n * storage (set via `attachSessionMemoryStorage`). Returns undefined when no\n * storage is attached (anonymous callers, tests without wiring).\n */\nexport function buildMemoryStorageForAfs(afs: AFSRoot): MemoryStorage | undefined {\n return (afs as unknown as Record<symbol, MemoryStorage | undefined>)[SESSION_MEMORY_STORAGE_KEY];\n}\n\n/** Per-session index dispatch config, attached in `buildCfSessionView`. */\nexport interface SessionIndexConfig {\n /** AFS whose `indexQueryPath`/`indexWritePath` resolve to the caller's index. */\n readonly afsRoot: { exec?: (...args: unknown[]) => unknown };\n readonly indexQueryPath: string;\n readonly indexWritePath: string;\n}\n\nconst SESSION_INDEX_KEY: unique symbol = Symbol(\"sessionIndexConfig\");\n\n/**\n * Point per-caller memory at a per-caller index. Companion to\n * `attachSessionMemoryStorage`: when set, `createMemoryProvider` dispatches\n * `indexEntry`/`queryIndex` to `config.afsRoot` at the given paths instead of\n * the process-global `agentIndexAfs` + `/modules/index`. Propagates through\n * `Object.create(sessionAfs)` prototype chains. CF calls this in\n * `buildCfSessionView`; Node leaves it unset (keeps the global index).\n */\nexport function attachSessionIndex(afs: AFSRoot, config: SessionIndexConfig): void {\n Object.defineProperty(afs, SESSION_INDEX_KEY, {\n value: config,\n writable: false,\n configurable: false,\n enumerable: false,\n });\n}\n\n/** Read the session-attached index config (via prototype chain), or undefined. */\nexport function readSessionIndex(afs: AFSRoot): SessionIndexConfig | undefined {\n return (afs as unknown as Record<symbol, SessionIndexConfig | undefined>)[SESSION_INDEX_KEY];\n}\n\n/**\n * Spread-ready index opts for `createMemoryProvider`, derived from the session's\n * attached index config. Empty object when none is attached (Node / anonymous)\n * → the provider keeps the process-global index root + default `/modules/index`.\n */\nexport function buildMemoryIndexOptsForAfs(\n afs: AFSRoot,\n): Pick<CreateMemoryProviderOptions, \"afsRoot\" | \"indexQueryPath\" | \"indexWritePath\"> {\n const cfg = readSessionIndex(afs);\n if (!cfg) return {};\n return {\n afsRoot: cfg.afsRoot,\n indexQueryPath: cfg.indexQueryPath,\n indexWritePath: cfg.indexWritePath,\n };\n}\n\nconst providerCache = new Map<string, AFSMemory>();\n\n/** Test-only — used by `afterEach` to drop cached instances. */\nexport function clearMemoryProviderCacheForTesting(): void {\n providerCache.clear();\n}\n\nfunction resolveStoragePath(opts: CreateMemoryProviderOptions): string | undefined {\n if (typeof opts.storagePath === \"string\" && opts.storagePath.length > 0) {\n return opts.storagePath;\n }\n if (typeof agentMemoryRoot === \"string\" && agentMemoryRoot.length > 0) {\n // The memory provider already namespaces every path by domain\n // (`/<domain>/observations/<id>`), and FSStorage just maps that\n // through to a JSON file. Returning the root unmodified makes the\n // on-disk layout `<root>/<domain>/observations/<id>.json` cleanly —\n // joining `domain` here once would yield `<root>/<domain>/<domain>/...`,\n // an extra level that breaks recall and consolidates jobs.\n return agentMemoryRoot;\n }\n return undefined;\n}\n\n/**\n * Instantiate an `AFSMemory` suitable for a scope-local `/memory/` mount.\n *\n * Storage selection follows this order:\n *\n * 1. Explicit `opts.storage` — used as-is, not cached (caller controls lifecycle).\n * 2. Explicit `opts.storagePath` (typically test fixtures).\n * 3. Process-wide `agentMemoryRoot` set by the daemon — yields\n * `<root>/<domain>` via `FSStorage`. Cross-session recall lives here.\n * 4. Default `InMemoryStorage` — used when no persistence is configured\n * (legacy + most tests).\n *\n * Provider instances are cached by `(domain, resolvedPath)` for cases 2–4.\n * Case 1 (explicit storage) is not cached because the storage is per-session.\n */\nexport function createMemoryProvider(opts: CreateMemoryProviderOptions): AFSMemory {\n validateDomain(opts.domain);\n\n // Case 1: explicit storage — bypass cache, create directly. This is the CF\n // per-caller path (storage = AFSBackedStorage over /user/memory), so honor an\n // explicit per-caller index root + paths when provided (falls back to the\n // process-global agentIndexAfs + default /modules/index when unset).\n if (opts.storage) {\n const provider = new AFSMemory({\n name: opts.name ?? `memory:${opts.domain}`,\n defaultDomain: opts.domain,\n enforceDefaultDomain: true,\n storage: opts.storage,\n indexQueryPath: opts.indexQueryPath,\n indexWritePath: opts.indexWritePath,\n });\n const indexAfs = opts.afsRoot ?? agentIndexAfs;\n if (indexAfs) {\n (provider as unknown as { afsRoot?: unknown }).afsRoot = indexAfs;\n }\n return provider;\n }\n\n const storagePath = resolveStoragePath(opts);\n const cacheKey = `${opts.domain}::${storagePath ?? \":memory:\"}`;\n\n if (!opts._bypassCache) {\n const cached = providerCache.get(cacheKey);\n if (cached) return cached;\n }\n\n const storage = storagePath ? new FSStorage(storagePath) : new InMemoryStorage();\n\n const provider = new AFSMemory({\n name: opts.name ?? `memory:${opts.domain}`,\n defaultDomain: opts.domain,\n enforceDefaultDomain: true,\n storage,\n });\n\n // Hand the daemon-configured AFS reference to the provider so its\n // `indexEntry` step (every remember/archive/boost/feedback) can\n // dispatch `/modules/index/.actions/index`. AFSBaseProvider's\n // `mount()` would normally set this on the provider during\n // `afs.mount()`, but scope-local memory mounts go through a\n // `Object.create(afs)` overlay (see `attachMemoryMount`) that\n // skips the lifecycle hook — so we set it here directly.\n if (agentIndexAfs) {\n (provider as unknown as { afsRoot?: unknown }).afsRoot = agentIndexAfs;\n }\n\n if (!opts._bypassCache) providerCache.set(cacheKey, provider);\n return provider;\n}\n\nfunction validateDomain(domain: string): void {\n if (typeof domain !== \"string\" || domain.length === 0) {\n throw new Error(`MEMORY_DOMAIN_INVALID: domain must be a non-empty string`);\n }\n if (!/^[a-zA-Z0-9_-]+$/.test(domain)) {\n throw new Error(\n `MEMORY_DOMAIN_INVALID: domain \"${domain}\" must match [a-zA-Z0-9_-]+ — segments, null bytes, and path traversal are rejected`,\n );\n }\n}\n\n/**\n * Overlay `/memory/*` onto `afs`, routing to `provider`. Delegates via\n * `Object.create(afs)` so siblings / other mounts on the same scope still\n * resolve through the parent's prototype chain.\n *\n * We forward to `provider.<op>(providerPath(path), …)` rather than to an\n * internal AFS + `afs.mount(provider, \"/memory\")` — the provider's\n * `AFSBaseProvider` already takes absolute-from-root paths, so one layer\n * of routing is enough. Skipping the extra AFS means we don't have to\n * await a `mount()` during scope projection (projectForChild is sync).\n *\n * Unlike the sibling `attachScratchMount` (which asserts valid scratch\n * segments at the overlay), we defer path-traversal / null-byte /\n * separator checks to `AFSMemory`'s own `AFSBaseProvider`. The provider\n * enforces the memory segment grammar at every action entry point, so a\n * parallel check here would be redundant. See\n * `providers/core/memory/src/provider.ts` and its action handlers.\n */\nexport function attachMemoryMount<T extends AFSRoot>(afs: T, provider: AFSMemory): T {\n const child = Object.create(afs) as T;\n\n Object.defineProperty(child, \"read\", {\n value: async (path: string, options?: AFSReadOptions): Promise<AFSReadResult> => {\n if (isMemoryPath(path)) return provider.read(providerPath(path), options);\n return (afs.read as any)!.call(afs, path, options);\n },\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(child, \"list\", {\n value: async (path: string, options?: AFSListOptions): Promise<AFSListResult> => {\n if (isMemoryPath(path)) return provider.list(providerPath(path), options);\n return (afs.list as any)!.call(afs, path, options);\n },\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(child, \"exec\", {\n value: async (\n path: string,\n args: Record<string, any>,\n options: AFSExecOptions,\n ): Promise<AFSExecResult> => {\n if (isMemoryPath(path)) return provider.exec(providerPath(path), args, options);\n return (afs.exec as any)!.call(afs, path, args, options);\n },\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(child, \"write\", {\n value: async (\n path: string,\n payload: { content: string },\n options?: AFSWriteOptions,\n ): Promise<AFSWriteResult> => {\n if (isMemoryPath(path)) return provider.write(providerPath(path), payload, options);\n if (typeof afs.write !== \"function\") {\n throw new Error(`MEMORY_MOUNT_NO_UNDERLYING_WRITE: afs.write not available for ${path}`);\n }\n return (afs.write as any)!.call(afs, path, payload, options);\n },\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(child, \"stat\", {\n value: async (path: string, options?: AFSStatOptions): Promise<AFSStatResult> => {\n if (isMemoryPath(path)) return provider.stat(providerPath(path), options);\n return (afs.stat as any)!.call(afs, path, options);\n },\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n Object.defineProperty(child, \"delete\", {\n value: async (path: string, options?: { recursive?: boolean }) => {\n if (isMemoryPath(path)) return provider.delete(providerPath(path), options);\n if (typeof (afs as any).delete !== \"function\") return { success: false } as any;\n return (afs as any).delete!.call(afs, path, options);\n },\n enumerable: false,\n writable: false,\n configurable: false,\n });\n\n return child;\n}\n\n/**\n * Resolve the domain used to instantiate a scope's AFSMemory.\n *\n * Precedence (design doc §3.3):\n * 1. Explicit `manifest.memory.domain`\n * 2. Parent scope's current memory domain (if already memory-backed)\n * 3. The child's member name, derived from memberPath (final non-empty segment)\n *\n * The resulting string is validated against the memory provider's segment\n * grammar; path traversal, separators, and empty strings all fail loudly.\n */\nexport function resolveMemoryDomain(opts: {\n manifestMemory: unknown;\n parentDomain?: string;\n memberPath: string;\n}): string {\n const fromManifest = extractManifestDomain(opts.manifestMemory);\n if (fromManifest) {\n validateDomain(fromManifest);\n return fromManifest;\n }\n if (opts.parentDomain) {\n validateDomain(opts.parentDomain);\n return opts.parentDomain;\n }\n const memberName = deriveMemberId(opts.memberPath);\n validateDomain(memberName);\n return memberName;\n}\n\nexport function extractManifestDomain(manifestMemory: unknown): string | undefined {\n if (typeof manifestMemory !== \"object\" || manifestMemory === null) return undefined;\n const domain = (manifestMemory as { domain?: unknown }).domain;\n return typeof domain === \"string\" && domain.length > 0 ? domain : undefined;\n}\n\n/**\n * Unified surface helper — mount /memory/ when a manifest declares memory.\n *\n * Consolidates the 3-step pattern (resolveMemoryDomain → createMemoryProvider →\n * attachMemoryMount) that previously appeared independently in each surface\n * handler (chat: agent-handler.ts, terminal: agent-runner-registry.ts).\n * All surfaces call this instead of replicating the logic.\n *\n * Safe to call unconditionally:\n * - skips when manifestMemory is falsy\n * - skips when the caller's AFS already carries a memory domain in scope\n * (inherits parent mount rather than layering a fresh provider)\n */\nexport function mountManifestMemory<T extends AFSRoot>(\n afs: T,\n manifestMemory: unknown,\n memberPath: string,\n): T {\n if (!manifestMemory || (typeof manifestMemory !== \"object\" && manifestMemory !== true))\n return afs;\n if ((afs as { scope?: { memoryDomain?: string } }).scope?.memoryDomain) return afs;\n const domain = resolveMemoryDomain({ manifestMemory, memberPath });\n // Prefer session-attached AFS-backed storage (DID Space /user/memory) when\n // available (authenticated callers). Falls back to FSStorage / InMemoryStorage.\n const storage = buildMemoryStorageForAfs(afs as AFSRoot);\n const provider = createMemoryProvider({\n domain,\n storage,\n ...buildMemoryIndexOptsForAfs(afs as AFSRoot),\n });\n return attachMemoryMount(afs, provider);\n}\n\nfunction deriveMemberId(memberPath: string): string {\n const trimmed = memberPath.replace(/\\/+$/, \"\");\n const tail = trimmed.slice(trimmed.lastIndexOf(\"/\") + 1);\n // Memory segment regex rejects \".\", so collapse leading/trailing dots and\n // turn unsafe separators into hyphens rather than fabricating opaque ids.\n const cleaned = tail.replace(/[^a-zA-Z0-9_-]/g, \"-\");\n return cleaned.length > 0 ? cleaned : \"scope\";\n}\n"],"mappings":"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"}
|