chapterhouse 0.13.0 → 0.14.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/dist/api/route-coverage.test.js +1 -3
- package/dist/api/server.js +0 -2
- package/dist/api/server.test.js +0 -281
- package/dist/config.js +3 -85
- package/dist/config.test.js +5 -123
- package/dist/copilot/agents.js +25 -13
- package/dist/copilot/agents.test.js +10 -11
- package/dist/copilot/memory-coordinator.js +12 -227
- package/dist/copilot/memory-coordinator.test.js +31 -250
- package/dist/copilot/orchestrator.js +8 -66
- package/dist/copilot/orchestrator.test.js +9 -467
- package/dist/copilot/skills.js +15 -1
- package/dist/copilot/system-message.js +9 -15
- package/dist/copilot/system-message.test.js +9 -22
- package/dist/copilot/tools/index.js +3 -3
- package/dist/copilot/tools-deps.js +1 -1
- package/dist/copilot/tools.agent.test.js +6 -0
- package/dist/copilot/tools.inventory.test.js +1 -14
- package/dist/daemon.js +7 -9
- package/dist/memory/assets.js +33 -0
- package/dist/memory/domains.js +58 -0
- package/dist/memory/domains.test.js +47 -0
- package/dist/memory/git.js +66 -0
- package/dist/memory/git.test.js +32 -0
- package/dist/memory/history.js +19 -0
- package/dist/memory/hottier.js +32 -0
- package/dist/memory/hottier.test.js +33 -0
- package/dist/memory/index.js +5 -13
- package/dist/memory/instructions.js +17 -0
- package/dist/memory/manager.js +84 -0
- package/dist/memory/markdown.js +78 -0
- package/dist/memory/markdown.test.js +42 -0
- package/dist/memory/mutex.js +18 -0
- package/dist/memory/path-guard.js +26 -0
- package/dist/memory/path-guard.test.js +27 -0
- package/dist/memory/paths.js +12 -0
- package/dist/memory/reconcile.js +75 -0
- package/dist/memory/reconcile.test.js +50 -0
- package/dist/memory/scaffold.js +37 -0
- package/dist/memory/scaffold.test.js +52 -0
- package/dist/memory/tools/commit-wrapper.js +32 -0
- package/dist/memory/tools/domains.js +73 -0
- package/dist/memory/tools/domains.test.js +66 -0
- package/dist/memory/tools/git.js +52 -0
- package/dist/memory/tools/index.js +25 -0
- package/dist/memory/tools/read.js +101 -0
- package/dist/memory/tools/read.test.js +69 -0
- package/dist/memory/tools/search.js +103 -0
- package/dist/memory/tools/search.test.js +63 -0
- package/dist/memory/tools/sessions.js +45 -0
- package/dist/memory/tools/sessions.test.js +74 -0
- package/dist/memory/tools/shared.js +7 -0
- package/dist/memory/tools/write.js +116 -0
- package/dist/memory/tools/write.test.js +107 -0
- package/dist/memory/walk.js +39 -0
- package/dist/store/repositories/sessions.js +40 -0
- package/dist/wiki/consolidation.js +3 -31
- package/dist/wiki/consolidation.test.js +0 -19
- package/dist/wiki/frontmatter.js +18 -6
- package/dist/wiki/frontmatter.test.js +40 -0
- package/package.json +1 -1
- package/skills/system/evolve/SKILL.md +131 -0
- package/skills/system/foresight/SKILL.md +116 -0
- package/skills/system/history/SKILL.md +58 -0
- package/skills/system/housekeeping/SKILL.md +185 -0
- package/skills/system/reflect/SKILL.md +214 -0
- package/skills/system/scenario/SKILL.md +198 -0
- package/skills/system/setup/SKILL.md +113 -0
- package/web/dist/assets/{WikiEdit-CGRxNazp.js → WikiEdit-BTsiBfbC.js} +2 -2
- package/web/dist/assets/{WikiEdit-CGRxNazp.js.map → WikiEdit-BTsiBfbC.js.map} +1 -1
- package/web/dist/assets/{WikiGraph-eVWNhZS3.js → WikiGraph-COOZbUeH.js} +2 -2
- package/web/dist/assets/{WikiGraph-eVWNhZS3.js.map → WikiGraph-COOZbUeH.js.map} +1 -1
- package/web/dist/assets/{index-gAvLNEvJ.js → index-aCcfpaLM.js} +101 -101
- package/web/dist/assets/index-aCcfpaLM.js.map +1 -0
- package/web/dist/index.html +1 -1
- package/dist/api/routes/memory.js +0 -475
- package/dist/api/routes/memory.test.js +0 -108
- package/dist/copilot/tools/memory.js +0 -678
- package/dist/copilot/tools.memory.test.js +0 -590
- package/dist/memory/action-items.js +0 -100
- package/dist/memory/action-items.test.js +0 -83
- package/dist/memory/active-scope.js +0 -78
- package/dist/memory/active-scope.test.js +0 -80
- package/dist/memory/checkpoint-prompt.js +0 -71
- package/dist/memory/checkpoint.js +0 -274
- package/dist/memory/checkpoint.test.js +0 -275
- package/dist/memory/decisions.js +0 -54
- package/dist/memory/decisions.test.js +0 -92
- package/dist/memory/entities.js +0 -70
- package/dist/memory/entities.test.js +0 -65
- package/dist/memory/eot.js +0 -459
- package/dist/memory/eot.test.js +0 -949
- package/dist/memory/hooks.js +0 -149
- package/dist/memory/hooks.test.js +0 -325
- package/dist/memory/hot-tier.js +0 -283
- package/dist/memory/hot-tier.test.js +0 -275
- package/dist/memory/housekeeping-scheduler.js +0 -187
- package/dist/memory/housekeeping-scheduler.test.js +0 -236
- package/dist/memory/housekeeping.js +0 -497
- package/dist/memory/housekeeping.test.js +0 -410
- package/dist/memory/inbox.js +0 -83
- package/dist/memory/inbox.test.js +0 -178
- package/dist/memory/migration.js +0 -244
- package/dist/memory/migration.test.js +0 -108
- package/dist/memory/observations.js +0 -46
- package/dist/memory/observations.test.js +0 -86
- package/dist/memory/recall.js +0 -269
- package/dist/memory/recall.test.js +0 -265
- package/dist/memory/reflect.js +0 -273
- package/dist/memory/reflect.test.js +0 -256
- package/dist/memory/scope-lock.js +0 -26
- package/dist/memory/scope-lock.test.js +0 -118
- package/dist/memory/scopes.js +0 -89
- package/dist/memory/scopes.test.js +0 -176
- package/dist/memory/tiering.js +0 -223
- package/dist/memory/tiering.test.js +0 -323
- package/dist/memory/types.js +0 -2
- package/web/dist/assets/index-gAvLNEvJ.js.map +0 -1
|
@@ -66,19 +66,11 @@ test("orchestrator prompt omits version banner when version is not provided", ()
|
|
|
66
66
|
const message = getOrchestratorSystemMessage();
|
|
67
67
|
assert.doesNotMatch(message, /chapterhouse v\d/);
|
|
68
68
|
});
|
|
69
|
-
test("orchestrator prompt
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
" <memory scope=\"chapterhouse\">",
|
|
75
|
-
" <decision id=\"decision-1\">Keep hot-tier notes small</decision>",
|
|
76
|
-
" </memory>",
|
|
77
|
-
"</memory_context>",
|
|
78
|
-
].join("\n");
|
|
79
|
-
const message = getOrchestratorSystemMessage({ hotTierXml });
|
|
80
|
-
assert.match(message, /<memory_context>/);
|
|
81
|
-
assert.ok(message.indexOf(hotTierXml) < message.indexOf("## Your Architecture"));
|
|
69
|
+
test("orchestrator prompt appends the memory operating instructions", () => {
|
|
70
|
+
const message = getOrchestratorSystemMessage();
|
|
71
|
+
// The bundled system-instructions.md is appended; tolerate its absence in
|
|
72
|
+
// environments where memory-assets/ is not present.
|
|
73
|
+
assert.match(message, /Memory & Wiki|## Your Architecture/);
|
|
82
74
|
});
|
|
83
75
|
test("orchestrator prompt omits memory_context when hot-tier XML is not provided", () => {
|
|
84
76
|
const message = getOrchestratorSystemMessage();
|
|
@@ -86,7 +78,7 @@ test("orchestrator prompt omits memory_context when hot-tier XML is not provided
|
|
|
86
78
|
});
|
|
87
79
|
test("orchestrator prompt requires wiki-conventions before write-sensitive wiki work", () => {
|
|
88
80
|
const message = getOrchestratorSystemMessage();
|
|
89
|
-
assert.match(message, /wiki-conventions[\s\S]{0,500}wiki_update[\s\S]{0,200}wiki_ingest_source
|
|
81
|
+
assert.match(message, /wiki-conventions[\s\S]{0,500}wiki_update[\s\S]{0,200}wiki_ingest_source/i);
|
|
90
82
|
assert.doesNotMatch(message, /`remember`|`recall`|`forget`|`wiki_ingest`(?!_source)|`wiki_lint`|`wiki_rebuild_index`|`wiki_fix`/);
|
|
91
83
|
assert.match(message, /before writing or restructuring wiki content/i);
|
|
92
84
|
});
|
|
@@ -96,14 +88,9 @@ test("orchestrator prompt describes the wiki orientation ritual", () => {
|
|
|
96
88
|
assert.match(message, /scan the last 20-30 entries of `pages\/_meta\/log\.md`/i);
|
|
97
89
|
assert.match(message, /run `wiki_search` for the topic/i);
|
|
98
90
|
});
|
|
99
|
-
test("orchestrator prompt
|
|
100
|
-
const message = getOrchestratorSystemMessage();
|
|
101
|
-
assert.match(message, /subagent proposals/i);
|
|
102
|
-
assert.match(message, /processed automatically at end-of-task|processed automatically at the end of the task/i);
|
|
103
|
-
assert.match(message, /do not need to manually review them mid-conversation|don't need to manually review them mid-conversation/i);
|
|
104
|
-
});
|
|
105
|
-
test("orchestrator prompt includes the memory_reflect tool in memory guidance", () => {
|
|
91
|
+
test("orchestrator prompt points at the file-based memory tools", () => {
|
|
106
92
|
const message = getOrchestratorSystemMessage();
|
|
107
|
-
assert.match(message, /
|
|
93
|
+
assert.match(message, /cog_read/);
|
|
94
|
+
assert.match(message, /cog_write/);
|
|
108
95
|
});
|
|
109
96
|
//# sourceMappingURL=system-message.test.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createAgentTools } from "./agent.js";
|
|
2
|
-
import { createMemoryTools } from "
|
|
2
|
+
import { createMemoryTools } from "../../memory/index.js";
|
|
3
3
|
import { createOkrTools } from "./okr.js";
|
|
4
4
|
import { createWikiTools } from "./wiki.js";
|
|
5
5
|
export { createAgentTools } from "./agent.js";
|
|
6
|
-
export { createMemoryTools } from "
|
|
6
|
+
export { createMemoryTools } from "../../memory/index.js";
|
|
7
7
|
export { createOkrTools, getMyOkrsSummary } from "./okr.js";
|
|
8
8
|
export { createWikiTools } from "./wiki.js";
|
|
9
9
|
export function createTools(deps) {
|
|
@@ -11,7 +11,7 @@ export function createTools(deps) {
|
|
|
11
11
|
allTools = [
|
|
12
12
|
...createAgentTools(deps, () => allTools),
|
|
13
13
|
...createOkrTools(deps),
|
|
14
|
-
...createMemoryTools(deps),
|
|
14
|
+
...createMemoryTools(deps.memoryManager),
|
|
15
15
|
...createWikiTools(deps),
|
|
16
16
|
];
|
|
17
17
|
return allTools;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * from "./agents.js";
|
|
2
2
|
export * as agentsModule from "./agents.js";
|
|
3
|
-
export { getCurrentSourceChannel, getCurrentActivityCallback, getCurrentActiveProjectRules, getCurrentSessionKey, sendToAgentSession, switchSessionModel, invalidateOrchestratorSession,
|
|
3
|
+
export { getCurrentSourceChannel, getCurrentActivityCallback, getCurrentActiveProjectRules, getCurrentSessionKey, sendToAgentSession, switchSessionModel, invalidateOrchestratorSession, } from "./orchestrator.js";
|
|
4
4
|
//# sourceMappingURL=tools-deps.js.map
|
|
@@ -156,6 +156,7 @@ test("delegate_to_agent persists the full dispatched task prompt", async (t) =>
|
|
|
156
156
|
});
|
|
157
157
|
const tools = module.createTools({
|
|
158
158
|
client: { async listModels() { return []; } },
|
|
159
|
+
memoryManager: { isReady: () => false },
|
|
159
160
|
onAgentTaskComplete: () => { },
|
|
160
161
|
});
|
|
161
162
|
const tool = tools.find((entry) => entry.name === "delegate_to_agent");
|
|
@@ -181,6 +182,7 @@ test("delegate_to_agent prepends active project rules to the dispatched task pro
|
|
|
181
182
|
});
|
|
182
183
|
const tools = module.createTools({
|
|
183
184
|
client: { async listModels() { return []; } },
|
|
185
|
+
memoryManager: { isReady: () => false },
|
|
184
186
|
onAgentTaskComplete: () => { },
|
|
185
187
|
});
|
|
186
188
|
const tool = tools.find((entry) => entry.name === "delegate_to_agent");
|
|
@@ -200,6 +202,7 @@ test("delegate_to_agent prepends project rule warnings above the dispatched task
|
|
|
200
202
|
});
|
|
201
203
|
const tools = module.createTools({
|
|
202
204
|
client: { async listModels() { return []; } },
|
|
205
|
+
memoryManager: { isReady: () => false },
|
|
203
206
|
onAgentTaskComplete: () => { },
|
|
204
207
|
});
|
|
205
208
|
const tool = tools.find((entry) => entry.name === "delegate_to_agent");
|
|
@@ -219,6 +222,7 @@ test("delegate_to_agent leaves the prompt unchanged when no active project is re
|
|
|
219
222
|
});
|
|
220
223
|
const tools = module.createTools({
|
|
221
224
|
client: { async listModels() { return []; } },
|
|
225
|
+
memoryManager: { isReady: () => false },
|
|
222
226
|
onAgentTaskComplete: () => { },
|
|
223
227
|
});
|
|
224
228
|
const tool = tools.find((entry) => entry.name === "delegate_to_agent");
|
|
@@ -238,6 +242,7 @@ test("delegate_to_agent does not inject orchestrator memory_context into subagen
|
|
|
238
242
|
});
|
|
239
243
|
const tools = module.createTools({
|
|
240
244
|
client: { async listModels() { return []; } },
|
|
245
|
+
memoryManager: { isReady: () => false },
|
|
241
246
|
onAgentTaskComplete: () => { },
|
|
242
247
|
});
|
|
243
248
|
const tool = tools.find((entry) => entry.name === "delegate_to_agent");
|
|
@@ -260,6 +265,7 @@ test("delegate_to_agent sends persistent agents through their backend session an
|
|
|
260
265
|
const completions = [];
|
|
261
266
|
const tools = module.createTools({
|
|
262
267
|
client: { async listModels() { return []; } },
|
|
268
|
+
memoryManager: { isReady: () => false },
|
|
263
269
|
onAgentTaskComplete: (completedTaskId, agentSlug, result) => {
|
|
264
270
|
completions.push({ taskId: completedTaskId, agentSlug, result });
|
|
265
271
|
},
|
|
@@ -8,6 +8,7 @@ test("createTools returns the full expected tool set without duplicate names", a
|
|
|
8
8
|
const toolsModule = await loadToolsIndex();
|
|
9
9
|
const tools = toolsModule.createTools({
|
|
10
10
|
client: { async listModels() { return []; } },
|
|
11
|
+
memoryManager: { isReady: () => false },
|
|
11
12
|
onAgentTaskComplete: () => { },
|
|
12
13
|
createReportGenerator: () => ({
|
|
13
14
|
async generateMonthlyReport() {
|
|
@@ -39,20 +40,6 @@ test("createTools returns the full expected tool set without duplicate names", a
|
|
|
39
40
|
"list_models",
|
|
40
41
|
"switch_model",
|
|
41
42
|
"toggle_auto",
|
|
42
|
-
"memory_remember",
|
|
43
|
-
"memory_propose",
|
|
44
|
-
"memory_create_scope",
|
|
45
|
-
"memory_add_action_item",
|
|
46
|
-
"memory_complete_action_item",
|
|
47
|
-
"memory_drop_action_item",
|
|
48
|
-
"memory_snooze_action_item",
|
|
49
|
-
"memory_list_action_items",
|
|
50
|
-
"memory_recall",
|
|
51
|
-
"memory_housekeep",
|
|
52
|
-
"memory_reflect",
|
|
53
|
-
"memory_promote",
|
|
54
|
-
"memory_demote",
|
|
55
|
-
"memory_set_scope",
|
|
56
43
|
"remember",
|
|
57
44
|
"recall",
|
|
58
45
|
"forget",
|
package/dist/daemon.js
CHANGED
|
@@ -21,13 +21,11 @@ import { registerShutdownSignals } from "./shutdown-signals.js";
|
|
|
21
21
|
import { logger } from "./util/logger.js";
|
|
22
22
|
import { CHAPTERHOUSE_VERSION } from "./version.js";
|
|
23
23
|
import { isWorkiqAutoInstallEnabled, ensureWorkiqMcpEntry } from "./copilot/workiq-installer.js";
|
|
24
|
-
import {
|
|
25
|
-
import { runP6Migration } from "./memory/migration.js";
|
|
24
|
+
import { getMemoryManager } from "./memory/index.js";
|
|
26
25
|
import { WikiConsolidationScheduler } from "./wiki/scheduler.js";
|
|
27
26
|
import { ensureWikiIndexPopulated } from "./wiki/index-manager.js";
|
|
28
27
|
const log = logger.child({ module: "daemon" });
|
|
29
28
|
const modeContext = new ModeContext(config);
|
|
30
|
-
let memoryHousekeepingScheduler;
|
|
31
29
|
let wikiConsolidationScheduler;
|
|
32
30
|
const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
33
31
|
/**
|
|
@@ -122,8 +120,6 @@ async function main() {
|
|
|
122
120
|
const moved = enforceTopicStructure();
|
|
123
121
|
log.info({ moved }, "Topic-structure migration complete");
|
|
124
122
|
}
|
|
125
|
-
const p6Migration = await runP6Migration(getDb());
|
|
126
|
-
log.info({ p6Migration }, "P6 wiki seed migration complete");
|
|
127
123
|
try {
|
|
128
124
|
const wikiReindex = ensureWikiIndexPopulated();
|
|
129
125
|
if (wikiReindex.reindexed) {
|
|
@@ -148,6 +144,12 @@ async function main() {
|
|
|
148
144
|
log.info("Entra auth detected — ensuring workiq MCP server is configured");
|
|
149
145
|
ensureWorkiqMcpEntry();
|
|
150
146
|
}
|
|
147
|
+
// Initialize the file-based memory subsystem (scaffold + git-backed tree)
|
|
148
|
+
if (config.memoryEnabled) {
|
|
149
|
+
log.info("Initializing memory system");
|
|
150
|
+
await getMemoryManager().ensureReady();
|
|
151
|
+
log.info("Memory system ready");
|
|
152
|
+
}
|
|
151
153
|
// Start Copilot SDK client
|
|
152
154
|
log.info("Starting Copilot SDK client");
|
|
153
155
|
const client = await getClient();
|
|
@@ -163,8 +165,6 @@ async function main() {
|
|
|
163
165
|
});
|
|
164
166
|
// Start HTTP API + serve the web UI
|
|
165
167
|
await startApiServer();
|
|
166
|
-
memoryHousekeepingScheduler = new MemoryHousekeepingScheduler();
|
|
167
|
-
memoryHousekeepingScheduler.start();
|
|
168
168
|
wikiConsolidationScheduler = new WikiConsolidationScheduler();
|
|
169
169
|
wikiConsolidationScheduler.start();
|
|
170
170
|
if (modeContext.canLogToAdo() && (config.adoPat || config.teamChapterhouseUrl)) {
|
|
@@ -220,7 +220,6 @@ async function shutdown() {
|
|
|
220
220
|
forceTimer.unref();
|
|
221
221
|
// Destroy all active agent sessions
|
|
222
222
|
await shutdownAgents();
|
|
223
|
-
await memoryHousekeepingScheduler?.stop();
|
|
224
223
|
await wikiConsolidationScheduler?.stop();
|
|
225
224
|
try {
|
|
226
225
|
stopEpisodeWriter();
|
|
@@ -243,7 +242,6 @@ export async function restartDaemon() {
|
|
|
243
242
|
}
|
|
244
243
|
// Destroy all active agent sessions
|
|
245
244
|
await shutdownAgents();
|
|
246
|
-
await memoryHousekeepingScheduler?.stop();
|
|
247
245
|
await wikiConsolidationScheduler?.stop();
|
|
248
246
|
try {
|
|
249
247
|
stopEpisodeWriter();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Resolves and reads the bundled memory assets (seed tree, templates, the
|
|
2
|
+
// domain-skill template, system instructions). chgo embeds these with go:embed;
|
|
3
|
+
// Chapterhouse ships them as a plain `memory-assets/` directory next to `dist/`.
|
|
4
|
+
import { cpSync, readFileSync } from "fs";
|
|
5
|
+
import { dirname, join } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
// This module compiles to dist/memory/assets.js (and runs from src/memory/
|
|
8
|
+
// under tsx); `../../memory-assets` resolves to the repo / package root in both.
|
|
9
|
+
const ASSETS_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "memory-assets");
|
|
10
|
+
/** Absolute path to the bundled memory-assets directory. */
|
|
11
|
+
export function memoryAssetsDir() {
|
|
12
|
+
return ASSETS_DIR;
|
|
13
|
+
}
|
|
14
|
+
/** Reads a bundled asset by its path relative to memory-assets/. */
|
|
15
|
+
export function readAsset(rel) {
|
|
16
|
+
return readFileSync(join(ASSETS_DIR, rel), "utf8");
|
|
17
|
+
}
|
|
18
|
+
/** Returns the per-domain SKILL.md template. */
|
|
19
|
+
export function domainSkillTemplate() {
|
|
20
|
+
return readAsset("domain-skill.md");
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Copies memory-assets/seed/** into destRoot, creating directories as needed.
|
|
24
|
+
* Existing files are never overwritten.
|
|
25
|
+
*/
|
|
26
|
+
export function copySeedTree(destRoot) {
|
|
27
|
+
cpSync(join(ASSETS_DIR, "seed"), destRoot, {
|
|
28
|
+
recursive: true,
|
|
29
|
+
force: false,
|
|
30
|
+
errorOnExist: false,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// The domain manifest (memory/domains.yml) — the single source of truth for how
|
|
2
|
+
// memory is partitioned. Ported from chgo's internal/memory/domains.go.
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
/** Valid domain types. */
|
|
5
|
+
export const DOMAIN_TYPES = ["personal", "work", "side-project", "system"];
|
|
6
|
+
function asStringArray(value) {
|
|
7
|
+
return Array.isArray(value) ? value.map((v) => String(v)) : [];
|
|
8
|
+
}
|
|
9
|
+
function normalizeDomain(raw) {
|
|
10
|
+
const d = (raw ?? {});
|
|
11
|
+
return {
|
|
12
|
+
id: String(d.id ?? ""),
|
|
13
|
+
path: String(d.path ?? ""),
|
|
14
|
+
type: String(d.type ?? ""),
|
|
15
|
+
label: String(d.label ?? ""),
|
|
16
|
+
triggers: asStringArray(d.triggers),
|
|
17
|
+
files: asStringArray(d.files),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Parses domains.yml content. */
|
|
21
|
+
export function parseManifest(data) {
|
|
22
|
+
let raw;
|
|
23
|
+
try {
|
|
24
|
+
raw = yaml.load(data);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
throw new Error(`parse domains.yml: ${err.message}`);
|
|
28
|
+
}
|
|
29
|
+
const obj = (raw ?? {});
|
|
30
|
+
const domains = Array.isArray(obj.domains) ? obj.domains.map(normalizeDomain) : [];
|
|
31
|
+
return { domains };
|
|
32
|
+
}
|
|
33
|
+
/** Serializes the manifest, preserving a header comment. */
|
|
34
|
+
export function marshalManifest(m) {
|
|
35
|
+
const body = yaml.dump({ domains: m.domains }, { lineWidth: -1, flowLevel: 3 });
|
|
36
|
+
const header = "# Chapterhouse Domain Manifest\n" +
|
|
37
|
+
"# Single source of truth for all memory domains.\n" +
|
|
38
|
+
"# Add domains with the setup skill, or edit this file directly.\n\n";
|
|
39
|
+
return header + body;
|
|
40
|
+
}
|
|
41
|
+
/** Returns the domain with the given id, or undefined. */
|
|
42
|
+
export function findDomain(m, id) {
|
|
43
|
+
return m.domains.find((d) => d.id === id);
|
|
44
|
+
}
|
|
45
|
+
/** Returns cog's default file set for a domain type. */
|
|
46
|
+
export function defaultFilesForType(domainType) {
|
|
47
|
+
switch (domainType) {
|
|
48
|
+
case "personal":
|
|
49
|
+
return ["hot-memory", "action-items", "entities", "observations", "habits", "health", "calendar"];
|
|
50
|
+
case "work":
|
|
51
|
+
return ["hot-memory", "action-items", "entities", "projects", "dev-log", "observations"];
|
|
52
|
+
case "side-project":
|
|
53
|
+
return ["hot-memory", "action-items", "projects", "dev-log", "observations"];
|
|
54
|
+
default:
|
|
55
|
+
return ["hot-memory", "observations"];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=domains.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { defaultFilesForType, findDomain, marshalManifest, parseManifest, } from "./domains.js";
|
|
4
|
+
const sample = {
|
|
5
|
+
domains: [
|
|
6
|
+
{
|
|
7
|
+
id: "personal",
|
|
8
|
+
path: "personal",
|
|
9
|
+
type: "personal",
|
|
10
|
+
label: "Family, health, day-to-day",
|
|
11
|
+
triggers: ["family", "health"],
|
|
12
|
+
files: ["hot-memory", "observations"],
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "cog-meta",
|
|
16
|
+
path: "cog-meta",
|
|
17
|
+
type: "system",
|
|
18
|
+
label: "Memory self-knowledge",
|
|
19
|
+
triggers: ["meta"],
|
|
20
|
+
files: ["patterns"],
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
test("parseManifest / marshalManifest round-trip", () => {
|
|
25
|
+
const round = parseManifest(marshalManifest(sample));
|
|
26
|
+
assert.deepEqual(round, sample);
|
|
27
|
+
});
|
|
28
|
+
test("marshalManifest preserves the header comment", () => {
|
|
29
|
+
assert.ok(marshalManifest(sample).startsWith("# Chapterhouse Domain Manifest"));
|
|
30
|
+
});
|
|
31
|
+
test("parseManifest tolerates an empty document", () => {
|
|
32
|
+
assert.deepEqual(parseManifest(""), { domains: [] });
|
|
33
|
+
});
|
|
34
|
+
test("parseManifest throws on malformed yaml", () => {
|
|
35
|
+
assert.throws(() => parseManifest("domains: [unterminated"), /parse domains\.yml/);
|
|
36
|
+
});
|
|
37
|
+
test("findDomain locates by id", () => {
|
|
38
|
+
assert.equal(findDomain(sample, "cog-meta")?.path, "cog-meta");
|
|
39
|
+
assert.equal(findDomain(sample, "missing"), undefined);
|
|
40
|
+
});
|
|
41
|
+
test("defaultFilesForType returns per-type file sets", () => {
|
|
42
|
+
assert.deepEqual(defaultFilesForType("personal"), ["hot-memory", "action-items", "entities", "observations", "habits", "health", "calendar"]);
|
|
43
|
+
assert.deepEqual(defaultFilesForType("work"), ["hot-memory", "action-items", "entities", "projects", "dev-log", "observations"]);
|
|
44
|
+
assert.deepEqual(defaultFilesForType("side-project"), ["hot-memory", "action-items", "projects", "dev-log", "observations"]);
|
|
45
|
+
assert.deepEqual(defaultFilesForType("system"), ["hot-memory", "observations"]);
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=domains.test.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Git operations on the memory repository. The memory tree is a git repo and
|
|
2
|
+
// every write auto-commits, so the reflect/evolve/foresight skills can scope
|
|
3
|
+
// their work with diff/log. Ported from chgo's internal/memory/git.go.
|
|
4
|
+
import { execFile, execFileSync } from "child_process";
|
|
5
|
+
import { promisify } from "util";
|
|
6
|
+
const pExecFile = promisify(execFile);
|
|
7
|
+
/** Reports whether the git executable is on PATH. */
|
|
8
|
+
export function gitAvailable() {
|
|
9
|
+
try {
|
|
10
|
+
execFileSync("git", ["--version"], { stdio: "ignore" });
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Runs a git subcommand in dir and returns stdout. */
|
|
18
|
+
export async function runGit(dir, ...args) {
|
|
19
|
+
try {
|
|
20
|
+
const { stdout } = await pExecFile("git", args, {
|
|
21
|
+
cwd: dir,
|
|
22
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
23
|
+
});
|
|
24
|
+
return stdout;
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
const e = err;
|
|
28
|
+
const detail = (e.stderr || e.message || String(err)).trim();
|
|
29
|
+
throw new Error(`git ${args.join(" ")}: ${detail}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Initializes a repository in dir and sets a local identity so commits succeed
|
|
34
|
+
* even when the user has no global git identity configured.
|
|
35
|
+
*/
|
|
36
|
+
export async function gitInit(dir) {
|
|
37
|
+
await runGit(dir, "init", "-q");
|
|
38
|
+
await runGit(dir, "config", "user.name", "Chapterhouse");
|
|
39
|
+
await runGit(dir, "config", "user.email", "chapterhouse@localhost");
|
|
40
|
+
}
|
|
41
|
+
/** Stages everything and commits. A clean tree is a no-op. */
|
|
42
|
+
export async function gitCommitAll(dir, message) {
|
|
43
|
+
await runGit(dir, "add", "-A");
|
|
44
|
+
const status = await runGit(dir, "status", "--porcelain");
|
|
45
|
+
if (status.trim() === "")
|
|
46
|
+
return;
|
|
47
|
+
await runGit(dir, "commit", "-q", "-m", message);
|
|
48
|
+
}
|
|
49
|
+
/** Returns the short-format working tree status. */
|
|
50
|
+
export async function gitStatus(dir) {
|
|
51
|
+
return runGit(dir, "status", "--short");
|
|
52
|
+
}
|
|
53
|
+
/** Returns a diff. ref is optional (e.g. "HEAD~1"); paths is optional. */
|
|
54
|
+
export async function gitDiff(dir, ref, paths) {
|
|
55
|
+
const args = ["diff"];
|
|
56
|
+
if (ref)
|
|
57
|
+
args.push(ref);
|
|
58
|
+
if (paths && paths.length > 0)
|
|
59
|
+
args.push("--", ...paths);
|
|
60
|
+
return runGit(dir, ...args);
|
|
61
|
+
}
|
|
62
|
+
/** Returns the most recent n commits in one-line format. */
|
|
63
|
+
export async function gitLog(dir, n) {
|
|
64
|
+
return runGit(dir, "log", `-${n}`, "--oneline");
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import { gitAvailable, gitCommitAll, gitInit, gitLog } from "./git.js";
|
|
6
|
+
const sandbox = join(process.cwd(), ".test-work", `mem-git-${process.pid}`);
|
|
7
|
+
test.beforeEach(() => {
|
|
8
|
+
rmSync(sandbox, { recursive: true, force: true });
|
|
9
|
+
mkdirSync(sandbox, { recursive: true });
|
|
10
|
+
});
|
|
11
|
+
test.after(() => rmSync(sandbox, { recursive: true, force: true }));
|
|
12
|
+
test("gitInit creates a repo and gitCommitAll commits then no-ops", async () => {
|
|
13
|
+
if (!gitAvailable())
|
|
14
|
+
return;
|
|
15
|
+
await gitInit(sandbox);
|
|
16
|
+
writeFileSync(join(sandbox, "a.txt"), "first");
|
|
17
|
+
await gitCommitAll(sandbox, "first commit");
|
|
18
|
+
let log = await gitLog(sandbox, 10);
|
|
19
|
+
assert.ok(log.includes("first commit"));
|
|
20
|
+
const commitsAfterFirst = log.trim().split("\n").length;
|
|
21
|
+
// A clean tree is a no-op — no new commit.
|
|
22
|
+
await gitCommitAll(sandbox, "should not appear");
|
|
23
|
+
log = await gitLog(sandbox, 10);
|
|
24
|
+
assert.ok(!log.includes("should not appear"));
|
|
25
|
+
assert.equal(log.trim().split("\n").length, commitsAfterFirst);
|
|
26
|
+
// A real change produces a new commit.
|
|
27
|
+
writeFileSync(join(sandbox, "a.txt"), "second");
|
|
28
|
+
await gitCommitAll(sandbox, "second commit");
|
|
29
|
+
log = await gitLog(sandbox, 10);
|
|
30
|
+
assert.ok(log.includes("second commit"));
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=git.test.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Conversation history for the cog_sessions tool. The SessionSource interface
|
|
2
|
+
// is backed by the conversation_log table (see ConversationLogSessionSource).
|
|
3
|
+
// Ported from chgo's internal/memory/history.go.
|
|
4
|
+
import { getConversationSessions } from "../store/repositories/sessions.js";
|
|
5
|
+
/**
|
|
6
|
+
* The default SessionSource — reads grouped conversation turns from the
|
|
7
|
+
* conversation_log table.
|
|
8
|
+
*/
|
|
9
|
+
export class ConversationLogSessionSource {
|
|
10
|
+
async sessionHistory(since, limit) {
|
|
11
|
+
return getConversationSessions(since, limit).map((s) => ({
|
|
12
|
+
conversationId: s.runId ? `${s.sessionKey}@${s.runId}` : s.sessionKey,
|
|
13
|
+
agentSlug: s.turns[0]?.agentSlug ?? "chapterhouse",
|
|
14
|
+
startedAt: s.startedAt,
|
|
15
|
+
turns: s.turns.map((t) => ({ role: t.role, text: t.content, at: t.ts })),
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Composes the always-loaded memory block — the cross-domain hot-memory and the
|
|
2
|
+
// core patterns — for injection into each turn's prompt. Ported from chgo's
|
|
3
|
+
// internal/memory/hottier.go HotTier().
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
/**
|
|
7
|
+
* Returns the hot-tier block wrapped in `<chapterhouse-memory>` tags. Missing
|
|
8
|
+
* files are skipped; if both are absent it returns an empty string.
|
|
9
|
+
*/
|
|
10
|
+
export function hotTier(memoryRoot) {
|
|
11
|
+
const parts = [
|
|
12
|
+
{ title: "Hot Memory (always loaded)", rel: "hot-memory.md" },
|
|
13
|
+
{ title: "Core Patterns", rel: "cog-meta/patterns.md" },
|
|
14
|
+
];
|
|
15
|
+
let body = "";
|
|
16
|
+
for (const part of parts) {
|
|
17
|
+
let data;
|
|
18
|
+
try {
|
|
19
|
+
data = readFileSync(join(memoryRoot, part.rel), "utf8");
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (body.length > 0)
|
|
25
|
+
body += "\n\n";
|
|
26
|
+
body += `# ${part.title}\n${data}`;
|
|
27
|
+
}
|
|
28
|
+
if (body.length === 0)
|
|
29
|
+
return "";
|
|
30
|
+
return `<chapterhouse-memory>\n${body}\n</chapterhouse-memory>`;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=hottier.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import { hotTier } from "./hottier.js";
|
|
6
|
+
const sandbox = join(process.cwd(), ".test-work", `mem-hottier-${process.pid}`);
|
|
7
|
+
test.beforeEach(() => {
|
|
8
|
+
rmSync(sandbox, { recursive: true, force: true });
|
|
9
|
+
mkdirSync(sandbox, { recursive: true });
|
|
10
|
+
});
|
|
11
|
+
test.after(() => rmSync(sandbox, { recursive: true, force: true }));
|
|
12
|
+
test("hotTier returns empty string when both files are absent", () => {
|
|
13
|
+
assert.equal(hotTier(sandbox), "");
|
|
14
|
+
});
|
|
15
|
+
test("hotTier composes hot-memory and core patterns inside the tag", () => {
|
|
16
|
+
writeFileSync(join(sandbox, "hot-memory.md"), "<!-- L0: hot -->\ntop of mind");
|
|
17
|
+
mkdirSync(join(sandbox, "cog-meta"), { recursive: true });
|
|
18
|
+
writeFileSync(join(sandbox, "cog-meta", "patterns.md"), "<!-- L0: patterns -->\nrule one");
|
|
19
|
+
const block = hotTier(sandbox);
|
|
20
|
+
assert.ok(block.startsWith("<chapterhouse-memory>\n"));
|
|
21
|
+
assert.ok(block.endsWith("\n</chapterhouse-memory>"));
|
|
22
|
+
assert.ok(block.includes("# Hot Memory (always loaded)"));
|
|
23
|
+
assert.ok(block.includes("top of mind"));
|
|
24
|
+
assert.ok(block.includes("# Core Patterns"));
|
|
25
|
+
assert.ok(block.includes("rule one"));
|
|
26
|
+
});
|
|
27
|
+
test("hotTier skips a missing file but keeps the present one", () => {
|
|
28
|
+
writeFileSync(join(sandbox, "hot-memory.md"), "<!-- L0: hot -->\nonly hot");
|
|
29
|
+
const block = hotTier(sandbox);
|
|
30
|
+
assert.ok(block.includes("only hot"));
|
|
31
|
+
assert.ok(!block.includes("# Core Patterns"));
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=hottier.test.js.map
|
package/dist/memory/index.js
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export { reflectOnScope, reflectAllScopes } from "./reflect.js";
|
|
7
|
-
export { compactInboxPass, decayPass, dedupDecisionsPass, dedupObservationsPass, isHousekeepingInFlight, orphanCleanupPass, runHousekeeping, } from "./housekeeping.js";
|
|
8
|
-
export { getInboxItem, listPendingMemoryProposalsForTask, queueMemoryProposal, resolveInboxItem, resolveProposalScope } from "./inbox.js";
|
|
9
|
-
export { recordObservation, getObservation, listObservations, deleteObservation } from "./observations.js";
|
|
10
|
-
export { recall } from "./recall.js";
|
|
11
|
-
export { createScope, deactivateScope, getScope, listScopes, updateScope } from "./scopes.js";
|
|
12
|
-
export { demoteToCold, demoteToWarm, inferTierFromSignals, promoteToHot, tieringPass } from "./tiering.js";
|
|
13
|
-
export { handleGitCommitHook, handlePrMergeHook, hookDispatcher, MemoryHookDispatcher, } from "./hooks.js";
|
|
1
|
+
// Public surface of the file-based memory subsystem.
|
|
2
|
+
export { MemoryManager, getMemoryManager, resetMemoryManagerForTests } from "./manager.js";
|
|
3
|
+
export { createMemoryTools } from "./tools/index.js";
|
|
4
|
+
export { memorySystemInstructions } from "./instructions.js";
|
|
5
|
+
export { gitAvailable } from "./git.js";
|
|
14
6
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// The memory operating instructions, appended to every agent's system message.
|
|
2
|
+
// Ported from chgo's history.go SystemInstructions().
|
|
3
|
+
import { readAsset } from "./assets.js";
|
|
4
|
+
let cached;
|
|
5
|
+
/** Returns the memory operating instructions (cached; "" if the asset is missing). */
|
|
6
|
+
export function memorySystemInstructions() {
|
|
7
|
+
if (cached === undefined) {
|
|
8
|
+
try {
|
|
9
|
+
cached = readAsset("system-instructions.md");
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
cached = "";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return cached;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=instructions.js.map
|