@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/scan.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Directory scan over a memdir through the optional `ctx.fs` seam: list topic
|
|
3
|
+
* files, parse their frontmatter, and read the always-loaded entrypoint. Used
|
|
4
|
+
* by the system-prompt section index and by recall.
|
|
5
|
+
* @module @dsh-cc/memory/scan
|
|
6
|
+
*/
|
|
7
|
+
import { parseMemoryFile } from "./parser.js";
|
|
8
|
+
import { ENTRYPOINT_NAME } from "./truncate.js";
|
|
9
|
+
/**
|
|
10
|
+
* Scan a memory directory for its entrypoint and topic files.
|
|
11
|
+
* A missing directory or entrypoint is not an error — the caller renders an
|
|
12
|
+
* empty section. Files are read with the caller's cancellation signal; the
|
|
13
|
+
* result contains only topics whose frontmatter parsed successfully.
|
|
14
|
+
* @param fs - the contiguous filesystem seam.
|
|
15
|
+
* @param dir - the memory directory to scan.
|
|
16
|
+
* @param signal - optional cancellation for the underlying fs reads.
|
|
17
|
+
* @returns the observed directory state, never throwing for absent files.
|
|
18
|
+
*/
|
|
19
|
+
export async function scanMemoryDirectory(fs, dir, signal) {
|
|
20
|
+
const topics = [];
|
|
21
|
+
let entrypoint;
|
|
22
|
+
let entries;
|
|
23
|
+
try {
|
|
24
|
+
const target = signal !== undefined
|
|
25
|
+
? await fs.resolve(dir, { signal })
|
|
26
|
+
: await fs.resolve(dir);
|
|
27
|
+
entries = await fs.listDir(target, signal);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Missing or unreadable memory directory: treat as empty.
|
|
31
|
+
return { dir, entrypoint: undefined, topics };
|
|
32
|
+
}
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (entry.type !== 'file')
|
|
35
|
+
continue;
|
|
36
|
+
if (entry.name === ENTRYPOINT_NAME) {
|
|
37
|
+
entrypoint = await readOptionalText(fs, entry.target, signal);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (!entry.name.endsWith('.md'))
|
|
41
|
+
continue;
|
|
42
|
+
const raw = await readOptionalText(fs, entry.target, signal);
|
|
43
|
+
if (raw === undefined)
|
|
44
|
+
continue;
|
|
45
|
+
const parsed = parseMemoryFile(raw);
|
|
46
|
+
if (parsed === undefined)
|
|
47
|
+
continue;
|
|
48
|
+
topics.push({ path: entry.target.displayPath, filename: entry.name, frontmatter: parsed.frontmatter });
|
|
49
|
+
}
|
|
50
|
+
topics.sort((a, b) => a.filename.localeCompare(b.filename));
|
|
51
|
+
return { dir, entrypoint, topics };
|
|
52
|
+
}
|
|
53
|
+
async function readOptionalText(fs, target, signal) {
|
|
54
|
+
try {
|
|
55
|
+
return await fs.readText(target, signal);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (isNotFound(error))
|
|
59
|
+
return undefined;
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function isNotFound(error) {
|
|
64
|
+
return typeof error === 'object' && error !== null
|
|
65
|
+
&& 'code' in error
|
|
66
|
+
&& (error.code === 'FS_NOT_FOUND'
|
|
67
|
+
|| error.code === 'FS_NOT_DIRECTORY'
|
|
68
|
+
|| error.code === 'ENOENT');
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=scan.js.map
|
package/lib/scan.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAa/C;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,EAAc,EACd,GAAW,EACX,MAAoB;IAEpB,MAAM,MAAM,GAAuB,EAAE,CAAA;IACrC,IAAI,UAA8B,CAAA;IAClC,IAAI,OAAO,CAAA;IACX,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,KAAK,SAAS;YACjC,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC;YACnC,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACzB,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;QAC1D,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,CAAA;IAC/C,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;YAAE,SAAQ;QACnC,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YACnC,UAAU,GAAG,MAAM,gBAAgB,CAAC,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;YAC7D,SAAQ;QACV,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAQ;QACzC,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC5D,IAAI,GAAG,KAAK,SAAS;YAAE,SAAQ;QAC/B,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAA;QACnC,IAAI,MAAM,KAAK,SAAS;YAAE,SAAQ;QAClC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAA;IACxG,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC3D,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,CAAA;AACpC,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,EAAc,EACd,MAAgB,EAChB,MAAoB;IAEpB,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,UAAU,CAAC,KAAK,CAAC;YAAE,OAAO,SAAS,CAAA;QACvC,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;WAC7C,MAAM,IAAI,KAAK;WACf,CAAE,KAAiB,CAAC,IAAI,KAAK,cAAc;eACxC,KAAiB,CAAC,IAAI,KAAK,kBAAkB;eAC7C,KAA2B,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;AACxD,CAAC"}
|
package/lib/section.d.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `memory` system-prompt section: save-channel guidance plus one layer
|
|
3
|
+
* per memory scope — the workspace-private directory, the global directory,
|
|
4
|
+
* and (when `teamEnabled`) the workspace's team directory. Plugins mount once
|
|
5
|
+
* on the root context, so a single global section serves every agent: the
|
|
6
|
+
* text callback receives the assembling agent through `AssembleContext.scope`
|
|
7
|
+
* (the agent loop passes `scope: agent`) and renders that agent's workspace
|
|
8
|
+
* layer. Delegated children (`delegationDepth > 0`) render an empty string so
|
|
9
|
+
* the section drops out of the child prompt. Directory scans run in the
|
|
10
|
+
* background through `ctx.fs`; rendered per-layer fragments are cached and
|
|
11
|
+
* `system-prompt/change` fires only when a fragment actually changed. The
|
|
12
|
+
* section always renders for a top-level agent (a memoryless layer shows a
|
|
13
|
+
* placeholder) so the save guidance never disappears.
|
|
14
|
+
* @module @dsh-cc/memory/section
|
|
15
|
+
*/
|
|
16
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
17
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
18
|
+
import type { MemoryDirectoryState } from './scan.ts';
|
|
19
|
+
/** Default order slot for the memory section (before tool guidance). */
|
|
20
|
+
export declare const MEMORY_SECTION_ORDER = 90;
|
|
21
|
+
/** The section's unique name. */
|
|
22
|
+
export declare const MEMORY_SECTION_NAME = "memory";
|
|
23
|
+
/** A memory layer surfaced in the section. */
|
|
24
|
+
export interface MemoryLayer {
|
|
25
|
+
/** Scope tag shown in the combined index. */
|
|
26
|
+
scope: 'workspace' | 'global' | 'team';
|
|
27
|
+
/** Heading label for the layer's entrypoint block. */
|
|
28
|
+
label: string;
|
|
29
|
+
/** The layer's directory. */
|
|
30
|
+
dir: string;
|
|
31
|
+
/** The last scanned state, or `undefined` while the first scan is pending. */
|
|
32
|
+
state: MemoryDirectoryState | undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Owner of the cached `memory` section text. Holds one scanned state per
|
|
36
|
+
* memory directory so the synchronous section provider can compose the text,
|
|
37
|
+
* and refreshes states from disk.
|
|
38
|
+
*/
|
|
39
|
+
export declare class MemorySection {
|
|
40
|
+
private readonly ctx;
|
|
41
|
+
private readonly home;
|
|
42
|
+
private readonly states;
|
|
43
|
+
private readonly fragments;
|
|
44
|
+
private readonly refreshers;
|
|
45
|
+
private readonly teamEnabled;
|
|
46
|
+
/**
|
|
47
|
+
* Create a cache holder bounded to a memory home.
|
|
48
|
+
* @param ctx - the host context whose `fs` seam and `system-prompt/change`
|
|
49
|
+
* channel drive refresh.
|
|
50
|
+
* @param home - the memory home: the global layer's directory, and the root
|
|
51
|
+
* under which each workspace's private directory lives (`projects/<slug>`).
|
|
52
|
+
* @param options - when `teamEnabled` is set, each workspace also surfaces
|
|
53
|
+
* its shared team directory (`<workspaceDir>/team`).
|
|
54
|
+
*/
|
|
55
|
+
constructor(ctx: Context, home: string, options?: {
|
|
56
|
+
teamEnabled?: boolean;
|
|
57
|
+
});
|
|
58
|
+
/** Register the `memory` section, its assemble-waterfall reconciliation,
|
|
59
|
+
* and start the global layer's first scan.
|
|
60
|
+
*
|
|
61
|
+
* The waterfall listener removes the first-assembly placeholder jitter:
|
|
62
|
+
* `systemPrompt.assemble()` runs BEFORE the agent pre-step, so the section
|
|
63
|
+
* text callback cannot await the directory scans — but the waterfall can.
|
|
64
|
+
* After the base assembly, a scope with unscanned layers joins the
|
|
65
|
+
* in-flight `refresh(agent)` (bounded by {@linkcode READINESS_BUDGET_MS};
|
|
66
|
+
* per-directory refreshers self-deduplicate, so this is the same promise
|
|
67
|
+
* the background scan already started) and re-renders the section with the
|
|
68
|
+
* same render function the section callback uses, so the first assembly
|
|
69
|
+
* already carries the scanned text and no later request sees a different
|
|
70
|
+
* prefix. Timeout or failure keeps the placeholder; the scan lands on a
|
|
71
|
+
* later assembly through `system-prompt/change`. */
|
|
72
|
+
start(): void;
|
|
73
|
+
/**
|
|
74
|
+
* The layers an agent's section renders: its workspace directory plus the
|
|
75
|
+
* global directory, and the workspace's team directory when enabled.
|
|
76
|
+
*/
|
|
77
|
+
private layersFor;
|
|
78
|
+
/** Compose the section text for the agent behind an assemble scope. */
|
|
79
|
+
private render;
|
|
80
|
+
/**
|
|
81
|
+
* Re-scan the global directory plus the agent's workspace (and team)
|
|
82
|
+
* directories. Overlapping scans of one directory share a single background
|
|
83
|
+
* promise. Emits `system-prompt/change` only when a rendered fragment
|
|
84
|
+
* actually changed, so an unchanged disk does not churn reassembly.
|
|
85
|
+
* @param agent - whose workspace layers to refresh; omitted = global only.
|
|
86
|
+
*/
|
|
87
|
+
refresh(agent?: Agent): Promise<void>;
|
|
88
|
+
private refreshDir;
|
|
89
|
+
private scanDir;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The save-channel guidance every memory section carries. It names the ONLY
|
|
93
|
+
* working save path: direct write/edit calls aimed at a memory directory are
|
|
94
|
+
* fenced by the fs sandbox (memory directories live outside every session
|
|
95
|
+
* workspace), so the model must call `memory_save` instead. Without this line
|
|
96
|
+
* a memoryless session presents no guidance at all and the model falls back
|
|
97
|
+
* to its Claude Code prior (`~/.claude/projects/<slug>/memory/`), whose
|
|
98
|
+
* writes fail the same fence.
|
|
99
|
+
* @param workspaceDir - the workspace memory directory, when known.
|
|
100
|
+
* @param globalDir - the global memory directory.
|
|
101
|
+
* @returns the guidance lines.
|
|
102
|
+
*/
|
|
103
|
+
export declare function saveGuidance(workspaceDir: string | undefined, globalDir: string): string[];
|
|
104
|
+
/**
|
|
105
|
+
* Render the memory section from the observed layers (workspace + global).
|
|
106
|
+
* The section is ALWAYS present (even with no memories) so the save guidance
|
|
107
|
+
* is stable model-visible context; an unscanned or empty layer renders a
|
|
108
|
+
* placeholder entrypoint body.
|
|
109
|
+
* @param globalDir - the global memory directory.
|
|
110
|
+
* @param workspaceDir - this workspace's private memory directory.
|
|
111
|
+
* @param globalState - the scanned global state, if available.
|
|
112
|
+
* @param workspaceState - the scanned workspace state, if available.
|
|
113
|
+
* @returns the rendered section.
|
|
114
|
+
*/
|
|
115
|
+
export declare function renderMemorySection(globalDir: string, workspaceDir: string, globalState?: MemoryDirectoryState, workspaceState?: MemoryDirectoryState): string;
|
|
116
|
+
/**
|
|
117
|
+
* Render the combined section with the workspace's team layer added.
|
|
118
|
+
* @param globalDir - the global memory directory.
|
|
119
|
+
* @param workspaceDir - this workspace's private memory directory.
|
|
120
|
+
* @param teamDir - this workspace's shared team directory.
|
|
121
|
+
* @param globalState - the scanned global state, if available.
|
|
122
|
+
* @param workspaceState - the scanned workspace state, if available.
|
|
123
|
+
* @param teamState - the scanned team state, if available.
|
|
124
|
+
* @returns the rendered section.
|
|
125
|
+
*/
|
|
126
|
+
export declare function renderTeamMemorySection(globalDir: string, workspaceDir: string, teamDir: string, globalState?: MemoryDirectoryState, workspaceState?: MemoryDirectoryState, teamState?: MemoryDirectoryState): string;
|
|
127
|
+
/** Compose the full section text from the observed layers. */
|
|
128
|
+
export declare function renderLayers(layers: readonly MemoryLayer[]): string;
|
|
129
|
+
//# sourceMappingURL=section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section.d.ts","sourceRoot":"","sources":["../src/section.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAInD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAIrD,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC,iCAAiC;AACjC,eAAO,MAAM,mBAAmB,WAAW,CAAA;AAwB3C,8CAA8C;AAC9C,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,KAAK,EAAE,WAAW,GAAG,QAAQ,GAAG,MAAM,CAAA;IACtC,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAA;IACb,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,8EAA8E;IAC9E,KAAK,EAAE,oBAAoB,GAAG,SAAS,CAAA;CACxC;AAcD;;;;GAIG;AACH,qBAAa,aAAa;IAgBtB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAhBvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;IACjE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmC;IAC9D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC;;;;;;;;OAQG;gBAEgB,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,EAC7B,OAAO,GAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAO;IAKzC;;;;;;;;;;;;;wDAaoD;IACpD,KAAK,IAAI,IAAI;IA8Bb;;;OAGG;IACH,OAAO,CAAC,SAAS;IA6BjB,uEAAuE;IACvE,OAAO,CAAC,MAAM;IAgBd;;;;;;OAMG;IACG,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAM3C,OAAO,CAAC,UAAU;YAcJ,OAAO;CAatB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAU1F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,oBAAoB,EAClC,cAAc,CAAC,EAAE,oBAAoB,GACpC,MAAM,CAKR;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,oBAAoB,EAClC,cAAc,CAAC,EAAE,oBAAoB,EACrC,SAAS,CAAC,EAAE,oBAAoB,GAC/B,MAAM,CAMR;AAWD,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CA+BnE"}
|
package/lib/section.js
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `memory` system-prompt section: save-channel guidance plus one layer
|
|
3
|
+
* per memory scope — the workspace-private directory, the global directory,
|
|
4
|
+
* and (when `teamEnabled`) the workspace's team directory. Plugins mount once
|
|
5
|
+
* on the root context, so a single global section serves every agent: the
|
|
6
|
+
* text callback receives the assembling agent through `AssembleContext.scope`
|
|
7
|
+
* (the agent loop passes `scope: agent`) and renders that agent's workspace
|
|
8
|
+
* layer. Delegated children (`delegationDepth > 0`) render an empty string so
|
|
9
|
+
* the section drops out of the child prompt. Directory scans run in the
|
|
10
|
+
* background through `ctx.fs`; rendered per-layer fragments are cached and
|
|
11
|
+
* `system-prompt/change` fires only when a fragment actually changed. The
|
|
12
|
+
* section always renders for a top-level agent (a memoryless layer shows a
|
|
13
|
+
* placeholder) so the save guidance never disappears.
|
|
14
|
+
* @module @dsh-cc/memory/section
|
|
15
|
+
*/
|
|
16
|
+
import { delegationDepthOf } from '@deepseek-ai/dsh-subagent';
|
|
17
|
+
import { ENTRYPOINT_NAME, truncateEntrypointContent } from "./truncate.js";
|
|
18
|
+
import { scanMemoryDirectory } from "./scan.js";
|
|
19
|
+
import { cwdOf, resolveWorkspaceMemoryDir } from "./paths.js";
|
|
20
|
+
import { resolveTeamMemoryRoot } from "./team.js";
|
|
21
|
+
/** Default order slot for the memory section (before tool guidance). */
|
|
22
|
+
export const MEMORY_SECTION_ORDER = 90;
|
|
23
|
+
/** The section's unique name. */
|
|
24
|
+
export const MEMORY_SECTION_NAME = 'memory';
|
|
25
|
+
/**
|
|
26
|
+
* How long the assemble waterfall may wait for a workspace's in-flight first
|
|
27
|
+
* scan before giving up and shipping the placeholder. Bounded so a slow or
|
|
28
|
+
* wedged scan delays the first request by at most this much; the background
|
|
29
|
+
* `system-prompt/change` path remains the fallback.
|
|
30
|
+
*/
|
|
31
|
+
const READINESS_BUDGET_MS = 500;
|
|
32
|
+
/**
|
|
33
|
+
* Join `promise`, but reject after `ms` milliseconds either way. The loser
|
|
34
|
+
* keeps running in the background (its eventual rejection is contained); the
|
|
35
|
+
* caller gets a rejection to degrade on.
|
|
36
|
+
*/
|
|
37
|
+
function withinBudget(promise, ms) {
|
|
38
|
+
let timer;
|
|
39
|
+
const budget = new Promise((_resolve, reject) => {
|
|
40
|
+
timer = setTimeout(() => reject(new Error(`readiness budget of ${ms}ms expired`)), ms);
|
|
41
|
+
});
|
|
42
|
+
budget.catch(() => { });
|
|
43
|
+
return Promise.race([promise, budget]).finally(() => clearTimeout(timer));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Extract the assembling agent from an `AssembleContext`. The agent loop
|
|
47
|
+
* assembles with `scope: agent` (a runtime contract; `ScopeKey` is opaque),
|
|
48
|
+
* so the scope IS the agent whenever a session drives the assembly.
|
|
49
|
+
*/
|
|
50
|
+
function agentFromScope(scope) {
|
|
51
|
+
if (typeof scope === 'object' && scope !== null && 'session' in scope) {
|
|
52
|
+
return scope;
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Owner of the cached `memory` section text. Holds one scanned state per
|
|
58
|
+
* memory directory so the synchronous section provider can compose the text,
|
|
59
|
+
* and refreshes states from disk.
|
|
60
|
+
*/
|
|
61
|
+
export class MemorySection {
|
|
62
|
+
ctx;
|
|
63
|
+
home;
|
|
64
|
+
states = new Map();
|
|
65
|
+
fragments = new Map();
|
|
66
|
+
refreshers = new Map();
|
|
67
|
+
teamEnabled;
|
|
68
|
+
/**
|
|
69
|
+
* Create a cache holder bounded to a memory home.
|
|
70
|
+
* @param ctx - the host context whose `fs` seam and `system-prompt/change`
|
|
71
|
+
* channel drive refresh.
|
|
72
|
+
* @param home - the memory home: the global layer's directory, and the root
|
|
73
|
+
* under which each workspace's private directory lives (`projects/<slug>`).
|
|
74
|
+
* @param options - when `teamEnabled` is set, each workspace also surfaces
|
|
75
|
+
* its shared team directory (`<workspaceDir>/team`).
|
|
76
|
+
*/
|
|
77
|
+
constructor(ctx, home, options = {}) {
|
|
78
|
+
this.ctx = ctx;
|
|
79
|
+
this.home = home;
|
|
80
|
+
this.teamEnabled = options.teamEnabled ?? false;
|
|
81
|
+
}
|
|
82
|
+
/** Register the `memory` section, its assemble-waterfall reconciliation,
|
|
83
|
+
* and start the global layer's first scan.
|
|
84
|
+
*
|
|
85
|
+
* The waterfall listener removes the first-assembly placeholder jitter:
|
|
86
|
+
* `systemPrompt.assemble()` runs BEFORE the agent pre-step, so the section
|
|
87
|
+
* text callback cannot await the directory scans — but the waterfall can.
|
|
88
|
+
* After the base assembly, a scope with unscanned layers joins the
|
|
89
|
+
* in-flight `refresh(agent)` (bounded by {@linkcode READINESS_BUDGET_MS};
|
|
90
|
+
* per-directory refreshers self-deduplicate, so this is the same promise
|
|
91
|
+
* the background scan already started) and re-renders the section with the
|
|
92
|
+
* same render function the section callback uses, so the first assembly
|
|
93
|
+
* already carries the scanned text and no later request sees a different
|
|
94
|
+
* prefix. Timeout or failure keeps the placeholder; the scan lands on a
|
|
95
|
+
* later assembly through `system-prompt/change`. */
|
|
96
|
+
start() {
|
|
97
|
+
this.ctx.systemPrompt.section({
|
|
98
|
+
name: MEMORY_SECTION_NAME,
|
|
99
|
+
order: MEMORY_SECTION_ORDER,
|
|
100
|
+
text: (context) => this.render(context.scope),
|
|
101
|
+
});
|
|
102
|
+
this.ctx.on('system-prompt/assemble', async (_assembly, context, next) => {
|
|
103
|
+
const result = await next();
|
|
104
|
+
const agent = agentFromScope(context.scope);
|
|
105
|
+
if (agent === undefined)
|
|
106
|
+
return result;
|
|
107
|
+
if (!isTopLevel(agent))
|
|
108
|
+
return result;
|
|
109
|
+
if (!this.layersFor(agent).some(layer => layer.state === undefined))
|
|
110
|
+
return result;
|
|
111
|
+
try {
|
|
112
|
+
await withinBudget(this.refresh(agent), READINESS_BUDGET_MS);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
this.ctx.logger.warn(`memory: first-assembly scan did not land within ${READINESS_BUDGET_MS}ms: ${String(error)}; keeping the placeholder`);
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
...result,
|
|
120
|
+
sections: result.sections.map(section => section.name === MEMORY_SECTION_NAME
|
|
121
|
+
? { ...section, text: this.render(agent) }
|
|
122
|
+
: section),
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
void this.refresh();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The layers an agent's section renders: its workspace directory plus the
|
|
129
|
+
* global directory, and the workspace's team directory when enabled.
|
|
130
|
+
*/
|
|
131
|
+
layersFor(agent) {
|
|
132
|
+
const layers = [];
|
|
133
|
+
if (agent !== undefined) {
|
|
134
|
+
const workspaceDir = resolveWorkspaceMemoryDir(this.home, cwdOf(agent));
|
|
135
|
+
layers.push({
|
|
136
|
+
scope: 'workspace',
|
|
137
|
+
label: 'this workspace',
|
|
138
|
+
dir: workspaceDir,
|
|
139
|
+
state: this.states.get(workspaceDir),
|
|
140
|
+
});
|
|
141
|
+
if (this.teamEnabled) {
|
|
142
|
+
const teamDir = resolveTeamMemoryRoot(workspaceDir);
|
|
143
|
+
layers.push({
|
|
144
|
+
scope: 'team',
|
|
145
|
+
label: 'team',
|
|
146
|
+
dir: teamDir,
|
|
147
|
+
state: this.states.get(teamDir),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
layers.push({
|
|
152
|
+
scope: 'global',
|
|
153
|
+
label: 'global',
|
|
154
|
+
dir: this.home,
|
|
155
|
+
state: this.states.get(this.home),
|
|
156
|
+
});
|
|
157
|
+
return layers;
|
|
158
|
+
}
|
|
159
|
+
/** Compose the section text for the agent behind an assemble scope. */
|
|
160
|
+
render(scope) {
|
|
161
|
+
const agent = agentFromScope(scope);
|
|
162
|
+
// Delegated children start fresh (CC Task isolation). The parent prompt
|
|
163
|
+
// must pass any facts the child needs; dumping MEMORY.md into every
|
|
164
|
+
// subagent is the token cost this mute removes. Fail closed: a throw
|
|
165
|
+
// from reading depth treats the agent as a child.
|
|
166
|
+
if (agent !== undefined && !isTopLevel(agent))
|
|
167
|
+
return '';
|
|
168
|
+
if (agent !== undefined) {
|
|
169
|
+
// First assembly for this workspace: render placeholders now and scan
|
|
170
|
+
// its directories in the background so the next step sees real content.
|
|
171
|
+
const unknown = this.layersFor(agent).some(layer => layer.state === undefined);
|
|
172
|
+
if (unknown)
|
|
173
|
+
void this.refresh(agent);
|
|
174
|
+
}
|
|
175
|
+
return renderLayers(this.layersFor(agent));
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Re-scan the global directory plus the agent's workspace (and team)
|
|
179
|
+
* directories. Overlapping scans of one directory share a single background
|
|
180
|
+
* promise. Emits `system-prompt/change` only when a rendered fragment
|
|
181
|
+
* actually changed, so an unchanged disk does not churn reassembly.
|
|
182
|
+
* @param agent - whose workspace layers to refresh; omitted = global only.
|
|
183
|
+
*/
|
|
184
|
+
async refresh(agent) {
|
|
185
|
+
const dirs = this.layersFor(agent).map(layer => layer.dir);
|
|
186
|
+
const scans = dirs.map(dir => this.refreshDir(dir));
|
|
187
|
+
await Promise.all(scans);
|
|
188
|
+
}
|
|
189
|
+
refreshDir(dir) {
|
|
190
|
+
const previous = this.refreshers.get(dir);
|
|
191
|
+
if (previous !== undefined) {
|
|
192
|
+
previous.catch(() => { });
|
|
193
|
+
return previous;
|
|
194
|
+
}
|
|
195
|
+
const current = this.scanDir(dir);
|
|
196
|
+
this.refreshers.set(dir, current);
|
|
197
|
+
void current.finally(() => {
|
|
198
|
+
if (this.refreshers.get(dir) === current)
|
|
199
|
+
this.refreshers.delete(dir);
|
|
200
|
+
});
|
|
201
|
+
return current;
|
|
202
|
+
}
|
|
203
|
+
async scanDir(dir) {
|
|
204
|
+
const fileSystem = this.ctx.get('fs');
|
|
205
|
+
if (fileSystem === undefined)
|
|
206
|
+
return;
|
|
207
|
+
const state = await scanMemoryDirectory(fileSystem, dir);
|
|
208
|
+
const fragment = renderLayerFragment({ scope: 'global', label: '', dir, state });
|
|
209
|
+
if (fragment === this.fragments.get(dir)) {
|
|
210
|
+
this.states.set(dir, state);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
this.states.set(dir, state);
|
|
214
|
+
this.fragments.set(dir, fragment);
|
|
215
|
+
this.ctx.emit('system-prompt/change');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The save-channel guidance every memory section carries. It names the ONLY
|
|
220
|
+
* working save path: direct write/edit calls aimed at a memory directory are
|
|
221
|
+
* fenced by the fs sandbox (memory directories live outside every session
|
|
222
|
+
* workspace), so the model must call `memory_save` instead. Without this line
|
|
223
|
+
* a memoryless session presents no guidance at all and the model falls back
|
|
224
|
+
* to its Claude Code prior (`~/.claude/projects/<slug>/memory/`), whose
|
|
225
|
+
* writes fail the same fence.
|
|
226
|
+
* @param workspaceDir - the workspace memory directory, when known.
|
|
227
|
+
* @param globalDir - the global memory directory.
|
|
228
|
+
* @returns the guidance lines.
|
|
229
|
+
*/
|
|
230
|
+
export function saveGuidance(workspaceDir, globalDir) {
|
|
231
|
+
const target = workspaceDir !== undefined
|
|
232
|
+
? `this workspace's directory (\`${workspaceDir}\`)`
|
|
233
|
+
: 'the current workspace\'s directory';
|
|
234
|
+
return [
|
|
235
|
+
`To save a durable fact, call the \`memory_save\` tool — it writes the topic file and updates ${ENTRYPOINT_NAME} for you. `
|
|
236
|
+
+ `By default the memory lands in ${target}, visible only to sessions of this workspace; `
|
|
237
|
+
+ `pass \`scope: "global"\` for facts useful across ALL workspaces (saved to \`${globalDir}\`).`,
|
|
238
|
+
'Never write or edit files under a memory directory directly: the sandbox fences writes outside the session workspace, so direct writes always fail.',
|
|
239
|
+
];
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Render the memory section from the observed layers (workspace + global).
|
|
243
|
+
* The section is ALWAYS present (even with no memories) so the save guidance
|
|
244
|
+
* is stable model-visible context; an unscanned or empty layer renders a
|
|
245
|
+
* placeholder entrypoint body.
|
|
246
|
+
* @param globalDir - the global memory directory.
|
|
247
|
+
* @param workspaceDir - this workspace's private memory directory.
|
|
248
|
+
* @param globalState - the scanned global state, if available.
|
|
249
|
+
* @param workspaceState - the scanned workspace state, if available.
|
|
250
|
+
* @returns the rendered section.
|
|
251
|
+
*/
|
|
252
|
+
export function renderMemorySection(globalDir, workspaceDir, globalState, workspaceState) {
|
|
253
|
+
return renderLayers([
|
|
254
|
+
{ scope: 'workspace', label: 'this workspace', dir: workspaceDir, state: workspaceState },
|
|
255
|
+
{ scope: 'global', label: 'global', dir: globalDir, state: globalState },
|
|
256
|
+
]);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Render the combined section with the workspace's team layer added.
|
|
260
|
+
* @param globalDir - the global memory directory.
|
|
261
|
+
* @param workspaceDir - this workspace's private memory directory.
|
|
262
|
+
* @param teamDir - this workspace's shared team directory.
|
|
263
|
+
* @param globalState - the scanned global state, if available.
|
|
264
|
+
* @param workspaceState - the scanned workspace state, if available.
|
|
265
|
+
* @param teamState - the scanned team state, if available.
|
|
266
|
+
* @returns the rendered section.
|
|
267
|
+
*/
|
|
268
|
+
export function renderTeamMemorySection(globalDir, workspaceDir, teamDir, globalState, workspaceState, teamState) {
|
|
269
|
+
return renderLayers([
|
|
270
|
+
{ scope: 'workspace', label: 'this workspace', dir: workspaceDir, state: workspaceState },
|
|
271
|
+
{ scope: 'team', label: 'team', dir: teamDir, state: teamState },
|
|
272
|
+
{ scope: 'global', label: 'global', dir: globalDir, state: globalState },
|
|
273
|
+
]);
|
|
274
|
+
}
|
|
275
|
+
/** Render one layer's entrypoint block (heading + truncated body). */
|
|
276
|
+
function renderLayerFragment(layer) {
|
|
277
|
+
const entry = layer.state?.entrypoint?.trim();
|
|
278
|
+
const body = entry !== undefined && entry.length > 0
|
|
279
|
+
? truncateEntrypointContent(entry).content
|
|
280
|
+
: '(no memories yet)';
|
|
281
|
+
return `## ${ENTRYPOINT_NAME} (${layer.label})\n\n${body}`;
|
|
282
|
+
}
|
|
283
|
+
/** Compose the full section text from the observed layers. */
|
|
284
|
+
export function renderLayers(layers) {
|
|
285
|
+
const workspace = layers.find(layer => layer.scope === 'workspace');
|
|
286
|
+
const global = layers.find(layer => layer.scope === 'global');
|
|
287
|
+
const team = layers.find(layer => layer.scope === 'team');
|
|
288
|
+
const globalDir = global?.dir ?? '';
|
|
289
|
+
const lines = [
|
|
290
|
+
'# Memory',
|
|
291
|
+
'',
|
|
292
|
+
'You have a persistent, file-based memory system. Use it to recall context across conversations and to save durable facts.',
|
|
293
|
+
'',
|
|
294
|
+
'There are two scope levels:',
|
|
295
|
+
`- workspace: memories private to the current workspace${workspace !== undefined ? `, stored at \`${workspace.dir}\`` : ''}.`,
|
|
296
|
+
`- global: memories shared by every workspace${global !== undefined ? `, stored at \`${global.dir}\`` : ''}.`,
|
|
297
|
+
];
|
|
298
|
+
if (team !== undefined) {
|
|
299
|
+
lines.push(`- team: memories shared with and contributed by all users of this project, stored at \`${team.dir}\`.`);
|
|
300
|
+
}
|
|
301
|
+
lines.push('', 'Each directory keeps its own index and topic files. Save each memory to the directory matching its scope; never write memory content directly into a MEMORY.md.', '', ...saveGuidance(workspace?.dir, globalDir), '');
|
|
302
|
+
const ordered = [workspace, team, global].filter((layer) => layer !== undefined);
|
|
303
|
+
lines.push(ordered.map(layer => renderLayerFragment(layer)).join('\n\n'));
|
|
304
|
+
const index = renderCombinedIndex(ordered);
|
|
305
|
+
if (index.length > 0)
|
|
306
|
+
lines.push('', ...index);
|
|
307
|
+
const search = renderSearch(ordered);
|
|
308
|
+
if (search.length > 0)
|
|
309
|
+
lines.push('', ...search);
|
|
310
|
+
return lines.join('\n');
|
|
311
|
+
}
|
|
312
|
+
/** A combined topic index with each layer's scope tagged, sorted by filename. */
|
|
313
|
+
function renderCombinedIndex(layers) {
|
|
314
|
+
const combined = layers.flatMap(layer => (layer.state?.topics ?? []).map(topic => ({ ...topic, scope: layer.scope })));
|
|
315
|
+
if (combined.length === 0)
|
|
316
|
+
return [];
|
|
317
|
+
combined.sort((a, b) => a.filename.localeCompare(b.filename));
|
|
318
|
+
const lines = combined.map((topic) => {
|
|
319
|
+
const type = topic.frontmatter.type === undefined ? '' : ` [${topic.frontmatter.type}]`;
|
|
320
|
+
return `- [${escapeLinkText(topic.frontmatter.name)}](${topic.filename}) — ${topic.frontmatter.description} (${topic.scope})${type}`;
|
|
321
|
+
});
|
|
322
|
+
return ['## Memory index', '', ...lines];
|
|
323
|
+
}
|
|
324
|
+
function renderSearch(layers) {
|
|
325
|
+
const populated = layers.filter(layer => (layer.state?.topics.length ?? 0) > 0);
|
|
326
|
+
if (populated.length === 0)
|
|
327
|
+
return [];
|
|
328
|
+
const dirs = populated.map(layer => `${layer.dir}/`).join(' ');
|
|
329
|
+
return [
|
|
330
|
+
'## Searching past context',
|
|
331
|
+
'When a MEMORY.md entry is not enough, search topic files and the transcript log with narrow terms (error messages, file paths, function names):',
|
|
332
|
+
'```',
|
|
333
|
+
`grep -rn "<search term>" ${dirs} --include="*.md"`,
|
|
334
|
+
'```',
|
|
335
|
+
];
|
|
336
|
+
}
|
|
337
|
+
function escapeLinkText(name) {
|
|
338
|
+
return name.replace(/[\\[\]]/g, '\\$&');
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Whether an assembling agent is top-level (not a delegated subagent).
|
|
342
|
+
* Fail closed: any throw from reading depth treats the agent as a child so
|
|
343
|
+
* the section never dumps MEMORY.md into a mis-stamped child.
|
|
344
|
+
*/
|
|
345
|
+
function isTopLevel(agent) {
|
|
346
|
+
try {
|
|
347
|
+
return delegationDepthOf(agent) === 0;
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=section.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section.js","sourceRoot":"","sources":["../src/section.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAA;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAE/C,OAAO,EAAE,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAA;AAEjD,wEAAwE;AACxE,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAEtC,iCAAiC;AACjC,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAA;AAE3C;;;;;GAKG;AACH,MAAM,mBAAmB,GAAG,GAAG,CAAA;AAE/B;;;;GAIG;AACH,SAAS,YAAY,CAAI,OAAmB,EAAE,EAAU;IACtD,IAAI,KAAgD,CAAA;IACpD,MAAM,MAAM,GAAG,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;QACrD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,EAAE,YAAY,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACxF,CAAC,CAAC,CAAA;IACF,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IACtB,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAA;AAC3E,CAAC;AAcD;;;;GAIG;AACH,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;QACtE,OAAO,KAAc,CAAA;IACvB,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,aAAa;IAgBL;IACA;IAhBF,MAAM,GAAG,IAAI,GAAG,EAAgC,CAAA;IAChD,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAA;IACrC,UAAU,GAAG,IAAI,GAAG,EAAyB,CAAA;IAC7C,WAAW,CAAS;IAErC;;;;;;;;OAQG;IACH,YACmB,GAAY,EACZ,IAAY,EAC7B,UAAqC,EAAE;QAFtB,QAAG,GAAH,GAAG,CAAS;QACZ,SAAI,GAAJ,IAAI,CAAQ;QAG7B,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,KAAK,CAAA;IACjD,CAAC;IAED;;;;;;;;;;;;;wDAaoD;IACpD,KAAK;QACH,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC;YAC5B,IAAI,EAAE,mBAAmB;YACzB,KAAK,EAAE,oBAAoB;YAC3B,IAAI,EAAE,CAAC,OAA4B,EAAU,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;SAC3E,CAAC,CAAA;QACF,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,wBAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;YACvE,MAAM,MAAM,GAAG,MAAM,IAAI,EAAE,CAAA;YAC3B,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YAC3C,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,MAAM,CAAA;YACtC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;gBAAE,OAAO,MAAM,CAAA;YACrC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC;gBAAE,OAAO,MAAM,CAAA;YAClF,IAAI,CAAC;gBACH,MAAM,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,mBAAmB,CAAC,CAAA;YAC9D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAClB,mDAAmD,mBAAmB,OAAO,MAAM,CAAC,KAAK,CAAC,2BAA2B,CACtH,CAAA;gBACD,OAAO,MAAM,CAAA;YACf,CAAC;YACD,OAAO;gBACL,GAAG,MAAM;gBACT,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,mBAAmB;oBAC3E,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;oBAC1C,CAAC,CAAC,OAAO,CAAC;aACb,CAAA;QACH,CAAC,CAAC,CAAA;QACF,KAAK,IAAI,CAAC,OAAO,EAAE,CAAA;IACrB,CAAC;IAED;;;OAGG;IACK,SAAS,CAAC,KAAwB;QACxC,MAAM,MAAM,GAAkB,EAAE,CAAA;QAChC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,YAAY,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;YACvE,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,WAAW;gBAClB,KAAK,EAAE,gBAAgB;gBACvB,GAAG,EAAE,YAAY;gBACjB,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC;aACrC,CAAC,CAAA;YACF,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACrB,MAAM,OAAO,GAAG,qBAAqB,CAAC,YAAY,CAAC,CAAA;gBACnD,MAAM,CAAC,IAAI,CAAC;oBACV,KAAK,EAAE,MAAM;oBACb,KAAK,EAAE,MAAM;oBACb,GAAG,EAAE,OAAO;oBACZ,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;iBAChC,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QACD,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE,QAAQ;YACf,KAAK,EAAE,QAAQ;YACf,GAAG,EAAE,IAAI,CAAC,IAAI;YACd,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;SAClC,CAAC,CAAA;QACF,OAAO,MAAM,CAAA;IACf,CAAC;IAED,uEAAuE;IAC/D,MAAM,CAAC,KAAc;QAC3B,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;QACnC,wEAAwE;QACxE,oEAAoE;QACpE,qEAAqE;QACrE,kDAAkD;QAClD,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,CAAA;QACxD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,sEAAsE;YACtE,wEAAwE;YACxE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAA;YAC9E,IAAI,OAAO;gBAAE,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QACvC,CAAC;QACD,OAAO,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAA;IAC5C,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,KAAa;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;QACnD,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAC1B,CAAC;IAEO,UAAU,CAAC,GAAW;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACzC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;YACxB,OAAO,QAAQ,CAAA;QACjB,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACjC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QACjC,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE;YACxB,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,OAAO;gBAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACvE,CAAC,CAAC,CAAA;QACF,OAAO,OAAO,CAAA;IAChB,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,GAAW;QAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACrC,IAAI,UAAU,KAAK,SAAS;YAAE,OAAM;QACpC,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,UAAU,EAAE,GAAG,CAAC,CAAA;QACxD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAA;QAChF,IAAI,QAAQ,KAAK,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;YAC3B,OAAM;QACR,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAC3B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;QACjC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAA;IACvC,CAAC;CACF;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAAC,YAAgC,EAAE,SAAiB;IAC9E,MAAM,MAAM,GAAG,YAAY,KAAK,SAAS;QACvC,CAAC,CAAC,iCAAiC,YAAY,KAAK;QACpD,CAAC,CAAC,oCAAoC,CAAA;IACxC,OAAO;QACL,gGAAgG,eAAe,YAAY;cACzH,kCAAkC,MAAM,gDAAgD;cACxF,+EAA+E,SAAS,MAAM;QAChG,qJAAqJ;KACtJ,CAAA;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CACjC,SAAiB,EACjB,YAAoB,EACpB,WAAkC,EAClC,cAAqC;IAErC,OAAO,YAAY,CAAC;QAClB,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,cAAc,EAAE;QACzF,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE;KACzE,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CACrC,SAAiB,EACjB,YAAoB,EACpB,OAAe,EACf,WAAkC,EAClC,cAAqC,EACrC,SAAgC;IAEhC,OAAO,YAAY,CAAC;QAClB,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,cAAc,EAAE;QACzF,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE;QAChE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE;KACzE,CAAC,CAAA;AACJ,CAAC;AAED,sEAAsE;AACtE,SAAS,mBAAmB,CAAC,KAAkB;IAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;IAC7C,MAAM,IAAI,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAClD,CAAC,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC,OAAO;QAC1C,CAAC,CAAC,mBAAmB,CAAA;IACvB,OAAO,MAAM,eAAe,KAAK,KAAK,CAAC,KAAK,QAAQ,IAAI,EAAE,CAAA;AAC5D,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,YAAY,CAAC,MAA8B;IACzD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,WAAW,CAAC,CAAA;IACnE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAA;IAC7D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,MAAM,CAAC,CAAA;IACzD,MAAM,SAAS,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,CAAA;IACnC,MAAM,KAAK,GAAG;QACZ,UAAU;QACV,EAAE;QACF,2HAA2H;QAC3H,EAAE;QACF,6BAA6B;QAC7B,yDAAyD,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG;QAC7H,+CAA+C,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG;KAC9G,CAAA;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,0FAA0F,IAAI,CAAC,GAAG,KAAK,CAAC,CAAA;IACrH,CAAC;IACD,KAAK,CAAC,IAAI,CACR,EAAE,EACF,iKAAiK,EACjK,EAAE,EACF,GAAG,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,SAAS,CAAC,EAC1C,EAAE,CACH,CAAA;IACD,MAAM,OAAO,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAwB,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAA;IACtG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IACzE,MAAM,KAAK,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAA;IAC1C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,KAAK,CAAC,CAAA;IAC9C,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IACpC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,CAAA;IAChD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAED,iFAAiF;AACjF,SAAS,mBAAmB,CAAC,MAA8B;IACzD,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CACtC,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAC7E,CAAA;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IACpC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC7D,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACnC,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,WAAW,CAAC,IAAI,GAAG,CAAA;QACvF,OAAO,MAAM,cAAc,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,QAAQ,OAAO,KAAK,CAAC,WAAW,CAAC,WAAW,KAAK,KAAK,CAAC,KAAK,IAAI,IAAI,EAAE,CAAA;IACtI,CAAC,CAAC,CAAA;IACF,OAAO,CAAC,iBAAiB,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,CAAA;AAC1C,CAAC;AAED,SAAS,YAAY,CAAC,MAA8B;IAClD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IAC/E,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IACrC,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC9D,OAAO;QACL,2BAA2B;QAC3B,iJAAiJ;QACjJ,KAAK;QACL,4BAA4B,IAAI,mBAAmB;QACnD,KAAK;KACN,CAAA;AACH,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;AACzC,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,KAAY;IAC9B,IAAI,CAAC;QACH,OAAO,iBAAiB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC"}
|