chapterhouse 0.13.1 → 0.14.1
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 +13 -10
- 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 +92 -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/memory-assets/domain-skill.md +38 -0
- package/memory-assets/seed/cog-meta/improvements.md +8 -0
- package/memory-assets/seed/cog-meta/patterns.md +5 -0
- package/memory-assets/seed/cog-meta/reflect-cursor.md +4 -0
- package/memory-assets/seed/cog-meta/scenario-calibration.md +14 -0
- package/memory-assets/seed/cog-meta/self-observations.md +4 -0
- package/memory-assets/seed/domains.yml +19 -0
- package/memory-assets/seed/glacier/index.md +6 -0
- package/memory-assets/seed/hot-memory.md +5 -0
- package/memory-assets/seed/link-index.md +6 -0
- package/memory-assets/system-instructions.md +214 -0
- package/memory-assets/templates/action-items.md +8 -0
- package/memory-assets/templates/entities.md +4 -0
- package/memory-assets/templates/generic.md +2 -0
- package/memory-assets/templates/hot-memory.md +4 -0
- package/memory-assets/templates/observations.md +4 -0
- package/package.json +2 -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
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { getDb } from "../store/db.js";
|
|
2
|
-
function toActionItem(row) {
|
|
3
|
-
return {
|
|
4
|
-
id: row.id,
|
|
5
|
-
scopeId: row.scope_id,
|
|
6
|
-
entityId: row.entity_id ?? undefined,
|
|
7
|
-
title: row.title,
|
|
8
|
-
detail: row.detail ?? undefined,
|
|
9
|
-
status: row.status,
|
|
10
|
-
dueAt: row.due_at ?? undefined,
|
|
11
|
-
snoozeUntil: row.snooze_until ?? undefined,
|
|
12
|
-
source: row.source ?? undefined,
|
|
13
|
-
tier: row.tier,
|
|
14
|
-
tierPinnedAt: row.tier_pinned_at ?? undefined,
|
|
15
|
-
tierReason: row.tier_reason ?? undefined,
|
|
16
|
-
lastRecalledAt: row.last_recalled_at ?? undefined,
|
|
17
|
-
createdAt: row.created_at,
|
|
18
|
-
updatedAt: row.updated_at,
|
|
19
|
-
resolvedAt: row.resolved_at ?? undefined,
|
|
20
|
-
resolutionReason: row.resolution_reason ?? undefined,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
const ACTION_ITEM_COLUMNS = `
|
|
24
|
-
id, scope_id, entity_id, title, detail, status, due_at, snooze_until, source,
|
|
25
|
-
created_at, updated_at, resolved_at, resolution_reason, tier, tier_pinned_at,
|
|
26
|
-
tier_reason, last_recalled_at
|
|
27
|
-
`;
|
|
28
|
-
export function recordActionItem(input) {
|
|
29
|
-
const result = getDb().prepare(`
|
|
30
|
-
INSERT INTO mem_action_items (
|
|
31
|
-
scope_id, entity_id, title, detail, due_at, source, tier, created_at, updated_at
|
|
32
|
-
)
|
|
33
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
34
|
-
`).run(input.scope_id, input.entity_id ?? null, input.title, input.detail ?? null, input.due_at ?? null, input.source ?? null, input.tier ?? "warm");
|
|
35
|
-
return getActionItem(Number(result.lastInsertRowid));
|
|
36
|
-
}
|
|
37
|
-
export function getActionItem(id) {
|
|
38
|
-
const row = getDb().prepare(`
|
|
39
|
-
SELECT ${ACTION_ITEM_COLUMNS}
|
|
40
|
-
FROM mem_action_items
|
|
41
|
-
WHERE id = ?
|
|
42
|
-
`).get(id);
|
|
43
|
-
return row ? toActionItem(row) : undefined;
|
|
44
|
-
}
|
|
45
|
-
export function listActionItems(input = {}) {
|
|
46
|
-
const rows = getDb().prepare(`
|
|
47
|
-
SELECT ${ACTION_ITEM_COLUMNS}
|
|
48
|
-
FROM mem_action_items
|
|
49
|
-
WHERE (? IS NULL OR scope_id = ?)
|
|
50
|
-
AND (
|
|
51
|
-
? IS NOT NULL
|
|
52
|
-
OR status = 'open'
|
|
53
|
-
OR (status = 'snoozed' AND snooze_until IS NOT NULL AND datetime(snooze_until) <= datetime('now'))
|
|
54
|
-
)
|
|
55
|
-
AND (? IS NULL OR status = ?)
|
|
56
|
-
AND (? IS NULL OR due_at IS NOT NULL AND datetime(due_at) <= datetime(?))
|
|
57
|
-
AND (? = 1 OR tier != 'cold')
|
|
58
|
-
ORDER BY
|
|
59
|
-
CASE WHEN due_at IS NULL THEN 1 ELSE 0 END ASC,
|
|
60
|
-
datetime(due_at) ASC,
|
|
61
|
-
datetime(created_at) DESC,
|
|
62
|
-
id DESC
|
|
63
|
-
LIMIT ? OFFSET ?
|
|
64
|
-
`).all(input.scope_id ?? null, input.scope_id ?? null, input.status ?? null, input.status ?? null, input.status ?? null, input.due_before ?? null, input.due_before ?? null, input.includeArchived ? 1 : 0, input.limit ?? 50, input.offset ?? 0);
|
|
65
|
-
return rows.map(toActionItem);
|
|
66
|
-
}
|
|
67
|
-
function resolveActionItem(id, status, reason) {
|
|
68
|
-
const result = getDb().prepare(`
|
|
69
|
-
UPDATE mem_action_items
|
|
70
|
-
SET status = ?,
|
|
71
|
-
resolved_at = CURRENT_TIMESTAMP,
|
|
72
|
-
resolution_reason = ?,
|
|
73
|
-
updated_at = CURRENT_TIMESTAMP
|
|
74
|
-
WHERE id = ?
|
|
75
|
-
`).run(status, reason ?? null, id);
|
|
76
|
-
if (result.changes === 0) {
|
|
77
|
-
throw new Error(`Unknown action item id '${id}'.`);
|
|
78
|
-
}
|
|
79
|
-
return getActionItem(id);
|
|
80
|
-
}
|
|
81
|
-
export function completeActionItem(id, resolutionReason) {
|
|
82
|
-
return resolveActionItem(id, "done", resolutionReason);
|
|
83
|
-
}
|
|
84
|
-
export function dropActionItem(id, reason) {
|
|
85
|
-
return resolveActionItem(id, "dropped", reason);
|
|
86
|
-
}
|
|
87
|
-
export function snoozeActionItem(id, snoozeUntil) {
|
|
88
|
-
const result = getDb().prepare(`
|
|
89
|
-
UPDATE mem_action_items
|
|
90
|
-
SET status = 'snoozed',
|
|
91
|
-
snooze_until = ?,
|
|
92
|
-
updated_at = CURRENT_TIMESTAMP
|
|
93
|
-
WHERE id = ?
|
|
94
|
-
`).run(snoozeUntil, id);
|
|
95
|
-
if (result.changes === 0) {
|
|
96
|
-
throw new Error(`Unknown action item id '${id}'.`);
|
|
97
|
-
}
|
|
98
|
-
return getActionItem(id);
|
|
99
|
-
}
|
|
100
|
-
//# sourceMappingURL=action-items.js.map
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdirSync, rmSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import test from "node:test";
|
|
5
|
-
const repoRoot = process.cwd();
|
|
6
|
-
const sandboxRoot = join(repoRoot, ".test-work", `memory-action-items-${process.pid}`);
|
|
7
|
-
const chapterhouseHome = join(sandboxRoot, ".chapterhouse");
|
|
8
|
-
process.env.CHAPTERHOUSE_HOME = sandboxRoot;
|
|
9
|
-
function resetSandbox() {
|
|
10
|
-
mkdirSync(join(repoRoot, ".test-work"), { recursive: true });
|
|
11
|
-
rmSync(sandboxRoot, { recursive: true, force: true });
|
|
12
|
-
mkdirSync(chapterhouseHome, { recursive: true });
|
|
13
|
-
}
|
|
14
|
-
async function loadModules() {
|
|
15
|
-
const dbModule = await import(new URL("../store/db.js", import.meta.url).href);
|
|
16
|
-
const memoryModule = await import(new URL("./index.js", import.meta.url).href);
|
|
17
|
-
return { dbModule, memoryModule };
|
|
18
|
-
}
|
|
19
|
-
function getFunction(module, name) {
|
|
20
|
-
const value = module[name];
|
|
21
|
-
assert.equal(typeof value, "function", `expected ${name} to be exported`);
|
|
22
|
-
return value;
|
|
23
|
-
}
|
|
24
|
-
test.beforeEach(async () => {
|
|
25
|
-
process.env.CHAPTERHOUSE_HOME = sandboxRoot;
|
|
26
|
-
const dbModule = await import(new URL("../store/db.js", import.meta.url).href);
|
|
27
|
-
dbModule.closeDb();
|
|
28
|
-
resetSandbox();
|
|
29
|
-
});
|
|
30
|
-
test.after(async () => {
|
|
31
|
-
const dbModule = await import(new URL("../store/db.js", import.meta.url).href);
|
|
32
|
-
dbModule.closeDb();
|
|
33
|
-
rmSync(sandboxRoot, { recursive: true, force: true });
|
|
34
|
-
});
|
|
35
|
-
test("action items round-trip through add, list, and complete with done hidden by default", async () => {
|
|
36
|
-
const { dbModule, memoryModule } = await loadModules();
|
|
37
|
-
dbModule.getDb();
|
|
38
|
-
const getScope = getFunction(memoryModule, "getScope");
|
|
39
|
-
const recordActionItem = getFunction(memoryModule, "recordActionItem");
|
|
40
|
-
const listActionItems = getFunction(memoryModule, "listActionItems");
|
|
41
|
-
const completeActionItem = getFunction(memoryModule, "completeActionItem");
|
|
42
|
-
const chapterhouse = getScope("chapterhouse");
|
|
43
|
-
assert.ok(chapterhouse);
|
|
44
|
-
const added = recordActionItem({
|
|
45
|
-
scope_id: chapterhouse.id,
|
|
46
|
-
title: "Migrate feature ideas into memory",
|
|
47
|
-
detail: "Move feature-ideas.md into mem_action_items once the schema exists.",
|
|
48
|
-
due_at: "2026-05-15T12:00:00.000Z",
|
|
49
|
-
source: "test",
|
|
50
|
-
});
|
|
51
|
-
assert.equal(added.status, "open");
|
|
52
|
-
assert.equal(added.title, "Migrate feature ideas into memory");
|
|
53
|
-
assert.equal(added.detail, "Move feature-ideas.md into mem_action_items once the schema exists.");
|
|
54
|
-
assert.equal(added.dueAt, "2026-05-15T12:00:00.000Z");
|
|
55
|
-
assert.deepEqual(listActionItems({ scope_id: chapterhouse.id }).map((item) => item.id), [added.id]);
|
|
56
|
-
const completed = completeActionItem(added.id, "Migrated successfully.");
|
|
57
|
-
assert.equal(completed.status, "done");
|
|
58
|
-
assert.ok(completed.resolvedAt);
|
|
59
|
-
assert.deepEqual(listActionItems({ scope_id: chapterhouse.id }).map((item) => item.id), []);
|
|
60
|
-
assert.deepEqual(listActionItems({ scope_id: chapterhouse.id, status: "done" }).map((item) => item.id), [added.id]);
|
|
61
|
-
});
|
|
62
|
-
test("snoozed action items are hidden by default until snooze_until passes", async () => {
|
|
63
|
-
const { dbModule, memoryModule } = await loadModules();
|
|
64
|
-
dbModule.getDb();
|
|
65
|
-
const getScope = getFunction(memoryModule, "getScope");
|
|
66
|
-
const recordActionItem = getFunction(memoryModule, "recordActionItem");
|
|
67
|
-
const snoozeActionItem = getFunction(memoryModule, "snoozeActionItem");
|
|
68
|
-
const listActionItems = getFunction(memoryModule, "listActionItems");
|
|
69
|
-
const chapterhouse = getScope("chapterhouse");
|
|
70
|
-
assert.ok(chapterhouse);
|
|
71
|
-
const future = recordActionItem({ scope_id: chapterhouse.id, title: "Remind infra later", source: "test" });
|
|
72
|
-
const expired = recordActionItem({ scope_id: chapterhouse.id, title: "Reappears now", source: "test" });
|
|
73
|
-
assert.equal(snoozeActionItem(future.id, "2999-01-01T00:00:00.000Z").status, "snoozed");
|
|
74
|
-
assert.equal(snoozeActionItem(expired.id, "2000-01-01T00:00:00.000Z").status, "snoozed");
|
|
75
|
-
const defaults = listActionItems({ scope_id: chapterhouse.id });
|
|
76
|
-
assert.equal(defaults.some((item) => item.id === future.id), false);
|
|
77
|
-
assert.equal(defaults.some((item) => item.id === expired.id), true);
|
|
78
|
-
assert.deepEqual(listActionItems({ scope_id: chapterhouse.id, status: "snoozed" }).map((item) => item.id).sort(), [
|
|
79
|
-
expired.id,
|
|
80
|
-
future.id,
|
|
81
|
-
].sort());
|
|
82
|
-
});
|
|
83
|
-
//# sourceMappingURL=action-items.test.js.map
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import { getDb } from "../store/db.js";
|
|
3
|
-
import { getScope, listScopes } from "./scopes.js";
|
|
4
|
-
const ACTIVE_SCOPE_KEY = "current_scope_slug";
|
|
5
|
-
const activeScopeOverride = new AsyncLocalStorage();
|
|
6
|
-
function setSetting(key, value) {
|
|
7
|
-
getDb().prepare(`
|
|
8
|
-
INSERT INTO mem_settings (key, value)
|
|
9
|
-
VALUES (?, ?)
|
|
10
|
-
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
11
|
-
`).run(key, value);
|
|
12
|
-
}
|
|
13
|
-
function getSetting(key) {
|
|
14
|
-
const row = getDb().prepare(`
|
|
15
|
-
SELECT value
|
|
16
|
-
FROM mem_settings
|
|
17
|
-
WHERE key = ?
|
|
18
|
-
`).get(key);
|
|
19
|
-
return row?.value;
|
|
20
|
-
}
|
|
21
|
-
function clearSetting(key) {
|
|
22
|
-
getDb().prepare(`DELETE FROM mem_settings WHERE key = ?`).run(key);
|
|
23
|
-
}
|
|
24
|
-
export function getActiveScope() {
|
|
25
|
-
const override = activeScopeOverride.getStore();
|
|
26
|
-
if (override !== undefined) {
|
|
27
|
-
return override === null ? null : (getScope(override) ?? null);
|
|
28
|
-
}
|
|
29
|
-
const slug = getSetting(ACTIVE_SCOPE_KEY);
|
|
30
|
-
if (!slug)
|
|
31
|
-
return null;
|
|
32
|
-
const scope = getScope(slug);
|
|
33
|
-
if (!scope) {
|
|
34
|
-
clearSetting(ACTIVE_SCOPE_KEY);
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
return scope;
|
|
38
|
-
}
|
|
39
|
-
export function withActiveScope(slug, fn) {
|
|
40
|
-
return activeScopeOverride.run(slug, fn);
|
|
41
|
-
}
|
|
42
|
-
export function setActiveScope(slug) {
|
|
43
|
-
if (slug === null) {
|
|
44
|
-
clearSetting(ACTIVE_SCOPE_KEY);
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
const scope = getScope(slug);
|
|
48
|
-
if (!scope) {
|
|
49
|
-
throw new Error(`Unknown memory scope '${slug}'.`);
|
|
50
|
-
}
|
|
51
|
-
setSetting(ACTIVE_SCOPE_KEY, scope.slug);
|
|
52
|
-
return scope;
|
|
53
|
-
}
|
|
54
|
-
export function inferScopeFromText(text) {
|
|
55
|
-
const lowered = text.toLowerCase();
|
|
56
|
-
const ranked = listScopes()
|
|
57
|
-
.filter((scope) => scope.active)
|
|
58
|
-
.map((scope) => {
|
|
59
|
-
const matchedKeywords = scope.keywords.filter((keyword, index, keywords) => lowered.includes(keyword.toLowerCase()) && keywords.indexOf(keyword) === index);
|
|
60
|
-
return { scope, matchedKeywords };
|
|
61
|
-
})
|
|
62
|
-
.filter((entry) => entry.matchedKeywords.length > 0)
|
|
63
|
-
.sort((a, b) => {
|
|
64
|
-
if (b.matchedKeywords.length !== a.matchedKeywords.length) {
|
|
65
|
-
return b.matchedKeywords.length - a.matchedKeywords.length;
|
|
66
|
-
}
|
|
67
|
-
return a.scope.slug.localeCompare(b.scope.slug);
|
|
68
|
-
});
|
|
69
|
-
const winner = ranked[0];
|
|
70
|
-
if (!winner)
|
|
71
|
-
return null;
|
|
72
|
-
return {
|
|
73
|
-
scope_id: winner.scope.id,
|
|
74
|
-
confidence: winner.matchedKeywords.length / Math.max(winner.scope.keywords.length, 1),
|
|
75
|
-
matched_keywords: winner.matchedKeywords,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
//# sourceMappingURL=active-scope.js.map
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdirSync, rmSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import test from "node:test";
|
|
5
|
-
const repoRoot = process.cwd();
|
|
6
|
-
const sandboxRoot = join(repoRoot, ".test-work", `memory-active-scope-${process.pid}`);
|
|
7
|
-
const chapterhouseHome = join(sandboxRoot, ".chapterhouse");
|
|
8
|
-
process.env.CHAPTERHOUSE_HOME = sandboxRoot;
|
|
9
|
-
function resetSandbox() {
|
|
10
|
-
mkdirSync(join(repoRoot, ".test-work"), { recursive: true });
|
|
11
|
-
rmSync(sandboxRoot, { recursive: true, force: true });
|
|
12
|
-
mkdirSync(chapterhouseHome, { recursive: true });
|
|
13
|
-
}
|
|
14
|
-
async function loadModules() {
|
|
15
|
-
const dbModule = await import(new URL("../store/db.js", import.meta.url).href);
|
|
16
|
-
const memoryModule = await import(new URL("./index.js", import.meta.url).href);
|
|
17
|
-
return { dbModule, memoryModule };
|
|
18
|
-
}
|
|
19
|
-
function getFunction(module, name) {
|
|
20
|
-
const value = module[name];
|
|
21
|
-
assert.equal(typeof value, "function", `expected ${name} to be exported`);
|
|
22
|
-
return value;
|
|
23
|
-
}
|
|
24
|
-
test.beforeEach(async () => {
|
|
25
|
-
const dbModule = await import(new URL("../store/db.js", import.meta.url).href);
|
|
26
|
-
dbModule.closeDb();
|
|
27
|
-
resetSandbox();
|
|
28
|
-
});
|
|
29
|
-
test.after(async () => {
|
|
30
|
-
const dbModule = await import(new URL("../store/db.js", import.meta.url).href);
|
|
31
|
-
dbModule.closeDb();
|
|
32
|
-
rmSync(sandboxRoot, { recursive: true, force: true });
|
|
33
|
-
});
|
|
34
|
-
test("active scope can be set, read, and cleared without changing scope activation status", async () => {
|
|
35
|
-
const { dbModule, memoryModule } = await loadModules();
|
|
36
|
-
dbModule.getDb();
|
|
37
|
-
const getActiveScope = getFunction(memoryModule, "getActiveScope");
|
|
38
|
-
const setActiveScope = getFunction(memoryModule, "setActiveScope");
|
|
39
|
-
const deactivateScope = getFunction(memoryModule, "deactivateScope");
|
|
40
|
-
const createScope = getFunction(memoryModule, "createScope");
|
|
41
|
-
assert.equal(getActiveScope(), null);
|
|
42
|
-
assert.equal(setActiveScope("chapterhouse")?.slug, "chapterhouse");
|
|
43
|
-
assert.equal(getActiveScope()?.slug, "chapterhouse");
|
|
44
|
-
const team = createScope({
|
|
45
|
-
slug: "team",
|
|
46
|
-
title: "Team",
|
|
47
|
-
description: "Team test scope",
|
|
48
|
-
keywords: ["team"],
|
|
49
|
-
});
|
|
50
|
-
const deactivated = deactivateScope(team.id);
|
|
51
|
-
assert.equal(deactivated.active, false);
|
|
52
|
-
assert.equal(getActiveScope()?.slug, "chapterhouse");
|
|
53
|
-
assert.equal(setActiveScope(null), null);
|
|
54
|
-
assert.equal(getActiveScope(), null);
|
|
55
|
-
});
|
|
56
|
-
test("inferScopeFromText prefers the highest keyword match count and breaks ties deterministically", async () => {
|
|
57
|
-
const { dbModule, memoryModule } = await loadModules();
|
|
58
|
-
dbModule.getDb();
|
|
59
|
-
const createScope = getFunction(memoryModule, "createScope");
|
|
60
|
-
const inferScopeFromText = getFunction(memoryModule, "inferScopeFromText");
|
|
61
|
-
const alpha = createScope({
|
|
62
|
-
slug: "alpha-release",
|
|
63
|
-
title: "Alpha Release",
|
|
64
|
-
description: "Alpha release work",
|
|
65
|
-
keywords: ["release"],
|
|
66
|
-
});
|
|
67
|
-
const beta = createScope({
|
|
68
|
-
slug: "beta-deploy",
|
|
69
|
-
title: "Beta Deploy",
|
|
70
|
-
description: "Deployment work",
|
|
71
|
-
keywords: ["deploy", "release"],
|
|
72
|
-
});
|
|
73
|
-
const bestMatch = inferScopeFromText("Please deploy the release workflow today.");
|
|
74
|
-
assert.equal(bestMatch?.scope_id, beta.id);
|
|
75
|
-
assert.deepEqual(bestMatch?.matched_keywords.sort(), ["deploy", "release"]);
|
|
76
|
-
const tie = inferScopeFromText("This release needs triage.");
|
|
77
|
-
assert.equal(tie?.scope_id, alpha.id);
|
|
78
|
-
assert.deepEqual(tie?.matched_keywords, ["release"]);
|
|
79
|
-
});
|
|
80
|
-
//# sourceMappingURL=active-scope.test.js.map
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
function renderTurns(turns) {
|
|
2
|
-
return turns
|
|
3
|
-
.map((turn, index) => [
|
|
4
|
-
`Turn ${index + 1}`,
|
|
5
|
-
`User: ${turn.user}`,
|
|
6
|
-
`Assistant: ${turn.assistant}`,
|
|
7
|
-
].join("\n"))
|
|
8
|
-
.join("\n\n");
|
|
9
|
-
}
|
|
10
|
-
function renderEntities(entities) {
|
|
11
|
-
if (entities.length === 0) {
|
|
12
|
-
return "- none";
|
|
13
|
-
}
|
|
14
|
-
return entities
|
|
15
|
-
.map((entity) => `- ${entity.name} (${entity.kind}): ${entity.summary ?? entity.name}`)
|
|
16
|
-
.join("\n");
|
|
17
|
-
}
|
|
18
|
-
function renderDecisions(decisions) {
|
|
19
|
-
if (decisions.length === 0) {
|
|
20
|
-
return "- none";
|
|
21
|
-
}
|
|
22
|
-
return decisions
|
|
23
|
-
.map((decision) => `- ${decision.title}: ${decision.rationale}`)
|
|
24
|
-
.join("\n");
|
|
25
|
-
}
|
|
26
|
-
export function buildCheckpointSystemPrompt() {
|
|
27
|
-
return [
|
|
28
|
-
"You extract durable agent memory from the most recent orchestrator turns.",
|
|
29
|
-
"Only return valid JSON. No prose, no markdown, no code fences.",
|
|
30
|
-
"Remember durable items only:",
|
|
31
|
-
"- decisions (architecture, process, user preferences)",
|
|
32
|
-
"- durable facts learned about code, tools, people, or infrastructure",
|
|
33
|
-
"- reusable gotchas or lessons learned",
|
|
34
|
-
"- named entities only when they are durable and worth remembering as an observation",
|
|
35
|
-
"Skip greetings, ephemeral progress updates, temporary branch names, one-off commit wording, and off-topic chatter.",
|
|
36
|
-
"Prefer concrete entries over vague summaries.",
|
|
37
|
-
"Use kind='decision' only when a real decision was made; otherwise use kind='observation'.",
|
|
38
|
-
"If nothing is worth remembering, return {\"proposals\":[]}.",
|
|
39
|
-
"JSON schema:",
|
|
40
|
-
"{\"proposals\":[{\"kind\":\"observation|decision\",\"title\":\"required for decision\",\"content\":\"string\",\"scope\":\"optional scope slug; omit to use active scope\",\"confidence\":0.0}]}",
|
|
41
|
-
"Example good output:",
|
|
42
|
-
"{\"proposals\":[{\"kind\":\"decision\",\"title\":\"Use SQLite FTS5 for recall\",\"content\":\"Chapterhouse uses SQLite FTS5 for scoped memory recall in v1.\",\"confidence\":0.93}]}",
|
|
43
|
-
"{\"proposals\":[{\"kind\":\"observation\",\"content\":\"Only orchestrator turns count toward memory checkpoints.\",\"confidence\":0.88}]}",
|
|
44
|
-
].join("\n");
|
|
45
|
-
}
|
|
46
|
-
export function buildCheckpointUserPrompt(input) {
|
|
47
|
-
const scopeChangeBlock = input.scopeChangeContext
|
|
48
|
-
? [
|
|
49
|
-
`Scope-change context: the user is moving from scope ${input.scopeChangeContext.from} to ${input.scopeChangeContext.to}.`,
|
|
50
|
-
`Extract everything worth remembering about scope ${input.scopeChangeContext.from} from the recent turns BEFORE the context shifts.`,
|
|
51
|
-
"",
|
|
52
|
-
].join("\n")
|
|
53
|
-
: "";
|
|
54
|
-
return [
|
|
55
|
-
`Active scope: ${input.activeScope.slug} — ${input.activeScope.title}`,
|
|
56
|
-
`Scope description: ${input.activeScope.description}`,
|
|
57
|
-
"",
|
|
58
|
-
scopeChangeBlock,
|
|
59
|
-
"Known entities in the active scope:",
|
|
60
|
-
renderEntities(input.entities),
|
|
61
|
-
"",
|
|
62
|
-
"Known decisions in the active scope:",
|
|
63
|
-
renderDecisions(input.decisions),
|
|
64
|
-
"",
|
|
65
|
-
"Recent orchestrator turns:",
|
|
66
|
-
renderTurns(input.turns),
|
|
67
|
-
"",
|
|
68
|
-
"Return only the JSON object.",
|
|
69
|
-
].join("\n");
|
|
70
|
-
}
|
|
71
|
-
//# sourceMappingURL=checkpoint-prompt.js.map
|