@rubytech/create-sitedesk-code 0.1.530 → 0.1.532
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/package.json +1 -1
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +4 -0
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/index.ts +4 -0
- package/payload/platform/lib/learning-constants/dist/index.d.ts +35 -0
- package/payload/platform/lib/learning-constants/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/learning-constants/dist/index.js +50 -0
- package/payload/platform/lib/learning-constants/dist/index.js.map +1 -0
- package/payload/platform/lib/learning-constants/package.json +7 -0
- package/payload/platform/lib/learning-constants/src/index.test.ts +47 -0
- package/payload/platform/lib/learning-constants/src/index.ts +57 -0
- package/payload/platform/lib/learning-constants/tsconfig.json +9 -0
- package/payload/platform/neo4j/schema.cypher +24 -0
- package/payload/platform/package.json +1 -1
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/insight/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +58 -10
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
- package/payload/platform/plugins/docs/references/linkedin-extension.md +18 -9
- package/payload/platform/plugins/docs/references/session-retrospective.md +39 -0
- package/payload/platform/plugins/linkedin-extension/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/linkedin-extension/PLUGIN.md +7 -10
- package/payload/platform/plugins/linkedin-extension/extension/README.md +20 -17
- package/payload/platform/plugins/linkedin-extension/extension/background/sw.js +96 -13
- package/payload/platform/plugins/linkedin-extension/extension/manifest.json +3 -18
- package/payload/platform/plugins/linkedin-extension/extension/options/options.html +2 -2
- package/payload/platform/plugins/linkedin-extension/extension/options/options.js +1 -1
- package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +15 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +169 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/session-jsonl.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/session-jsonl.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/session-jsonl.test.js +95 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/session-jsonl.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/session-jsonl.d.ts +60 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/session-jsonl.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/session-jsonl.js +153 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/session-jsonl.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.js +6 -4
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.js +160 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.js +64 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-scan.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-scan.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-scan.test.js +78 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-scan.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-derive.d.ts +41 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-derive.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-derive.js +225 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-derive.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-recall.d.ts +31 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-recall.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-recall.js +67 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-recall.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-scan.d.ts +40 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-scan.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-scan.js +55 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-scan.js.map +1 -0
- package/payload/platform/plugins/memory/references/schema-base.md +3 -0
- package/payload/platform/plugins/memory/skills/learnings-recall/SKILL.md +27 -0
- package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
- package/payload/platform/plugins/whatsapp/PLUGIN.md +1 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +5 -5
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +6 -6
- package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +6 -2
- package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +6 -2
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +3 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/learning-index.d.ts +28 -0
- package/payload/platform/services/claude-session-manager/dist/learning-index.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/learning-index.js +70 -0
- package/payload/platform/services/claude-session-manager/dist/learning-index.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +18 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +6 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +17 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
- package/payload/platform/templates/specialists/agents/librarian.md +1 -1
- package/payload/server/{chunk-2YMGNYA3.js → chunk-UH6GCQO4.js} +78 -38
- package/payload/server/{manager-XPRN5UF5.js → manager-H34562SN.js} +5 -1
- package/payload/server/server.js +121 -38
- package/payload/platform/plugins/linkedin-extension/extension/assets/pill.css +0 -52
- package/payload/platform/plugins/linkedin-extension/extension/content/profile.js +0 -100
- package/payload/platform/plugins/linkedin-extension/extension/content/thread.js +0 -102
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 2220 — session transcript reading for the learnings pass.
|
|
3
|
+
*
|
|
4
|
+
* The task specified `readJsonlSession` / `readJsonlSessionAtPath` from
|
|
5
|
+
* `claude-session-manager`. Both return a `DehydratedSession` of metadata
|
|
6
|
+
* (id, mtime, title, turn count) parsed from a bounded tail; neither can
|
|
7
|
+
* produce transcript content, and nothing outside that service imports the
|
|
8
|
+
* package. So the reading lives here, self-contained.
|
|
9
|
+
*
|
|
10
|
+
* Directory shape, per `runDuplicateSenderAudit` (ui/server/index.ts:627):
|
|
11
|
+
* $CLAUDE_CONFIG_DIR/projects/<slug>/<sessionId>.jsonl
|
|
12
|
+
* $CLAUDE_CONFIG_DIR/projects/<slug>/subagents/<id>.jsonl <- flagged, skipped by callers
|
|
13
|
+
* $CLAUDE_CONFIG_DIR/projects/<slug>/archive/<id>.jsonl <- flagged, skipped by callers
|
|
14
|
+
* Account scope comes from the `<sessionId>.meta.json` sidecar, never from the
|
|
15
|
+
* transcript body.
|
|
16
|
+
*/
|
|
17
|
+
export interface SessionFile {
|
|
18
|
+
path: string;
|
|
19
|
+
sessionId: string;
|
|
20
|
+
isSubagent: boolean;
|
|
21
|
+
archived: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface EnumerateResult {
|
|
24
|
+
files: SessionFile[];
|
|
25
|
+
/** On-disk .jsonl count BEFORE any filtering. The discriminator between a
|
|
26
|
+
* broken sinceIso comparison and a broken enumerator path. */
|
|
27
|
+
candidateFiles: number;
|
|
28
|
+
/** Directories that existed but could not be read. */
|
|
29
|
+
skipped: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* `$CLAUDE_CONFIG_DIR/projects`. Throws when the variable is absent or empty.
|
|
33
|
+
*
|
|
34
|
+
* Deliberately no default path: pty-spawner stamps CLAUDE_CONFIG_DIR only for
|
|
35
|
+
* `isSpecialistSpawn || role === 'admin'`, so a spawn shape that does not carry
|
|
36
|
+
* it must fail loudly. Falling back to `~/.claude` would read another brand's
|
|
37
|
+
* sessions, and brand isolation is absolute.
|
|
38
|
+
*/
|
|
39
|
+
export declare function claudeProjectsRoot(): string;
|
|
40
|
+
/**
|
|
41
|
+
* Walk every slug directory under `projectsRoot`, plus its `subagents/` and
|
|
42
|
+
* `archive/` subdirectories. Flags rather than drops, so the caller's counters
|
|
43
|
+
* can distinguish "nothing on disk" from "everything filtered".
|
|
44
|
+
*/
|
|
45
|
+
export declare function enumerateSessionJsonls(projectsRoot: string): EnumerateResult;
|
|
46
|
+
/** Account scope for one session, from its `.meta.json` sidecar. Null when the
|
|
47
|
+
* sidecar is missing, unreadable, or carries no accountId. */
|
|
48
|
+
export declare function readSidecarAccountId(jsonlPath: string): string | null;
|
|
49
|
+
/** ISO of the session's last write, used for the sinceIso filter. */
|
|
50
|
+
export declare function sessionLastWriteIso(jsonlPath: string): string | null;
|
|
51
|
+
/**
|
|
52
|
+
* One page of turn-attributed transcript. `chunkLines` counts TURNS, not file
|
|
53
|
+
* lines: a transcript's line count is dominated by tool traffic the claim pass
|
|
54
|
+
* has no use for.
|
|
55
|
+
*/
|
|
56
|
+
export declare function chunkTranscript(jsonlPath: string, chunkIndex: number, chunkLines: number): {
|
|
57
|
+
body: string;
|
|
58
|
+
totalChunks: number;
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=session-jsonl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-jsonl.d.ts","sourceRoot":"","sources":["../../src/lib/session-jsonl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB;mEAC+D;IAC/D,cAAc,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAQ3C;AAaD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,CA+B5E;AAED;+DAC+D;AAC/D,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQrE;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMpE;AA4BD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAevC"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 2220 — session transcript reading for the learnings pass.
|
|
3
|
+
*
|
|
4
|
+
* The task specified `readJsonlSession` / `readJsonlSessionAtPath` from
|
|
5
|
+
* `claude-session-manager`. Both return a `DehydratedSession` of metadata
|
|
6
|
+
* (id, mtime, title, turn count) parsed from a bounded tail; neither can
|
|
7
|
+
* produce transcript content, and nothing outside that service imports the
|
|
8
|
+
* package. So the reading lives here, self-contained.
|
|
9
|
+
*
|
|
10
|
+
* Directory shape, per `runDuplicateSenderAudit` (ui/server/index.ts:627):
|
|
11
|
+
* $CLAUDE_CONFIG_DIR/projects/<slug>/<sessionId>.jsonl
|
|
12
|
+
* $CLAUDE_CONFIG_DIR/projects/<slug>/subagents/<id>.jsonl <- flagged, skipped by callers
|
|
13
|
+
* $CLAUDE_CONFIG_DIR/projects/<slug>/archive/<id>.jsonl <- flagged, skipped by callers
|
|
14
|
+
* Account scope comes from the `<sessionId>.meta.json` sidecar, never from the
|
|
15
|
+
* transcript body.
|
|
16
|
+
*/
|
|
17
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
18
|
+
import { basename, join } from "node:path";
|
|
19
|
+
/**
|
|
20
|
+
* `$CLAUDE_CONFIG_DIR/projects`. Throws when the variable is absent or empty.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately no default path: pty-spawner stamps CLAUDE_CONFIG_DIR only for
|
|
23
|
+
* `isSpecialistSpawn || role === 'admin'`, so a spawn shape that does not carry
|
|
24
|
+
* it must fail loudly. Falling back to `~/.claude` would read another brand's
|
|
25
|
+
* sessions, and brand isolation is absolute.
|
|
26
|
+
*/
|
|
27
|
+
export function claudeProjectsRoot() {
|
|
28
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
29
|
+
if (!configDir) {
|
|
30
|
+
throw new Error("learnings: CLAUDE_CONFIG_DIR is not set — refusing to guess a projects root");
|
|
31
|
+
}
|
|
32
|
+
return join(configDir, "projects");
|
|
33
|
+
}
|
|
34
|
+
function jsonlsIn(dir, isSubagent, archived) {
|
|
35
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
36
|
+
.filter((e) => e.isFile() && e.name.endsWith(".jsonl"))
|
|
37
|
+
.map((e) => ({
|
|
38
|
+
path: join(dir, e.name),
|
|
39
|
+
sessionId: basename(e.name, ".jsonl"),
|
|
40
|
+
isSubagent,
|
|
41
|
+
archived,
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Walk every slug directory under `projectsRoot`, plus its `subagents/` and
|
|
46
|
+
* `archive/` subdirectories. Flags rather than drops, so the caller's counters
|
|
47
|
+
* can distinguish "nothing on disk" from "everything filtered".
|
|
48
|
+
*/
|
|
49
|
+
export function enumerateSessionJsonls(projectsRoot) {
|
|
50
|
+
const files = [];
|
|
51
|
+
let skipped = 0;
|
|
52
|
+
let slugs;
|
|
53
|
+
try {
|
|
54
|
+
slugs = readdirSync(projectsRoot, { withFileTypes: true })
|
|
55
|
+
.filter((d) => d.isDirectory())
|
|
56
|
+
.map((d) => d.name);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
if (err.code === "ENOENT") {
|
|
60
|
+
return { files, candidateFiles: 0, skipped: 0 };
|
|
61
|
+
}
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
const shapes = [
|
|
65
|
+
["", false, false],
|
|
66
|
+
["subagents", true, false],
|
|
67
|
+
["archive", false, true],
|
|
68
|
+
];
|
|
69
|
+
for (const slug of slugs) {
|
|
70
|
+
const slugDir = join(projectsRoot, slug);
|
|
71
|
+
for (const [sub, isSubagent, archived] of shapes) {
|
|
72
|
+
const dir = sub ? join(slugDir, sub) : slugDir;
|
|
73
|
+
try {
|
|
74
|
+
files.push(...jsonlsIn(dir, isSubagent, archived));
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
if (err.code !== "ENOENT")
|
|
78
|
+
skipped += 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return { files, candidateFiles: files.length, skipped };
|
|
83
|
+
}
|
|
84
|
+
/** Account scope for one session, from its `.meta.json` sidecar. Null when the
|
|
85
|
+
* sidecar is missing, unreadable, or carries no accountId. */
|
|
86
|
+
export function readSidecarAccountId(jsonlPath) {
|
|
87
|
+
try {
|
|
88
|
+
const raw = readFileSync(jsonlPath.replace(/\.jsonl$/, ".meta.json"), "utf8");
|
|
89
|
+
const parsed = JSON.parse(raw);
|
|
90
|
+
return typeof parsed.accountId === "string" ? parsed.accountId : null;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** ISO of the session's last write, used for the sinceIso filter. */
|
|
97
|
+
export function sessionLastWriteIso(jsonlPath) {
|
|
98
|
+
try {
|
|
99
|
+
return statSync(jsonlPath).mtime.toISOString();
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function turnText(line) {
|
|
106
|
+
let parsed;
|
|
107
|
+
try {
|
|
108
|
+
parsed = JSON.parse(line);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
if (parsed.type !== "user" && parsed.type !== "assistant")
|
|
114
|
+
return null;
|
|
115
|
+
const content = parsed.message?.content;
|
|
116
|
+
if (typeof content === "string")
|
|
117
|
+
return `${parsed.type}: ${content}`;
|
|
118
|
+
if (Array.isArray(content)) {
|
|
119
|
+
const text = content
|
|
120
|
+
.filter((b) => typeof b === "object" &&
|
|
121
|
+
b !== null &&
|
|
122
|
+
b.type === "text" &&
|
|
123
|
+
typeof b.text === "string")
|
|
124
|
+
.map((b) => b.text)
|
|
125
|
+
.join("\n");
|
|
126
|
+
return text ? `${parsed.type}: ${text}` : null;
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* One page of turn-attributed transcript. `chunkLines` counts TURNS, not file
|
|
132
|
+
* lines: a transcript's line count is dominated by tool traffic the claim pass
|
|
133
|
+
* has no use for.
|
|
134
|
+
*/
|
|
135
|
+
export function chunkTranscript(jsonlPath, chunkIndex, chunkLines) {
|
|
136
|
+
let raw;
|
|
137
|
+
try {
|
|
138
|
+
raw = readFileSync(jsonlPath, "utf8");
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return { body: "", totalChunks: 0 };
|
|
142
|
+
}
|
|
143
|
+
const turns = raw
|
|
144
|
+
.split("\n")
|
|
145
|
+
.map(turnText)
|
|
146
|
+
.filter((t) => t !== null);
|
|
147
|
+
if (turns.length === 0)
|
|
148
|
+
return { body: "", totalChunks: 0 };
|
|
149
|
+
const totalChunks = Math.ceil(turns.length / chunkLines);
|
|
150
|
+
const start = chunkIndex * chunkLines;
|
|
151
|
+
return { body: turns.slice(start, start + chunkLines).join("\n"), totalChunks };
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=session-jsonl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-jsonl.js","sourceRoot":"","sources":["../../src/lib/session-jsonl.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,7 +11,7 @@ import { fileURLToPath } from "node:url";
|
|
|
11
11
|
// handler and nowhere it should not be.
|
|
12
12
|
const SRC = resolve(dirname(fileURLToPath(import.meta.url)), "../../index.ts");
|
|
13
13
|
const src = readFileSync(SRC, "utf8");
|
|
14
|
-
// The
|
|
14
|
+
// The 41 tools whose read/write re-scopes on the resolved account. The first 38
|
|
15
15
|
// key SOLELY on accountId (Task 1455). profile-read/update/delete (Task 1461)
|
|
16
16
|
// additionally key on userId, but the cross-account path resolves the target
|
|
17
17
|
// sub-account's OWNER AdminUser userId, so the resolved account still governs
|
|
@@ -20,6 +20,8 @@ const TARGETABLE = [
|
|
|
20
20
|
"memory-search", "memory-lookup-by-name", "memory-node-exists", "memory-find-candidates",
|
|
21
21
|
"memory-brain-capture-recent", "memory-write", "memory-update", "memory-update-by-name",
|
|
22
22
|
"memory-edge", "memory-delete", "memory-restore", "memory-empty-trash", "memory-archive-write",
|
|
23
|
+
// Task 2220 — the learnings surface.
|
|
24
|
+
"learnings-scan", "learnings-derive", "learnings-recall",
|
|
23
25
|
"memory-list-attachments", "memory-read-attachment", "memory-edit-attachment",
|
|
24
26
|
"memory-rename-attachment", "memory-report-write", "memory-report-read-latest",
|
|
25
27
|
"memory-report-list", "conversation-list", "conversation-search", "conversation-memory-expunge",
|
|
@@ -54,10 +56,10 @@ describe("cross-account tool coverage (Task 1455)", () => {
|
|
|
54
56
|
it("image-fetch is NOT threaded (n/a — no sub-account scope)", () => {
|
|
55
57
|
expect(src).not.toContain(`resolveToolAccount("image-fetch"`);
|
|
56
58
|
});
|
|
57
|
-
it("exactly
|
|
59
|
+
it("exactly 41 tools take a target; 5 are own-account-only", () => {
|
|
58
60
|
const withTarget = src.match(/resolveToolAccount\("[a-z0-9-]+", targetAccountId\)/g) ?? [];
|
|
59
61
|
const ownOnly = src.match(/resolveToolAccount\("[a-z0-9-]+", undefined\)/g) ?? [];
|
|
60
|
-
expect(TARGETABLE.length).toBe(
|
|
62
|
+
expect(TARGETABLE.length).toBe(41);
|
|
61
63
|
expect(NOT_TARGETABLE.length).toBe(5);
|
|
62
64
|
expect(withTarget.length).toBe(TARGETABLE.length);
|
|
63
65
|
expect(ownOnly.length).toBe(NOT_TARGETABLE.length);
|
|
@@ -66,7 +68,7 @@ describe("cross-account tool coverage (Task 1455)", () => {
|
|
|
66
68
|
const shadows = src.match(/const accountId = scoped\.accountId;/g) ?? [];
|
|
67
69
|
expect(shadows.length).toBe(TARGETABLE.length + NOT_TARGETABLE.length);
|
|
68
70
|
});
|
|
69
|
-
it("no schema exposes targetAccountId beyond the
|
|
71
|
+
it("no schema exposes targetAccountId beyond the 41 targetable tools", () => {
|
|
70
72
|
const fields = src.match(/targetAccountId: z\.string\(\)\.optional\(\)/g) ?? [];
|
|
71
73
|
expect(fields.length).toBe(TARGETABLE.length);
|
|
72
74
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cross-account.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/cross-account.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cross-account.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/cross-account.test.ts"],"names":[],"mappings":"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"}
|
package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learnings-derive.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/learnings-derive.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// Task 2220 — four branches: novel create at initial confidence, amplify on
|
|
2
|
+
// re-observation, relegate on contradiction with contradictedAt stamped, and
|
|
3
|
+
// the decay pass reproducing the preference curve off a STRING observedAt.
|
|
4
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
5
|
+
const mockSession = {
|
|
6
|
+
run: vi.fn(),
|
|
7
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
8
|
+
};
|
|
9
|
+
vi.mock("../../lib/neo4j.js", () => ({ getSession: () => mockSession }));
|
|
10
|
+
vi.mock("../../lib/embeddings.js", () => ({
|
|
11
|
+
embed: vi.fn().mockResolvedValue([0.1, 0.2, 0.3]),
|
|
12
|
+
}));
|
|
13
|
+
import { learningsDerive } from "../learnings-derive.js";
|
|
14
|
+
const rec = (row) => ({ get: (k) => row[k] });
|
|
15
|
+
function wireSession(opts) {
|
|
16
|
+
mockSession.run.mockReset();
|
|
17
|
+
mockSession.run.mockImplementation(async (q) => {
|
|
18
|
+
if (q.includes("queryNodes('learning_embedding'")) {
|
|
19
|
+
return { records: (opts.neighbourRows ?? []).map(rec) };
|
|
20
|
+
}
|
|
21
|
+
if (q.includes("RETURN l.learningId AS learningId")) {
|
|
22
|
+
return { records: (opts.decayRows ?? []).map(rec) };
|
|
23
|
+
}
|
|
24
|
+
return { records: [] };
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
beforeEach(() => wireSession({}));
|
|
28
|
+
describe("learningsDerive", () => {
|
|
29
|
+
it("creates a novel claim at initial confidence", async () => {
|
|
30
|
+
const out = await learningsDerive({
|
|
31
|
+
accountId: "acct-1",
|
|
32
|
+
sessionId: "sess-1",
|
|
33
|
+
claims: [{ title: "Prefer exit codes", body: "Never grep prose.", mode: "reinforce" }],
|
|
34
|
+
});
|
|
35
|
+
expect(out).toMatchObject({ created: 1, amplified: 0, relegated: 0 });
|
|
36
|
+
const create = mockSession.run.mock.calls.find(([q]) => String(q).includes("CREATE (l:Learning"));
|
|
37
|
+
expect(create).toBeDefined();
|
|
38
|
+
expect(create[1].confidence).toBe(0.5);
|
|
39
|
+
});
|
|
40
|
+
it("amplifies a matched claim on the reinforcement curve", async () => {
|
|
41
|
+
// similarity = 2 * 0.95 - 1 = 0.9, above SAME_SUBJECT_COSINE (0.85)
|
|
42
|
+
wireSession({
|
|
43
|
+
neighbourRows: [
|
|
44
|
+
{ learningId: "L1", title: "Prefer exit codes", confidence: 0.5, score: 0.95 },
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
const out = await learningsDerive({
|
|
48
|
+
accountId: "acct-1",
|
|
49
|
+
sessionId: "sess-2",
|
|
50
|
+
claims: [{ title: "Use exit codes", body: "Same rule, new words.", mode: "reinforce" }],
|
|
51
|
+
});
|
|
52
|
+
expect(out).toMatchObject({ created: 0, amplified: 1, relegated: 0 });
|
|
53
|
+
const set = mockSession.run.mock.calls.find(([q]) => String(q).includes("SET l.confidence = $confidence"));
|
|
54
|
+
// 0.5 + 0.15 * (1 - 0.5) = 0.575
|
|
55
|
+
expect(set[1].confidence).toBeCloseTo(0.575, 10);
|
|
56
|
+
});
|
|
57
|
+
it("relegates on contradiction and stamps contradictedAt", async () => {
|
|
58
|
+
wireSession({
|
|
59
|
+
neighbourRows: [
|
|
60
|
+
{ learningId: "L1", title: "Prefer exit codes", confidence: 0.8, score: 0.95 },
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
const out = await learningsDerive({
|
|
64
|
+
accountId: "acct-1",
|
|
65
|
+
sessionId: "sess-3",
|
|
66
|
+
claims: [
|
|
67
|
+
{ title: "Exit codes are wrong", body: "Operator said otherwise.", mode: "contradict" },
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
expect(out).toMatchObject({ created: 0, amplified: 0, relegated: 1 });
|
|
71
|
+
const set = mockSession.run.mock.calls.find(([q]) => String(q).includes("SET l.confidence = $confidence"));
|
|
72
|
+
expect(set[1].confidence).toBeCloseTo(0.5, 10);
|
|
73
|
+
expect(String(set[0])).toContain("contradictedAt");
|
|
74
|
+
});
|
|
75
|
+
it("creates rather than matching when the neighbour is below the cosine floor", async () => {
|
|
76
|
+
// similarity = 2 * 0.8 - 1 = 0.6, below 0.85
|
|
77
|
+
wireSession({
|
|
78
|
+
neighbourRows: [{ learningId: "L1", title: "Unrelated", confidence: 0.5, score: 0.8 }],
|
|
79
|
+
});
|
|
80
|
+
const out = await learningsDerive({
|
|
81
|
+
accountId: "acct-1",
|
|
82
|
+
sessionId: "sess-4",
|
|
83
|
+
claims: [{ title: "New thing", body: "Nothing like the other.", mode: "reinforce" }],
|
|
84
|
+
});
|
|
85
|
+
expect(out).toMatchObject({ created: 1, amplified: 0 });
|
|
86
|
+
});
|
|
87
|
+
it("decays a string observedAt past the threshold and floors at zero", async () => {
|
|
88
|
+
const now = Date.now();
|
|
89
|
+
const daysAgo = (n) => new Date(now - n * 86_400_000).toISOString();
|
|
90
|
+
wireSession({
|
|
91
|
+
decayRows: [
|
|
92
|
+
{ learningId: "A", confidence: 0.8, observedAt: daysAgo(5) },
|
|
93
|
+
{ learningId: "B", confidence: 0.8, observedAt: daysAgo(20) },
|
|
94
|
+
{ learningId: "C", confidence: 0.1, observedAt: daysAgo(200) },
|
|
95
|
+
],
|
|
96
|
+
});
|
|
97
|
+
const out = await learningsDerive({
|
|
98
|
+
accountId: "acct-1",
|
|
99
|
+
sessionId: "sess-5",
|
|
100
|
+
claims: [],
|
|
101
|
+
runDecay: true,
|
|
102
|
+
});
|
|
103
|
+
expect(out.decay).toEqual({ examined: 3, decayed: 2, floored: 1 });
|
|
104
|
+
});
|
|
105
|
+
it("does not run the decay pass unless asked", async () => {
|
|
106
|
+
const out = await learningsDerive({
|
|
107
|
+
accountId: "acct-1",
|
|
108
|
+
sessionId: "sess-6",
|
|
109
|
+
claims: [],
|
|
110
|
+
});
|
|
111
|
+
expect(out.decay).toBeNull();
|
|
112
|
+
});
|
|
113
|
+
// Review finding: `MATCH (p:UserProfile ...) CREATE ...` silently no-ops when
|
|
114
|
+
// the profile is absent (profile-update.ts:309-311), and MERGE on accountId
|
|
115
|
+
// alone would mint a UserProfile missing its required userId.
|
|
116
|
+
it("creates the learning without a UserProfile and attaches the edge only if one exists", async () => {
|
|
117
|
+
await learningsDerive({
|
|
118
|
+
accountId: "acct-1",
|
|
119
|
+
sessionId: "sess-8",
|
|
120
|
+
claims: [{ title: "Standalone", body: "No profile in this graph.", mode: "reinforce" }],
|
|
121
|
+
});
|
|
122
|
+
const create = mockSession.run.mock.calls.find(([q]) => String(q).includes("CREATE (l:Learning"));
|
|
123
|
+
const q = String(create[0]);
|
|
124
|
+
expect(q).not.toContain("MERGE (p:UserProfile");
|
|
125
|
+
expect(q).toContain("OPTIONAL MATCH (p:UserProfile");
|
|
126
|
+
// The CREATE is unconditional: it precedes the profile lookup.
|
|
127
|
+
expect(q.indexOf("CREATE (l:Learning")).toBeLessThan(q.indexOf("OPTIONAL MATCH"));
|
|
128
|
+
});
|
|
129
|
+
// Review finding: :Learning is vector-indexed, so a zero-length embedding
|
|
130
|
+
// makes the node permanently invisible to the neighbour query — it could
|
|
131
|
+
// never amplify, and every re-observation would mint a duplicate.
|
|
132
|
+
it("omits the embedding property entirely when the embedder returns nothing", async () => {
|
|
133
|
+
const { embed } = await import("../../lib/embeddings.js");
|
|
134
|
+
embed
|
|
135
|
+
.mockResolvedValueOnce(null);
|
|
136
|
+
const write = vi.spyOn(process.stderr, "write").mockReturnValue(true);
|
|
137
|
+
await learningsDerive({
|
|
138
|
+
accountId: "acct-1",
|
|
139
|
+
sessionId: "sess-9",
|
|
140
|
+
claims: [{ title: "Unembeddable", body: "Embedder was down.", mode: "reinforce" }],
|
|
141
|
+
});
|
|
142
|
+
const create = mockSession.run.mock.calls.find(([q]) => String(q).includes("CREATE (l:Learning"));
|
|
143
|
+
// Never an empty array in the created property block.
|
|
144
|
+
expect(create[1].embedding).toBeNull();
|
|
145
|
+
const line = write.mock.calls
|
|
146
|
+
.map(([c]) => String(c))
|
|
147
|
+
.find((c) => c.includes("op=derive-embed-missing"));
|
|
148
|
+
expect(line).toContain("reason=embedder-returned-nothing");
|
|
149
|
+
write.mockRestore();
|
|
150
|
+
});
|
|
151
|
+
it("rejects an unrecognised mode by name", async () => {
|
|
152
|
+
await expect(learningsDerive({
|
|
153
|
+
accountId: "acct-1",
|
|
154
|
+
sessionId: "sess-7",
|
|
155
|
+
// deliberately outside the union
|
|
156
|
+
claims: [{ title: "x", body: "y", mode: "merge" }],
|
|
157
|
+
})).rejects.toThrow(/Invalid mode "merge"/);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
//# sourceMappingURL=learnings-derive.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learnings-derive.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/learnings-derive.test.ts"],"names":[],"mappings":"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"}
|
package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learnings-recall.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/learnings-recall.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Task 2220 — the only path to a learning body. The skill cannot emit a log
|
|
2
|
+
// line, so the tool does: op=recall with the id and the matched title. Without
|
|
3
|
+
// it, "index injected but never invoked" would be unobservable, and that
|
|
4
|
+
// measurement is what decides whether MAX_INDEXED_LEARNINGS falls.
|
|
5
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
6
|
+
const mockSession = { run: vi.fn(), close: vi.fn().mockResolvedValue(undefined) };
|
|
7
|
+
vi.mock("../../lib/neo4j.js", () => ({ getSession: () => mockSession }));
|
|
8
|
+
import { learningsRecall } from "../learnings-recall.js";
|
|
9
|
+
const rec = (row) => ({ get: (k) => row[k] });
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
mockSession.run.mockReset();
|
|
12
|
+
mockSession.run.mockResolvedValue({ records: [] });
|
|
13
|
+
});
|
|
14
|
+
describe("learningsRecall", () => {
|
|
15
|
+
it("returns the body for a matched title and emits op=recall", async () => {
|
|
16
|
+
const write = vi.spyOn(process.stderr, "write").mockReturnValue(true);
|
|
17
|
+
mockSession.run.mockResolvedValue({
|
|
18
|
+
records: [
|
|
19
|
+
rec({
|
|
20
|
+
learningId: "L1",
|
|
21
|
+
title: "Prefer exit codes",
|
|
22
|
+
body: "Never grep a CLI's prose for control flow.",
|
|
23
|
+
confidence: 0.7,
|
|
24
|
+
observedAt: "2026-07-29T00:00:00.000Z",
|
|
25
|
+
observationCount: 3,
|
|
26
|
+
contradictedAt: null,
|
|
27
|
+
}),
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
const out = await learningsRecall({ accountId: "acct-1", title: "exit codes" });
|
|
31
|
+
expect(out.rows[0].body).toContain("Never grep");
|
|
32
|
+
const line = write.mock.calls
|
|
33
|
+
.map(([c]) => String(c))
|
|
34
|
+
.find((c) => c.includes("op=recall"));
|
|
35
|
+
expect(line).toContain("learningId=L1");
|
|
36
|
+
expect(line).toContain("matchedTitle=");
|
|
37
|
+
write.mockRestore();
|
|
38
|
+
});
|
|
39
|
+
it("surfaces contradictedAt so a disputed learning is not read as settled", async () => {
|
|
40
|
+
mockSession.run.mockResolvedValue({
|
|
41
|
+
records: [
|
|
42
|
+
rec({
|
|
43
|
+
learningId: "L2",
|
|
44
|
+
title: "Disputed",
|
|
45
|
+
body: "text",
|
|
46
|
+
confidence: 0.2,
|
|
47
|
+
observedAt: "2026-07-29T00:00:00.000Z",
|
|
48
|
+
observationCount: 2,
|
|
49
|
+
contradictedAt: "2026-07-30T00:00:00.000Z",
|
|
50
|
+
}),
|
|
51
|
+
],
|
|
52
|
+
});
|
|
53
|
+
const out = await learningsRecall({ accountId: "acct-1", learningId: "L2" });
|
|
54
|
+
expect(out.rows[0].contradictedAt).toBe("2026-07-30T00:00:00.000Z");
|
|
55
|
+
});
|
|
56
|
+
it("requires one of title or learningId", async () => {
|
|
57
|
+
await expect(learningsRecall({ accountId: "acct-1" })).rejects.toThrow(/title or learningId is required/);
|
|
58
|
+
});
|
|
59
|
+
it("returns no rows without throwing when nothing matches", async () => {
|
|
60
|
+
const out = await learningsRecall({ accountId: "acct-1", title: "nothing" });
|
|
61
|
+
expect(out.rows).toEqual([]);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=learnings-recall.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learnings-recall.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/learnings-recall.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learnings-scan.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/learnings-scan.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Task 2220 — learnings-scan returns only sessions written after sinceIso,
|
|
2
|
+
// skips subagent and archived transcripts, and scopes by sidecar accountId.
|
|
3
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
4
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, utimesSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { learningsScan } from "../learnings-scan.js";
|
|
8
|
+
let root;
|
|
9
|
+
let slug;
|
|
10
|
+
function seedSession(name, accountId, mtime, dir) {
|
|
11
|
+
const target = dir ?? slug;
|
|
12
|
+
const p = join(target, `${name}.jsonl`);
|
|
13
|
+
writeFileSync(p, [
|
|
14
|
+
JSON.stringify({ type: "user", message: { content: `hello from ${name}` } }),
|
|
15
|
+
JSON.stringify({
|
|
16
|
+
type: "assistant",
|
|
17
|
+
message: { content: [{ type: "text", text: "ack" }] },
|
|
18
|
+
}),
|
|
19
|
+
].join("\n"));
|
|
20
|
+
if (accountId !== null) {
|
|
21
|
+
writeFileSync(join(target, `${name}.meta.json`), JSON.stringify({ accountId }));
|
|
22
|
+
}
|
|
23
|
+
utimesSync(p, mtime, mtime);
|
|
24
|
+
}
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
root = mkdtempSync(join(tmpdir(), "learnings-scan-tool-"));
|
|
27
|
+
slug = join(root, "projects", "-Users-neo-proj");
|
|
28
|
+
mkdirSync(join(slug, "subagents"), { recursive: true });
|
|
29
|
+
mkdirSync(join(slug, "archive"), { recursive: true });
|
|
30
|
+
process.env.CLAUDE_CONFIG_DIR = root;
|
|
31
|
+
});
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
rmSync(root, { recursive: true, force: true });
|
|
34
|
+
delete process.env.CLAUDE_CONFIG_DIR;
|
|
35
|
+
});
|
|
36
|
+
describe("learningsScan", () => {
|
|
37
|
+
it("returns only sessions written after sinceIso", async () => {
|
|
38
|
+
seedSession("old", "acct-1", new Date("2026-07-01T00:00:00Z"));
|
|
39
|
+
seedSession("new", "acct-1", new Date("2026-07-29T00:00:00Z"));
|
|
40
|
+
const out = await learningsScan({
|
|
41
|
+
accountId: "acct-1",
|
|
42
|
+
sinceIso: "2026-07-15T00:00:00.000Z",
|
|
43
|
+
});
|
|
44
|
+
expect(out.rows.map((r) => r.sessionId)).toEqual(["new"]);
|
|
45
|
+
expect(out.sessions).toBe(1);
|
|
46
|
+
expect(out.candidateFiles).toBe(2);
|
|
47
|
+
});
|
|
48
|
+
it("skips subagent and archived transcripts", async () => {
|
|
49
|
+
seedSession("live", "acct-1", new Date("2026-07-29T00:00:00Z"));
|
|
50
|
+
seedSession("sub", "acct-1", new Date("2026-07-29T00:00:00Z"), join(slug, "subagents"));
|
|
51
|
+
seedSession("arch", "acct-1", new Date("2026-07-29T00:00:00Z"), join(slug, "archive"));
|
|
52
|
+
const out = await learningsScan({ accountId: "acct-1" });
|
|
53
|
+
expect(out.rows.map((r) => r.sessionId)).toEqual(["live"]);
|
|
54
|
+
expect(out.candidateFiles).toBe(3);
|
|
55
|
+
});
|
|
56
|
+
it("drops sessions belonging to another account and untagged sessions", async () => {
|
|
57
|
+
seedSession("mine", "acct-1", new Date("2026-07-29T00:00:00Z"));
|
|
58
|
+
seedSession("theirs", "acct-2", new Date("2026-07-29T00:00:00Z"));
|
|
59
|
+
seedSession("untagged", null, new Date("2026-07-29T00:00:00Z"));
|
|
60
|
+
const out = await learningsScan({ accountId: "acct-1" });
|
|
61
|
+
expect(out.rows.map((r) => r.sessionId)).toEqual(["mine"]);
|
|
62
|
+
});
|
|
63
|
+
it("refuses when CLAUDE_CONFIG_DIR is unset", async () => {
|
|
64
|
+
delete process.env.CLAUDE_CONFIG_DIR;
|
|
65
|
+
await expect(learningsScan({ accountId: "acct-1" })).rejects.toThrow(/CLAUDE_CONFIG_DIR is not set/);
|
|
66
|
+
});
|
|
67
|
+
it("rejects a malformed sinceIso rather than scanning everything", async () => {
|
|
68
|
+
seedSession("any", "acct-1", new Date("2026-07-29T00:00:00Z"));
|
|
69
|
+
await expect(learningsScan({ accountId: "acct-1", sinceIso: "last tuesday" })).rejects.toThrow(/not a valid ISO timestamp/);
|
|
70
|
+
});
|
|
71
|
+
it("pages chunks within a session", async () => {
|
|
72
|
+
seedSession("multi", "acct-1", new Date("2026-07-29T00:00:00Z"));
|
|
73
|
+
const out = await learningsScan({ accountId: "acct-1", chunkTurns: 1, chunkIndex: 1 });
|
|
74
|
+
expect(out.rows[0].totalChunks).toBe(2);
|
|
75
|
+
expect(out.rows[0].body).toContain("ack");
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=learnings-scan.test.js.map
|