chapterhouse 0.3.26 → 0.4.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/server.js +12 -0
- package/dist/api/server.test.js +39 -0
- package/dist/config.js +70 -0
- package/dist/config.test.js +109 -0
- package/dist/copilot/agents.js +32 -6
- package/dist/copilot/agents.test.js +41 -0
- package/dist/copilot/oneshot.js +54 -0
- package/dist/copilot/orchestrator.js +224 -3
- package/dist/copilot/orchestrator.test.js +380 -0
- package/dist/copilot/prompt-date.js +8 -0
- package/dist/copilot/system-message.js +8 -0
- package/dist/copilot/system-message.test.js +58 -0
- package/dist/copilot/tools.agent.test.js +24 -0
- package/dist/copilot/tools.js +351 -4
- package/dist/copilot/tools.memory.test.js +297 -0
- package/dist/copilot/turn-event-log-env.test.js +19 -0
- package/dist/copilot/turn-event-log.js +22 -23
- package/dist/copilot/turn-event-log.test.js +61 -2
- package/dist/memory/active-scope.js +69 -0
- package/dist/memory/active-scope.test.js +76 -0
- package/dist/memory/checkpoint-prompt.js +71 -0
- package/dist/memory/checkpoint.js +257 -0
- package/dist/memory/checkpoint.test.js +255 -0
- package/dist/memory/decisions.js +53 -0
- package/dist/memory/decisions.test.js +92 -0
- package/dist/memory/entities.js +59 -0
- package/dist/memory/entities.test.js +65 -0
- package/dist/memory/eot.js +219 -0
- package/dist/memory/eot.test.js +263 -0
- package/dist/memory/hot-tier.js +187 -0
- package/dist/memory/hot-tier.test.js +197 -0
- package/dist/memory/housekeeping.js +352 -0
- package/dist/memory/housekeeping.test.js +280 -0
- package/dist/memory/inbox.js +73 -0
- package/dist/memory/index.js +11 -0
- package/dist/memory/observations.js +46 -0
- package/dist/memory/observations.test.js +86 -0
- package/dist/memory/recall.js +210 -0
- package/dist/memory/recall.test.js +238 -0
- package/dist/memory/scopes.js +89 -0
- package/dist/memory/scopes.test.js +201 -0
- package/dist/memory/tiering.js +193 -0
- package/dist/memory/types.js +2 -0
- package/dist/paths.js +7 -1
- package/dist/store/db.js +412 -8
- package/dist/store/db.test.js +83 -0
- package/dist/test/setup-env.js +16 -0
- package/dist/test/setup-env.test.js +4 -0
- package/package.json +1 -1
- package/web/dist/assets/{index-BRPJa1DK.js → index-DmYLALt0.js} +70 -70
- package/web/dist/assets/index-DmYLALt0.js.map +1 -0
- package/web/dist/index.html +1 -1
- package/web/dist/assets/index-BRPJa1DK.js.map +0 -1
|
@@ -3,6 +3,11 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { approveAll } from "@github/copilot-sdk";
|
|
4
4
|
import { createTools } from "./tools.js";
|
|
5
5
|
import { getOrchestratorSystemMessage } from "./system-message.js";
|
|
6
|
+
import { renderHotTierForActiveScope } from "../memory/hot-tier.js";
|
|
7
|
+
import { getActiveScope } from "../memory/active-scope.js";
|
|
8
|
+
import { CheckpointTracker, isCheckpointInFlight, runCheckpointExtraction } from "../memory/checkpoint.js";
|
|
9
|
+
import { isHousekeepingInFlight, runHousekeeping } from "../memory/housekeeping.js";
|
|
10
|
+
import { runEndOfTaskMemoryHook } from "../memory/eot.js";
|
|
6
11
|
import { CHAPTERHOUSE_VERSION } from "../version.js";
|
|
7
12
|
import { config, DEFAULT_MODEL } from "../config.js";
|
|
8
13
|
import { loadMcpConfig } from "./mcp-config.js";
|
|
@@ -14,6 +19,7 @@ import { getWikiSummary } from "../wiki/context.js";
|
|
|
14
19
|
import { SESSIONS_DIR } from "../paths.js";
|
|
15
20
|
import { resolveModel } from "./router.js";
|
|
16
21
|
import { loadAgents, ensureDefaultAgents, clearActiveTasks, getAgentRegistry, setActiveAgent, parseAtMention, buildAgentRoster, getActiveTasks, } from "./agents.js";
|
|
22
|
+
import * as agentsModule from "./agents.js";
|
|
17
23
|
import { childLogger } from "../util/logger.js";
|
|
18
24
|
import { agentEventBus } from "./agent-event-bus.js";
|
|
19
25
|
import { initTaskEventLog } from "./task-event-log.js";
|
|
@@ -67,9 +73,163 @@ let currentUserContext;
|
|
|
67
73
|
let currentAuthenticatedUser;
|
|
68
74
|
let currentAuthorizationHeader;
|
|
69
75
|
let lastRouteResult;
|
|
76
|
+
const checkpointTrackers = new Map();
|
|
77
|
+
const checkpointTurnsBySession = new Map();
|
|
78
|
+
const housekeepingTurnsBySession = new Map();
|
|
79
|
+
const MAX_CHECKPOINT_CHARS_PER_SIDE = 4_000;
|
|
70
80
|
export function getLastRouteResult() {
|
|
71
81
|
return lastRouteResult;
|
|
72
82
|
}
|
|
83
|
+
function truncateCheckpointText(value) {
|
|
84
|
+
const trimmed = value.trim();
|
|
85
|
+
if (trimmed.length <= MAX_CHECKPOINT_CHARS_PER_SIDE) {
|
|
86
|
+
return trimmed;
|
|
87
|
+
}
|
|
88
|
+
return `${trimmed.slice(0, MAX_CHECKPOINT_CHARS_PER_SIDE)}…`;
|
|
89
|
+
}
|
|
90
|
+
function getCheckpointTracker(sessionKey) {
|
|
91
|
+
let tracker = checkpointTrackers.get(sessionKey);
|
|
92
|
+
if (!tracker) {
|
|
93
|
+
tracker = new CheckpointTracker();
|
|
94
|
+
checkpointTrackers.set(sessionKey, tracker);
|
|
95
|
+
}
|
|
96
|
+
return tracker;
|
|
97
|
+
}
|
|
98
|
+
export function resetCheckpointSessionState(sessionKey) {
|
|
99
|
+
getCheckpointTracker(sessionKey).reset();
|
|
100
|
+
checkpointTurnsBySession.delete(sessionKey);
|
|
101
|
+
housekeepingTurnsBySession.delete(sessionKey);
|
|
102
|
+
}
|
|
103
|
+
function appendCheckpointTurn(sessionKey, turn) {
|
|
104
|
+
const turns = checkpointTurnsBySession.get(sessionKey) ?? [];
|
|
105
|
+
turns.push(turn);
|
|
106
|
+
const overflow = turns.length - config.memoryCheckpointTurns;
|
|
107
|
+
if (overflow > 0) {
|
|
108
|
+
turns.splice(0, overflow);
|
|
109
|
+
}
|
|
110
|
+
checkpointTurnsBySession.set(sessionKey, turns);
|
|
111
|
+
return turns;
|
|
112
|
+
}
|
|
113
|
+
function scheduleCheckpointExtraction(sessionKey, prompt, finalContent, source) {
|
|
114
|
+
if (source.type === "background") {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const tracker = getCheckpointTracker(sessionKey);
|
|
118
|
+
const turns = appendCheckpointTurn(sessionKey, {
|
|
119
|
+
user: truncateCheckpointText(prompt),
|
|
120
|
+
assistant: truncateCheckpointText(finalContent),
|
|
121
|
+
});
|
|
122
|
+
if (!config.memoryCheckpointEnabled) {
|
|
123
|
+
log.info({ sessionKey }, "memory.checkpoint.disabled");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
tracker.tickOrchestratorTurn();
|
|
127
|
+
if (!tracker.shouldFire()) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
tracker.markFired();
|
|
131
|
+
if (isCheckpointInFlight(sessionKey)) {
|
|
132
|
+
log.info({ sessionKey }, "memory.checkpoint.in_flight_skip");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (!copilotClient) {
|
|
136
|
+
log.error({ sessionKey }, "memory.checkpoint.error");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const activeScope = getActiveScope();
|
|
140
|
+
void runCheckpointExtraction({
|
|
141
|
+
sessionKey,
|
|
142
|
+
turns: turns.slice(-config.memoryCheckpointTurns),
|
|
143
|
+
activeScope,
|
|
144
|
+
copilotClient,
|
|
145
|
+
trigger: "cadence",
|
|
146
|
+
}).catch((error) => {
|
|
147
|
+
log.error({ err: error, sessionKey }, "memory.checkpoint.error");
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function scheduleHousekeeping(sessionKey, source) {
|
|
151
|
+
if (source.type === "background") {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (!config.memoryHousekeepingEnabled) {
|
|
155
|
+
log.info({ sessionKey }, "memory.housekeeping.disabled");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const turns = (housekeepingTurnsBySession.get(sessionKey) ?? 0) + 1;
|
|
159
|
+
if (turns < config.memoryHousekeepingTurns) {
|
|
160
|
+
housekeepingTurnsBySession.set(sessionKey, turns);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
housekeepingTurnsBySession.set(sessionKey, 0);
|
|
164
|
+
const activeScope = getActiveScope();
|
|
165
|
+
if (!activeScope) {
|
|
166
|
+
log.info({ sessionKey }, "memory.housekeeping.no_active_scope");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const scopeIds = [activeScope.id];
|
|
170
|
+
if (isHousekeepingInFlight(scopeIds)) {
|
|
171
|
+
log.info({ sessionKey, scope_ids: scopeIds }, "memory.housekeeping.in_flight_skip");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
void runHousekeeping({ scopeIds });
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
log.error({ err: error, sessionKey, scope_ids: scopeIds }, "memory.housekeeping.error");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
export function maybeScheduleScopeChangeCheckpoint(sessionKey, previousScope, nextScope) {
|
|
182
|
+
if (!previousScope) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (!config.memoryCheckpointOnScopeChange) {
|
|
186
|
+
log.info({ sessionKey, scope: previousScope.slug }, "memory.checkpoint.scope_change_disabled");
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const tracker = getCheckpointTracker(sessionKey);
|
|
190
|
+
const turnsSinceLast = tracker.turnsSinceLastFire();
|
|
191
|
+
if (turnsSinceLast < config.memoryCheckpointMinTurnsForScopeFire) {
|
|
192
|
+
log.info({
|
|
193
|
+
sessionKey,
|
|
194
|
+
scope: previousScope.slug,
|
|
195
|
+
turns_since_last: turnsSinceLast,
|
|
196
|
+
min_required: config.memoryCheckpointMinTurnsForScopeFire,
|
|
197
|
+
}, "memory.checkpoint.scope_change_skip");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (isCheckpointInFlight(sessionKey)) {
|
|
201
|
+
log.info({ sessionKey, trigger: "scope_change" }, "memory.checkpoint.in_flight_skip");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (!copilotClient) {
|
|
205
|
+
log.error({ sessionKey }, "memory.checkpoint.error");
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const turns = checkpointTurnsBySession.get(sessionKey) ?? [];
|
|
209
|
+
if (turns.length === 0) {
|
|
210
|
+
log.info({
|
|
211
|
+
sessionKey,
|
|
212
|
+
scope: previousScope.slug,
|
|
213
|
+
turns_since_last: turnsSinceLast,
|
|
214
|
+
min_required: config.memoryCheckpointMinTurnsForScopeFire,
|
|
215
|
+
}, "memory.checkpoint.scope_change_skip");
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
tracker.markScopeChangeFire();
|
|
219
|
+
void runCheckpointExtraction({
|
|
220
|
+
sessionKey,
|
|
221
|
+
turns: turns.slice(-config.memoryCheckpointTurns),
|
|
222
|
+
activeScope: previousScope,
|
|
223
|
+
copilotClient,
|
|
224
|
+
trigger: "scope_change",
|
|
225
|
+
scopeChangeContext: {
|
|
226
|
+
from: previousScope.slug,
|
|
227
|
+
to: nextScope?.slug ?? "no active scope",
|
|
228
|
+
},
|
|
229
|
+
}).catch((error) => {
|
|
230
|
+
log.error({ err: error, sessionKey }, "memory.checkpoint.error");
|
|
231
|
+
});
|
|
232
|
+
}
|
|
73
233
|
export function subscribeTaskEvents(taskId, listener) {
|
|
74
234
|
return agentEventBus.subscribe("session:tool_call", (event) => {
|
|
75
235
|
if (event.sessionId !== taskId)
|
|
@@ -152,18 +312,30 @@ export function getCurrentAuthorizationHeader() {
|
|
|
152
312
|
// Internal helpers
|
|
153
313
|
// ---------------------------------------------------------------------------
|
|
154
314
|
function getSessionConfig() {
|
|
155
|
-
const
|
|
315
|
+
const baseTools = createTools({
|
|
156
316
|
client: copilotClient,
|
|
157
317
|
onAgentTaskComplete: feedAgentResult,
|
|
158
318
|
});
|
|
319
|
+
const tools = agentsModule.bindToolsToAgent?.("chapterhouse", baseTools) ?? baseTools;
|
|
159
320
|
const mcpServers = loadMcpConfig();
|
|
160
321
|
const skillDirectories = getSkillDirectories();
|
|
161
322
|
return { tools, mcpServers, skillDirectories };
|
|
162
323
|
}
|
|
324
|
+
function buildHotTierContext() {
|
|
325
|
+
if (!config.memoryInjectEnabled) {
|
|
326
|
+
return undefined;
|
|
327
|
+
}
|
|
328
|
+
const hotTierXml = renderHotTierForActiveScope();
|
|
329
|
+
if (!hotTierXml) {
|
|
330
|
+
return undefined;
|
|
331
|
+
}
|
|
332
|
+
return hotTierXml.trimEnd();
|
|
333
|
+
}
|
|
163
334
|
function getSystemMessageOptions(memorySummary) {
|
|
164
335
|
return {
|
|
165
336
|
selfEditEnabled: config.selfEditEnabled,
|
|
166
337
|
memorySummary: memorySummary || undefined,
|
|
338
|
+
hotTierXml: buildHotTierContext(),
|
|
167
339
|
agentRoster: buildAgentRoster(),
|
|
168
340
|
userContext: currentUserContext,
|
|
169
341
|
};
|
|
@@ -183,6 +355,9 @@ function updateUserContext(source) {
|
|
|
183
355
|
// Invalidate the default session so it's recreated with the updated system message
|
|
184
356
|
registry?.get("default")?.invalidateSession();
|
|
185
357
|
}
|
|
358
|
+
export function invalidateOrchestratorSession(sessionKey) {
|
|
359
|
+
registry?.get(sessionKey)?.invalidateSession();
|
|
360
|
+
}
|
|
186
361
|
function updateRequestContext(source) {
|
|
187
362
|
if (source.type !== "web" && source.type !== "sse-web") {
|
|
188
363
|
currentAuthenticatedUser = undefined;
|
|
@@ -196,6 +371,15 @@ function updateRequestContext(source) {
|
|
|
196
371
|
}
|
|
197
372
|
}
|
|
198
373
|
export function feedAgentResult(taskId, agentSlug, result) {
|
|
374
|
+
if (copilotClient) {
|
|
375
|
+
void runEndOfTaskMemoryHook({
|
|
376
|
+
taskId,
|
|
377
|
+
finalResult: result,
|
|
378
|
+
copilotClient,
|
|
379
|
+
}).catch((error) => {
|
|
380
|
+
log.error({ err: error, taskId }, "memory.eot.error");
|
|
381
|
+
});
|
|
382
|
+
}
|
|
199
383
|
const prompt = `[Agent task completed] @${agentSlug} finished task ${taskId}:\n\n${result}`;
|
|
200
384
|
const sessionKey = getTaskSessionKey(taskId);
|
|
201
385
|
sendToOrchestrator(prompt, { type: "background", sessionKey }, (text, done) => {
|
|
@@ -276,6 +460,7 @@ async function createOrResumeSession(sessionKey, projectRoot) {
|
|
|
276
460
|
infiniteSessions,
|
|
277
461
|
});
|
|
278
462
|
log.info({ sessionKey }, "Session resumed successfully");
|
|
463
|
+
resetCheckpointSessionState(sessionKey);
|
|
279
464
|
upsertCopilotSession(sessionKey, isProjectSession ? "project" : "default", session.sessionId, projectRoot, config.copilotModel);
|
|
280
465
|
const mgr = registry?.get(sessionKey);
|
|
281
466
|
if (mgr)
|
|
@@ -301,6 +486,7 @@ async function createOrResumeSession(sessionKey, projectRoot) {
|
|
|
301
486
|
infiniteSessions,
|
|
302
487
|
});
|
|
303
488
|
log.info({ sessionKey, sessionId: session.sessionId.slice(0, 8) }, "Session created");
|
|
489
|
+
resetCheckpointSessionState(sessionKey);
|
|
304
490
|
upsertCopilotSession(sessionKey, isProjectSession ? "project" : "default", session.sessionId, projectRoot, config.copilotModel);
|
|
305
491
|
if (sessionKey === "default")
|
|
306
492
|
setState(ORCHESTRATOR_SESSION_KEY, session.sessionId);
|
|
@@ -397,9 +583,17 @@ async function executeOnSession(manager, item) {
|
|
|
397
583
|
// Correlates the SDK's subagent.started event (which only carries agent_type fields) with the
|
|
398
584
|
// actual spawn parameters (name, description) passed to the task() tool call.
|
|
399
585
|
const spawnArgsMap = new Map();
|
|
586
|
+
const toolStartDetails = new Map();
|
|
400
587
|
// Unconditional capture — must fire even when onActivity is absent so the DB handler can resolve names.
|
|
401
588
|
const unsubSpawnCapture = session.on("tool.execution_start", (event) => {
|
|
402
589
|
const data = event.data;
|
|
590
|
+
if (data.toolCallId) {
|
|
591
|
+
toolStartDetails.set(data.toolCallId, {
|
|
592
|
+
toolName: String(data.toolName ?? "unknown"),
|
|
593
|
+
mcpServerName: typeof data.mcpServerName === "string" ? data.mcpServerName : undefined,
|
|
594
|
+
arguments: data.arguments,
|
|
595
|
+
});
|
|
596
|
+
}
|
|
403
597
|
if (data.toolName === "task" && data.toolCallId) {
|
|
404
598
|
const args = (data.arguments ?? {});
|
|
405
599
|
spawnArgsMap.set(data.toolCallId, {
|
|
@@ -424,6 +618,9 @@ async function executeOnSession(manager, item) {
|
|
|
424
618
|
: typeof result?.content === "string"
|
|
425
619
|
? result.content
|
|
426
620
|
: undefined;
|
|
621
|
+
const toolCallId = String(data.toolCallId ?? "");
|
|
622
|
+
const startDetails = toolStartDetails.get(toolCallId);
|
|
623
|
+
const completionToolName = data.toolName;
|
|
427
624
|
if (item.onActivity) {
|
|
428
625
|
item.onActivity({
|
|
429
626
|
kind: "tool_complete",
|
|
@@ -436,13 +633,20 @@ async function executeOnSession(manager, item) {
|
|
|
436
633
|
// Emit turn:delta with tool-call part (coexistence — #130)
|
|
437
634
|
const toolPart = {
|
|
438
635
|
type: "tool-call",
|
|
439
|
-
toolCallId
|
|
440
|
-
toolName:
|
|
636
|
+
toolCallId,
|
|
637
|
+
toolName: typeof completionToolName === "string" && completionToolName.length > 0
|
|
638
|
+
? completionToolName
|
|
639
|
+
: (startDetails?.toolName ?? "unknown"),
|
|
640
|
+
mcpServerName: startDetails?.mcpServerName,
|
|
641
|
+
arguments: startDetails?.arguments,
|
|
441
642
|
status: data.success !== false ? "done" : "failed",
|
|
442
643
|
resultPreview,
|
|
443
644
|
detailedContent,
|
|
444
645
|
};
|
|
445
646
|
emitTurnEvent(sessionKey, { type: "turn:delta", turnId: item.turnId, sessionKey, part: toolPart });
|
|
647
|
+
if (toolCallId) {
|
|
648
|
+
toolStartDetails.delete(toolCallId);
|
|
649
|
+
}
|
|
446
650
|
});
|
|
447
651
|
const unsubToolStart = item.onActivity
|
|
448
652
|
? session.on("tool.execution_start", (event) => {
|
|
@@ -586,6 +790,15 @@ async function executeOnSession(manager, item) {
|
|
|
586
790
|
spawnArgsMap.delete(taskId);
|
|
587
791
|
activeSubagentTaskIds.delete(taskId);
|
|
588
792
|
db.prepare(`UPDATE agent_tasks SET status = 'completed', result = ?, completed_at = CURRENT_TIMESTAMP WHERE task_id = ?`).run(finalResult?.slice(0, 10000) ?? null, taskId);
|
|
793
|
+
if (copilotClient && finalResult) {
|
|
794
|
+
void runEndOfTaskMemoryHook({
|
|
795
|
+
taskId,
|
|
796
|
+
finalResult,
|
|
797
|
+
copilotClient,
|
|
798
|
+
}).catch((error) => {
|
|
799
|
+
log.error({ err: error, taskId }, "memory.eot.error");
|
|
800
|
+
});
|
|
801
|
+
}
|
|
589
802
|
const taskRow = db.prepare(`SELECT agent_slug FROM agent_tasks WHERE task_id = ?`).get(taskId);
|
|
590
803
|
void agentEventBus.emit({
|
|
591
804
|
type: "session:destroyed",
|
|
@@ -898,6 +1111,8 @@ export async function sendToOrchestrator(prompt, source, callback, attachments,
|
|
|
898
1111
|
logConversation("assistant", finalContent, sourceLabel, sessionKey);
|
|
899
1112
|
}
|
|
900
1113
|
catch { /* best-effort */ }
|
|
1114
|
+
scheduleCheckpointExtraction(sessionKey, prompt, finalContent, source);
|
|
1115
|
+
scheduleHousekeeping(sessionKey, source);
|
|
901
1116
|
if (copilotClient) {
|
|
902
1117
|
maybeWriteEpisode(copilotClient).catch((err) => {
|
|
903
1118
|
log.error({ err: err instanceof Error ? err.message : err }, "Episode write failed (non-fatal)");
|
|
@@ -997,6 +1212,8 @@ export async function interruptCurrentTurn(sessionKey, newPrompt, source, callba
|
|
|
997
1212
|
logConversation("assistant", finalContent, sourceLabel, sessionKey);
|
|
998
1213
|
}
|
|
999
1214
|
catch { /* best-effort */ }
|
|
1215
|
+
scheduleCheckpointExtraction(sessionKey, newPrompt, finalContent, source);
|
|
1216
|
+
scheduleHousekeeping(sessionKey, source);
|
|
1000
1217
|
if (copilotClient) {
|
|
1001
1218
|
maybeWriteEpisode(copilotClient).catch((err) => {
|
|
1002
1219
|
log.error({ err: err instanceof Error ? err.message : err }, "Episode write failed (non-fatal)");
|
|
@@ -1123,10 +1340,14 @@ export function getAgentInfo() {
|
|
|
1123
1340
|
/** Clean up on shutdown/restart. */
|
|
1124
1341
|
export async function shutdownAgents() {
|
|
1125
1342
|
if (!registry) {
|
|
1343
|
+
checkpointTrackers.clear();
|
|
1344
|
+
checkpointTurnsBySession.clear();
|
|
1126
1345
|
await clearActiveTasks();
|
|
1127
1346
|
return;
|
|
1128
1347
|
}
|
|
1129
1348
|
await registry.shutdown();
|
|
1349
|
+
checkpointTrackers.clear();
|
|
1350
|
+
checkpointTurnsBySession.clear();
|
|
1130
1351
|
await clearActiveTasks();
|
|
1131
1352
|
}
|
|
1132
1353
|
//# sourceMappingURL=orchestrator.js.map
|