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
package/dist/copilot/agents.js
CHANGED
|
@@ -12,6 +12,7 @@ import { loadMcpConfig } from "./mcp-config.js";
|
|
|
12
12
|
import { getCurrentDateSystemLine } from "./prompt-date.js";
|
|
13
13
|
import { getSkillDirectories } from "./skills.js";
|
|
14
14
|
import { EXCLUDED_BUILTIN_TOOLS } from "./builtin-tools.js";
|
|
15
|
+
import { memorySystemInstructions } from "../memory/index.js";
|
|
15
16
|
import { childLogger } from "../util/logger.js";
|
|
16
17
|
const log = childLogger("agents");
|
|
17
18
|
const toolAgentContext = new AsyncLocalStorage();
|
|
@@ -23,6 +24,7 @@ const agentFrontmatterSchema = z.object({
|
|
|
23
24
|
model: z.string().min(1),
|
|
24
25
|
skills: z.array(z.string()).optional(),
|
|
25
26
|
tools: z.array(z.string()).optional(),
|
|
27
|
+
management_tools: z.array(z.string()).optional(),
|
|
26
28
|
mcpServers: z.array(z.string()).optional(),
|
|
27
29
|
allowed_paths: z.array(z.string()).optional(),
|
|
28
30
|
persistent: z.union([z.boolean(), z.string()]).optional().transform((value) => {
|
|
@@ -83,6 +85,7 @@ export function parseAgentMdOrThrow(content, slug) {
|
|
|
83
85
|
scope: fm.scope,
|
|
84
86
|
skills: fm.skills,
|
|
85
87
|
tools: fm.tools,
|
|
88
|
+
managementTools: fm.management_tools,
|
|
86
89
|
mcpServers: fm.mcpServers,
|
|
87
90
|
allowedPaths: fm.allowed_paths,
|
|
88
91
|
systemMessage: body,
|
|
@@ -295,12 +298,12 @@ function getAgentBasePrompt() {
|
|
|
295
298
|
You are an agent within Chapterhouse, a team-level AI assistant for engineering teams. You run on the user's local machine.
|
|
296
299
|
|
|
297
300
|
### Agent Memory
|
|
298
|
-
Chapterhouse
|
|
301
|
+
Chapterhouse gives every agent a persistent, file-based memory tree under \`memory/\`, organized into domains. Read it with \`cog_read\`, \`cog_l0_scan\`, \`cog_l1_outline\`, \`cog_tree\`, and \`cog_search\`; write directly with \`cog_write\`, \`cog_edit\`, \`cog_append\`, and \`cog_move\` whenever you learn something worth remembering — don't wait for end of task. Every write auto-commits to git. The full memory operating instructions are included below.
|
|
299
302
|
|
|
300
303
|
### Shared Wiki
|
|
301
304
|
All agents share a wiki knowledge base for persistent memory. Use \`wiki_read\` and \`wiki_search\` to find existing knowledge, and \`wiki_update\` to save important findings.
|
|
302
305
|
|
|
303
|
-
Invoke \`wiki-conventions\` before wiki writes or restructuring work. Treat \`wiki_update\` and \`wiki_ingest_source\` as write-sensitive workflows
|
|
306
|
+
Invoke \`wiki-conventions\` before wiki writes or restructuring work. Treat \`wiki_update\` and \`wiki_ingest_source\` as write-sensitive workflows. Before using wiki write tools, read \`pages/index.md\`, scan the last 20-30 entries of \`pages/_meta/log.md\`, and run \`wiki_search\` for the topic when the wiki is large or the topic is ambiguous.
|
|
304
307
|
|
|
305
308
|
### Communication
|
|
306
309
|
- You receive tasks from @chapterhouse (the orchestrator) or directly from the user
|
|
@@ -319,11 +322,12 @@ export function composeAgentSystemMessage(agent, rosterInfo) {
|
|
|
319
322
|
const agentPrompt = agent.systemMessage;
|
|
320
323
|
const currentDateLine = getCurrentDateSystemLine();
|
|
321
324
|
const currentDateBlock = currentDateLine ? `${currentDateLine}\n\n` : "";
|
|
325
|
+
const memoryBlock = memorySystemInstructions() ? `\n\n${memorySystemInstructions()}` : "";
|
|
322
326
|
// For @chapterhouse, inject the agent roster
|
|
323
327
|
if (agent.slug === "chapterhouse" && rosterInfo) {
|
|
324
|
-
return `${currentDateBlock}${agentPrompt.replace("{agent_roster}", rosterInfo)}`;
|
|
328
|
+
return `${currentDateBlock}${agentPrompt.replace("{agent_roster}", rosterInfo)}${memoryBlock}`;
|
|
325
329
|
}
|
|
326
|
-
return `${currentDateBlock}${agentPrompt}\n\n${base}`;
|
|
330
|
+
return `${currentDateBlock}${agentPrompt}\n\n${base}${memoryBlock}`;
|
|
327
331
|
}
|
|
328
332
|
/** Build a roster description of all agents for @chapterhouse's system prompt. */
|
|
329
333
|
export function buildAgentRoster() {
|
|
@@ -339,10 +343,13 @@ export function buildAgentRoster() {
|
|
|
339
343
|
return "No agents registered.";
|
|
340
344
|
return chLines.join("\n");
|
|
341
345
|
}
|
|
342
|
-
//
|
|
343
|
-
|
|
346
|
+
// Tools every agent gets regardless of tool config: the shared wiki and the
|
|
347
|
+
// full file-based memory surface (all agents read and write memory directly).
|
|
348
|
+
const ALWAYS_AVAILABLE_TOOL_NAMES = new Set([
|
|
344
349
|
"wiki_search", "wiki_read", "wiki_update", "wiki_reindex", "wiki_append_timeline", "wiki_ingest_source",
|
|
345
|
-
"
|
|
350
|
+
"cog_read", "cog_l0_scan", "cog_l1_outline", "cog_tree", "cog_search", "cog_stats",
|
|
351
|
+
"cog_write", "cog_edit", "cog_append", "cog_move",
|
|
352
|
+
"cog_git", "cog_domains", "cog_domain_create", "cog_sessions",
|
|
346
353
|
]);
|
|
347
354
|
// Management tools that only @chapterhouse should have
|
|
348
355
|
const MANAGEMENT_TOOL_NAMES = new Set([
|
|
@@ -351,8 +358,6 @@ const MANAGEMENT_TOOL_NAMES = new Set([
|
|
|
351
358
|
"switch_model", "toggle_auto", "list_models",
|
|
352
359
|
"restart_chapterhouse", "list_skills", "learn_skill", "uninstall_skill",
|
|
353
360
|
"list_machine_sessions", "attach_machine_session",
|
|
354
|
-
"memory_remember", "memory_set_scope", "memory_housekeep", "memory_reflect", "memory_promote", "memory_demote",
|
|
355
|
-
"memory_add_action_item", "memory_complete_action_item", "memory_drop_action_item", "memory_snooze_action_item",
|
|
356
361
|
]);
|
|
357
362
|
export function getCurrentToolAgentSlug() {
|
|
358
363
|
return toolAgentContext.getStore();
|
|
@@ -373,14 +378,21 @@ export function bindToolsToAgent(agentSlug, allTools, taskId) {
|
|
|
373
378
|
export function filterToolsForAgent(agent, allTools) {
|
|
374
379
|
if (agent.tools && agent.tools.length > 0) {
|
|
375
380
|
// Agent specifies an explicit allowlist — give those + wiki tools
|
|
376
|
-
const allowed = new Set([...agent.tools, ...
|
|
377
|
-
return allTools.filter((t) => allowed.has(t.name)
|
|
381
|
+
const allowed = new Set([...agent.tools, ...ALWAYS_AVAILABLE_TOOL_NAMES]);
|
|
382
|
+
return allTools.filter((t) => allowed.has(t.name));
|
|
378
383
|
}
|
|
379
|
-
// Default: all tools except management (only @chapterhouse gets those)
|
|
384
|
+
// Default: all tools except management (only @chapterhouse gets those by default)
|
|
380
385
|
if (agent.slug === "chapterhouse") {
|
|
381
386
|
return allTools;
|
|
382
387
|
}
|
|
383
|
-
|
|
388
|
+
const baseTools = allTools.filter((t) => !MANAGEMENT_TOOL_NAMES.has(t.name));
|
|
389
|
+
// Agents can opt into specific management tools via management_tools: in their config
|
|
390
|
+
if (agent.managementTools && agent.managementTools.length > 0) {
|
|
391
|
+
const optedIn = new Set(agent.managementTools);
|
|
392
|
+
const extra = allTools.filter((t) => MANAGEMENT_TOOL_NAMES.has(t.name) && optedIn.has(t.name));
|
|
393
|
+
return [...baseTools, ...extra];
|
|
394
|
+
}
|
|
395
|
+
return baseTools;
|
|
384
396
|
}
|
|
385
397
|
/** Filter MCP servers based on agent config. */
|
|
386
398
|
export function filterMcpServersForAgent(agent, allMcpServers) {
|
|
@@ -50,18 +50,17 @@ test("composeAgentSystemMessage steers wiki-capable agents to wiki-conventions",
|
|
|
50
50
|
for (const slug of ["coder", "general-purpose"]) {
|
|
51
51
|
const message = composeAgentSystemMessage(makeAgent(slug));
|
|
52
52
|
assert.match(message, /invoke `wiki-conventions` before wiki writes/i);
|
|
53
|
-
assert.match(message, /wiki_update[\s\S]{0,120}wiki_ingest_source
|
|
53
|
+
assert.match(message, /wiki_update[\s\S]{0,120}wiki_ingest_source/i);
|
|
54
54
|
assert.doesNotMatch(message, /`remember`|`forget`|`wiki_ingest`(?!_source)|`wiki_lint`|`wiki_rebuild_index`/);
|
|
55
55
|
assert.match(message, /read `pages\/index\.md`/i);
|
|
56
56
|
assert.match(message, /scan the last 20-30 entries of `pages\/_meta\/log\.md`/i);
|
|
57
57
|
}
|
|
58
58
|
});
|
|
59
|
-
test("composeAgentSystemMessage teaches subagents the
|
|
59
|
+
test("composeAgentSystemMessage teaches subagents the file-based memory tools", () => {
|
|
60
60
|
const message = composeAgentSystemMessage(makeAgent("coder"));
|
|
61
|
-
assert.match(message, /
|
|
62
|
-
assert.match(message, /
|
|
63
|
-
assert.match(message, /
|
|
64
|
-
assert.match(message, /do not call `memory_remember` directly|should not call `memory_remember` directly/i);
|
|
61
|
+
assert.match(message, /cog_read/);
|
|
62
|
+
assert.match(message, /cog_write/);
|
|
63
|
+
assert.match(message, /write directly/i);
|
|
65
64
|
});
|
|
66
65
|
test("parseAgentMd detects persistent agent scope from charter frontmatter", () => {
|
|
67
66
|
const agent = parseAgentMd([
|
|
@@ -103,22 +102,22 @@ test("korg charter is persistent and standardizes on the Summary heading", () =>
|
|
|
103
102
|
assert.match(charter, /## Summary/);
|
|
104
103
|
assert.doesNotMatch(charter, /## Compiled Truth/);
|
|
105
104
|
});
|
|
106
|
-
test("persistent agents cannot receive
|
|
105
|
+
test("persistent agents cannot receive management tools", () => {
|
|
107
106
|
const agent = {
|
|
108
107
|
...makeAgent("bellonda"),
|
|
109
108
|
persistent: true,
|
|
110
109
|
scope: "infra",
|
|
111
110
|
};
|
|
112
111
|
const tools = [
|
|
113
|
-
{ name: "
|
|
114
|
-
{ name: "
|
|
115
|
-
{ name: "memory_set_scope" },
|
|
112
|
+
{ name: "cog_read" },
|
|
113
|
+
{ name: "cog_write" },
|
|
116
114
|
{ name: "delegate_to_agent" },
|
|
115
|
+
{ name: "hire_agent" },
|
|
117
116
|
{ name: "bash" },
|
|
118
117
|
];
|
|
119
118
|
const filtered = filterToolsForAgent(agent, tools);
|
|
120
119
|
const names = filtered.map((tool) => tool.name);
|
|
121
|
-
assert.deepEqual(names.sort(), ["bash", "
|
|
120
|
+
assert.deepEqual(names.sort(), ["bash", "cog_read", "cog_write"].sort());
|
|
122
121
|
});
|
|
123
122
|
test("bindToolsToAgent uses the per-turn task context when no fixed task id is provided", async () => {
|
|
124
123
|
const tools = bindToolsToAgent("bellonda", [{
|
|
@@ -1,234 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { childLogger } from "../util/logger.js";
|
|
9
|
-
const log = childLogger("memory-coordinator");
|
|
10
|
-
const MAX_CHECKPOINT_CHARS_PER_SIDE = 4_000;
|
|
1
|
+
import { getMemoryManager } from "../memory/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Wires the file-based memory subsystem into the agent runtime. Its only job is
|
|
4
|
+
* to inject the always-loaded hot-tier block into each turn via the
|
|
5
|
+
* onUserPromptSubmitted hook — all memory reads and writes are agent-driven via
|
|
6
|
+
* the cog_* tools.
|
|
7
|
+
*/
|
|
11
8
|
export class MemoryCoordinator {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
completedTaskIds = new Set();
|
|
16
|
-
getCopilotClient;
|
|
17
|
-
resolveScopeForSession;
|
|
18
|
-
config;
|
|
19
|
-
constructor(options) {
|
|
20
|
-
this.getCopilotClient = options.getCopilotClient;
|
|
21
|
-
this.resolveScopeForSession = options.resolveScopeForSession ?? (() => getActiveScope());
|
|
22
|
-
this.config = options.config ?? defaultConfig;
|
|
23
|
-
}
|
|
24
|
-
async onTurnComplete(sessionKey, prompt, response, source) {
|
|
25
|
-
const sourceType = this.normalizeSource(source);
|
|
26
|
-
if (sourceType === "background") {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
this.scheduleCheckpointExtraction(sessionKey, prompt, response);
|
|
30
|
-
this.scheduleHousekeeping(sessionKey);
|
|
31
|
-
}
|
|
32
|
-
async onScopeChange(sessionKey, prev, next) {
|
|
33
|
-
if (!prev) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
const previousScope = getScope(prev) ?? null;
|
|
37
|
-
if (!previousScope) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
if (!this.config.memoryCheckpointOnScopeChange) {
|
|
41
|
-
log.info({ sessionKey, scope: previousScope.slug }, "memory.checkpoint.scope_change_disabled");
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const tracker = this.getCheckpointTracker(sessionKey);
|
|
45
|
-
const turnsSinceLast = tracker.turnsSinceLastFire();
|
|
46
|
-
if (turnsSinceLast < this.config.memoryCheckpointMinTurnsForScopeFire) {
|
|
47
|
-
log.info({
|
|
48
|
-
sessionKey,
|
|
49
|
-
scope: previousScope.slug,
|
|
50
|
-
turns_since_last: turnsSinceLast,
|
|
51
|
-
min_required: this.config.memoryCheckpointMinTurnsForScopeFire,
|
|
52
|
-
}, "memory.checkpoint.scope_change_skip");
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
if (isCheckpointInFlight(sessionKey)) {
|
|
56
|
-
log.info({ sessionKey, trigger: "scope_change" }, "memory.checkpoint.in_flight_skip");
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const copilotClient = this.getCopilotClient();
|
|
60
|
-
if (!copilotClient) {
|
|
61
|
-
log.error({ sessionKey }, "memory.checkpoint.error");
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const turns = this.checkpointTurnsBySession.get(sessionKey) ?? [];
|
|
65
|
-
if (turns.length === 0) {
|
|
66
|
-
log.info({
|
|
67
|
-
sessionKey,
|
|
68
|
-
scope: previousScope.slug,
|
|
69
|
-
turns_since_last: turnsSinceLast,
|
|
70
|
-
min_required: this.config.memoryCheckpointMinTurnsForScopeFire,
|
|
71
|
-
}, "memory.checkpoint.scope_change_skip");
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
tracker.markScopeChangeFire();
|
|
75
|
-
const nextScope = next ? (getScope(next) ?? null) : null;
|
|
76
|
-
void runCheckpointExtraction({
|
|
77
|
-
sessionKey,
|
|
78
|
-
turns: turns.slice(-this.config.memoryCheckpointTurns),
|
|
79
|
-
activeScope: previousScope,
|
|
80
|
-
copilotClient,
|
|
81
|
-
trigger: "scope_change",
|
|
82
|
-
scopeChangeContext: {
|
|
83
|
-
from: previousScope.slug,
|
|
84
|
-
to: nextScope?.slug ?? "no active scope",
|
|
85
|
-
},
|
|
86
|
-
}).catch((error) => {
|
|
87
|
-
log.error({ err: error, sessionKey }, "memory.checkpoint.error");
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
async buildHotTierContext(sessionKey) {
|
|
91
|
-
if (!this.config.memoryInjectEnabled) {
|
|
92
|
-
return "";
|
|
93
|
-
}
|
|
94
|
-
const scope = this.resolveScopeForSession(sessionKey);
|
|
95
|
-
if (!scope) {
|
|
96
|
-
return "";
|
|
97
|
-
}
|
|
98
|
-
const activeScope = getActiveScope();
|
|
99
|
-
const hotTierXml = activeScope?.id === scope.id
|
|
100
|
-
? renderHotTierForActiveScope()
|
|
101
|
-
: renderHotTierXML(getHotTierEntries(scope.id));
|
|
102
|
-
return hotTierXml ? hotTierXml.trimEnd() : "";
|
|
103
|
-
}
|
|
104
|
-
buildPerTurnHooks(sessionKey) {
|
|
105
|
-
if (!this.config.memoryInjectEnabled) {
|
|
106
|
-
return undefined;
|
|
107
|
-
}
|
|
108
|
-
const hooks = {
|
|
9
|
+
/** Returns the per-turn hooks that inject the hot-tier memory block. */
|
|
10
|
+
buildPerTurnHooks() {
|
|
11
|
+
return {
|
|
109
12
|
onUserPromptSubmitted: async () => {
|
|
110
|
-
const
|
|
111
|
-
return
|
|
13
|
+
const block = getMemoryManager().hotTier();
|
|
14
|
+
return block ? { additionalContext: block } : undefined;
|
|
112
15
|
},
|
|
113
16
|
};
|
|
114
|
-
return hooks;
|
|
115
|
-
}
|
|
116
|
-
async onAgentTaskComplete(taskId, result) {
|
|
117
|
-
if (this.completedTaskIds.has(taskId)) {
|
|
118
|
-
log.info({ taskId }, "memory.eot.duplicate_skip");
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
this.completedTaskIds.add(taskId);
|
|
122
|
-
const copilotClient = this.getCopilotClient();
|
|
123
|
-
if (!copilotClient) {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
const finalResult = typeof result === "string" ? result : result == null ? "" : String(result);
|
|
127
|
-
await runEndOfTaskMemoryHook({
|
|
128
|
-
taskId,
|
|
129
|
-
finalResult,
|
|
130
|
-
copilotClient,
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
reset(sessionKey) {
|
|
134
|
-
this.getCheckpointTracker(sessionKey).reset();
|
|
135
|
-
this.checkpointTurnsBySession.delete(sessionKey);
|
|
136
|
-
this.housekeepingTurnsBySession.delete(sessionKey);
|
|
137
|
-
this.completedTaskIds.clear();
|
|
138
|
-
}
|
|
139
|
-
shutdown() {
|
|
140
|
-
this.checkpointTrackers.clear();
|
|
141
|
-
this.checkpointTurnsBySession.clear();
|
|
142
|
-
this.housekeepingTurnsBySession.clear();
|
|
143
|
-
this.completedTaskIds.clear();
|
|
144
|
-
}
|
|
145
|
-
normalizeSource(source) {
|
|
146
|
-
return source === "background" ? "background" : source === "sse-web" ? "sse-web" : "web";
|
|
147
|
-
}
|
|
148
|
-
truncateCheckpointText(value) {
|
|
149
|
-
const trimmed = value.trim();
|
|
150
|
-
if (trimmed.length <= MAX_CHECKPOINT_CHARS_PER_SIDE) {
|
|
151
|
-
return trimmed;
|
|
152
|
-
}
|
|
153
|
-
return `${trimmed.slice(0, MAX_CHECKPOINT_CHARS_PER_SIDE)}…`;
|
|
154
|
-
}
|
|
155
|
-
getCheckpointTracker(sessionKey) {
|
|
156
|
-
let tracker = this.checkpointTrackers.get(sessionKey);
|
|
157
|
-
if (!tracker) {
|
|
158
|
-
tracker = new CheckpointTracker();
|
|
159
|
-
this.checkpointTrackers.set(sessionKey, tracker);
|
|
160
|
-
}
|
|
161
|
-
return tracker;
|
|
162
|
-
}
|
|
163
|
-
appendCheckpointTurn(sessionKey, turn) {
|
|
164
|
-
const turns = this.checkpointTurnsBySession.get(sessionKey) ?? [];
|
|
165
|
-
turns.push(turn);
|
|
166
|
-
const overflow = turns.length - this.config.memoryCheckpointTurns;
|
|
167
|
-
if (overflow > 0) {
|
|
168
|
-
turns.splice(0, overflow);
|
|
169
|
-
}
|
|
170
|
-
this.checkpointTurnsBySession.set(sessionKey, turns);
|
|
171
|
-
return turns;
|
|
172
|
-
}
|
|
173
|
-
scheduleCheckpointExtraction(sessionKey, prompt, response) {
|
|
174
|
-
const tracker = this.getCheckpointTracker(sessionKey);
|
|
175
|
-
const turns = this.appendCheckpointTurn(sessionKey, {
|
|
176
|
-
user: this.truncateCheckpointText(prompt),
|
|
177
|
-
assistant: this.truncateCheckpointText(response),
|
|
178
|
-
});
|
|
179
|
-
if (!this.config.memoryCheckpointEnabled) {
|
|
180
|
-
log.info({ sessionKey }, "memory.checkpoint.disabled");
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
tracker.tickOrchestratorTurn();
|
|
184
|
-
if (!tracker.shouldFire()) {
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
tracker.markFired();
|
|
188
|
-
if (isCheckpointInFlight(sessionKey)) {
|
|
189
|
-
log.info({ sessionKey }, "memory.checkpoint.in_flight_skip");
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
const copilotClient = this.getCopilotClient();
|
|
193
|
-
if (!copilotClient) {
|
|
194
|
-
log.error({ sessionKey }, "memory.checkpoint.error");
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
const activeScope = this.resolveScopeForSession(sessionKey);
|
|
198
|
-
void runCheckpointExtraction({
|
|
199
|
-
sessionKey,
|
|
200
|
-
turns: turns.slice(-this.config.memoryCheckpointTurns),
|
|
201
|
-
activeScope,
|
|
202
|
-
copilotClient,
|
|
203
|
-
trigger: "cadence",
|
|
204
|
-
}).catch((error) => {
|
|
205
|
-
log.error({ err: error, sessionKey }, "memory.checkpoint.error");
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
scheduleHousekeeping(sessionKey) {
|
|
209
|
-
if (!this.config.memoryHousekeepingEnabled) {
|
|
210
|
-
log.info({ sessionKey }, "memory.housekeeping.disabled");
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
const turns = (this.housekeepingTurnsBySession.get(sessionKey) ?? 0) + 1;
|
|
214
|
-
if (turns < this.config.memoryHousekeepingTurns) {
|
|
215
|
-
this.housekeepingTurnsBySession.set(sessionKey, turns);
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
this.housekeepingTurnsBySession.set(sessionKey, 0);
|
|
219
|
-
const activeScope = this.resolveScopeForSession(sessionKey);
|
|
220
|
-
if (!activeScope) {
|
|
221
|
-
log.info({ sessionKey }, "memory.housekeeping.no_active_scope");
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
const scopeIds = [activeScope.id];
|
|
225
|
-
if (isHousekeepingInFlight(scopeIds)) {
|
|
226
|
-
log.info({ sessionKey, scope_ids: scopeIds }, "memory.housekeeping.in_flight_skip");
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
void runHousekeeping({ scopeIds }).catch((error) => {
|
|
230
|
-
log.error({ err: error, sessionKey, scope_ids: scopeIds }, "memory.housekeeping.error");
|
|
231
|
-
});
|
|
232
17
|
}
|
|
233
18
|
}
|
|
234
19
|
//# sourceMappingURL=memory-coordinator.js.map
|