@mclawnet/agent 0.6.29 → 0.6.31
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/__tests__/hub-connection-reconnect.test.d.ts +2 -0
- package/dist/__tests__/hub-connection-reconnect.test.d.ts.map +1 -0
- package/dist/backend-adapter.d.ts +7 -0
- package/dist/backend-adapter.d.ts.map +1 -1
- package/dist/{chunk-Y4J44CKF.js → chunk-U5CD3OZ3.js} +179 -35
- package/dist/chunk-U5CD3OZ3.js.map +1 -0
- package/dist/hub-connection.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/session-manager.d.ts +21 -0
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +1 -1
- package/package.json +6 -6
- package/dist/chunk-Y4J44CKF.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-connection-reconnect.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/hub-connection-reconnect.test.ts"],"names":[],"mappings":""}
|
|
@@ -46,6 +46,13 @@ export interface SpawnOptions {
|
|
|
46
46
|
mcpConfigPath?: string;
|
|
47
47
|
/** Memory system role ID. When set, SessionManager auto-injects memory prompt + MCP config */
|
|
48
48
|
roleId?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Optional first user input. When set, SessionManager uses this as the
|
|
51
|
+
* semantic-retrieval query for `buildMemorySection` (Pipeline A) so the
|
|
52
|
+
* injected memory section is relevant to what the user is about to ask.
|
|
53
|
+
* Falls back to an empty query (importance-top-K) when omitted.
|
|
54
|
+
*/
|
|
55
|
+
initialUserInput?: string;
|
|
49
56
|
/** Extra directories to mount via --add-dir */
|
|
50
57
|
additionalDirs?: string[];
|
|
51
58
|
/** Optional allowlist of tool names to forward to the backend (PR3.5). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend-adapter.d.ts","sourceRoot":"","sources":["../src/backend-adapter.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"backend-adapter.d.ts","sourceRoot":"","sources":["../src/backend-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -470,6 +470,7 @@ async function handleSwarmControl(coord, msg, opts) {
|
|
|
470
470
|
|
|
471
471
|
// src/hub-connection.ts
|
|
472
472
|
import { createLogger as createLogger2, previewFields } from "@mclawnet/logger";
|
|
473
|
+
import { DEFAULT_ASSISTANT_ROLE_ID } from "@mclawnet/memory";
|
|
473
474
|
var log2 = createLogger2({ module: "agent" });
|
|
474
475
|
var HubConnection = class {
|
|
475
476
|
ws = null;
|
|
@@ -559,13 +560,13 @@ var HubConnection = class {
|
|
|
559
560
|
}
|
|
560
561
|
connect() {
|
|
561
562
|
if (this.destroyed) return;
|
|
562
|
-
if (this.
|
|
563
|
+
if (this.reconnectTimer) return;
|
|
564
|
+
if (this.ws && (this.ws.readyState === WebSocket.CONNECTING || this.ws.readyState === WebSocket.OPEN)) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
563
567
|
this.cleanupConnection();
|
|
564
568
|
this.authState = "pending";
|
|
565
|
-
this.ws = new WebSocket(this.hubUrl);
|
|
566
|
-
this.ws.on("open", () => {
|
|
567
|
-
this.reconnectDelay = DEFAULT_RECONNECT_MS;
|
|
568
|
-
});
|
|
569
|
+
this.ws = new WebSocket(this.hubUrl, { handshakeTimeout: 1e4 });
|
|
569
570
|
this.ws.on("message", (raw) => {
|
|
570
571
|
let data;
|
|
571
572
|
try {
|
|
@@ -589,6 +590,7 @@ var HubConnection = class {
|
|
|
589
590
|
log2.info({ agentId: data.agentId }, "registered with hub");
|
|
590
591
|
this.authState = "authenticated";
|
|
591
592
|
this.agentId = data.agentId ?? null;
|
|
593
|
+
this.reconnectDelay = DEFAULT_RECONNECT_MS;
|
|
592
594
|
this.startHeartbeat();
|
|
593
595
|
this.onConnectCb?.(this.agentId);
|
|
594
596
|
this.tryRecoverSwarms();
|
|
@@ -597,10 +599,16 @@ var HubConnection = class {
|
|
|
597
599
|
}
|
|
598
600
|
if (this.authState === "authenticated") {
|
|
599
601
|
this.lastAckAt = Date.now();
|
|
602
|
+
if (data && data.type === "heartbeat_ack") {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
600
605
|
if (this.handleSessionMessage(data)) return;
|
|
601
606
|
this.onMessage?.(data);
|
|
602
607
|
}
|
|
603
608
|
});
|
|
609
|
+
this.ws.on("pong", () => {
|
|
610
|
+
this.lastAckAt = Date.now();
|
|
611
|
+
});
|
|
604
612
|
this.ws.on("close", (code, reason) => {
|
|
605
613
|
log2.warn({ code, reason: reason.toString() }, "disconnected from hub");
|
|
606
614
|
this.stopHeartbeat();
|
|
@@ -616,6 +624,18 @@ var HubConnection = class {
|
|
|
616
624
|
this.ws.on("error", (err) => {
|
|
617
625
|
log2.error({ err }, "ws connection error");
|
|
618
626
|
this.onError?.(err);
|
|
627
|
+
if (this.ws) {
|
|
628
|
+
const dying = this.ws;
|
|
629
|
+
this.ws = null;
|
|
630
|
+
try {
|
|
631
|
+
dying.removeAllListeners();
|
|
632
|
+
dying.terminate();
|
|
633
|
+
} catch {
|
|
634
|
+
}
|
|
635
|
+
this.stopHeartbeat();
|
|
636
|
+
this.authState = "pending";
|
|
637
|
+
this.scheduleReconnect();
|
|
638
|
+
}
|
|
619
639
|
});
|
|
620
640
|
}
|
|
621
641
|
send(data) {
|
|
@@ -1001,10 +1021,12 @@ var HubConnection = class {
|
|
|
1001
1021
|
workDir,
|
|
1002
1022
|
resumeId,
|
|
1003
1023
|
useBrainCore,
|
|
1004
|
-
roleId:
|
|
1024
|
+
roleId: DEFAULT_ASSISTANT_ROLE_ID,
|
|
1005
1025
|
maxOutputTokens
|
|
1006
1026
|
}).then(() => {
|
|
1007
|
-
sm.
|
|
1027
|
+
sm.sendUserInput(sessionId, content).catch((err) => {
|
|
1028
|
+
log2.warn({ err, sessionId }, "sendUserInput failed");
|
|
1029
|
+
});
|
|
1008
1030
|
}).catch((err) => {
|
|
1009
1031
|
this.send({
|
|
1010
1032
|
type: "session.error",
|
|
@@ -1016,7 +1038,9 @@ var HubConnection = class {
|
|
|
1016
1038
|
if (sm.isHealthy(sessionId)) {
|
|
1017
1039
|
log2.info({ sessionId }, "claude.execute: reusing healthy session");
|
|
1018
1040
|
log2.debug({ sessionId, ...previewFields(content) }, "claude.execute: input");
|
|
1019
|
-
sm.
|
|
1041
|
+
sm.sendUserInput(sessionId, content).catch((err) => {
|
|
1042
|
+
log2.warn({ err, sessionId }, "sendUserInput failed");
|
|
1043
|
+
});
|
|
1020
1044
|
} else if (sm.hasSession(sessionId) && claudeSessionId) {
|
|
1021
1045
|
const recommendedMax = sm.getRecommendedMaxOutputTokens(sessionId);
|
|
1022
1046
|
log2.warn({ sessionId, claudeSessionId }, "claude.execute: session unhealthy, recreating with --resume");
|
|
@@ -1056,12 +1080,12 @@ var HubConnection = class {
|
|
|
1056
1080
|
return true;
|
|
1057
1081
|
}
|
|
1058
1082
|
if (msg.type === "session.create") {
|
|
1059
|
-
log2.info({ sessionId: msg.sessionId, roleId:
|
|
1083
|
+
log2.info({ sessionId: msg.sessionId, roleId: DEFAULT_ASSISTANT_ROLE_ID }, "session.create with memory injection");
|
|
1060
1084
|
this.sessionManager.createSession({
|
|
1061
1085
|
sessionId: msg.sessionId,
|
|
1062
1086
|
workDir: msg.workDir,
|
|
1063
1087
|
resumeId: msg.resumeId,
|
|
1064
|
-
roleId:
|
|
1088
|
+
roleId: DEFAULT_ASSISTANT_ROLE_ID
|
|
1065
1089
|
}).then((claudeSessionId) => {
|
|
1066
1090
|
this.send({
|
|
1067
1091
|
type: "session.created",
|
|
@@ -1088,7 +1112,9 @@ var HubConnection = class {
|
|
|
1088
1112
|
{ sessionId: msg.sessionId, ...previewFields(msg.content) },
|
|
1089
1113
|
"claude.input"
|
|
1090
1114
|
);
|
|
1091
|
-
this.sessionManager.
|
|
1115
|
+
this.sessionManager.sendUserInput(msg.sessionId, msg.content).catch((err) => {
|
|
1116
|
+
log2.warn({ err, sessionId: msg.sessionId }, "sendUserInput failed");
|
|
1117
|
+
});
|
|
1092
1118
|
return true;
|
|
1093
1119
|
}
|
|
1094
1120
|
return false;
|
|
@@ -1148,6 +1174,10 @@ var HubConnection = class {
|
|
|
1148
1174
|
return;
|
|
1149
1175
|
}
|
|
1150
1176
|
this.send({ type: "heartbeat", ts: Date.now() });
|
|
1177
|
+
try {
|
|
1178
|
+
this.ws?.ping();
|
|
1179
|
+
} catch {
|
|
1180
|
+
}
|
|
1151
1181
|
}, this.heartbeatInterval);
|
|
1152
1182
|
}
|
|
1153
1183
|
stopHeartbeat() {
|
|
@@ -1236,12 +1266,15 @@ var HubConnection = class {
|
|
|
1236
1266
|
scheduleReconnect() {
|
|
1237
1267
|
if (this.destroyed) return;
|
|
1238
1268
|
if (this.reconnectTimer) return;
|
|
1239
|
-
|
|
1269
|
+
const cap = Math.min(this.reconnectDelay, this.maxReconnectDelay);
|
|
1270
|
+
const base = Math.min(DEFAULT_RECONNECT_MS, cap);
|
|
1271
|
+
const delay = Math.floor(base + Math.random() * Math.max(0, cap - base));
|
|
1272
|
+
log2.warn({ delayMs: delay, capMs: cap }, "reconnecting to hub...");
|
|
1240
1273
|
this.reconnectTimer = setTimeout(() => {
|
|
1241
1274
|
this.reconnectTimer = null;
|
|
1242
1275
|
this.connect();
|
|
1243
|
-
},
|
|
1244
|
-
this.reconnectDelay = Math.min(
|
|
1276
|
+
}, delay);
|
|
1277
|
+
this.reconnectDelay = Math.min(cap * 2, this.maxReconnectDelay);
|
|
1245
1278
|
}
|
|
1246
1279
|
cleanup() {
|
|
1247
1280
|
this.cleanupConnection();
|
|
@@ -1678,7 +1711,12 @@ function makeRealSwarmStarter(_deps) {
|
|
|
1678
1711
|
|
|
1679
1712
|
// src/session-manager.ts
|
|
1680
1713
|
import { createLogger as createLogger5, previewFields as previewFields2 } from "@mclawnet/logger";
|
|
1681
|
-
import {
|
|
1714
|
+
import {
|
|
1715
|
+
buildMemorySection,
|
|
1716
|
+
EmbeddingService,
|
|
1717
|
+
createEmbeddingProviders,
|
|
1718
|
+
initDatabase
|
|
1719
|
+
} from "@mclawnet/memory";
|
|
1682
1720
|
import { MAX_TOKENS_LADDER, clampLadderIndex } from "@mclawnet/shared";
|
|
1683
1721
|
|
|
1684
1722
|
// src/skill-loader.ts
|
|
@@ -2040,6 +2078,13 @@ function isPidAlive(pid) {
|
|
|
2040
2078
|
}
|
|
2041
2079
|
|
|
2042
2080
|
// src/session-manager.ts
|
|
2081
|
+
var sharedEmbeddingService = null;
|
|
2082
|
+
function getSharedEmbeddingService() {
|
|
2083
|
+
if (sharedEmbeddingService) return sharedEmbeddingService;
|
|
2084
|
+
const db = initDatabase();
|
|
2085
|
+
sharedEmbeddingService = new EmbeddingService(db, createEmbeddingProviders());
|
|
2086
|
+
return sharedEmbeddingService;
|
|
2087
|
+
}
|
|
2043
2088
|
var log5 = createLogger5({ module: "agent/session-manager" });
|
|
2044
2089
|
var DEFAULT_MAX_PROCESSES = 30;
|
|
2045
2090
|
var MAX_PROCESSES = Number(process.env.CLAWNET_MAX_PROCESSES) || DEFAULT_MAX_PROCESSES;
|
|
@@ -2071,6 +2116,13 @@ var SessionManager = class {
|
|
|
2071
2116
|
// correctly routed to the "spawn new + --resume" branch instead of trying
|
|
2072
2117
|
// to write to a process we are about to kill.
|
|
2073
2118
|
aborting = /* @__PURE__ */ new Set();
|
|
2119
|
+
// Sessions whose exit was triggered by an explicit close/abort/closeAll —
|
|
2120
|
+
// used by the onExit hook to label the resulting exit event as `expected`
|
|
2121
|
+
// (so SwarmCoordinator can skip flipping the role to `crashed`). The set is
|
|
2122
|
+
// populated *before* `adapter.stop()` runs and drained inside the onExit
|
|
2123
|
+
// handler. A leftover entry would only matter if a session id were reused,
|
|
2124
|
+
// which createSession already forbids (line 193 throws on duplicate).
|
|
2125
|
+
expectedExits = /* @__PURE__ */ new Set();
|
|
2074
2126
|
idleSweepTimer = null;
|
|
2075
2127
|
// PR-A: effective sweeper config. Initialized from env at construct time
|
|
2076
2128
|
// and overridable per-instance via startIdleSweeper(overrides) — the
|
|
@@ -2106,6 +2158,14 @@ var SessionManager = class {
|
|
|
2106
2158
|
onSessionError;
|
|
2107
2159
|
onSessionStarted;
|
|
2108
2160
|
onBeforeClose;
|
|
2161
|
+
/**
|
|
2162
|
+
* Fires whenever the underlying child process exits — both expected
|
|
2163
|
+
* (closeSession/abortSession) and unexpected (crash, OOM, external SIGKILL).
|
|
2164
|
+
* `expected` lets the listener distinguish so a swarm coordinator can flip
|
|
2165
|
+
* the role to `crashed` only on unplanned exits. Wired in start.ts to route
|
|
2166
|
+
* swarm-role exits to SwarmCoordinator.handleRoleCrashed.
|
|
2167
|
+
*/
|
|
2168
|
+
onSessionExit;
|
|
2109
2169
|
// PR-A: classifies a sessionId as 'chat' or 'swarm-role'. Injected by
|
|
2110
2170
|
// start.ts via SwarmCoordinator.isSwarmSession to keep SessionManager
|
|
2111
2171
|
// independent of the swarm package. Defaults to 'chat' if absent — safe
|
|
@@ -2119,6 +2179,7 @@ var SessionManager = class {
|
|
|
2119
2179
|
this.onSessionError = options.onSessionError;
|
|
2120
2180
|
this.onSessionStarted = options.onSessionStarted;
|
|
2121
2181
|
this.onBeforeClose = options.onBeforeClose;
|
|
2182
|
+
this.onSessionExit = options.onSessionExit;
|
|
2122
2183
|
this.classify = options.classify ?? (() => "chat");
|
|
2123
2184
|
this.checkpointPath = options.checkpointPath ?? null;
|
|
2124
2185
|
this.checkpointDebounceMs = options.checkpointDebounceMs ?? 5e3;
|
|
@@ -2130,19 +2191,26 @@ var SessionManager = class {
|
|
|
2130
2191
|
if (this.sessions.size >= MAX_PROCESSES) {
|
|
2131
2192
|
throw new SessionLimitReachedError(this.snapshotPoolForError());
|
|
2132
2193
|
}
|
|
2133
|
-
if (options.
|
|
2194
|
+
if (options.workDir) {
|
|
2134
2195
|
try {
|
|
2135
|
-
const
|
|
2136
|
-
const
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2196
|
+
const query = options.initialUserInput ?? "";
|
|
2197
|
+
const memorySection = await buildMemorySection({
|
|
2198
|
+
query,
|
|
2199
|
+
workDir: options.workDir,
|
|
2200
|
+
embeddingService: getSharedEmbeddingService()
|
|
2201
|
+
});
|
|
2202
|
+
options.systemPrompt = options.systemPrompt ? `${memorySection}
|
|
2140
2203
|
|
|
2141
|
-
${options.systemPrompt}` :
|
|
2142
|
-
log5.debug({
|
|
2204
|
+
${options.systemPrompt}` : memorySection;
|
|
2205
|
+
log5.debug({ workDir: options.workDir, sessionId: options.sessionId }, "memory prompt injected");
|
|
2143
2206
|
} catch (err) {
|
|
2144
|
-
log5.warn({ err,
|
|
2207
|
+
log5.warn({ err, workDir: options.workDir }, "failed to build memory section, proceeding without");
|
|
2145
2208
|
}
|
|
2209
|
+
} else {
|
|
2210
|
+
log5.warn(
|
|
2211
|
+
{ sessionId: options.sessionId, roleId: options.roleId },
|
|
2212
|
+
"session created without workDir \u2014 memory injection skipped (all memories would be cross-workdir; pass workDir to enable scoped retrieval)"
|
|
2213
|
+
);
|
|
2146
2214
|
}
|
|
2147
2215
|
try {
|
|
2148
2216
|
const notice = getPendingNotification();
|
|
@@ -2160,6 +2228,12 @@ ${notice.text}`;
|
|
|
2160
2228
|
} catch (err) {
|
|
2161
2229
|
log5.debug({ err }, "failed to inject pending notification");
|
|
2162
2230
|
}
|
|
2231
|
+
if (this.classify(options.sessionId) === "chat") {
|
|
2232
|
+
const ideaHint = `
|
|
2233
|
+
|
|
2234
|
+
[Idea Capture] When you propose a concrete user-actionable idea, feature suggestion, or follow-up TODO, additionally emit a single tag like \`<idea title="short title" body="one-paragraph context"/>\` at the end of that paragraph. Keep it concise and use it sparingly \u2014 only for things the user would plausibly want to save and revisit. The tag itself will be hidden from the chat view.`;
|
|
2235
|
+
options.systemPrompt = options.systemPrompt ? `${options.systemPrompt}${ideaHint}` : ideaHint.trimStart();
|
|
2236
|
+
}
|
|
2163
2237
|
try {
|
|
2164
2238
|
const process2 = await this.adapter.spawn(options);
|
|
2165
2239
|
this.sessions.set(options.sessionId, process2);
|
|
@@ -2257,13 +2331,37 @@ ${notice.text}`;
|
|
|
2257
2331
|
}
|
|
2258
2332
|
this.adapter.onExit?.(process2, (code) => {
|
|
2259
2333
|
if (this.sessions.get(options.sessionId) === process2) {
|
|
2334
|
+
const expected = this.expectedExits.delete(options.sessionId);
|
|
2260
2335
|
this.sessions.delete(options.sessionId);
|
|
2261
2336
|
this.sessionMeta.delete(options.sessionId);
|
|
2262
2337
|
this.activelyExecuting.delete(options.sessionId);
|
|
2263
2338
|
this.conversationBuffer.delete(options.sessionId);
|
|
2264
2339
|
this.scheduleCheckpoint();
|
|
2265
|
-
|
|
2266
|
-
|
|
2340
|
+
if (!expected) {
|
|
2341
|
+
log5.warn({ sessionId: options.sessionId, exitCode: code }, "backend process exited unexpectedly, evicted from session map");
|
|
2342
|
+
this.onSessionError(options.sessionId, `backend process exited (code=${code ?? "null"})`);
|
|
2343
|
+
} else {
|
|
2344
|
+
log5.debug({ sessionId: options.sessionId, exitCode: code }, "backend process exited as expected");
|
|
2345
|
+
}
|
|
2346
|
+
try {
|
|
2347
|
+
this.onSessionExit?.(options.sessionId, {
|
|
2348
|
+
code: code ?? null,
|
|
2349
|
+
expected,
|
|
2350
|
+
...expected ? {} : { reason: `exit code=${code ?? "null"}` }
|
|
2351
|
+
});
|
|
2352
|
+
} catch (err) {
|
|
2353
|
+
log5.warn({ err, sessionId: options.sessionId }, "onSessionExit listener threw");
|
|
2354
|
+
}
|
|
2355
|
+
} else {
|
|
2356
|
+
const expected = this.expectedExits.delete(options.sessionId);
|
|
2357
|
+
try {
|
|
2358
|
+
this.onSessionExit?.(options.sessionId, {
|
|
2359
|
+
code: code ?? null,
|
|
2360
|
+
expected
|
|
2361
|
+
});
|
|
2362
|
+
} catch (err) {
|
|
2363
|
+
log5.warn({ err, sessionId: options.sessionId }, "onSessionExit listener threw");
|
|
2364
|
+
}
|
|
2267
2365
|
}
|
|
2268
2366
|
});
|
|
2269
2367
|
return process2.id;
|
|
@@ -2288,10 +2386,44 @@ ${notice.text}`;
|
|
|
2288
2386
|
);
|
|
2289
2387
|
this.adapter.send(process2, input);
|
|
2290
2388
|
}
|
|
2389
|
+
/**
|
|
2390
|
+
* User-input variant of sendInput: best-effort prepend a `<memory-context>`
|
|
2391
|
+
* block sourced from semantic retrieval (Pipeline B). Falls back to plain
|
|
2392
|
+
* sendInput if retrieval errors or exceeds the 200ms budget — we never
|
|
2393
|
+
* block the user's turn on memory.
|
|
2394
|
+
*/
|
|
2395
|
+
async sendUserInput(sessionId, content) {
|
|
2396
|
+
const meta = this.sessionMeta.get(sessionId);
|
|
2397
|
+
const workDir = meta?.workDir;
|
|
2398
|
+
let prefixed = content;
|
|
2399
|
+
try {
|
|
2400
|
+
const section = await Promise.race([
|
|
2401
|
+
buildMemorySection({
|
|
2402
|
+
query: content,
|
|
2403
|
+
workDir,
|
|
2404
|
+
embeddingService: getSharedEmbeddingService()
|
|
2405
|
+
}),
|
|
2406
|
+
new Promise(
|
|
2407
|
+
(resolve) => setTimeout(() => resolve(""), 200)
|
|
2408
|
+
)
|
|
2409
|
+
]);
|
|
2410
|
+
if (section && section.trim().length > 0) {
|
|
2411
|
+
prefixed = `<memory-context>
|
|
2412
|
+
${section}
|
|
2413
|
+
</memory-context>
|
|
2414
|
+
|
|
2415
|
+
${content}`;
|
|
2416
|
+
}
|
|
2417
|
+
} catch (err) {
|
|
2418
|
+
log5.debug({ err, sessionId }, "sendUserInput: memory retrieval skipped");
|
|
2419
|
+
}
|
|
2420
|
+
this.sendInput(sessionId, prefixed);
|
|
2421
|
+
}
|
|
2291
2422
|
async abortSession(sessionId) {
|
|
2292
2423
|
const process2 = this.sessions.get(sessionId);
|
|
2293
2424
|
if (!process2) return;
|
|
2294
2425
|
this.aborting.add(sessionId);
|
|
2426
|
+
this.expectedExits.add(sessionId);
|
|
2295
2427
|
this.conversationBuffer.delete(sessionId);
|
|
2296
2428
|
this.sessions.delete(sessionId);
|
|
2297
2429
|
this.sessionMeta.delete(sessionId);
|
|
@@ -2312,6 +2444,7 @@ ${notice.text}`;
|
|
|
2312
2444
|
});
|
|
2313
2445
|
}
|
|
2314
2446
|
this.conversationBuffer.delete(sessionId);
|
|
2447
|
+
this.expectedExits.add(sessionId);
|
|
2315
2448
|
this.sessions.delete(sessionId);
|
|
2316
2449
|
this.sessionMeta.delete(sessionId);
|
|
2317
2450
|
this.activelyExecuting.delete(sessionId);
|
|
@@ -2328,6 +2461,7 @@ ${notice.text}`;
|
|
|
2328
2461
|
});
|
|
2329
2462
|
}
|
|
2330
2463
|
this.conversationBuffer.delete(sessionId);
|
|
2464
|
+
this.expectedExits.add(sessionId);
|
|
2331
2465
|
this.sessions.delete(sessionId);
|
|
2332
2466
|
this.sessionMeta.delete(sessionId);
|
|
2333
2467
|
this.activelyExecuting.delete(sessionId);
|
|
@@ -2992,11 +3126,12 @@ function createSwarmAwareSessionStartedHandler(deps) {
|
|
|
2992
3126
|
// src/start.ts
|
|
2993
3127
|
import { createLogger as createLogger9 } from "@mclawnet/logger";
|
|
2994
3128
|
import {
|
|
2995
|
-
initDatabase,
|
|
3129
|
+
initDatabase as initDatabase2,
|
|
2996
3130
|
MemoryStore,
|
|
2997
|
-
EmbeddingService,
|
|
2998
|
-
createEmbeddingProviders,
|
|
2999
|
-
distillConversation
|
|
3131
|
+
EmbeddingService as EmbeddingService2,
|
|
3132
|
+
createEmbeddingProviders as createEmbeddingProviders2,
|
|
3133
|
+
distillConversation,
|
|
3134
|
+
DEFAULT_ASSISTANT_ROLE_ID as DEFAULT_ASSISTANT_ROLE_ID2
|
|
3000
3135
|
} from "@mclawnet/memory";
|
|
3001
3136
|
import {
|
|
3002
3137
|
SkillStore,
|
|
@@ -3061,9 +3196,9 @@ async function startAgent(options) {
|
|
|
3061
3196
|
let skillStore = null;
|
|
3062
3197
|
let memoryDb = null;
|
|
3063
3198
|
try {
|
|
3064
|
-
memoryDb =
|
|
3199
|
+
memoryDb = initDatabase2(dbPath);
|
|
3065
3200
|
memoryStore = new MemoryStore(memoryDb);
|
|
3066
|
-
embeddingService = new
|
|
3201
|
+
embeddingService = new EmbeddingService2(memoryDb, createEmbeddingProviders2());
|
|
3067
3202
|
skillStore = new SkillStore(clawnetDir);
|
|
3068
3203
|
evolutionPipeline = new EvolutionPipeline(clawnetDir);
|
|
3069
3204
|
} catch (err) {
|
|
@@ -3075,7 +3210,7 @@ async function startAgent(options) {
|
|
|
3075
3210
|
try {
|
|
3076
3211
|
await distillConversation(
|
|
3077
3212
|
messages,
|
|
3078
|
-
|
|
3213
|
+
DEFAULT_ASSISTANT_ROLE_ID2,
|
|
3079
3214
|
memoryStore,
|
|
3080
3215
|
embeddingService
|
|
3081
3216
|
);
|
|
@@ -3086,7 +3221,7 @@ async function startAgent(options) {
|
|
|
3086
3221
|
if (!skillStore || !evolutionPipeline) return;
|
|
3087
3222
|
const skills = skillStore.scan();
|
|
3088
3223
|
if (skills.length === 0) return;
|
|
3089
|
-
const recent = memoryStore.
|
|
3224
|
+
const recent = memoryStore.getRecentMemoriesByRole(DEFAULT_ASSISTANT_ROLE_ID2, { limit: 50 });
|
|
3090
3225
|
const refs = recent.map((m) => ({
|
|
3091
3226
|
id: m.id,
|
|
3092
3227
|
type: m.type,
|
|
@@ -3140,6 +3275,15 @@ async function startAgent(options) {
|
|
|
3140
3275
|
error
|
|
3141
3276
|
});
|
|
3142
3277
|
},
|
|
3278
|
+
onSessionExit: (sessionId, info) => {
|
|
3279
|
+
if (info.expected) return;
|
|
3280
|
+
if (!swarmCoordinator?.isSwarmSession(sessionId)) return;
|
|
3281
|
+
try {
|
|
3282
|
+
swarmCoordinator.handleRoleCrashed(sessionId, info.reason ?? `exit code=${info.code ?? "null"}`);
|
|
3283
|
+
} catch (err) {
|
|
3284
|
+
log9.warn({ err, sessionId }, "swarmCoordinator.handleRoleCrashed threw");
|
|
3285
|
+
}
|
|
3286
|
+
},
|
|
3143
3287
|
onBeforeClose,
|
|
3144
3288
|
// PR-A: classify session kind for the idle sweeper. SessionManager stays
|
|
3145
3289
|
// independent of the swarm package; we hand it a closure that defers to
|
|
@@ -3197,4 +3341,4 @@ export {
|
|
|
3197
3341
|
FsBridge,
|
|
3198
3342
|
startAgent
|
|
3199
3343
|
};
|
|
3200
|
-
//# sourceMappingURL=chunk-
|
|
3344
|
+
//# sourceMappingURL=chunk-U5CD3OZ3.js.map
|