@nexrall/code-core 1.4.46 → 1.4.47
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/dist/agent/agentTypes.d.ts.map +1 -1
- package/dist/agent/agentTypes.js +6 -2
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +14 -1
- package/dist/agent/memory.d.ts +38 -10
- package/dist/agent/memory.d.ts.map +1 -1
- package/dist/agent/memory.js +223 -67
- package/dist/agent/skills.d.ts.map +1 -1
- package/dist/agent/skills.js +3 -14
- package/dist/agent/trust.d.ts +7 -0
- package/dist/agent/trust.d.ts.map +1 -0
- package/dist/agent/trust.js +151 -0
- package/dist/api/client.d.ts +30 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +52 -0
- package/dist/checkpoint/manager.d.ts.map +1 -1
- package/dist/checkpoint/manager.js +6 -0
- package/dist/commands/loader.d.ts.map +1 -1
- package/dist/commands/loader.js +2 -13
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/permissions/modePolicy.d.ts.map +1 -1
- package/dist/permissions/modePolicy.js +1 -0
- package/dist/permissions/rules.js +1 -1
- package/dist/plugins/index.d.ts +31 -4
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +72 -5
- package/dist/plugins/installer.d.ts +47 -0
- package/dist/plugins/installer.d.ts.map +1 -1
- package/dist/plugins/installer.js +173 -0
- package/dist/tools/executor.d.ts.map +1 -1
- package/dist/tools/executor.js +196 -5
- package/dist/util/frontmatter.d.ts +22 -0
- package/dist/util/frontmatter.d.ts.map +1 -0
- package/dist/util/frontmatter.js +33 -0
- package/package.json +5 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentTypes.d.ts","sourceRoot":"","sources":["../../src/agent/agentTypes.ts"],"names":[],"mappings":"AAkCA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACpD;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,qDAAqD;AACrD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"agentTypes.d.ts","sourceRoot":"","sources":["../../src/agent/agentTypes.ts"],"names":[],"mappings":"AAkCA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACpD;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,qDAAqD;AACrD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAmX5D;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAuND;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAE3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;CAAE,CAqB5G;AAED,yFAAyF;AACzF,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC;AAED,2FAA2F;AAC3F,wBAAgB,aAAa,IAAI,SAAS,EAAE,CAE3C;AACD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,CAY1D;AASD,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAIjG"}
|
package/dist/agent/agentTypes.js
CHANGED
|
@@ -113,6 +113,9 @@ const WRITE_TOOLS = [
|
|
|
113
113
|
// knows its shape (`source` is cell CONTENT, not a path). Omitting it just meant
|
|
114
114
|
// test-writer silently could not touch notebooks.
|
|
115
115
|
'notebook_edit',
|
|
116
|
+
// Office document generation — same write-access tier as write_file, just a
|
|
117
|
+
// structured content shape instead of raw text (see tools/executor.ts).
|
|
118
|
+
'write_docx', 'write_xlsx', 'write_pptx',
|
|
116
119
|
];
|
|
117
120
|
const BUILTIN_AGENTS = [
|
|
118
121
|
{
|
|
@@ -360,12 +363,12 @@ const BUILTIN_AGENTS = [
|
|
|
360
363
|
* exactly like fetch_url, so it no longer needs special-casing in that comment.
|
|
361
364
|
*/
|
|
362
365
|
const KNOWN_TOOL_NAMES = new Set([
|
|
363
|
-
'read_file', 'write_file', 'edit_file', 'multi_edit', 'list_directory', 'create_directory',
|
|
366
|
+
'read_file', 'write_file', 'write_docx', 'write_xlsx', 'write_pptx', 'edit_file', 'multi_edit', 'list_directory', 'create_directory',
|
|
364
367
|
'move_file', 'copy_file', 'delete_file', 'search_files', 'glob',
|
|
365
368
|
'bash', 'bash_output', 'kill_shell',
|
|
366
369
|
'notebook_read', 'notebook_edit',
|
|
367
370
|
'todo_write', 'todo_read', 'memory_write', 'memory_read', 'use_skill',
|
|
368
|
-
'fetch_url', 'web_search', 'generate_image', 'stock_photo', 'open_in_browser',
|
|
371
|
+
'fetch_url', 'web_search', 'generate_image', 'stock_photo', 'open_in_browser', 'browser_action',
|
|
369
372
|
'task',
|
|
370
373
|
// Granted by a `memory:` scope rather than listed in a `tools:` line, but a user may
|
|
371
374
|
// still name it explicitly — so it must validate rather than be flagged as a typo.
|
|
@@ -688,6 +691,7 @@ function summariseAgents(types) {
|
|
|
688
691
|
const WRITE_TOOL_HINTS = new Set([
|
|
689
692
|
'write_file', 'edit_file', 'multi_edit', 'notebook_edit',
|
|
690
693
|
'delete_file', 'move_file', 'copy_file',
|
|
694
|
+
'write_docx', 'write_xlsx', 'write_pptx',
|
|
691
695
|
]);
|
|
692
696
|
function findAgentType(types, name) {
|
|
693
697
|
if (!name)
|
package/dist/agent/loop.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAKP,UAAU,EACV,gBAAgB,EAChB,UAAU,EACX,MAAM,UAAU,CAAC;AAIlB,OAAO,EAAkE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAMrH,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,CAAC;AA0K7B;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,GAC9C,MAAM,CAKR;AAED,+CAA+C;AAC/C,eAAO,MAAM,YAAY;;;CAAsC,CAAC;AAEhE;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,uBAAuB,CAAC;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;CAgB3B,CAAC;AAEX,mEAAmE;AACnE,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,gBAAgB,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,EAAE,kBAAkB,GAAG,SAAS,EACvC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC,CA8BrB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAClB,OAAO,GACP,SAAS,GACT,oBAAoB,GACpB,gBAAgB,GAChB,cAAc,GACd,YAAY,GACZ,SAAS,GACT,gBAAgB,GAChB,QAAQ,GACR,SAAS,CAAC;AAEd;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,UAAU,EAClB,GAAG,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO,GACzD,MAAM,GAAG,IAAI,CA2Cf;AAaD,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,CAYR;
|
|
1
|
+
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/loop.js
CHANGED
|
@@ -446,7 +446,8 @@ async function withFileLocks(absPaths, fn) {
|
|
|
446
446
|
}
|
|
447
447
|
/** Tools that mutate the filesystem and must be serialised per path. */
|
|
448
448
|
const WRITE_TOOLS = new Set([
|
|
449
|
-
'write_file', '
|
|
449
|
+
'write_file', 'write_docx', 'write_xlsx', 'write_pptx',
|
|
450
|
+
'edit_file', 'multi_edit', 'delete_file', 'move_file', 'copy_file', 'notebook_edit',
|
|
450
451
|
]);
|
|
451
452
|
/**
|
|
452
453
|
* Shared lock key for bash commands that mutate repository-wide state.
|
|
@@ -796,6 +797,18 @@ function humanDescription(name, input) {
|
|
|
796
797
|
return `Get hover info: ${input.path ?? ''}:${input.line ?? ''}:${input.character ?? ''}`;
|
|
797
798
|
case 'open_in_browser':
|
|
798
799
|
return `Open in browser: ${input.url ?? '(unknown)'}`;
|
|
800
|
+
case 'browser_action': {
|
|
801
|
+
const action = String(input.action ?? '');
|
|
802
|
+
if (action === 'navigate')
|
|
803
|
+
return `Browser: navigate to ${input.url ?? '(unknown)'}`;
|
|
804
|
+
if (action === 'click')
|
|
805
|
+
return `Browser: click ${input.ref ?? '(unknown)'}`;
|
|
806
|
+
if (action === 'type')
|
|
807
|
+
return `Browser: type "${String(input.text ?? '').slice(0, 40)}"`;
|
|
808
|
+
if (action === 'snapshot')
|
|
809
|
+
return 'Browser: read page';
|
|
810
|
+
return `Browser: ${action || '(unknown action)'}`;
|
|
811
|
+
}
|
|
799
812
|
default:
|
|
800
813
|
return `Use tool: ${name}`;
|
|
801
814
|
}
|
package/dist/agent/memory.d.ts
CHANGED
|
@@ -8,39 +8,68 @@ export declare const MEMORY_ENTRY_MAX_CHARS = 400;
|
|
|
8
8
|
export declare const MEMORY_MAX_BYTES = 12000;
|
|
9
9
|
export declare const MEMORY_COMPACT_TRIGGER_BYTES = 16000;
|
|
10
10
|
export declare const MEMORY_HARD_CAP_BYTES: number;
|
|
11
|
+
export declare const MEMORY_ARCHIVE_MAX_BYTES: number;
|
|
11
12
|
export interface MemoryWriteResult {
|
|
12
13
|
ok: boolean;
|
|
13
14
|
already: boolean;
|
|
14
15
|
scope: MemoryScope;
|
|
15
16
|
file: string;
|
|
17
|
+
/** True when this call also moved a prior entry into the Superseded section. */
|
|
18
|
+
superseded?: boolean;
|
|
16
19
|
}
|
|
17
|
-
export
|
|
18
|
-
|
|
20
|
+
export interface WriteMemoryOptions {
|
|
21
|
+
/** Fingerprint text of the OLD active fact this new one replaces/corrects.
|
|
22
|
+
* Matched the same way the near-duplicate check is (first 60 chars, case-
|
|
23
|
+
* insensitive substring) so callers can reuse whatever text they'd have used
|
|
24
|
+
* to check for a duplicate. The matched entry is moved to `## Superseded`,
|
|
25
|
+
* never deleted outright. */
|
|
26
|
+
supersedes?: string;
|
|
27
|
+
/** Short provenance tag (a file/module name) stored inline as `[src: X]` ahead
|
|
28
|
+
* of the fact — lets a later session trace a suspicious fact back to what
|
|
29
|
+
* prompted it. Counts toward MEMORY_ENTRY_MAX_CHARS. */
|
|
30
|
+
source?: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function writeMemory(content: string, scope: MemoryScope, workDir?: string, opts?: WriteMemoryOptions): Promise<MemoryWriteResult>;
|
|
33
|
+
export interface ReadMemoryOptions {
|
|
34
|
+
/** Include the `## Superseded` section (dead facts kept for audit/provenance).
|
|
35
|
+
* Default false — prompt injection should not keep re-billing context on facts
|
|
36
|
+
* that are known to no longer be true; pass true for the rarer "let me verify
|
|
37
|
+
* this is really still the case" lookup. */
|
|
38
|
+
includeArchived?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare function readMemory(scope: MemoryScope, workDir?: string, opts?: ReadMemoryOptions): string;
|
|
19
41
|
/** Merge BOTH scopes for prompt injection — project-specific first (most
|
|
20
42
|
* relevant to what the agent is doing right now), global preferences after. */
|
|
21
|
-
export declare function readAllMemory(workDir?: string): string;
|
|
43
|
+
export declare function readAllMemory(workDir?: string, opts?: ReadMemoryOptions): string;
|
|
22
44
|
export declare function clearMemory(scope: MemoryScope, workDir?: string): void;
|
|
23
45
|
export declare function memoryStats(scope: MemoryScope, workDir?: string): {
|
|
24
46
|
file: string;
|
|
25
47
|
bytes: number;
|
|
26
48
|
entries: number;
|
|
49
|
+
archived: number;
|
|
27
50
|
};
|
|
28
51
|
/**
|
|
29
|
-
* Summarize the OLDEST half of a memory file into a few consolidated
|
|
30
|
-
* an LLM call, keeping the most recent entries verbatim — same "compact,
|
|
31
|
-
* silently drop" instinct as the conversation-history auto-compactor, applied to
|
|
32
|
-
* memory instead of a transcript. This is the EXPECTED bounding mechanism for
|
|
33
|
-
*
|
|
34
|
-
*
|
|
52
|
+
* Summarize the OLDEST half of a memory file's ACTIVE entries into a few consolidated
|
|
53
|
+
* bullets via an LLM call, keeping the most recent entries verbatim — same "compact,
|
|
54
|
+
* don't silently drop" instinct as the conversation-history auto-compactor, applied to
|
|
55
|
+
* memory instead of a transcript. This is the EXPECTED bounding mechanism for memory
|
|
56
|
+
* file size — called opportunistically (fire-and-forget) right after a successful
|
|
57
|
+
* memory_write (see maybeCompactMemory in loop.ts) once a file crosses
|
|
35
58
|
* MEMORY_COMPACT_TRIGGER_BYTES. writeMemory's own emergency synchronous eviction
|
|
36
59
|
* (MEMORY_HARD_CAP_BYTES) sits well above this threshold specifically so this
|
|
37
60
|
* summarizing pass gets the chance to run first in the common case; the blunt
|
|
38
61
|
* eviction is a last resort for if compaction has been failing/unavailable.
|
|
39
62
|
*
|
|
63
|
+
* Archived (superseded) entries are pruned by plain eviction BEFORE any LLM call is
|
|
64
|
+
* considered — there is no value in spending a model call "consolidating" facts that
|
|
65
|
+
* a memory_write already marked as no longer true; that budget is reserved for the
|
|
66
|
+
* active set, where genuine near-duplicate/stale-wording cleanup still helps.
|
|
67
|
+
*
|
|
40
68
|
* `summarize` is injected (rather than importing streamChat directly) to avoid
|
|
41
69
|
* pulling api/client.ts's network/auth machinery into every consumer of this
|
|
42
70
|
* module and to keep this file trivially unit-testable.
|
|
43
71
|
*/
|
|
72
|
+
export declare function compactMemoryIfNeeded(scope: MemoryScope, workDir: string | undefined, summarize: (prompt: string) => Promise<string>): Promise<boolean>;
|
|
44
73
|
/** Max chars injected into a sub-agent's prompt from its own notes. */
|
|
45
74
|
export declare const AGENT_MEMORY_INJECT_MAX = 8000;
|
|
46
75
|
/** Hard cap on one agent's notes file. Same eviction-on-overflow rule as above. */
|
|
@@ -85,5 +114,4 @@ export declare function writeAgentMemory(agentName: string, scope: 'project' | '
|
|
|
85
114
|
* outgrows the injection budget.
|
|
86
115
|
*/
|
|
87
116
|
export declare function agentMemoryPreamble(agentName: string, scope: 'project' | 'user' | 'local', workDir?: string): string;
|
|
88
|
-
export declare function compactMemoryIfNeeded(scope: MemoryScope, workDir: string | undefined, summarize: (prompt: string) => Promise<string>): Promise<boolean>;
|
|
89
117
|
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/agent/memory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/agent/memory.ts"],"names":[],"mappings":"AAiEA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/C;;;sDAGsD;AACtD,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAI3E;AAED,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,gBAAgB,QAAS,CAAC;AACvC,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAMnD,eAAO,MAAM,qBAAqB,QAAmC,CAAC;AAKtE,eAAO,MAAM,wBAAwB,QAAmC,CAAC;AAoFzE,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,WAAW,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;kCAI8B;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;6DAEyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE,MAAM,EAChB,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CA2D5B;AAED,MAAM,WAAW,iBAAiB;IAChC;;;iDAG6C;IAC7C,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,MAAM,CAKrG;AAED;gFACgF;AAChF,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,MAAM,CASpF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAGtE;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE,MAAM,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAUpE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAC7C,OAAO,CAAC,OAAO,CAAC,CAsDlB;AAkBD,uEAAuE;AACvE,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AAC7C,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,EACnC,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAqBf;AAED,iEAAiE;AACjE,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,EACnC,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CAIR;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,EACnC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAyB1D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,EACnC,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CAYR"}
|
package/dist/agent/memory.js
CHANGED
|
@@ -33,19 +33,19 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.AGENT_MEMORY_MAX_BYTES = exports.AGENT_MEMORY_INJECT_MAX = exports.MEMORY_HARD_CAP_BYTES = exports.MEMORY_COMPACT_TRIGGER_BYTES = exports.MEMORY_MAX_BYTES = exports.MEMORY_ENTRY_MAX_CHARS = void 0;
|
|
36
|
+
exports.AGENT_MEMORY_MAX_BYTES = exports.AGENT_MEMORY_INJECT_MAX = exports.MEMORY_ARCHIVE_MAX_BYTES = exports.MEMORY_HARD_CAP_BYTES = exports.MEMORY_COMPACT_TRIGGER_BYTES = exports.MEMORY_MAX_BYTES = exports.MEMORY_ENTRY_MAX_CHARS = void 0;
|
|
37
37
|
exports.memoryFilePath = memoryFilePath;
|
|
38
38
|
exports.writeMemory = writeMemory;
|
|
39
39
|
exports.readMemory = readMemory;
|
|
40
40
|
exports.readAllMemory = readAllMemory;
|
|
41
41
|
exports.clearMemory = clearMemory;
|
|
42
42
|
exports.memoryStats = memoryStats;
|
|
43
|
+
exports.compactMemoryIfNeeded = compactMemoryIfNeeded;
|
|
43
44
|
exports.isSafeAgentName = isSafeAgentName;
|
|
44
45
|
exports.agentMemoryPath = agentMemoryPath;
|
|
45
46
|
exports.readAgentMemory = readAgentMemory;
|
|
46
47
|
exports.writeAgentMemory = writeAgentMemory;
|
|
47
48
|
exports.agentMemoryPreamble = agentMemoryPreamble;
|
|
48
|
-
exports.compactMemoryIfNeeded = compactMemoryIfNeeded;
|
|
49
49
|
const fs = __importStar(require("fs"));
|
|
50
50
|
const os = __importStar(require("os"));
|
|
51
51
|
const path = __importStar(require("path"));
|
|
@@ -72,6 +72,38 @@ const crypto = __importStar(require("crypto"));
|
|
|
72
72
|
// blind truncation (which would silently discard facts instead of condensing
|
|
73
73
|
// them), and a real file lock around the read-dedupe-write sequence so two
|
|
74
74
|
// concurrent CLI/VS Code sessions can't race into a duplicate entry.
|
|
75
|
+
//
|
|
76
|
+
// ─── Supersession + Active/Archived split (2026-08-18 audit) ───────────────
|
|
77
|
+
//
|
|
78
|
+
// The dedupe check above (fingerprint = first 60 chars, case-insensitive substring
|
|
79
|
+
// match) only catches entries that are near-verbatim RESTATEMENTS of an existing one.
|
|
80
|
+
// It does nothing for CONTRADICTIONS: "Deploys via npm run deploy" and "Deploys via
|
|
81
|
+
// wrangler publish, no longer npm" have completely different fingerprints, so both
|
|
82
|
+
// sat side-by-side forever — the model reading memory back had no signal which one
|
|
83
|
+
// was current, and the only "resolution" was an LLM compaction pass that ran late
|
|
84
|
+
// (only once the file crossed 16KB) and had to GUESS supersession from prose alone.
|
|
85
|
+
//
|
|
86
|
+
// Fix: `memory_write` now accepts an optional `supersedes` hint — the fingerprint
|
|
87
|
+
// text of the OLD fact this new one replaces, supplied by the agent that is actually
|
|
88
|
+
// making the correction (the one with the context to know it IS a correction, right
|
|
89
|
+
// when it happens — not a compaction job reconstructing intent later from a flat
|
|
90
|
+
// list). Superseded facts are NOT deleted (an agent guessing wrong about supersession
|
|
91
|
+
// would otherwise silently destroy a true fact) — they move into a `## Superseded`
|
|
92
|
+
// section at the end of the file with a `— superseded YYYY-MM-DD` note, e.g. Mem0's
|
|
93
|
+
// UPDATE/DELETE-vs-NOOP resolution model or Zep's valid_at/invalid_at temporal facts,
|
|
94
|
+
// but done with the cheapest structure that plain-text + fs still allows.
|
|
95
|
+
//
|
|
96
|
+
// That structural split then answers proposal #2 for free: prompt injection
|
|
97
|
+
// (readMemory/readAllMemory) defaults to ACTIVE facts only, so a file that
|
|
98
|
+
// accumulates years of corrected-away facts doesn't keep re-billing context on
|
|
99
|
+
// every turn — `memory_read`'s `include_archived` flag (or the CLI's
|
|
100
|
+
// `/memory archived`) is there for the rarer "let me verify this is really true"
|
|
101
|
+
// case the third proposal was about.
|
|
102
|
+
//
|
|
103
|
+
// Provenance (proposal #3): an optional `source` tag (a short module/file name)
|
|
104
|
+
// is stored inline as `[src: X]` ahead of the fact text, so a fact that turns out
|
|
105
|
+
// to be wrong can be traced back to what prompted it without re-deriving it from
|
|
106
|
+
// conversation history that has long since scrolled away.
|
|
75
107
|
// NEXRALL_MEMORY_DIR overrides the store root — used by tests to avoid touching
|
|
76
108
|
// the real ~/.nexrall/memory (mirrors CheckpointManager's NEXRALL_CHECKPOINT_DIR).
|
|
77
109
|
const MEMORY_ROOT = process.env.NEXRALL_MEMORY_DIR || path.join(os.homedir(), '.nexrall', 'memory');
|
|
@@ -95,7 +127,14 @@ exports.MEMORY_COMPACT_TRIGGER_BYTES = 16000; // LLM compaction (see compactMemo
|
|
|
95
127
|
// if compaction has been unavailable/failing for a while (e.g. offline, API errors), so the file
|
|
96
128
|
// still can't grow completely unbounded even in that worst case.
|
|
97
129
|
exports.MEMORY_HARD_CAP_BYTES = 4 * exports.MEMORY_COMPACT_TRIGGER_BYTES;
|
|
130
|
+
// Archived (superseded) facts are lower-value than active ones — they exist for audit/
|
|
131
|
+
// provenance, not for the model to act on — so they get a much smaller budget and are
|
|
132
|
+
// pruned by plain eviction (see compactMemoryIfNeeded) rather than spending an LLM call
|
|
133
|
+
// "summarizing" facts that are already known to be dead.
|
|
134
|
+
exports.MEMORY_ARCHIVE_MAX_BYTES = Math.floor(exports.MEMORY_MAX_BYTES / 2);
|
|
98
135
|
const FINGERPRINT_LEN = 60; // near-duplicate heuristic: same as before, first N chars, case-insensitive
|
|
136
|
+
const SOURCE_TAG_MAX_CHARS = 60; // provenance tag budget — short by design, it's a pointer, not a description
|
|
137
|
+
const SUPERSEDED_HEADER = '## Superseded';
|
|
99
138
|
// ─── Simple per-file async lock ────────────────────────────────────────────
|
|
100
139
|
// Fixes the read→dedupe-check→append race: two concurrent memoryWrite() calls
|
|
101
140
|
// (two CLI processes, or a sub-agent + the main agent) could both read "fact
|
|
@@ -131,55 +170,131 @@ function writeMemoryFile(file, content) {
|
|
|
131
170
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
132
171
|
fs.writeFileSync(file, content, 'utf-8');
|
|
133
172
|
}
|
|
134
|
-
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Split a raw memory file into its two sections: entries still considered true
|
|
175
|
+
* ("active", everything before the `## Superseded` heading) and entries a later
|
|
176
|
+
* memory_write explicitly marked as replaced ("archived", everything after it).
|
|
177
|
+
* A file with no heading is entirely active — this is also what every
|
|
178
|
+
* pre-supersession file on disk looks like, so it round-trips unchanged.
|
|
179
|
+
*/
|
|
180
|
+
function parseSections(content) {
|
|
181
|
+
const lines = content.split('\n');
|
|
182
|
+
const headerIdx = lines.findIndex((l) => l.trim() === SUPERSEDED_HEADER);
|
|
183
|
+
if (headerIdx === -1) {
|
|
184
|
+
return { active: lines.filter((l) => l.trim().length > 0), archived: [] };
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
active: lines.slice(0, headerIdx).filter((l) => l.trim().length > 0),
|
|
188
|
+
archived: lines.slice(headerIdx + 1).filter((l) => l.trim().length > 0),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/** Inverse of parseSections. Omits the heading entirely when nothing is archived,
|
|
192
|
+
* so a file that never uses supersession looks EXACTLY like the old flat format. */
|
|
193
|
+
function serializeSections(active, archived) {
|
|
194
|
+
let out = '\n' + active.join('\n');
|
|
195
|
+
if (archived.length > 0)
|
|
196
|
+
out += `\n\n${SUPERSEDED_HEADER}\n` + archived.join('\n');
|
|
197
|
+
return out;
|
|
198
|
+
}
|
|
199
|
+
/** Evict the OLDEST entries until under the byte cap — archived (already-dead)
|
|
200
|
+
* entries go first since they carry less value than active ones, then oldest
|
|
201
|
+
* active. Used both as writeMemory's emergency backstop and compaction's final
|
|
202
|
+
* safety net; never touches the LAST active line so a single huge entry can't
|
|
203
|
+
* evict itself into an empty, unrecoverable file. */
|
|
204
|
+
function evictToFit(active, archived, maxBytes) {
|
|
205
|
+
const a = [...active];
|
|
206
|
+
const arch = [...archived];
|
|
207
|
+
let content = serializeSections(a, arch);
|
|
208
|
+
while (Buffer.byteLength(content, 'utf-8') > maxBytes && (arch.length > 0 || a.length > 1)) {
|
|
209
|
+
if (arch.length > 0)
|
|
210
|
+
arch.shift();
|
|
211
|
+
else
|
|
212
|
+
a.shift();
|
|
213
|
+
content = serializeSections(a, arch);
|
|
214
|
+
}
|
|
215
|
+
return content;
|
|
144
216
|
}
|
|
145
|
-
|
|
217
|
+
function sanitizeSourceTag(source) {
|
|
218
|
+
if (!source)
|
|
219
|
+
return '';
|
|
220
|
+
return source.trim().replace(/[[\]\n\r]/g, '').slice(0, SOURCE_TAG_MAX_CHARS);
|
|
221
|
+
}
|
|
222
|
+
async function writeMemory(content, scope, workDir, opts = {}) {
|
|
146
223
|
let trimmed = content.trim();
|
|
147
224
|
if (!trimmed)
|
|
148
225
|
return { ok: false, already: false, scope, file: '' };
|
|
149
|
-
|
|
150
|
-
|
|
226
|
+
const sourceTag = sanitizeSourceTag(opts.source);
|
|
227
|
+
const prefix = sourceTag ? `[src: ${sourceTag}] ` : '';
|
|
228
|
+
const budget = exports.MEMORY_ENTRY_MAX_CHARS - prefix.length;
|
|
229
|
+
if (budget <= 0) {
|
|
230
|
+
// Degenerate case: an absurdly long source tag ate the whole budget. Drop the
|
|
231
|
+
// tag rather than emit a fact-less entry.
|
|
232
|
+
if (trimmed.length > exports.MEMORY_ENTRY_MAX_CHARS)
|
|
233
|
+
trimmed = trimmed.slice(0, exports.MEMORY_ENTRY_MAX_CHARS - 1).trimEnd() + '…';
|
|
234
|
+
}
|
|
235
|
+
else if (trimmed.length > budget) {
|
|
236
|
+
trimmed = trimmed.slice(0, budget - 1).trimEnd() + '…';
|
|
237
|
+
}
|
|
238
|
+
const body = prefix && budget > 0 ? prefix + trimmed : trimmed;
|
|
151
239
|
const file = memoryFilePath(scope, workDir);
|
|
152
240
|
return withLock(file, async () => {
|
|
153
241
|
const existing = readMemoryFile(file);
|
|
154
|
-
|
|
155
|
-
if (existing.toLowerCase().includes(fingerprint)) {
|
|
156
|
-
return { ok: true, already: true, scope, file };
|
|
157
|
-
}
|
|
242
|
+
let { active, archived } = parseSections(existing);
|
|
158
243
|
const today = new Date().toISOString().slice(0, 10);
|
|
159
|
-
|
|
244
|
+
// Supersession happens BEFORE the dedupe check: the caller may be replacing a
|
|
245
|
+
// fact with a differently-worded one, which is exactly the case the fingerprint
|
|
246
|
+
// check is too naive to link together on its own.
|
|
247
|
+
let superseded = false;
|
|
248
|
+
if (opts.supersedes && opts.supersedes.trim()) {
|
|
249
|
+
const supFingerprint = opts.supersedes.trim().toLowerCase().slice(0, FINGERPRINT_LEN);
|
|
250
|
+
const stillActive = [];
|
|
251
|
+
for (const line of active) {
|
|
252
|
+
if (!superseded && line.toLowerCase().includes(supFingerprint)) {
|
|
253
|
+
archived = [...archived, `${line} — superseded ${today}`];
|
|
254
|
+
superseded = true;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
stillActive.push(line);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
active = stillActive;
|
|
261
|
+
}
|
|
262
|
+
const fingerprint = body.toLowerCase().slice(0, FINGERPRINT_LEN);
|
|
263
|
+
if (active.some((l) => l.toLowerCase().includes(fingerprint))) {
|
|
264
|
+
// Still persist a supersession move even if the replacement text happens to
|
|
265
|
+
// already be present — otherwise the old (contradicted) fact would linger.
|
|
266
|
+
if (superseded)
|
|
267
|
+
writeMemoryFile(file, serializeSections(active, archived));
|
|
268
|
+
return { ok: true, already: true, scope, file, superseded };
|
|
269
|
+
}
|
|
270
|
+
active = [...active, `- [${today}] ${body}`];
|
|
271
|
+
let next = serializeSections(active, archived);
|
|
160
272
|
// Emergency-only backstop — see MEMORY_HARD_CAP_BYTES. The EXPECTED bounding
|
|
161
273
|
// mechanism is compactMemoryIfNeeded (LLM summarization, called opportunistically
|
|
162
274
|
// right after this from loop.ts); this only fires if compaction hasn't kept up.
|
|
163
275
|
if (Buffer.byteLength(next, 'utf-8') > exports.MEMORY_HARD_CAP_BYTES) {
|
|
164
|
-
next =
|
|
276
|
+
next = evictToFit(active, archived, exports.MEMORY_MAX_BYTES);
|
|
165
277
|
}
|
|
166
278
|
writeMemoryFile(file, next);
|
|
167
|
-
return { ok: true, already: false, scope, file };
|
|
279
|
+
return { ok: true, already: false, scope, file, superseded };
|
|
168
280
|
});
|
|
169
281
|
}
|
|
170
|
-
function readMemory(scope, workDir) {
|
|
282
|
+
function readMemory(scope, workDir, opts = {}) {
|
|
171
283
|
const file = memoryFilePath(scope, workDir);
|
|
172
|
-
|
|
284
|
+
const { active, archived } = parseSections(readMemoryFile(file));
|
|
285
|
+
if (!opts.includeArchived)
|
|
286
|
+
return active.length ? serializeSections(active, []).trim() : '';
|
|
287
|
+
return serializeSections(active, archived).trim();
|
|
173
288
|
}
|
|
174
289
|
/** Merge BOTH scopes for prompt injection — project-specific first (most
|
|
175
290
|
* relevant to what the agent is doing right now), global preferences after. */
|
|
176
|
-
function readAllMemory(workDir) {
|
|
291
|
+
function readAllMemory(workDir, opts = {}) {
|
|
177
292
|
const parts = [];
|
|
178
|
-
const globalMem = readMemory('global');
|
|
293
|
+
const globalMem = readMemory('global', undefined, opts);
|
|
179
294
|
if (globalMem)
|
|
180
295
|
parts.push(`[Global memories — apply to every project]\n${globalMem}`);
|
|
181
296
|
if (workDir) {
|
|
182
|
-
const projMem = readMemory('project', workDir);
|
|
297
|
+
const projMem = readMemory('project', workDir, opts);
|
|
183
298
|
if (projMem)
|
|
184
299
|
parts.push(`[Project memories — ${path.basename(path.resolve(workDir))}]\n${projMem}`);
|
|
185
300
|
}
|
|
@@ -195,25 +310,87 @@ function clearMemory(scope, workDir) {
|
|
|
195
310
|
function memoryStats(scope, workDir) {
|
|
196
311
|
const file = memoryFilePath(scope, workDir);
|
|
197
312
|
const content = readMemoryFile(file);
|
|
198
|
-
const
|
|
199
|
-
return {
|
|
313
|
+
const { active, archived } = parseSections(content);
|
|
314
|
+
return {
|
|
315
|
+
file,
|
|
316
|
+
bytes: Buffer.byteLength(content, 'utf-8'),
|
|
317
|
+
entries: active.filter((l) => /^-\s\[\d{4}-\d{2}-\d{2}\]/.test(l)).length,
|
|
318
|
+
archived: archived.filter((l) => /^-\s\[\d{4}-\d{2}-\d{2}\]/.test(l)).length,
|
|
319
|
+
};
|
|
200
320
|
}
|
|
201
321
|
/**
|
|
202
|
-
* Summarize the OLDEST half of a memory file into a few consolidated
|
|
203
|
-
* an LLM call, keeping the most recent entries verbatim — same "compact,
|
|
204
|
-
* silently drop" instinct as the conversation-history auto-compactor, applied to
|
|
205
|
-
* memory instead of a transcript. This is the EXPECTED bounding mechanism for
|
|
206
|
-
*
|
|
207
|
-
*
|
|
322
|
+
* Summarize the OLDEST half of a memory file's ACTIVE entries into a few consolidated
|
|
323
|
+
* bullets via an LLM call, keeping the most recent entries verbatim — same "compact,
|
|
324
|
+
* don't silently drop" instinct as the conversation-history auto-compactor, applied to
|
|
325
|
+
* memory instead of a transcript. This is the EXPECTED bounding mechanism for memory
|
|
326
|
+
* file size — called opportunistically (fire-and-forget) right after a successful
|
|
327
|
+
* memory_write (see maybeCompactMemory in loop.ts) once a file crosses
|
|
208
328
|
* MEMORY_COMPACT_TRIGGER_BYTES. writeMemory's own emergency synchronous eviction
|
|
209
329
|
* (MEMORY_HARD_CAP_BYTES) sits well above this threshold specifically so this
|
|
210
330
|
* summarizing pass gets the chance to run first in the common case; the blunt
|
|
211
331
|
* eviction is a last resort for if compaction has been failing/unavailable.
|
|
212
332
|
*
|
|
333
|
+
* Archived (superseded) entries are pruned by plain eviction BEFORE any LLM call is
|
|
334
|
+
* considered — there is no value in spending a model call "consolidating" facts that
|
|
335
|
+
* a memory_write already marked as no longer true; that budget is reserved for the
|
|
336
|
+
* active set, where genuine near-duplicate/stale-wording cleanup still helps.
|
|
337
|
+
*
|
|
213
338
|
* `summarize` is injected (rather than importing streamChat directly) to avoid
|
|
214
339
|
* pulling api/client.ts's network/auth machinery into every consumer of this
|
|
215
340
|
* module and to keep this file trivially unit-testable.
|
|
216
341
|
*/
|
|
342
|
+
async function compactMemoryIfNeeded(scope, workDir, summarize) {
|
|
343
|
+
const file = memoryFilePath(scope, workDir);
|
|
344
|
+
return withLock(file, async () => {
|
|
345
|
+
const content = readMemoryFile(file);
|
|
346
|
+
if (Buffer.byteLength(content, 'utf-8') <= exports.MEMORY_COMPACT_TRIGGER_BYTES)
|
|
347
|
+
return false;
|
|
348
|
+
let { active, archived } = parseSections(content);
|
|
349
|
+
let changed = false;
|
|
350
|
+
// 1. Cheap pass first: prune the oldest archived (already-dead) entries with
|
|
351
|
+
// plain eviction — no model call needed.
|
|
352
|
+
if (Buffer.byteLength(archived.join('\n'), 'utf-8') > exports.MEMORY_ARCHIVE_MAX_BYTES) {
|
|
353
|
+
while (archived.length > 1 && Buffer.byteLength(archived.join('\n'), 'utf-8') > exports.MEMORY_ARCHIVE_MAX_BYTES) {
|
|
354
|
+
archived = archived.slice(1);
|
|
355
|
+
}
|
|
356
|
+
changed = true;
|
|
357
|
+
}
|
|
358
|
+
// 2. If the active set alone is still driving the size past the trigger,
|
|
359
|
+
// summarize its oldest half.
|
|
360
|
+
if (Buffer.byteLength(serializeSections(active, archived), 'utf-8') > exports.MEMORY_COMPACT_TRIGGER_BYTES && active.length >= 8) {
|
|
361
|
+
const cut = Math.floor(active.length / 2);
|
|
362
|
+
const older = active.slice(0, cut);
|
|
363
|
+
const recent = active.slice(cut);
|
|
364
|
+
const prompt = `Consolidate these persistent-memory bullet entries into a shorter set of bullets, ` +
|
|
365
|
+
`merging duplicates/near-duplicates that describe the same fact in different words. ` +
|
|
366
|
+
`Do NOT invent a resolution for facts that seem to conflict — contradictions are handled ` +
|
|
367
|
+
`structurally elsewhere; if two entries genuinely disagree, keep both rather than guessing ` +
|
|
368
|
+
`which is current. Keep each resulting bullet under ${exports.MEMORY_ENTRY_MAX_CHARS} characters, ` +
|
|
369
|
+
`one fact per line, prefixed "- [YYYY-MM-DD] " using the LATEST date among the entries it ` +
|
|
370
|
+
`draws from, and preserve any leading "[src: ...]" provenance tag verbatim when present. ` +
|
|
371
|
+
`Output ONLY the bullet lines, nothing else.\n\n${older.join('\n')}`;
|
|
372
|
+
try {
|
|
373
|
+
const summarized = (await summarize(prompt)).trim();
|
|
374
|
+
if (summarized) {
|
|
375
|
+
active = [...summarized.split('\n').filter((l) => l.trim().length > 0), ...recent];
|
|
376
|
+
changed = true;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
// Summarization failed — leave the active set as-is. Any archive pruning
|
|
381
|
+
// from step 1 above is still saved below; writeMemory's sync eviction cap
|
|
382
|
+
// still bounds worst-case growth in the meantime.
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (!changed)
|
|
386
|
+
return false;
|
|
387
|
+
let next = serializeSections(active, archived);
|
|
388
|
+
if (Buffer.byteLength(next, 'utf-8') > exports.MEMORY_MAX_BYTES)
|
|
389
|
+
next = evictToFit(active, archived, exports.MEMORY_MAX_BYTES);
|
|
390
|
+
writeMemoryFile(file, next);
|
|
391
|
+
return true;
|
|
392
|
+
});
|
|
393
|
+
}
|
|
217
394
|
// ─── Per-agent memory (the `memory:` frontmatter field) ──────────────────────
|
|
218
395
|
//
|
|
219
396
|
// A SEPARATE store from the shared global/project memory above. The shared store is
|
|
@@ -222,6 +399,13 @@ function memoryStats(scope, workDir) {
|
|
|
222
399
|
// without polluting the shared file (and without 4 parallel sub-agents fighting over
|
|
223
400
|
// it). The cost, stated plainly: these stores are ISOLATED — two agents learn nothing
|
|
224
401
|
// from one another.
|
|
402
|
+
//
|
|
403
|
+
// Deliberately NOT given the same supersession/provenance treatment as the shared
|
|
404
|
+
// store above: these files are small (24KB cap vs the shared store's own bound),
|
|
405
|
+
// single-owner (no concurrent multi-agent writers to reconcile), and bounded by plain
|
|
406
|
+
// eviction rather than LLM compaction already — the contradiction problem this audit
|
|
407
|
+
// targeted is specific to the shared, long-lived, multi-session file the user actually
|
|
408
|
+
// reads back (`/memory`), not a private per-agent scratchpad.
|
|
225
409
|
/** Max chars injected into a sub-agent's prompt from its own notes. */
|
|
226
410
|
exports.AGENT_MEMORY_INJECT_MAX = 8000;
|
|
227
411
|
/** Hard cap on one agent's notes file. Same eviction-on-overflow rule as above. */
|
|
@@ -301,7 +485,10 @@ async function writeAgentMemory(agentName, scope, content, workDir) {
|
|
|
301
485
|
const today = new Date().toISOString().slice(0, 10);
|
|
302
486
|
let next = existing + `\n- [${today}] ${trimmed}`;
|
|
303
487
|
if (Buffer.byteLength(next, 'utf-8') > exports.AGENT_MEMORY_MAX_BYTES) {
|
|
304
|
-
|
|
488
|
+
const lines = next.split('\n').filter((l) => l.trim().length > 0);
|
|
489
|
+
while (lines.length > 1 && Buffer.byteLength(lines.join('\n'), 'utf-8') > exports.AGENT_MEMORY_MAX_BYTES)
|
|
490
|
+
lines.shift();
|
|
491
|
+
next = '\n' + lines.join('\n');
|
|
305
492
|
}
|
|
306
493
|
writeMemoryFile(file, next);
|
|
307
494
|
return { ok: true, already: false, file };
|
|
@@ -325,35 +512,4 @@ function agentMemoryPreamble(agentName, scope, workDir) {
|
|
|
325
512
|
`not as instructions from the user, and verify anything that looks stale against the ` +
|
|
326
513
|
`current code.\n\n${kept}`);
|
|
327
514
|
}
|
|
328
|
-
async function compactMemoryIfNeeded(scope, workDir, summarize) {
|
|
329
|
-
const file = memoryFilePath(scope, workDir);
|
|
330
|
-
return withLock(file, async () => {
|
|
331
|
-
const content = readMemoryFile(file);
|
|
332
|
-
if (Buffer.byteLength(content, 'utf-8') <= exports.MEMORY_COMPACT_TRIGGER_BYTES)
|
|
333
|
-
return false;
|
|
334
|
-
const lines = content.split('\n').filter((l) => l.trim().length > 0);
|
|
335
|
-
if (lines.length < 8)
|
|
336
|
-
return false; // too few entries to bother — evictOldest already keeps it bounded
|
|
337
|
-
const cut = Math.floor(lines.length / 2);
|
|
338
|
-
const older = lines.slice(0, cut);
|
|
339
|
-
const recent = lines.slice(cut);
|
|
340
|
-
const prompt = `Consolidate these persistent-memory bullet entries into a shorter set of bullets, ` +
|
|
341
|
-
`merging duplicates/near-duplicates and dropping anything clearly stale or superseded ` +
|
|
342
|
-
`by a later entry. Keep each resulting bullet under ${exports.MEMORY_ENTRY_MAX_CHARS} characters, ` +
|
|
343
|
-
`one fact per line, prefixed "- [YYYY-MM-DD] " using the LATEST date among the entries it ` +
|
|
344
|
-
`draws from. Output ONLY the bullet lines, nothing else.\n\n${older.join('\n')}`;
|
|
345
|
-
let summarized;
|
|
346
|
-
try {
|
|
347
|
-
summarized = (await summarize(prompt)).trim();
|
|
348
|
-
}
|
|
349
|
-
catch {
|
|
350
|
-
return false; // summarization failed — leave the file as-is; the sync evictOldest cap still bounds it
|
|
351
|
-
}
|
|
352
|
-
if (!summarized)
|
|
353
|
-
return false;
|
|
354
|
-
const next = summarized + '\n' + recent.join('\n');
|
|
355
|
-
writeMemoryFile(file, Buffer.byteLength(next, 'utf-8') > exports.MEMORY_MAX_BYTES ? evictOldest(next, exports.MEMORY_MAX_BYTES) : next);
|
|
356
|
-
return true;
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
515
|
//# sourceMappingURL=memory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/agent/skills.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/agent/skills.ts"],"names":[],"mappings":"AA4CA,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACpD,0IAA0I;IAC1I,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kHAAkH;IAClH,sBAAsB,EAAE,OAAO,CAAC;IAChC,iGAAiG;IACjG,aAAa,EAAE,OAAO,CAAC;CACxB;AA8HD;;wDAEwD;AACxD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,EAAE,CAanD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAG1E;AAED,6GAA6G;AAC7G,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAE5D;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAE5D;AAED,oHAAoH;AACpH,wBAAgB,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAIvD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAcpF"}
|
package/dist/agent/skills.js
CHANGED
|
@@ -44,18 +44,7 @@ const path = __importStar(require("path"));
|
|
|
44
44
|
const os = __importStar(require("os"));
|
|
45
45
|
const index_1 = require("../plugins/index");
|
|
46
46
|
const loader_1 = require("../commands/loader");
|
|
47
|
-
|
|
48
|
-
const m = /^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
|
|
49
|
-
if (!m)
|
|
50
|
-
return { meta: {}, body: raw.trim() };
|
|
51
|
-
const meta = {};
|
|
52
|
-
for (const line of m[1].split(/\r?\n/)) {
|
|
53
|
-
const kv = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line.trim());
|
|
54
|
-
if (kv)
|
|
55
|
-
meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, '');
|
|
56
|
-
}
|
|
57
|
-
return { meta, body: (m[2] ?? '').trim() };
|
|
58
|
-
}
|
|
47
|
+
const frontmatter_1 = require("../util/frontmatter");
|
|
59
48
|
function parseBool(v, fallback) {
|
|
60
49
|
if (v === undefined)
|
|
61
50
|
return fallback;
|
|
@@ -95,7 +84,7 @@ function loadFlatCommandDir(dir, source, into) {
|
|
|
95
84
|
for (const file of files) {
|
|
96
85
|
try {
|
|
97
86
|
const raw = fs.readFileSync(path.join(dir, file), 'utf-8');
|
|
98
|
-
const { meta, body } = parseFrontmatter(raw);
|
|
87
|
+
const { meta, body } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
99
88
|
const name = (meta.name || path.basename(file, '.md')).trim().toLowerCase();
|
|
100
89
|
if (!name)
|
|
101
90
|
continue;
|
|
@@ -131,7 +120,7 @@ function loadSkillDir(root, source, into) {
|
|
|
131
120
|
}
|
|
132
121
|
try {
|
|
133
122
|
const raw = fs.readFileSync(skillFile, 'utf-8');
|
|
134
|
-
const { meta, body } = parseFrontmatter(raw);
|
|
123
|
+
const { meta, body } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
135
124
|
const name = (meta.name || entry.name).trim().toLowerCase();
|
|
136
125
|
if (!name)
|
|
137
126
|
continue;
|