@dsh-cc/memory 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.i18n.yaml +6 -0
- package/README.md +158 -0
- package/README.zh.md +125 -0
- package/lib/index.d.ts +75 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +91 -0
- package/lib/index.js.map +1 -0
- package/lib/invariant.d.ts +16 -0
- package/lib/invariant.d.ts.map +1 -0
- package/lib/invariant.js +22 -0
- package/lib/invariant.js.map +1 -0
- package/lib/parser.d.ts +30 -0
- package/lib/parser.d.ts.map +1 -0
- package/lib/parser.js +96 -0
- package/lib/parser.js.map +1 -0
- package/lib/paths.d.ts +98 -0
- package/lib/paths.d.ts.map +1 -0
- package/lib/paths.js +236 -0
- package/lib/paths.js.map +1 -0
- package/lib/recall.d.ts +91 -0
- package/lib/recall.d.ts.map +1 -0
- package/lib/recall.js +253 -0
- package/lib/recall.js.map +1 -0
- package/lib/save.d.ts +53 -0
- package/lib/save.d.ts.map +1 -0
- package/lib/save.js +180 -0
- package/lib/save.js.map +1 -0
- package/lib/scan.d.ts +29 -0
- package/lib/scan.d.ts.map +1 -0
- package/lib/scan.js +70 -0
- package/lib/scan.js.map +1 -0
- package/lib/section.d.ts +129 -0
- package/lib/section.d.ts.map +1 -0
- package/lib/section.js +353 -0
- package/lib/section.js.map +1 -0
- package/lib/team.d.ts +90 -0
- package/lib/team.d.ts.map +1 -0
- package/lib/team.js +167 -0
- package/lib/team.js.map +1 -0
- package/lib/truncate.d.ts +35 -0
- package/lib/truncate.d.ts.map +1 -0
- package/lib/truncate.js +52 -0
- package/lib/truncate.js.map +1 -0
- package/lib/types.d.ts +34 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +18 -0
- package/lib/types.js.map +1 -0
- package/lib/writeback.d.ts +85 -0
- package/lib/writeback.d.ts.map +1 -0
- package/lib/writeback.js +121 -0
- package/lib/writeback.js.map +1 -0
- package/package.json +65 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code-style file-based memory: the memdir format and parser, the
|
|
3
|
+
* `memory` system-prompt section, and dynamic recall by a forked side-query.
|
|
4
|
+
*
|
|
5
|
+
* All file access goes through the optional `ctx.fs` seam, so a remote or
|
|
6
|
+
* sandboxed backend works unchanged; a providerless host mounts memory as a
|
|
7
|
+
* no-op. Recall needs `ctx.subagents` and a registered one-shot provider;
|
|
8
|
+
* absence of either skips recall without error.
|
|
9
|
+
*
|
|
10
|
+
* @module @dsh-cc/memory
|
|
11
|
+
*/
|
|
12
|
+
import z from '@deepseek-ai/schemastery';
|
|
13
|
+
import { toAgentOptions } from '@dsh-cc/model-aliases';
|
|
14
|
+
import { resolveMemoryHome } from "./paths.js";
|
|
15
|
+
import { MemorySection } from "./section.js";
|
|
16
|
+
import { MemoryRecall, SubagentMemorySelector } from "./recall.js";
|
|
17
|
+
import { registerMemorySaveTool } from "./save.js";
|
|
18
|
+
export { parseMemoryFile } from "./parser.js";
|
|
19
|
+
export { truncateEntrypointContent, ENTRYPOINT_NAME, MAX_ENTRYPOINT_LINES, MAX_ENTRYPOINT_BYTES } from "./truncate.js";
|
|
20
|
+
export { MEMORY_TYPES, parseMemoryType } from "./types.js";
|
|
21
|
+
export { scanMemoryDirectory } from "./scan.js";
|
|
22
|
+
export { resolveMemoryHome, resolveProjectMemoryRoot, resolveWorkspaceMemoryDir, canonicalMemoryRoot, projectSlug, cwdOf, gitExecSync, PROJECT_MEMORY_DIR, PROJECTS_DIR, GIT_PROBE_TIMEOUT_MS, } from "./paths.js";
|
|
23
|
+
export { MemorySection, renderMemorySection, renderTeamMemorySection, renderLayers, saveGuidance, MEMORY_SECTION_NAME, MEMORY_SECTION_ORDER } from "./section.js";
|
|
24
|
+
export { MemoryRecall, SubagentMemorySelector, extractSelectedNames, MAX_RECALL_MEMORIES } from "./recall.js";
|
|
25
|
+
export { TeamMemoryError, sanitizePathKey, resolveTeamMemoryRoot, validateTeamMemKey, readTeamMemFile, TEAM_MEMORY_DIR, TEAM_ENTRYPOINT_NAME } from "./team.js";
|
|
26
|
+
export { MEMORY_SAVE_TOOL, MEMORY_SAVE_SCOPES, MemorySaveError, pointerLine, registerMemorySaveTool, renderTopicFile, upsertPointer, } from "./save.js";
|
|
27
|
+
export { MEMORY_WRITES_SCHEMA, WRITEBACK_MAX_FILE_BYTES, WRITEBACK_MAX_FILES, WRITEBACK_MAX_TOTAL_BYTES, memoryWritePolicy, validateMemoryWrites, writeMemoryFiles, } from "./writeback.js";
|
|
28
|
+
export const name = 'memory';
|
|
29
|
+
/** Core services required for section registration and event listeners. */
|
|
30
|
+
export const inject = ['systemPrompt'];
|
|
31
|
+
export const Config = z.object({
|
|
32
|
+
memoryHome: z.string(),
|
|
33
|
+
sectionEnabled: z.boolean().default(true),
|
|
34
|
+
recallEnabled: z.boolean().default(true),
|
|
35
|
+
recallProviderName: z.string().default('fork'),
|
|
36
|
+
recallAgentOptions: z.any(),
|
|
37
|
+
recallUseSmallFast: z.boolean().default(false),
|
|
38
|
+
teamEnabled: z.boolean().default(false),
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the recall selector's `agentOptions` stamp.
|
|
42
|
+
*
|
|
43
|
+
* Priority: explicit `recallAgentOptions` (raw overlay, NOT alias-resolved) →
|
|
44
|
+
* `recallUseSmallFast` + the configured `haiku` alias → nothing (inherit).
|
|
45
|
+
* @param c - the host context (gateway to the optional `ccModelRoutes`).
|
|
46
|
+
* @param config - the memory plugin config.
|
|
47
|
+
* @returns the `agentOptions` record, or `undefined` for inherit.
|
|
48
|
+
*/
|
|
49
|
+
function resolveRecallAgentOptions(c, config) {
|
|
50
|
+
if (config.recallAgentOptions !== undefined)
|
|
51
|
+
return config.recallAgentOptions;
|
|
52
|
+
if (config.recallUseSmallFast !== true)
|
|
53
|
+
return undefined;
|
|
54
|
+
const routes = c.get('ccModelRoutes');
|
|
55
|
+
return toAgentOptions(routes?.resolve('haiku'));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Register the `memory` system-prompt section and recall listener.
|
|
59
|
+
* @param ctx - the host context carrying the system-prompt and agent seams.
|
|
60
|
+
* @param config - memory behavior knobs.
|
|
61
|
+
*/
|
|
62
|
+
export function apply(ctx, config = {}) {
|
|
63
|
+
// Pass the raw configured root through: resolveMemoryHome appends `memory/`
|
|
64
|
+
// ONLY for undefined/empty — handing it defaultDshHome() here would resolve
|
|
65
|
+
// to the harness home itself and write memory files into its root.
|
|
66
|
+
const home = resolveMemoryHome(config.memoryHome);
|
|
67
|
+
if (config.sectionEnabled ?? true) {
|
|
68
|
+
const section = new MemorySection(ctx, home, {
|
|
69
|
+
teamEnabled: config.teamEnabled === true,
|
|
70
|
+
});
|
|
71
|
+
section.start();
|
|
72
|
+
// The save channel: memory directories sit outside every session's
|
|
73
|
+
// sandbox writable roots, so direct write/edit always fails; the tool
|
|
74
|
+
// writes host-side instead. No-op on hosts without a tools service.
|
|
75
|
+
registerMemorySaveTool(ctx, home, section);
|
|
76
|
+
// Re-scan at each turn boundary so host-side writes (memory_save,
|
|
77
|
+
// memory-consolidation's write-back, external edits) surface in the
|
|
78
|
+
// section without a restart; refresh() self-dedupes unchanged content.
|
|
79
|
+
ctx.on('agent/turn-stopping', ({ agent }) => {
|
|
80
|
+
void section.refresh(agent);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (config.recallEnabled ?? true) {
|
|
84
|
+
new MemoryRecall(ctx, home, {
|
|
85
|
+
providerName: config.recallProviderName ?? 'fork',
|
|
86
|
+
enabled: true,
|
|
87
|
+
createSelector: (c, agent) => new SubagentMemorySelector(c, agent, config.recallProviderName ?? 'fork', resolveRecallAgentOptions(c, config)),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@dsh-cc/memory`.
|
|
3
|
+
* @module @dsh-cc/memory/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "memory-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
+
//# sourceMappingURL=invariant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invariant.d.ts","sourceRoot":"","sources":["../src/invariant.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAKlD,oCAAoC;AACpC,eAAO,MAAM,IAAI,qBAAqB,CAAA;AACtC,2EAA2E;AAC3E,eAAO,MAAM,MAAM,UAAiB,CAAA;AAQpC;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,OAAO,KAAG,OAAO,CAAC,MAAM,IAAI,CACU,CAAA"}
|
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@dsh-cc/memory`.
|
|
3
|
+
* @module @dsh-cc/memory/invariant
|
|
4
|
+
*/
|
|
5
|
+
const PACKAGE_NAME = '@dsh-cc/memory';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export const name = 'memory-invariant';
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export const inject = ['invariants'];
|
|
10
|
+
/**
|
|
11
|
+
* No runtime invariant: recall deduplication and section freshness are
|
|
12
|
+
* enforced at the owning plugin seams, with no independent event/data relation.
|
|
13
|
+
*/
|
|
14
|
+
const install = () => { };
|
|
15
|
+
/**
|
|
16
|
+
* Register this package's invariant companion.
|
|
17
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
18
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
19
|
+
*/
|
|
20
|
+
export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
21
|
+
/* jscpd:ignore-end */
|
|
22
|
+
//# sourceMappingURL=invariant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invariant.js","sourceRoot":"","sources":["../src/invariant.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,YAAY,GAAG,gBAAgB,CAAA;AAErC,oCAAoC;AACpC,MAAM,CAAC,MAAM,IAAI,GAAG,kBAAkB,CAAA;AACtC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,YAAY,CAAC,CAAA;AAEpC;;;GAGG;AACH,MAAM,OAAO,GAAuB,GAAG,EAAE,GAAE,CAAC,CAAA;AAE5C;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,GAAY,EAAuB,EAAE,CACzD,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAA;AACjE,sBAAsB"}
|
package/lib/parser.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing for the memdir topic-file format: `---`-delimited YAML frontmatter
|
|
3
|
+
* (name/description/type) plus a Markdown body. Binary parsing and the body
|
|
4
|
+
* are deliberately decoupled so recall, the system-prompt section index, and
|
|
5
|
+
* consolidation can each consume only what they need.
|
|
6
|
+
* @module @dsh-cc/memory/parser
|
|
7
|
+
*/
|
|
8
|
+
import { type MemoryFrontmatter } from './types.ts';
|
|
9
|
+
/**
|
|
10
|
+
* One topic file separated into its rationalized header and Markdown body.
|
|
11
|
+
* `undefined` marks a file that is not a valid memory topic (unreadable
|
|
12
|
+
* frontmatter, or missing name/description).
|
|
13
|
+
*/
|
|
14
|
+
export interface ParsedMemoryFile {
|
|
15
|
+
/** Rationalized frontmatter header. */
|
|
16
|
+
frontmatter: MemoryFrontmatter;
|
|
17
|
+
/** The Markdown body after the closing `---`, trimmed of surrounding blanks. */
|
|
18
|
+
body: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parse one topic file's raw text into its frontmatter and body.
|
|
22
|
+
* The header must open with a `---` line and close with a later `---` line;
|
|
23
|
+
* `name` and `description` must be non-empty strings; `type` is optional and
|
|
24
|
+
* unknown values are dropped. A malformed or incomplete file returns
|
|
25
|
+
* `undefined` rather than throwing.
|
|
26
|
+
* @param raw - the raw UTF-8 text of a memory topic file.
|
|
27
|
+
* @returns the parsed header/body, or `undefined` when the file is not a topic.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseMemoryFile(raw: string): ParsedMemoryFile | undefined;
|
|
30
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAmB,KAAK,iBAAiB,EAAmB,MAAM,YAAY,CAAA;AAErF;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,WAAW,EAAE,iBAAiB,CAAA;IAC9B,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAA;CACb;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAWzE"}
|
package/lib/parser.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing for the memdir topic-file format: `---`-delimited YAML frontmatter
|
|
3
|
+
* (name/description/type) plus a Markdown body. Binary parsing and the body
|
|
4
|
+
* are deliberately decoupled so recall, the system-prompt section index, and
|
|
5
|
+
* consolidation can each consume only what they need.
|
|
6
|
+
* @module @dsh-cc/memory/parser
|
|
7
|
+
*/
|
|
8
|
+
import { parseMemoryType } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Parse one topic file's raw text into its frontmatter and body.
|
|
11
|
+
* The header must open with a `---` line and close with a later `---` line;
|
|
12
|
+
* `name` and `description` must be non-empty strings; `type` is optional and
|
|
13
|
+
* unknown values are dropped. A malformed or incomplete file returns
|
|
14
|
+
* `undefined` rather than throwing.
|
|
15
|
+
* @param raw - the raw UTF-8 text of a memory topic file.
|
|
16
|
+
* @returns the parsed header/body, or `undefined` when the file is not a topic.
|
|
17
|
+
*/
|
|
18
|
+
export function parseMemoryFile(raw) {
|
|
19
|
+
const header = parseFrontmatterHeader(raw);
|
|
20
|
+
if (header === undefined)
|
|
21
|
+
return undefined;
|
|
22
|
+
const fields = parseScalarFields(header.body);
|
|
23
|
+
const name = nonEmpty(fields.name);
|
|
24
|
+
const description = nonEmpty(fields.description);
|
|
25
|
+
if (name === undefined || description === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
return {
|
|
28
|
+
frontmatter: { name, description, ...parseOptionalType(fields.type) },
|
|
29
|
+
body: header.after.trim(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Split the `---`-delimited frontmatter block by scanning line-by-line.
|
|
34
|
+
* Returns the YAML block body and the markdown text after the closing fence.
|
|
35
|
+
*/
|
|
36
|
+
function parseFrontmatterHeader(raw) {
|
|
37
|
+
const firstLineEnd = raw.indexOf('\n');
|
|
38
|
+
if (firstLineEnd < 0)
|
|
39
|
+
return undefined;
|
|
40
|
+
const firstLine = raw.slice(0, firstLineEnd).replace(/\r$/, '');
|
|
41
|
+
if (firstLine !== '---')
|
|
42
|
+
return undefined;
|
|
43
|
+
let lineStart = firstLineEnd + 1;
|
|
44
|
+
while (lineStart <= raw.length) {
|
|
45
|
+
const nextNewline = raw.indexOf('\n', lineStart);
|
|
46
|
+
const lineEnd = nextNewline < 0 ? raw.length : nextNewline;
|
|
47
|
+
if (raw.slice(lineStart, lineEnd).replace(/\r$/, '') === '---') {
|
|
48
|
+
// `lineEnd + 1` is past the closing fence and any trailing newline; when
|
|
49
|
+
// the fence is the last byte lineEnd === raw.length so the slice is empty.
|
|
50
|
+
return {
|
|
51
|
+
body: raw.slice(firstLineEnd + 1, lineStart),
|
|
52
|
+
after: raw.slice(lineEnd + 1),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (nextNewline < 0)
|
|
56
|
+
return undefined;
|
|
57
|
+
lineStart = nextNewline + 1;
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
/** Parse the simple `key: value` scalar fields the memdir format uses. */
|
|
62
|
+
function parseScalarFields(yaml) {
|
|
63
|
+
const fields = {};
|
|
64
|
+
for (const rawLine of yaml.split('\n')) {
|
|
65
|
+
const line = rawLine.replace(/\r$/, '');
|
|
66
|
+
const colon = line.indexOf(':');
|
|
67
|
+
if (colon < 0)
|
|
68
|
+
continue;
|
|
69
|
+
const key = line.slice(0, colon).trim();
|
|
70
|
+
if (key !== 'name' && key !== 'description' && key !== 'type')
|
|
71
|
+
continue;
|
|
72
|
+
fields[key] = unquoteScalar(line.slice(colon + 1).trim());
|
|
73
|
+
}
|
|
74
|
+
return fields;
|
|
75
|
+
}
|
|
76
|
+
/** Strip a single surrounding pair of quotes from a scalar value. */
|
|
77
|
+
function unquoteScalar(value) {
|
|
78
|
+
if (value.length >= 2) {
|
|
79
|
+
const first = value[0];
|
|
80
|
+
const last = value[value.length - 1];
|
|
81
|
+
if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
|
|
82
|
+
return value.slice(1, -1);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
/** Treat a missing or whitespace-only value as absent. */
|
|
88
|
+
function nonEmpty(value) {
|
|
89
|
+
const trimmed = value?.trim();
|
|
90
|
+
return trimmed !== undefined && trimmed.length > 0 ? trimmed : undefined;
|
|
91
|
+
}
|
|
92
|
+
function parseOptionalType(type) {
|
|
93
|
+
const parsed = parseMemoryType(type);
|
|
94
|
+
return parsed === undefined ? {} : { type: parsed };
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
|
package/lib/paths.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memdir root resolution: the default harness-home memory directory plus an
|
|
3
|
+
* optional per-project `.claude/memory` overlay. All reads go through the
|
|
4
|
+
* optional `ctx.fs` seam so remote backends work unchanged.
|
|
5
|
+
* @module @dsh-cc/memory/paths
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
9
|
+
/** Directory label for the project-scoped memory overlay. */
|
|
10
|
+
export declare const PROJECT_MEMORY_DIR = ".claude/memory";
|
|
11
|
+
/** Directory label grouping the per-workspace memory directories. */
|
|
12
|
+
export declare const PROJECTS_DIR = "projects";
|
|
13
|
+
/**
|
|
14
|
+
* Git probe timeout for {@link canonicalMemoryRoot}. Bounded so a hung git
|
|
15
|
+
* (network FS, broken env) cannot stall the synchronous system-prompt
|
|
16
|
+
* `text` callback past the section's first-assembly budget.
|
|
17
|
+
*/
|
|
18
|
+
export declare const GIT_PROBE_TIMEOUT_MS = 500;
|
|
19
|
+
/** A successful synchronous git invocation. */
|
|
20
|
+
export interface MemoryGitExecResult {
|
|
21
|
+
stdout: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Run one git argv in `cwd`, synchronously. Returns undefined on spawn
|
|
25
|
+
* failure, non-zero exit, or timeout — the caller treats that as "not a git
|
|
26
|
+
* repository". Injectable so tests script the git conversation.
|
|
27
|
+
*/
|
|
28
|
+
export type MemoryGitExec = (argv: readonly string[], cwd: string) => MemoryGitExecResult | undefined;
|
|
29
|
+
/** The default exec: real `git` via spawnSync with a bounded timeout. */
|
|
30
|
+
export declare function gitExecSync(argv: readonly string[], cwd: string): MemoryGitExecResult | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the default (harness-home) memory root. A configured root wins;
|
|
33
|
+
* otherwise the shared harness home.
|
|
34
|
+
* @param configured - an explicit root, or `undefined` for the default.
|
|
35
|
+
* @returns the resolved harness-home memory directory.
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveMemoryHome(configured?: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Encode a workspace path as a single filesystem-safe directory name. Ported
|
|
40
|
+
* from upstream `projectKey` (`session-persistence-jsonl/src/format.ts`),
|
|
41
|
+
* minus the `--` wrapper: separators and drive colons collapse to `-`, unsafe
|
|
42
|
+
* code units escape as `~XXXX`, leading dashes strip, and the result
|
|
43
|
+
* truncates to 251 chars (fallback `root`). The input is the canonical git
|
|
44
|
+
* root from {@link canonicalMemoryRoot}, so worktrees of one repo share a
|
|
45
|
+
* slug even though session transcripts still group by raw cwd.
|
|
46
|
+
* @param cwd - the workspace path to encode.
|
|
47
|
+
* @returns the workspace slug.
|
|
48
|
+
*/
|
|
49
|
+
export declare function projectSlug(cwd: string): string;
|
|
50
|
+
/** Test hook: drop the module-level memo (also safe as a no-op cleanup). */
|
|
51
|
+
export declare function __clearMemoryRootCache(): void;
|
|
52
|
+
/**
|
|
53
|
+
* The canonical git-repo root a cwd's workspace memories belong to.
|
|
54
|
+
*
|
|
55
|
+
* Matches Claude Code auto-memory identity: derived from the git repository
|
|
56
|
+
* so worktrees and subdirectories share one store. A linked worktree
|
|
57
|
+
* collapses onto the main checkout (`dirname(git-common-dir)`); a submodule
|
|
58
|
+
* keeps its own toplevel. Non-git / git failure / timeout degrades to
|
|
59
|
+
* `resolve(cwd)` — today's pre-collapse behavior.
|
|
60
|
+
*
|
|
61
|
+
* Results are memoised per resolved `cwd` so the synchronous system-prompt
|
|
62
|
+
* section probes git at most once per working directory. Only default-exec
|
|
63
|
+
* calls participate: an injected `exec` bypasses the cache entirely.
|
|
64
|
+
* @param cwd - the session working directory.
|
|
65
|
+
* @param exec - git probe; defaults to {@link gitExecSync}.
|
|
66
|
+
* @returns the absolute canonical root to slug.
|
|
67
|
+
*/
|
|
68
|
+
export declare function canonicalMemoryRoot(cwd: string, exec?: MemoryGitExec): string;
|
|
69
|
+
/**
|
|
70
|
+
* Resolve the per-workspace private memory directory under a memory home:
|
|
71
|
+
* `<home>/projects/<slug>` where the slug encodes the canonical git root
|
|
72
|
+
* of `cwd` (worktrees and subdirectories of one repo share a directory).
|
|
73
|
+
* @param home - the resolved memory home (the global layer's directory).
|
|
74
|
+
* @param cwd - the session working directory; collapsed via {@link canonicalMemoryRoot}.
|
|
75
|
+
* @param exec - optional git probe (tests); omitted uses the default exec.
|
|
76
|
+
* @returns the workspace memory directory.
|
|
77
|
+
*/
|
|
78
|
+
export declare function resolveWorkspaceMemoryDir(home: string, cwd: string, exec?: MemoryGitExec): string;
|
|
79
|
+
/**
|
|
80
|
+
* The workspace path an agent's memories belong to: the session's bound cwd,
|
|
81
|
+
* falling back to the process cwd when the header carries none.
|
|
82
|
+
*
|
|
83
|
+
* This is the *live working copy*, not the memory-bucket identity. Task uses
|
|
84
|
+
* it to find `.claude/agents` inside a worktree; {@link resolveWorkspaceMemoryDir}
|
|
85
|
+
* collapses it onto the git root before slugging.
|
|
86
|
+
* @param agent - the agent whose workspace is needed.
|
|
87
|
+
* @returns the workspace cwd.
|
|
88
|
+
*/
|
|
89
|
+
export declare function cwdOf(agent: Agent): string;
|
|
90
|
+
/**
|
|
91
|
+
* Disco the project-scoped memory root for a cwd, or `undefined` when the
|
|
92
|
+
* filesystem service is absent or no project root is reachable.
|
|
93
|
+
* @param ctx - the host context carrying the optional `fs` service.
|
|
94
|
+
* @param cwd - the descendant path whose project root bounds the overlay.
|
|
95
|
+
* @returns the project memory directory, or `undefined` when not discoverable.
|
|
96
|
+
*/
|
|
97
|
+
export declare function resolveProjectMemoryRoot(ctx: Context, cwd: string): Promise<string | undefined>;
|
|
98
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAInD,6DAA6D;AAC7D,eAAO,MAAM,kBAAkB,mBAAmB,CAAA;AAElD,qEAAqE;AACrE,eAAO,MAAM,YAAY,aAAa,CAAA;AAEtC;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAA;AAEvC,+CAA+C;AAC/C,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,KAAK,mBAAmB,GAAG,SAAS,CAAA;AAErG,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAQjG;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAI7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAoB/C;AAoCD,4EAA4E;AAC5E,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,aAA2B,GAAG,MAAM,CAS1F;AAqBD;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,CAGjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAE1C;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO7B"}
|
package/lib/paths.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memdir root resolution: the default harness-home memory directory plus an
|
|
3
|
+
* optional per-project `.claude/memory` overlay. All reads go through the
|
|
4
|
+
* optional `ctx.fs` seam so remote backends work unchanged.
|
|
5
|
+
* @module @dsh-cc/memory/paths
|
|
6
|
+
*/
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import { realpathSync } from 'node:fs';
|
|
9
|
+
import { dirname, join, resolve, sep } from 'node:path';
|
|
10
|
+
import { defaultDshHome } from '@deepseek-ai/dsh-home-paths';
|
|
11
|
+
/** Directory label for the project-scoped memory overlay. */
|
|
12
|
+
export const PROJECT_MEMORY_DIR = '.claude/memory';
|
|
13
|
+
/** Directory label grouping the per-workspace memory directories. */
|
|
14
|
+
export const PROJECTS_DIR = 'projects';
|
|
15
|
+
/**
|
|
16
|
+
* Git probe timeout for {@link canonicalMemoryRoot}. Bounded so a hung git
|
|
17
|
+
* (network FS, broken env) cannot stall the synchronous system-prompt
|
|
18
|
+
* `text` callback past the section's first-assembly budget.
|
|
19
|
+
*/
|
|
20
|
+
export const GIT_PROBE_TIMEOUT_MS = 500;
|
|
21
|
+
/** The default exec: real `git` via spawnSync with a bounded timeout. */
|
|
22
|
+
export function gitExecSync(argv, cwd) {
|
|
23
|
+
const result = spawnSync('git', [...argv], {
|
|
24
|
+
cwd,
|
|
25
|
+
encoding: 'utf8',
|
|
26
|
+
timeout: GIT_PROBE_TIMEOUT_MS,
|
|
27
|
+
});
|
|
28
|
+
if (result.error !== undefined || result.status !== 0)
|
|
29
|
+
return undefined;
|
|
30
|
+
return { stdout: result.stdout };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the default (harness-home) memory root. A configured root wins;
|
|
34
|
+
* otherwise the shared harness home.
|
|
35
|
+
* @param configured - an explicit root, or `undefined` for the default.
|
|
36
|
+
* @returns the resolved harness-home memory directory.
|
|
37
|
+
*/
|
|
38
|
+
export function resolveMemoryHome(configured) {
|
|
39
|
+
return configured !== undefined && configured.length > 0
|
|
40
|
+
? configured
|
|
41
|
+
: join(defaultDshHome(), 'memory');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Encode a workspace path as a single filesystem-safe directory name. Ported
|
|
45
|
+
* from upstream `projectKey` (`session-persistence-jsonl/src/format.ts`),
|
|
46
|
+
* minus the `--` wrapper: separators and drive colons collapse to `-`, unsafe
|
|
47
|
+
* code units escape as `~XXXX`, leading dashes strip, and the result
|
|
48
|
+
* truncates to 251 chars (fallback `root`). The input is the canonical git
|
|
49
|
+
* root from {@link canonicalMemoryRoot}, so worktrees of one repo share a
|
|
50
|
+
* slug even though session transcripts still group by raw cwd.
|
|
51
|
+
* @param cwd - the workspace path to encode.
|
|
52
|
+
* @returns the workspace slug.
|
|
53
|
+
*/
|
|
54
|
+
export function projectSlug(cwd) {
|
|
55
|
+
if (cwd.length === 0)
|
|
56
|
+
return 'root';
|
|
57
|
+
let readable = '';
|
|
58
|
+
let separatorRun = false;
|
|
59
|
+
for (let i = 0; i < cwd.length; i++) {
|
|
60
|
+
const code = cwd.charCodeAt(i);
|
|
61
|
+
const ch = String.fromCharCode(code);
|
|
62
|
+
if (ch === '/' || ch === '\\' || ch === ':') {
|
|
63
|
+
if (!separatorRun)
|
|
64
|
+
readable += '-';
|
|
65
|
+
separatorRun = true;
|
|
66
|
+
}
|
|
67
|
+
else if (ch !== '~' && /^[A-Za-z0-9._-]$/.test(ch)) {
|
|
68
|
+
readable += ch;
|
|
69
|
+
separatorRun = false;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
readable += '~' + code.toString(16).toUpperCase().padStart(4, '0');
|
|
73
|
+
separatorRun = false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const slug = readable.replace(/^-+/, '') || 'root';
|
|
77
|
+
return slug.slice(0, 251);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* True when `commonDir` marks a *linked* worktree whose project root is its
|
|
81
|
+
* directory's parent. The common dir of a standard layout is `<root>/.git`;
|
|
82
|
+
* a linked worktree reports its main checkout's `/.git`, which differs from
|
|
83
|
+
* its own `top/.git`. Anything else (main checkout, a submodule's
|
|
84
|
+
* `<super>/.git/modules/<name>`, bare, exotic GIT_DIR) is not a linked
|
|
85
|
+
* worktree and keeps `resolve(top)` as its project root — so submodules do
|
|
86
|
+
* not collapse onto their superproject.
|
|
87
|
+
*/
|
|
88
|
+
function isLinkedWorktree(commonDir, top) {
|
|
89
|
+
if (!commonDir.endsWith(`${sep}.git`))
|
|
90
|
+
return false;
|
|
91
|
+
return resolve(commonDir) !== resolve(join(top, '.git'));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Canonicalise a path: `resolve()` then dereference symlinks via realpath so
|
|
95
|
+
* a repo reached through a symlinked ancestor (e.g. macOS `/var` →
|
|
96
|
+
* `/private/var`) yields the SAME string whether it came from `resolve(cwd)`,
|
|
97
|
+
* git's toplevel, or git's common-dir — git may mix realpath'd and non-
|
|
98
|
+
* realpath'd output for the same directory, which would otherwise split one
|
|
99
|
+
* memory bucket into two. Falls back to `resolve()` if realpath fails (e.g.
|
|
100
|
+
* the path no longer exists).
|
|
101
|
+
*/
|
|
102
|
+
function canonical(path) {
|
|
103
|
+
try {
|
|
104
|
+
return realpathSync(path);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return resolve(path);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** Module-level memo keyed by `resolve(cwd)` (see {@link canonicalMemoryRoot}). */
|
|
111
|
+
const memoryRootCache = new Map();
|
|
112
|
+
/** Test hook: drop the module-level memo (also safe as a no-op cleanup). */
|
|
113
|
+
export function __clearMemoryRootCache() {
|
|
114
|
+
memoryRootCache.clear();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The canonical git-repo root a cwd's workspace memories belong to.
|
|
118
|
+
*
|
|
119
|
+
* Matches Claude Code auto-memory identity: derived from the git repository
|
|
120
|
+
* so worktrees and subdirectories share one store. A linked worktree
|
|
121
|
+
* collapses onto the main checkout (`dirname(git-common-dir)`); a submodule
|
|
122
|
+
* keeps its own toplevel. Non-git / git failure / timeout degrades to
|
|
123
|
+
* `resolve(cwd)` — today's pre-collapse behavior.
|
|
124
|
+
*
|
|
125
|
+
* Results are memoised per resolved `cwd` so the synchronous system-prompt
|
|
126
|
+
* section probes git at most once per working directory. Only default-exec
|
|
127
|
+
* calls participate: an injected `exec` bypasses the cache entirely.
|
|
128
|
+
* @param cwd - the session working directory.
|
|
129
|
+
* @param exec - git probe; defaults to {@link gitExecSync}.
|
|
130
|
+
* @returns the absolute canonical root to slug.
|
|
131
|
+
*/
|
|
132
|
+
export function canonicalMemoryRoot(cwd, exec = gitExecSync) {
|
|
133
|
+
const key = resolve(cwd);
|
|
134
|
+
if (exec === gitExecSync) {
|
|
135
|
+
const cached = memoryRootCache.get(key);
|
|
136
|
+
if (cached !== undefined)
|
|
137
|
+
return cached;
|
|
138
|
+
}
|
|
139
|
+
const root = probeMemoryRoot(cwd, exec);
|
|
140
|
+
if (exec === gitExecSync)
|
|
141
|
+
memoryRootCache.set(key, root);
|
|
142
|
+
return root;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* One spawn: `git rev-parse --show-toplevel --git-common-dir` prints the
|
|
146
|
+
* toplevel then the common dir, one per line. A relative common-dir is
|
|
147
|
+
* resolved against the probe cwd.
|
|
148
|
+
*/
|
|
149
|
+
function probeMemoryRoot(cwd, exec) {
|
|
150
|
+
const parsed = exec(['rev-parse', '--show-toplevel', '--git-common-dir'], cwd)?.stdout;
|
|
151
|
+
if (parsed === undefined)
|
|
152
|
+
return resolve(cwd);
|
|
153
|
+
const lines = parsed.split('\n').map(line => line.trim()).filter(line => line.length > 0);
|
|
154
|
+
const top = lines[0];
|
|
155
|
+
const commonRaw = lines[1];
|
|
156
|
+
if (top === undefined || top.length === 0 || commonRaw === undefined || commonRaw.length === 0) {
|
|
157
|
+
return resolve(cwd);
|
|
158
|
+
}
|
|
159
|
+
const topAbs = canonical(top);
|
|
160
|
+
const commonDir = canonical(resolve(cwd, commonRaw));
|
|
161
|
+
return isLinkedWorktree(commonDir, topAbs) ? dirname(commonDir) : topAbs;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Resolve the per-workspace private memory directory under a memory home:
|
|
165
|
+
* `<home>/projects/<slug>` where the slug encodes the canonical git root
|
|
166
|
+
* of `cwd` (worktrees and subdirectories of one repo share a directory).
|
|
167
|
+
* @param home - the resolved memory home (the global layer's directory).
|
|
168
|
+
* @param cwd - the session working directory; collapsed via {@link canonicalMemoryRoot}.
|
|
169
|
+
* @param exec - optional git probe (tests); omitted uses the default exec.
|
|
170
|
+
* @returns the workspace memory directory.
|
|
171
|
+
*/
|
|
172
|
+
export function resolveWorkspaceMemoryDir(home, cwd, exec) {
|
|
173
|
+
const root = exec === undefined ? canonicalMemoryRoot(cwd) : canonicalMemoryRoot(cwd, exec);
|
|
174
|
+
return join(home, PROJECTS_DIR, projectSlug(root));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* The workspace path an agent's memories belong to: the session's bound cwd,
|
|
178
|
+
* falling back to the process cwd when the header carries none.
|
|
179
|
+
*
|
|
180
|
+
* This is the *live working copy*, not the memory-bucket identity. Task uses
|
|
181
|
+
* it to find `.claude/agents` inside a worktree; {@link resolveWorkspaceMemoryDir}
|
|
182
|
+
* collapses it onto the git root before slugging.
|
|
183
|
+
* @param agent - the agent whose workspace is needed.
|
|
184
|
+
* @returns the workspace cwd.
|
|
185
|
+
*/
|
|
186
|
+
export function cwdOf(agent) {
|
|
187
|
+
return agent.session.header.cwd ?? process.cwd();
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Disco the project-scoped memory root for a cwd, or `undefined` when the
|
|
191
|
+
* filesystem service is absent or no project root is reachable.
|
|
192
|
+
* @param ctx - the host context carrying the optional `fs` service.
|
|
193
|
+
* @param cwd - the descendant path whose project root bounds the overlay.
|
|
194
|
+
* @returns the project memory directory, or `undefined` when not discoverable.
|
|
195
|
+
*/
|
|
196
|
+
export async function resolveProjectMemoryRoot(ctx, cwd) {
|
|
197
|
+
const fileSystem = ctx.get('fs');
|
|
198
|
+
if (fileSystem === undefined)
|
|
199
|
+
return undefined;
|
|
200
|
+
const cwdResolved = await readResolve(fileSystem, cwd, ctx);
|
|
201
|
+
const root = await findProjectRoot(cwdResolved, fileSystem);
|
|
202
|
+
if (root === undefined)
|
|
203
|
+
return undefined;
|
|
204
|
+
return join(root, PROJECT_MEMORY_DIR);
|
|
205
|
+
}
|
|
206
|
+
async function readResolve(fs, path, ctx) {
|
|
207
|
+
try {
|
|
208
|
+
return (await fs.resolve(path)).displayPath;
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
ctx.logger.warn(`memory: failed to resolve cwd ${path}: ${String(error)}`);
|
|
212
|
+
return path;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async function findProjectRoot(cwd, fs) {
|
|
216
|
+
let current = cwd;
|
|
217
|
+
while (true) {
|
|
218
|
+
if (await pathExists(fs, join(current, '.git')))
|
|
219
|
+
return current;
|
|
220
|
+
const parent = dirname(current);
|
|
221
|
+
if (parent === current)
|
|
222
|
+
return undefined;
|
|
223
|
+
current = parent;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async function pathExists(fs, path) {
|
|
227
|
+
try {
|
|
228
|
+
const target = await fs.resolve(path);
|
|
229
|
+
return await fs.stat(target) !== undefined;
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// A backend may refuse or hide the candidate; keep walking upward.
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=paths.js.map
|
package/lib/paths.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
|