om-memory-system 5.5.0 → 5.7.0-next.218
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/adapters/pi/extension.js +5 -0
- package/dist/cli/space-command.d.ts +6 -5
- package/dist/cli/space-command.js +154 -13
- package/dist/cli/space-transfer-command.d.ts +25 -0
- package/dist/cli/space-transfer-command.js +79 -0
- package/dist/core/memory-operations.d.ts +12 -2
- package/dist/core/memory-operations.js +14 -4
- package/dist/core/recall-summary.js +7 -5
- package/dist/core/retrieval.js +5 -2
- package/dist/core/space-tool.d.ts +9 -4
- package/dist/core/space-tool.js +35 -3
- package/dist/services/active-space-access.d.ts +6 -0
- package/dist/services/active-space-access.js +8 -0
- package/dist/services/chat/chat-runtime.js +1 -1
- package/dist/services/client.d.ts +6 -16
- package/dist/services/client.js +54 -62
- package/dist/services/context.d.ts +2 -0
- package/dist/services/context.js +12 -1
- package/dist/services/linked-search.d.ts +108 -0
- package/dist/services/linked-search.js +219 -0
- package/dist/services/space-routes.d.ts +2 -0
- package/dist/services/space-routes.js +4 -3
- package/dist/services/space-transfer-routes.d.ts +10 -0
- package/dist/services/space-transfer-routes.js +344 -0
- package/dist/services/spaces/active-space.js +14 -7
- package/dist/services/spaces/delete.d.ts +2 -0
- package/dist/services/spaces/delete.js +4 -0
- package/dist/services/spaces/export-space.d.ts +60 -0
- package/dist/services/spaces/export-space.js +367 -0
- package/dist/services/spaces/identifier-scrub.d.ts +26 -0
- package/dist/services/spaces/identifier-scrub.js +40 -0
- package/dist/services/spaces/import-space.d.ts +85 -0
- package/dist/services/spaces/import-space.js +349 -0
- package/dist/services/spaces/operations.d.ts +57 -5
- package/dist/services/spaces/operations.js +161 -5
- package/dist/services/spaces/read-store-cache.d.ts +13 -0
- package/dist/services/spaces/read-store-cache.js +34 -0
- package/dist/services/spaces/registry-store.d.ts +4 -0
- package/dist/services/spaces/registry-store.js +15 -1
- package/dist/services/spaces/registry.d.ts +32 -0
- package/dist/services/spaces/registry.js +81 -7
- package/dist/services/spaces/space-store.d.ts +50 -0
- package/dist/services/spaces/space-store.js +72 -0
- package/dist/services/spaces/transfer-service.d.ts +41 -0
- package/dist/services/spaces/transfer-service.js +408 -0
- package/dist/services/spaces/transfer-types.d.ts +163 -0
- package/dist/services/spaces/transfer-types.js +10 -0
- package/dist/services/spaces/transfer.d.ts +31 -0
- package/dist/services/spaces/transfer.js +276 -0
- package/dist/services/turso/connection-manager.d.ts +4 -2
- package/dist/services/turso/connection-manager.js +11 -5
- package/dist/services/turso/cross-process-write-lock.d.ts +1 -1
- package/dist/services/turso/cross-process-write-lock.js +5 -5
- package/dist/services/turso/operation-lock.d.ts +2 -2
- package/dist/services/turso/operation-lock.js +5 -5
- package/dist/services/turso/shard-manager.d.ts +8 -0
- package/dist/services/turso/shard-manager.js +23 -22
- package/dist/services/turso/vector-search.d.ts +6 -2
- package/dist/services/turso/vector-search.js +8 -4
- package/dist/services/user-profile/user-profile-manager.d.ts +5 -1
- package/dist/services/user-profile/user-profile-manager.js +13 -10
- package/dist/services/web-route-modules.d.ts +5 -1
- package/dist/services/web-route-modules.js +4 -0
- package/dist/services/web-server.js +1 -1
- package/dist/shared/api/space-export-schemas.d.ts +159 -0
- package/dist/shared/api/space-export-schemas.js +72 -0
- package/dist/v2/adapter.d.ts +15 -0
- package/dist/v2/adapter.js +5 -0
- package/dist/web/assets/index-2BNRLGqe.css +2 -0
- package/dist/web/assets/index-C6WnDE6h.js +170 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/skills/omms-memory/SKILL.md +16 -14
- package/dist/web/assets/index-Dcvz3EXt.js +0 -170
- package/dist/web/assets/index-b8k5Tuei.css +0 -2
package/README.md
CHANGED
|
@@ -119,7 +119,7 @@ cannot be saved.
|
|
|
119
119
|
|
|
120
120
|
### Memory spaces
|
|
121
121
|
|
|
122
|
-
Keep separate stores for separate lines of work. The Spaces item in the web app sidebar opens a page with one card for each space. Switching a space applies to every host. See [Memory spaces](docs/memory-spaces.md).
|
|
122
|
+
Keep separate stores for separate lines of work. The Spaces item in the web app sidebar opens a page with one card for each space. Switching a space applies to every host. Memories can be copied or moved between spaces, exported to a shareable file and imported into a new space, and a space can read the spaces it links. See [Memory spaces](docs/memory-spaces.md).
|
|
123
123
|
|
|
124
124
|
### Settings
|
|
125
125
|
|
|
@@ -350,6 +350,11 @@ export default function ommsPiExtension(pi) {
|
|
|
350
350
|
allowLinkedSource: Type.Optional(Type.Boolean()),
|
|
351
351
|
spaceAction: Type.Optional(Type.String()),
|
|
352
352
|
spaceName: Type.Optional(Type.String()),
|
|
353
|
+
spaceTo: Type.Optional(Type.String()),
|
|
354
|
+
spaceSince: Type.Optional(Type.String()),
|
|
355
|
+
spaceUntil: Type.Optional(Type.String()),
|
|
356
|
+
spaceProject: Type.Optional(Type.String()),
|
|
357
|
+
spaceIn: Type.Optional(Type.String()),
|
|
353
358
|
}),
|
|
354
359
|
async execute(_toolCallId, args, _signal, _onUpdate, ctx) {
|
|
355
360
|
const result = await executeMemoryOperation(args, {
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `om-memory-system space <action> [name]`: list, create, switch, archive
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* `om-memory-system space <action> [name] [options]`: list, create, switch, archive,
|
|
3
|
+
* unarchive, preview, copy, link, unlink, export and import memory spaces from a
|
|
4
|
+
* terminal. Deleting, renaming, backing up, restoring and moving exist only in the
|
|
5
|
+
* web app, so those actions reach the shared operation and are refused there.
|
|
6
|
+
* Parsing stays free of engine imports so `--help` is fast.
|
|
6
7
|
*/
|
|
7
8
|
export interface SpaceCommandIO {
|
|
8
9
|
cwd?: string;
|
|
9
10
|
stdout?: (text: string) => void;
|
|
10
11
|
stderr?: (text: string) => void;
|
|
11
12
|
}
|
|
12
|
-
export declare const spaceCommandUsage = "Usage: om-memory-system space <action> [name]\n\nActions:\n list List spaces and show the active one\n status Show the space this project uses now\n create <name> Add an empty space (it does not become active)\n use <name> Make a space the active one for every host, from the next turn\n archive <name> Hide a space from the switcher and keep its data\n unarchive <name> Show an archived space again\n\n<name> is a space name or id.
|
|
13
|
+
export declare const spaceCommandUsage = "Usage: om-memory-system space <action> [name] [options]\n\nActions:\n list List spaces and show the active one\n status Show the space this project uses now\n create <name> Add an empty space (it does not become active)\n use <name> Make a space the active one for every host, from the next turn\n archive <name> Hide a space from the switcher and keep its data\n unarchive <name> Show an archived space again\n preview [--from <a>] --to <b> [--since <date>] [--until <date>] [--project <tag>]\n Count the memories a copy would carry; writes nothing\n copy [--from <a>] --to <b> [--since <date>] [--until <date>] [--project <tag>]\n Copy memories into another space; the source stays unchanged\n link <name> [--in <space>]\n Make <name> searchable from <space> (the active space by default)\n unlink <name> [--in <space>]\n Remove that link\n export <name> --out <file> [--since <date>] [--until <date>] [--include-profile]\n Write one space to a JSON file that is safe to share\n import <file> --name <new> [--retag-to-project <folder>]\n Create a new space from an export file\n\n<name> is a space name or id. Dates are ISO 8601 and both bounds are inclusive;\n--project takes the project tag a preview's groups show. Moving memories between\nspaces, and deleting, renaming, backing up or restoring a space, are only possible\nin the web app.\n\nExamples:\n om-memory-system space list\n om-memory-system space create \"Client work\"\n om-memory-system space use \"Client work\"\n om-memory-system space copy --from Experiments --to \"Client work\" --since 2026-10-01\n om-memory-system space link \"Client work\" --in Experiments\n om-memory-system space export \"Client work\" --out client.json";
|
|
13
14
|
/** Run `om-memory-system space ...` and return the process exit code. */
|
|
14
15
|
export declare function runSpaceCommand(argv: string[], io?: SpaceCommandIO): Promise<number>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { SPACE_ACTIONS, WEB_ONLY_SPACE_ACTIONS } from "../services/spaces/operations.js";
|
|
3
|
-
|
|
3
|
+
import { runSpaceExport, runSpaceImport } from "./space-transfer-command.js";
|
|
4
|
+
export const spaceCommandUsage = `Usage: om-memory-system space <action> [name] [options]
|
|
4
5
|
|
|
5
6
|
Actions:
|
|
6
7
|
list List spaces and show the active one
|
|
@@ -9,36 +10,176 @@ Actions:
|
|
|
9
10
|
use <name> Make a space the active one for every host, from the next turn
|
|
10
11
|
archive <name> Hide a space from the switcher and keep its data
|
|
11
12
|
unarchive <name> Show an archived space again
|
|
13
|
+
preview [--from <a>] --to <b> [--since <date>] [--until <date>] [--project <tag>]
|
|
14
|
+
Count the memories a copy would carry; writes nothing
|
|
15
|
+
copy [--from <a>] --to <b> [--since <date>] [--until <date>] [--project <tag>]
|
|
16
|
+
Copy memories into another space; the source stays unchanged
|
|
17
|
+
link <name> [--in <space>]
|
|
18
|
+
Make <name> searchable from <space> (the active space by default)
|
|
19
|
+
unlink <name> [--in <space>]
|
|
20
|
+
Remove that link
|
|
21
|
+
export <name> --out <file> [--since <date>] [--until <date>] [--include-profile]
|
|
22
|
+
Write one space to a JSON file that is safe to share
|
|
23
|
+
import <file> --name <new> [--retag-to-project <folder>]
|
|
24
|
+
Create a new space from an export file
|
|
12
25
|
|
|
13
|
-
<name> is a space name or id.
|
|
14
|
-
|
|
26
|
+
<name> is a space name or id. Dates are ISO 8601 and both bounds are inclusive;
|
|
27
|
+
--project takes the project tag a preview's groups show. Moving memories between
|
|
28
|
+
spaces, and deleting, renaming, backing up or restoring a space, are only possible
|
|
29
|
+
in the web app.
|
|
15
30
|
|
|
16
31
|
Examples:
|
|
17
32
|
om-memory-system space list
|
|
18
33
|
om-memory-system space create "Client work"
|
|
19
|
-
om-memory-system space use "Client work"
|
|
34
|
+
om-memory-system space use "Client work"
|
|
35
|
+
om-memory-system space copy --from Experiments --to "Client work" --since 2026-10-01
|
|
36
|
+
om-memory-system space link "Client work" --in Experiments
|
|
37
|
+
om-memory-system space export "Client work" --out client.json`;
|
|
38
|
+
/** Every action the parser admits; the shared operation refuses the web-only ones. */
|
|
39
|
+
const KNOWN_ACTIONS = new Set([
|
|
40
|
+
...SPACE_ACTIONS,
|
|
41
|
+
...WEB_ONLY_SPACE_ACTIONS,
|
|
42
|
+
"move",
|
|
43
|
+
"export",
|
|
44
|
+
"import",
|
|
45
|
+
]);
|
|
46
|
+
/** The value flags of the transfer forms. `--directory` is the runner's own flag. */
|
|
47
|
+
const VALUE_FLAGS = new Set([
|
|
48
|
+
"--from",
|
|
49
|
+
"--to",
|
|
50
|
+
"--since",
|
|
51
|
+
"--until",
|
|
52
|
+
"--project",
|
|
53
|
+
"--in",
|
|
54
|
+
"--out",
|
|
55
|
+
"--name",
|
|
56
|
+
"--retag-to-project",
|
|
57
|
+
"--directory",
|
|
58
|
+
]);
|
|
59
|
+
/** How many positional arguments each action takes and which value flags it allows. */
|
|
60
|
+
const ACTION_FORMS = {
|
|
61
|
+
list: { positionals: 0, flags: new Set() },
|
|
62
|
+
status: { positionals: 0, flags: new Set() },
|
|
63
|
+
create: { positionals: 1, flags: new Set() },
|
|
64
|
+
use: { positionals: 1, flags: new Set() },
|
|
65
|
+
archive: { positionals: 1, flags: new Set() },
|
|
66
|
+
unarchive: { positionals: 1, flags: new Set() },
|
|
67
|
+
delete: { positionals: 1, flags: new Set() },
|
|
68
|
+
rename: { positionals: 1, flags: new Set() },
|
|
69
|
+
backup: { positionals: 1, flags: new Set() },
|
|
70
|
+
restore: { positionals: 1, flags: new Set() },
|
|
71
|
+
move: { positionals: 1, flags: new Set(["--from", "--to"]) },
|
|
72
|
+
preview: {
|
|
73
|
+
positionals: 1,
|
|
74
|
+
flags: new Set(["--from", "--to", "--since", "--until", "--project"]),
|
|
75
|
+
},
|
|
76
|
+
copy: { positionals: 1, flags: new Set(["--from", "--to", "--since", "--until", "--project"]) },
|
|
77
|
+
link: { positionals: 1, flags: new Set(["--in"]) },
|
|
78
|
+
unlink: { positionals: 1, flags: new Set(["--in"]) },
|
|
79
|
+
export: { positionals: 1, flags: new Set(["--out", "--since", "--until"]) },
|
|
80
|
+
import: { positionals: 1, flags: new Set(["--name", "--retag-to-project"]) },
|
|
81
|
+
};
|
|
82
|
+
/** One pass over the argument list; every option is `--flag value` or a positional. */
|
|
83
|
+
function parseSpaceArgs(argv) {
|
|
84
|
+
const parsed = {
|
|
85
|
+
values: new Map(),
|
|
86
|
+
includeProfile: false,
|
|
87
|
+
positionals: [],
|
|
88
|
+
directory: undefined,
|
|
89
|
+
};
|
|
90
|
+
for (let i = 0; i < argv.length; i++) {
|
|
91
|
+
const token = argv[i];
|
|
92
|
+
if (token === "--include-profile") {
|
|
93
|
+
parsed.includeProfile = true;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (token.startsWith("--")) {
|
|
97
|
+
if (!VALUE_FLAGS.has(token))
|
|
98
|
+
return `Unknown option "${token}".`;
|
|
99
|
+
const value = argv[++i];
|
|
100
|
+
if (value === undefined)
|
|
101
|
+
return `Option "${token}" needs a value.`;
|
|
102
|
+
parsed.values.set(token.slice(2), value);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
parsed.positionals.push(token);
|
|
106
|
+
}
|
|
107
|
+
parsed.directory = parsed.values.get("directory");
|
|
108
|
+
return parsed;
|
|
109
|
+
}
|
|
20
110
|
/** Run `om-memory-system space ...` and return the process exit code. */
|
|
21
111
|
export async function runSpaceCommand(argv, io = {}) {
|
|
22
112
|
const stdout = io.stdout ?? ((text) => console.log(text));
|
|
23
113
|
const stderr = io.stderr ?? ((text) => console.error(text));
|
|
24
|
-
const [action,
|
|
114
|
+
const [action, ...rest] = argv;
|
|
25
115
|
if (action === undefined || action === "--help" || action === "-h") {
|
|
26
116
|
stdout(spaceCommandUsage);
|
|
27
117
|
return 0;
|
|
28
118
|
}
|
|
29
|
-
const
|
|
30
|
-
if (!known.includes(action) || extra.length > 0 || (name?.startsWith("--") ?? false)) {
|
|
31
|
-
const problem = extra.length > 0 ? `Unexpected argument "${extra[0]}"` : `Unknown space action "${action}"`;
|
|
119
|
+
const fail = (problem) => {
|
|
32
120
|
stderr(`${problem}\n\n${spaceCommandUsage}`);
|
|
33
121
|
return 1;
|
|
122
|
+
};
|
|
123
|
+
if (!KNOWN_ACTIONS.has(action))
|
|
124
|
+
return fail(`Unknown space action "${action}"`);
|
|
125
|
+
const parsed = parseSpaceArgs(rest);
|
|
126
|
+
if (typeof parsed === "string")
|
|
127
|
+
return fail(parsed);
|
|
128
|
+
const form = ACTION_FORMS[action];
|
|
129
|
+
const { values, positionals, includeProfile } = parsed;
|
|
130
|
+
if (positionals.length > form.positionals) {
|
|
131
|
+
return fail(`Unexpected argument "${positionals[form.positionals]}"`);
|
|
132
|
+
}
|
|
133
|
+
for (const flag of values.keys()) {
|
|
134
|
+
if (flag === "directory")
|
|
135
|
+
continue;
|
|
136
|
+
if (!form.flags.has(`--${flag}`))
|
|
137
|
+
return fail(`The ${action} action does not take --${flag}.`);
|
|
138
|
+
}
|
|
139
|
+
if (includeProfile && action !== "export") {
|
|
140
|
+
return fail(`The ${action} action does not take --include-profile.`);
|
|
34
141
|
}
|
|
35
|
-
const directory = resolve(io.cwd ?? process.cwd());
|
|
142
|
+
const directory = resolve(parsed.directory ?? io.cwd ?? process.cwd());
|
|
36
143
|
let result;
|
|
37
144
|
try {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
145
|
+
if (action === "export" || action === "import") {
|
|
146
|
+
// The portability runners own their config bridge; the memory tool has none.
|
|
147
|
+
result =
|
|
148
|
+
action === "export"
|
|
149
|
+
? await runSpaceExport({
|
|
150
|
+
name: positionals[0] ?? "",
|
|
151
|
+
outPath: values.get("out") ?? "",
|
|
152
|
+
...(values.has("since") ? { since: values.get("since") } : {}),
|
|
153
|
+
...(values.has("until") ? { until: values.get("until") } : {}),
|
|
154
|
+
...(includeProfile ? { includeProfile: true } : {}),
|
|
155
|
+
}, directory)
|
|
156
|
+
: await runSpaceImport({
|
|
157
|
+
filePath: positionals[0] ?? "",
|
|
158
|
+
name: values.get("name") ?? "",
|
|
159
|
+
...(values.has("retag-to-project")
|
|
160
|
+
? { retagToProject: values.get("retag-to-project") }
|
|
161
|
+
: {}),
|
|
162
|
+
}, directory);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const { initConfig } = await import("../config.js");
|
|
166
|
+
initConfig(directory);
|
|
167
|
+
const { runSpaceMode } = await import("../core/space-tool.js");
|
|
168
|
+
result = await runSpaceMode({
|
|
169
|
+
mode: "space",
|
|
170
|
+
spaceAction: action,
|
|
171
|
+
// The transfer source may be positional or --from; the other actions take a name.
|
|
172
|
+
spaceName: positionals[0] ??
|
|
173
|
+
(action === "preview" || action === "copy" || action === "move"
|
|
174
|
+
? values.get("from")
|
|
175
|
+
: undefined),
|
|
176
|
+
...(values.has("to") ? { spaceTo: values.get("to") } : {}),
|
|
177
|
+
...(values.has("since") ? { spaceSince: values.get("since") } : {}),
|
|
178
|
+
...(values.has("until") ? { spaceUntil: values.get("until") } : {}),
|
|
179
|
+
...(values.has("project") ? { spaceProject: values.get("project") } : {}),
|
|
180
|
+
...(values.has("in") ? { spaceIn: values.get("in") } : {}),
|
|
181
|
+
}, directory);
|
|
182
|
+
}
|
|
42
183
|
}
|
|
43
184
|
catch (error) {
|
|
44
185
|
result = { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `space export` and `space import` runners (design D8). Both are CLI-only: the
|
|
3
|
+
* `memory` tool `space` mode refuses them. This file is the one place that bridges
|
|
4
|
+
* CONFIG into `exportSpace` and `importSpace`, which themselves never read CONFIG;
|
|
5
|
+
* the shared space operation runs them only when the CLI hands it this bridge.
|
|
6
|
+
*/
|
|
7
|
+
export interface SpaceExportCliRequest {
|
|
8
|
+
/** The space to export: a name or id. */
|
|
9
|
+
name: string;
|
|
10
|
+
outPath: string;
|
|
11
|
+
since?: string;
|
|
12
|
+
until?: string;
|
|
13
|
+
includeProfile?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface SpaceImportCliRequest {
|
|
16
|
+
/** Path of the export file to read. */
|
|
17
|
+
filePath: string;
|
|
18
|
+
/** The name of the new space the file becomes. */
|
|
19
|
+
name: string;
|
|
20
|
+
retagToProject?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Runs `space export <name> --out <file>` through the shared space operation. */
|
|
23
|
+
export declare function runSpaceExport(request: SpaceExportCliRequest, directory: string): Promise<Record<string, unknown>>;
|
|
24
|
+
/** Runs `space import <file> --name <new>` through the shared space operation. */
|
|
25
|
+
export declare function runSpaceImport(request: SpaceImportCliRequest, directory: string): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
/** The bridge the CLI hands the shared operation: the export/import capability made real. */
|
|
3
|
+
async function portabilityBridge(files, directory) {
|
|
4
|
+
const { CONFIG } = await import("../config.js");
|
|
5
|
+
return {
|
|
6
|
+
export: async (request) => {
|
|
7
|
+
const { exportSpace, getOmmsVersion } = await import("../services/spaces/export-space.js");
|
|
8
|
+
return exportSpace({
|
|
9
|
+
...request,
|
|
10
|
+
embeddingModel: CONFIG.embeddingModel,
|
|
11
|
+
embeddingDimensions: CONFIG.embeddingDimensions,
|
|
12
|
+
ommsVersion: getOmmsVersion(),
|
|
13
|
+
homePath: homedir(),
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
import: async (request) => {
|
|
17
|
+
const { importSpace } = await import("../services/spaces/import-space.js");
|
|
18
|
+
const { embeddingService } = await import("../services/embedding.js");
|
|
19
|
+
const { getTags } = await import("../services/tags.js");
|
|
20
|
+
const user = getTags(directory).user;
|
|
21
|
+
return importSpace({
|
|
22
|
+
...request,
|
|
23
|
+
files,
|
|
24
|
+
embedding: { model: CONFIG.embeddingModel, dimensions: CONFIG.embeddingDimensions },
|
|
25
|
+
embed: (text) => embeddingService.embedWithTimeout(text, { task: "document" }),
|
|
26
|
+
user: {
|
|
27
|
+
userTag: user.tag,
|
|
28
|
+
displayName: user.displayName,
|
|
29
|
+
...(user.userName ? { userName: user.userName } : {}),
|
|
30
|
+
...(user.userEmail ? { userEmail: user.userEmail } : {}),
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Runs `space export <name> --out <file>` through the shared space operation. */
|
|
37
|
+
export async function runSpaceExport(request, directory) {
|
|
38
|
+
const { CONFIG, getActiveSpace, initConfig } = await import("../config.js");
|
|
39
|
+
initConfig(directory);
|
|
40
|
+
const { executeSpaceOperation } = await import("../services/spaces/operations.js");
|
|
41
|
+
const { spaceFiles } = await import("../services/spaces/registry-store.js");
|
|
42
|
+
const { ommsHomeDir } = await import("../services/spaces/storage-resolution.js");
|
|
43
|
+
const { spaceOperationSettings } = await import("../core/space-tool.js");
|
|
44
|
+
const files = spaceFiles(ommsHomeDir());
|
|
45
|
+
return executeSpaceOperation({
|
|
46
|
+
action: "export",
|
|
47
|
+
name: request.name,
|
|
48
|
+
outPath: request.outPath,
|
|
49
|
+
...(request.since === undefined ? {} : { since: request.since }),
|
|
50
|
+
...(request.until === undefined ? {} : { until: request.until }),
|
|
51
|
+
...(request.includeProfile ? { includeProfile: true } : {}),
|
|
52
|
+
}, {
|
|
53
|
+
files,
|
|
54
|
+
current: getActiveSpace(),
|
|
55
|
+
settings: spaceOperationSettings(CONFIG, files.spacesRoot),
|
|
56
|
+
portability: await portabilityBridge(files, directory),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/** Runs `space import <file> --name <new>` through the shared space operation. */
|
|
60
|
+
export async function runSpaceImport(request, directory) {
|
|
61
|
+
const { CONFIG, getActiveSpace, initConfig } = await import("../config.js");
|
|
62
|
+
initConfig(directory);
|
|
63
|
+
const { executeSpaceOperation } = await import("../services/spaces/operations.js");
|
|
64
|
+
const { spaceFiles } = await import("../services/spaces/registry-store.js");
|
|
65
|
+
const { ommsHomeDir } = await import("../services/spaces/storage-resolution.js");
|
|
66
|
+
const { spaceOperationSettings } = await import("../core/space-tool.js");
|
|
67
|
+
const files = spaceFiles(ommsHomeDir());
|
|
68
|
+
return executeSpaceOperation({
|
|
69
|
+
action: "import",
|
|
70
|
+
name: request.name,
|
|
71
|
+
filePath: request.filePath,
|
|
72
|
+
...(request.retagToProject === undefined ? {} : { retagToProject: request.retagToProject }),
|
|
73
|
+
}, {
|
|
74
|
+
files,
|
|
75
|
+
current: getActiveSpace(),
|
|
76
|
+
settings: spaceOperationSettings(CONFIG, files.spacesRoot),
|
|
77
|
+
portability: await portabilityBridge(files, directory),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -17,10 +17,20 @@ export interface MemoryOperationArgs {
|
|
|
17
17
|
inputPath?: string;
|
|
18
18
|
dryRun?: boolean;
|
|
19
19
|
allowLinkedSource?: boolean;
|
|
20
|
-
/** `space` mode: list, status, create, archive, unarchive or
|
|
20
|
+
/** `space` mode: list, status, create, archive, unarchive, use, preview, copy, link or unlink. */
|
|
21
21
|
spaceAction?: string;
|
|
22
|
-
/** `space` mode: the space name or id. */
|
|
22
|
+
/** `space` mode: the space name or id; the transfer source or the link target. */
|
|
23
23
|
spaceName?: string;
|
|
24
|
+
/** `space` mode: the transfer target (preview, copy). */
|
|
25
|
+
spaceTo?: string;
|
|
26
|
+
/** `space` mode: inclusive ISO 8601 lower bound on the creation time (preview, copy). */
|
|
27
|
+
spaceSince?: string;
|
|
28
|
+
/** `space` mode: inclusive ISO 8601 upper bound on the creation time (preview, copy). */
|
|
29
|
+
spaceUntil?: string;
|
|
30
|
+
/** `space` mode: only memories with this project tag (preview, copy). */
|
|
31
|
+
spaceProject?: string;
|
|
32
|
+
/** `space` mode: the space that owns a link (link, unlink); the active space when omitted. */
|
|
33
|
+
spaceIn?: string;
|
|
24
34
|
}
|
|
25
35
|
export interface MemoryOperationContext {
|
|
26
36
|
directory: string;
|
|
@@ -14,6 +14,8 @@ function formatSearchResults(query, results, limit) {
|
|
|
14
14
|
content: result.memory || result.chunk,
|
|
15
15
|
type: result.type,
|
|
16
16
|
similarity: Math.round(result.similarity * 100),
|
|
17
|
+
// A linked result carries its space so the caller can tell it is read-only.
|
|
18
|
+
...(result.spaceName ? { space: result.spaceName } : {}),
|
|
17
19
|
})),
|
|
18
20
|
};
|
|
19
21
|
}
|
|
@@ -81,8 +83,16 @@ export async function executeMemoryOperation(args, context) {
|
|
|
81
83
|
{ command: "list", description: "List recent memories", args: ["limit?"] },
|
|
82
84
|
{
|
|
83
85
|
command: "space",
|
|
84
|
-
description: "Manage memory spaces: list, status, create, archive, unarchive, use.
|
|
85
|
-
args: [
|
|
86
|
+
description: "Manage memory spaces: list, status, create, archive, unarchive, use, preview, copy, link, unlink. Moving, deleting, exporting and importing are not offered here",
|
|
87
|
+
args: [
|
|
88
|
+
"spaceAction?",
|
|
89
|
+
"spaceName?",
|
|
90
|
+
"spaceTo?",
|
|
91
|
+
"spaceSince?",
|
|
92
|
+
"spaceUntil?",
|
|
93
|
+
"spaceProject?",
|
|
94
|
+
"spaceIn?",
|
|
95
|
+
],
|
|
86
96
|
},
|
|
87
97
|
{ command: "forget", description: "Remove memory", args: ["memoryId"] },
|
|
88
98
|
{
|
|
@@ -141,7 +151,7 @@ export async function executeMemoryOperation(args, context) {
|
|
|
141
151
|
case "search": {
|
|
142
152
|
if (!args.query)
|
|
143
153
|
return { success: false, error: "query required" };
|
|
144
|
-
const result = await memoryClient.searchMemories(args.query, tags.project.tag, args.scope ?? CONFIG.memory.defaultScope);
|
|
154
|
+
const result = await memoryClient.searchMemories(args.query, tags.project.tag, args.scope ?? CONFIG.memory.defaultScope, { includeLinked: true });
|
|
145
155
|
if (!result.success)
|
|
146
156
|
return { success: false, error: result.error };
|
|
147
157
|
return formatSearchResults(args.query, result, args.limit);
|
|
@@ -210,7 +220,7 @@ export async function executeMemoryOperation(args, context) {
|
|
|
210
220
|
};
|
|
211
221
|
}
|
|
212
222
|
case "list": {
|
|
213
|
-
const result = await memoryClient.listMemories(tags.project.tag, args.limit || 20, args.scope ?? CONFIG.memory.defaultScope);
|
|
223
|
+
const result = await memoryClient.listMemories(tags.project.tag, args.limit || 20, args.scope ?? CONFIG.memory.defaultScope, { includeLinked: true });
|
|
214
224
|
if (!result.success)
|
|
215
225
|
return { success: false, error: result.error };
|
|
216
226
|
return {
|
|
@@ -7,12 +7,14 @@ export function queryLine(query, max = QUERY_CUT) {
|
|
|
7
7
|
return line.length > max ? `${line.slice(0, max - 3)}...` : line;
|
|
8
8
|
}
|
|
9
9
|
// Entry shape comes from `formatContextForPrompt`: `<memory id="..." relevance="N%">`, with the
|
|
10
|
-
// id missing when the result had none. A
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
10
|
+
// id missing when the result had none. A linked memory carries further attributes after the
|
|
11
|
+
// relevance (`space="..."`), so any number of them may sit before the tag closes. A body ends at
|
|
12
|
+
// its own `</memory>` when another entry, a section close or the end of the text follows it. The
|
|
13
|
+
// Claude Code hook can cut a block short and leave tags open, so a body also ends at a section
|
|
14
|
+
// close or at the end of the text. An opening tag inside the text, even at the start of a line,
|
|
15
|
+
// does not end it: a memory can quote the tag.
|
|
14
16
|
const SECTION_CLOSE = String.raw `<\/project_knowledge>|<\/memory_context>|<\/omms-retrieval>`;
|
|
15
|
-
const MEMORY_ENTRY = new RegExp(String.raw `<memory(?:\s+id="([^"]*)")?\s+relevance="(\d+)%"
|
|
17
|
+
const MEMORY_ENTRY = new RegExp(String.raw `<memory(?:\s+id="([^"]*)")?\s+relevance="(\d+)%"(?:\s+[a-z][a-z-]*="[^"]*")*>([\s\S]*?)` +
|
|
16
18
|
String.raw `(?=<\/memory>\s*(?:<memory\s|${SECTION_CLOSE}|$)|${SECTION_CLOSE}|$)`, "g");
|
|
17
19
|
// A restored-session section from `formatMemoriesForCompaction`: `### Memory N (id)`, no relevance.
|
|
18
20
|
// Same rule as the Claude Code parser (`hooks/omms-recall-parse.js`).
|
package/dist/core/retrieval.js
CHANGED
|
@@ -17,7 +17,10 @@ export async function searchSectionMemories(query, projectTag, sessionId) {
|
|
|
17
17
|
// Snapshot before the async search: an excludeCurrentSession edit during
|
|
18
18
|
// the search must not change this operation's filtering.
|
|
19
19
|
const excludeCurrentSession = CONFIG.chatMessage.excludeCurrentSession;
|
|
20
|
-
|
|
20
|
+
// Recall reads linked spaces too (D6); capture and profile steps keep the default.
|
|
21
|
+
const search = await memoryClient.searchMemories(query, projectTag, "project", {
|
|
22
|
+
includeLinked: true,
|
|
23
|
+
});
|
|
21
24
|
if (!search.success)
|
|
22
25
|
return null;
|
|
23
26
|
const results = excludeCurrentSession
|
|
@@ -57,7 +60,7 @@ export async function buildRetrievalSection(prompt, directory, sessionId, budget
|
|
|
57
60
|
* Code `SessionStart`). Returns null when no memory qualifies.
|
|
58
61
|
*/
|
|
59
62
|
export async function buildRecentMemoriesSection(options) {
|
|
60
|
-
const listResult = await memoryClient.listMemories(options.projectTag, options.maxMemories);
|
|
63
|
+
const listResult = await memoryClient.listMemories(options.projectTag, options.maxMemories, "project", { includeLinked: true });
|
|
61
64
|
let memories = listResult.success ? listResult.memories : [];
|
|
62
65
|
if (options.excludeCurrentSession) {
|
|
63
66
|
memories = memories.filter((m) => m.metadata?.sessionID !== options.sessionId);
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { MemoryOperationArgs } from "./memory-operations.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import type { SpaceOperationSettings } from "../services/spaces/operations.js";
|
|
3
|
+
/** Snapshots the settings the space-bound operations need from a CONFIG-shaped object. */
|
|
4
|
+
export declare function spaceOperationSettings(config: {
|
|
5
|
+
skillStoragePath?: string;
|
|
6
|
+
storagePath: string;
|
|
7
|
+
embeddingModel: string;
|
|
8
|
+
embeddingDimensions: number;
|
|
9
|
+
maxVectorsPerShard: number;
|
|
10
|
+
}, spacesRoot: string): SpaceOperationSettings;
|
|
6
11
|
export declare function runSpaceMode(args: MemoryOperationArgs, directory: string): Promise<Record<string, unknown>>;
|
package/dist/core/space-tool.js
CHANGED
|
@@ -1,13 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The `space` mode of the `memory` tool. It loads on demand, so the other modes never import
|
|
3
|
-
* the space code.
|
|
3
|
+
* the space code. `list`, `use` and `create` need no embedding model and no open memory store;
|
|
4
|
+
* `preview` and `copy` open space stores, so only they carry the settings snapshot.
|
|
4
5
|
*/
|
|
6
|
+
/** The actions that read or write a space store, so their settings are loaded only for them. */
|
|
7
|
+
const STORE_ACTIONS = new Set(["preview", "copy"]);
|
|
8
|
+
/** Snapshots the settings the space-bound operations need from a CONFIG-shaped object. */
|
|
9
|
+
export function spaceOperationSettings(config, spacesRoot) {
|
|
10
|
+
return {
|
|
11
|
+
paths: {
|
|
12
|
+
// Default's folder is the global store, even while a space or project folder is in use.
|
|
13
|
+
globalStoragePath: config.skillStoragePath ?? config.storagePath,
|
|
14
|
+
spacesRoot,
|
|
15
|
+
},
|
|
16
|
+
embedding: { model: config.embeddingModel, dimensions: config.embeddingDimensions },
|
|
17
|
+
maxVectorsPerShard: config.maxVectorsPerShard,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
5
20
|
export async function runSpaceMode(args, directory) {
|
|
6
|
-
const { getActiveSpace, refreshConfigIfChanged } = await import("../config.js");
|
|
21
|
+
const { CONFIG, getActiveSpace, refreshConfigIfChanged } = await import("../config.js");
|
|
7
22
|
const { executeSpaceOperation } = await import("../services/spaces/operations.js");
|
|
8
23
|
const { spaceFiles } = await import("../services/spaces/registry-store.js");
|
|
9
24
|
const { ommsHomeDir } = await import("../services/spaces/storage-resolution.js");
|
|
10
|
-
const
|
|
25
|
+
const files = spaceFiles(ommsHomeDir());
|
|
26
|
+
const action = args.spaceAction?.trim().toLowerCase();
|
|
27
|
+
const result = await executeSpaceOperation({
|
|
28
|
+
action: args.spaceAction,
|
|
29
|
+
name: args.spaceName,
|
|
30
|
+
...(args.spaceTo === undefined ? {} : { to: args.spaceTo }),
|
|
31
|
+
...(args.spaceSince === undefined ? {} : { since: args.spaceSince }),
|
|
32
|
+
...(args.spaceUntil === undefined ? {} : { until: args.spaceUntil }),
|
|
33
|
+
...(args.spaceProject === undefined ? {} : { project: args.spaceProject }),
|
|
34
|
+
...(args.spaceIn === undefined ? {} : { owner: args.spaceIn }),
|
|
35
|
+
}, {
|
|
36
|
+
files,
|
|
37
|
+
current: getActiveSpace(),
|
|
38
|
+
// The transfer actions open space stores, so only they get the settings (design D8).
|
|
39
|
+
...(STORE_ACTIONS.has(action ?? "")
|
|
40
|
+
? { settings: spaceOperationSettings(CONFIG, files.spacesRoot) }
|
|
41
|
+
: {}),
|
|
42
|
+
});
|
|
11
43
|
// A switch made here takes effect in this process now, not only on its next config refresh.
|
|
12
44
|
refreshConfigIfChanged(directory);
|
|
13
45
|
return result;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { LinkedOwner } from "./linked-search.js";
|
|
2
|
+
/**
|
|
3
|
+
* Reads the active space the way partial test stubs allow: a `config.js` stub may
|
|
4
|
+
* leave out `getActiveSpace`, and a missing one means the Default space.
|
|
5
|
+
*/
|
|
6
|
+
export declare function activeOwner(): LinkedOwner;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as configModule from "../config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Reads the active space the way partial test stubs allow: a `config.js` stub may
|
|
4
|
+
* leave out `getActiveSpace`, and a missing one means the Default space.
|
|
5
|
+
*/
|
|
6
|
+
export function activeOwner() {
|
|
7
|
+
return configModule.getActiveSpace?.() ?? { id: "default", customStoragePath: false };
|
|
8
|
+
}
|
|
@@ -89,7 +89,7 @@ export async function chatServiceDeps(config) {
|
|
|
89
89
|
retrieve: {
|
|
90
90
|
searchMemories: async (query, scopeTag) => {
|
|
91
91
|
const { memoryClient } = await import("../client.js");
|
|
92
|
-
const result = await memoryClient.searchMemories(query, scopeTag ?? "", scopeTag ? "project" : "all-projects");
|
|
92
|
+
const result = await memoryClient.searchMemories(query, scopeTag ?? "", scopeTag ? "project" : "all-projects", { includeLinked: true });
|
|
93
93
|
return result.success ? result.results : [];
|
|
94
94
|
},
|
|
95
95
|
embed: async (text) => {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type MemoryReadOptions } from "./linked-search.js";
|
|
1
2
|
import type { MemoryType } from "../types/index.js";
|
|
3
|
+
import type { SearchResult } from "./turso/types.js";
|
|
2
4
|
export type MemoryScope = "project" | "all-projects";
|
|
3
5
|
export declare class LocalMemoryClient {
|
|
4
6
|
private initPromise;
|
|
@@ -16,10 +18,10 @@ export declare class LocalMemoryClient {
|
|
|
16
18
|
};
|
|
17
19
|
reset(): void;
|
|
18
20
|
close(): Promise<void>;
|
|
19
|
-
searchMemories(query: string, containerTag: string, scope?: MemoryScope): Promise<{
|
|
21
|
+
searchMemories(query: string, containerTag: string, scope?: MemoryScope, options?: MemoryReadOptions): Promise<{
|
|
20
22
|
warnings?: string[] | undefined;
|
|
21
23
|
success: true;
|
|
22
|
-
results:
|
|
24
|
+
results: SearchResult[];
|
|
23
25
|
total: number;
|
|
24
26
|
timing: number;
|
|
25
27
|
queryVector: Float32Array<ArrayBufferLike>;
|
|
@@ -60,21 +62,9 @@ export declare class LocalMemoryClient {
|
|
|
60
62
|
success: boolean;
|
|
61
63
|
error: string;
|
|
62
64
|
}>;
|
|
63
|
-
listMemories(containerTag: string, limit?: number, scope?: MemoryScope): Promise<{
|
|
65
|
+
listMemories(containerTag: string, limit?: number, scope?: MemoryScope, options?: MemoryReadOptions): Promise<{
|
|
64
66
|
success: true;
|
|
65
|
-
memories:
|
|
66
|
-
id: any;
|
|
67
|
-
summary: any;
|
|
68
|
-
type: string | undefined;
|
|
69
|
-
createdAt: string;
|
|
70
|
-
metadata: any;
|
|
71
|
-
displayName: any;
|
|
72
|
-
userName: any;
|
|
73
|
-
userEmail: any;
|
|
74
|
-
projectPath: any;
|
|
75
|
-
projectName: any;
|
|
76
|
-
gitRepoUrl: any;
|
|
77
|
-
}[];
|
|
67
|
+
memories: any[];
|
|
78
68
|
pagination: {
|
|
79
69
|
currentPage: number;
|
|
80
70
|
totalItems: number;
|