@sesamespace/hivemind 0.12.5 → 0.12.7
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/{chunk-PGLO6WA5.js → chunk-CEJEBZTB.js} +773 -44
- package/dist/chunk-CEJEBZTB.js.map +1 -0
- package/dist/{chunk-TJBUZUVY.js → chunk-H2ZWSKGT.js} +3 -3
- package/dist/{chunk-XW4AQDZA.js → chunk-HTSJVPAJ.js} +2 -2
- package/dist/{chunk-FIHSUSBJ.js → chunk-LYH7OPSY.js} +2 -2
- package/dist/{chunk-J3WGHS5W.js → chunk-MTMLEXMO.js} +2 -2
- package/dist/{chunk-YDK3Z5IW.js → chunk-YLM37KCE.js} +2 -2
- package/dist/commands/fleet.js +3 -3
- package/dist/commands/init.js +3 -3
- package/dist/commands/start.js +3 -3
- package/dist/commands/watchdog.js +3 -3
- package/dist/index.js +2 -2
- package/dist/main.js +6 -6
- package/dist/start.js +1 -1
- package/package.json +1 -1
- package/.session-log.json +0 -58
- package/dist/chunk-PGLO6WA5.js.map +0 -1
- /package/dist/{chunk-TJBUZUVY.js.map → chunk-H2ZWSKGT.js.map} +0 -0
- /package/dist/{chunk-XW4AQDZA.js.map → chunk-HTSJVPAJ.js.map} +0 -0
- /package/dist/{chunk-FIHSUSBJ.js.map → chunk-LYH7OPSY.js.map} +0 -0
- /package/dist/{chunk-J3WGHS5W.js.map → chunk-MTMLEXMO.js.map} +0 -0
- /package/dist/{chunk-YDK3Z5IW.js.map → chunk-YLM37KCE.js.map} +0 -0
|
@@ -3049,7 +3049,7 @@ var SesameClient = class {
|
|
|
3049
3049
|
* Connect to the real-time WebSocket gateway.
|
|
3050
3050
|
*/
|
|
3051
3051
|
connect() {
|
|
3052
|
-
return new Promise((
|
|
3052
|
+
return new Promise((resolve22, reject) => {
|
|
3053
3053
|
try {
|
|
3054
3054
|
this.ws = new WebSocket(`${this.config.wsUrl}/v1/connect`);
|
|
3055
3055
|
this.ws.on("open", () => {
|
|
@@ -3063,7 +3063,7 @@ var SesameClient = class {
|
|
|
3063
3063
|
this.authenticated = true;
|
|
3064
3064
|
this.startHeartbeat(event.heartbeatIntervalMs ?? 3e4);
|
|
3065
3065
|
this.sendReplay();
|
|
3066
|
-
|
|
3066
|
+
resolve22();
|
|
3067
3067
|
return;
|
|
3068
3068
|
}
|
|
3069
3069
|
if (event.type === "pong")
|
|
@@ -3249,6 +3249,10 @@ var SesameClient2 = class {
|
|
|
3249
3249
|
agentId = null;
|
|
3250
3250
|
channels = /* @__PURE__ */ new Map();
|
|
3251
3251
|
typingIntervals = /* @__PURE__ */ new Map();
|
|
3252
|
+
/** Messages held briefly to allow streaming edits to replace initial fragments */
|
|
3253
|
+
pendingMessages = /* @__PURE__ */ new Map();
|
|
3254
|
+
/** How long to wait for streaming edits before delivering (ms) */
|
|
3255
|
+
MESSAGE_HOLD_MS = 3e3;
|
|
3252
3256
|
constructor(config) {
|
|
3253
3257
|
this.config = config;
|
|
3254
3258
|
this.sdk = new SesameClient({
|
|
@@ -3305,8 +3309,9 @@ var SesameClient2 = class {
|
|
|
3305
3309
|
if (senderId === this.agentId) return;
|
|
3306
3310
|
if (!this.messageHandler || !msg.content) return;
|
|
3307
3311
|
const channelInfo = this.channels.get(msg.channelId);
|
|
3308
|
-
|
|
3309
|
-
|
|
3312
|
+
const messageId = msg.id || "unknown";
|
|
3313
|
+
const sesameMsg = {
|
|
3314
|
+
id: messageId,
|
|
3310
3315
|
channelId: msg.channelId || "unknown",
|
|
3311
3316
|
channelKind: channelInfo?.kind || "dm",
|
|
3312
3317
|
content: msg.content,
|
|
@@ -3315,7 +3320,52 @@ var SesameClient2 = class {
|
|
|
3315
3320
|
handle: msg.senderHandle || msg.metadata?.senderHandle || "unknown"
|
|
3316
3321
|
},
|
|
3317
3322
|
timestamp: msg.createdAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
3318
|
-
}
|
|
3323
|
+
};
|
|
3324
|
+
const existing = this.pendingMessages.get(messageId);
|
|
3325
|
+
if (existing && !existing.delivered) {
|
|
3326
|
+
existing.message = sesameMsg;
|
|
3327
|
+
return;
|
|
3328
|
+
}
|
|
3329
|
+
const pending = {
|
|
3330
|
+
message: sesameMsg,
|
|
3331
|
+
delivered: false,
|
|
3332
|
+
timer: setTimeout(() => {
|
|
3333
|
+
const p = this.pendingMessages.get(messageId);
|
|
3334
|
+
if (p && !p.delivered && this.messageHandler) {
|
|
3335
|
+
p.delivered = true;
|
|
3336
|
+
this.messageHandler(p.message);
|
|
3337
|
+
}
|
|
3338
|
+
this.pendingMessages.delete(messageId);
|
|
3339
|
+
}, this.MESSAGE_HOLD_MS)
|
|
3340
|
+
};
|
|
3341
|
+
this.pendingMessages.set(messageId, pending);
|
|
3342
|
+
});
|
|
3343
|
+
this.sdk.on("message.edited", (event) => {
|
|
3344
|
+
const msg = event.data || event.message || event;
|
|
3345
|
+
const senderId = msg.senderId || msg.sender?.id;
|
|
3346
|
+
if (senderId === this.agentId) return;
|
|
3347
|
+
if (event.streaming) return;
|
|
3348
|
+
const messageId = msg.id || "unknown";
|
|
3349
|
+
const channelInfo = this.channels.get(msg.channelId);
|
|
3350
|
+
const updatedMsg = {
|
|
3351
|
+
id: messageId,
|
|
3352
|
+
channelId: msg.channelId || "unknown",
|
|
3353
|
+
channelKind: channelInfo?.kind || "dm",
|
|
3354
|
+
content: msg.content,
|
|
3355
|
+
author: {
|
|
3356
|
+
id: senderId || "unknown",
|
|
3357
|
+
handle: msg.senderHandle || msg.metadata?.senderHandle || "unknown"
|
|
3358
|
+
},
|
|
3359
|
+
timestamp: msg.updatedAt || msg.createdAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
3360
|
+
};
|
|
3361
|
+
const pending = this.pendingMessages.get(messageId);
|
|
3362
|
+
if (pending && !pending.delivered) {
|
|
3363
|
+
pending.message = updatedMsg;
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
if (this.messageHandler && msg.content) {
|
|
3367
|
+
this.messageHandler(updatedMsg);
|
|
3368
|
+
}
|
|
3319
3369
|
});
|
|
3320
3370
|
await this.sdk.connect();
|
|
3321
3371
|
console.log("[sesame] WebSocket connected");
|
|
@@ -4225,12 +4275,12 @@ var LogWatcher = class extends BackgroundProcess {
|
|
|
4225
4275
|
}
|
|
4226
4276
|
savePositions() {
|
|
4227
4277
|
try {
|
|
4228
|
-
const { mkdirSync:
|
|
4229
|
-
const { dirname:
|
|
4230
|
-
const dir =
|
|
4231
|
-
if (!existsSync7(dir))
|
|
4278
|
+
const { mkdirSync: mkdirSync17, writeFileSync: writeFileSync11 } = __require("fs");
|
|
4279
|
+
const { dirname: dirname10 } = __require("path");
|
|
4280
|
+
const dir = dirname10(this.positionsFile);
|
|
4281
|
+
if (!existsSync7(dir)) mkdirSync17(dir, { recursive: true });
|
|
4232
4282
|
const data = Object.fromEntries(this.tailPositions);
|
|
4233
|
-
|
|
4283
|
+
writeFileSync11(this.positionsFile, JSON.stringify(data));
|
|
4234
4284
|
} catch (err) {
|
|
4235
4285
|
log2.warn("failed to save tail positions", {
|
|
4236
4286
|
error: err.message
|
|
@@ -4750,10 +4800,10 @@ function parseQuery(url) {
|
|
|
4750
4800
|
return params;
|
|
4751
4801
|
}
|
|
4752
4802
|
function readBody(req) {
|
|
4753
|
-
return new Promise((
|
|
4803
|
+
return new Promise((resolve22, reject) => {
|
|
4754
4804
|
const chunks = [];
|
|
4755
4805
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
4756
|
-
req.on("end", () =>
|
|
4806
|
+
req.on("end", () => resolve22(Buffer.concat(chunks).toString()));
|
|
4757
4807
|
req.on("error", reject);
|
|
4758
4808
|
});
|
|
4759
4809
|
}
|
|
@@ -5178,13 +5228,13 @@ function registerFileTools(registry, workspaceDir) {
|
|
|
5178
5228
|
required: []
|
|
5179
5229
|
},
|
|
5180
5230
|
async (params) => {
|
|
5181
|
-
const { readdirSync:
|
|
5231
|
+
const { readdirSync: readdirSync6, statSync: statSync5 } = await import("fs");
|
|
5182
5232
|
const dirPath = params.path ? resolvePath(workspaceDir, params.path) : workspaceDir;
|
|
5183
5233
|
if (!existsSync10(dirPath)) {
|
|
5184
5234
|
return `Error: Directory not found: ${dirPath}`;
|
|
5185
5235
|
}
|
|
5186
5236
|
try {
|
|
5187
|
-
const entries =
|
|
5237
|
+
const entries = readdirSync6(dirPath);
|
|
5188
5238
|
const results = [];
|
|
5189
5239
|
for (const entry of entries) {
|
|
5190
5240
|
if (entry.startsWith(".")) continue;
|
|
@@ -7898,7 +7948,7 @@ function registerCodingAgentTools(registry, workspaceDir, contextBridge) {
|
|
|
7898
7948
|
contextBridge.on("error-encountered", errorHandler);
|
|
7899
7949
|
}
|
|
7900
7950
|
try {
|
|
7901
|
-
const output = await new Promise((
|
|
7951
|
+
const output = await new Promise((resolve22, reject) => {
|
|
7902
7952
|
const child = exec2(command, {
|
|
7903
7953
|
cwd,
|
|
7904
7954
|
timeout: timeoutSeconds * 1e3,
|
|
@@ -7915,7 +7965,7 @@ function registerCodingAgentTools(registry, workspaceDir, contextBridge) {
|
|
|
7915
7965
|
error.stderr = stderr;
|
|
7916
7966
|
reject(error);
|
|
7917
7967
|
} else {
|
|
7918
|
-
|
|
7968
|
+
resolve22(stdout);
|
|
7919
7969
|
}
|
|
7920
7970
|
});
|
|
7921
7971
|
child.stdin?.end();
|
|
@@ -7966,6 +8016,9 @@ ${output || err.message}`;
|
|
|
7966
8016
|
if (contextBridge && errorHandler) {
|
|
7967
8017
|
contextBridge.off("error-encountered", errorHandler);
|
|
7968
8018
|
}
|
|
8019
|
+
if (contextBridge) {
|
|
8020
|
+
contextBridge.cleanupContextFile();
|
|
8021
|
+
}
|
|
7969
8022
|
}
|
|
7970
8023
|
}
|
|
7971
8024
|
);
|
|
@@ -8633,12 +8686,12 @@ Path: ${skillDir}`;
|
|
|
8633
8686
|
}
|
|
8634
8687
|
|
|
8635
8688
|
// packages/runtime/src/pipeline.ts
|
|
8636
|
-
import { readFileSync as
|
|
8637
|
-
import { resolve as
|
|
8689
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync10, unlinkSync as unlinkSync4 } from "fs";
|
|
8690
|
+
import { resolve as resolve21, dirname as dirname9 } from "path";
|
|
8638
8691
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8639
8692
|
|
|
8640
8693
|
// packages/runtime/src/tools/context-bridge.ts
|
|
8641
|
-
import { writeFileSync as writeFileSync8, readFileSync as readFileSync15, statSync as statSync4, existsSync as existsSync20, mkdirSync as mkdirSync15, openSync, readSync, closeSync } from "fs";
|
|
8694
|
+
import { writeFileSync as writeFileSync8, readFileSync as readFileSync15, statSync as statSync4, existsSync as existsSync20, mkdirSync as mkdirSync15, openSync, readSync, closeSync, readdirSync as readdirSync5, unlinkSync as unlinkSync3 } from "fs";
|
|
8642
8695
|
import { createHash as createHash2 } from "crypto";
|
|
8643
8696
|
import { join as join8 } from "path";
|
|
8644
8697
|
import { homedir } from "os";
|
|
@@ -8649,16 +8702,20 @@ var ClaudeContextBridge = class extends EventEmitter2 {
|
|
|
8649
8702
|
logger;
|
|
8650
8703
|
sessionLogPath;
|
|
8651
8704
|
contextFilePath;
|
|
8705
|
+
sessionArchiveDir;
|
|
8652
8706
|
watchInterval = null;
|
|
8653
8707
|
lastLogSize = 0;
|
|
8654
8708
|
seenPatterns = /* @__PURE__ */ new Set();
|
|
8709
|
+
currentSessionId;
|
|
8655
8710
|
constructor(config) {
|
|
8656
8711
|
super();
|
|
8657
8712
|
this.workspaceDir = config.workspaceDir;
|
|
8658
8713
|
this.memoryClient = config.memoryClient;
|
|
8659
8714
|
this.logger = config.logger;
|
|
8660
8715
|
this.sessionLogPath = join8(homedir(), ".claude", "session.log");
|
|
8661
|
-
this.
|
|
8716
|
+
this.sessionArchiveDir = join8(homedir(), ".claude", "sessions");
|
|
8717
|
+
this.contextFilePath = join8(this.workspaceDir, ".claude-context-current.md");
|
|
8718
|
+
this.currentSessionId = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8662
8719
|
}
|
|
8663
8720
|
/**
|
|
8664
8721
|
* Initialize the context bridge
|
|
@@ -8668,10 +8725,49 @@ var ClaudeContextBridge = class extends EventEmitter2 {
|
|
|
8668
8725
|
if (!existsSync20(claudeDir)) {
|
|
8669
8726
|
mkdirSync15(claudeDir, { recursive: true });
|
|
8670
8727
|
}
|
|
8728
|
+
if (!existsSync20(this.sessionArchiveDir)) {
|
|
8729
|
+
mkdirSync15(this.sessionArchiveDir, { recursive: true });
|
|
8730
|
+
}
|
|
8731
|
+
await this.archiveSessionLog();
|
|
8671
8732
|
await this.setupClaudeSettings();
|
|
8672
8733
|
this.startLogMonitoring();
|
|
8673
8734
|
this.logger.info("Claude context bridge initialized");
|
|
8674
8735
|
}
|
|
8736
|
+
/**
|
|
8737
|
+
* Archive previous session log if it exists
|
|
8738
|
+
*/
|
|
8739
|
+
async archiveSessionLog() {
|
|
8740
|
+
if (existsSync20(this.sessionLogPath)) {
|
|
8741
|
+
const stats = statSync4(this.sessionLogPath);
|
|
8742
|
+
if (stats.size > 0) {
|
|
8743
|
+
const archivePath = join8(this.sessionArchiveDir, `${this.currentSessionId}.log`);
|
|
8744
|
+
const content = readFileSync15(this.sessionLogPath, "utf-8");
|
|
8745
|
+
writeFileSync8(archivePath, content);
|
|
8746
|
+
writeFileSync8(this.sessionLogPath, "");
|
|
8747
|
+
this.logger.info("Archived previous session log", { path: archivePath });
|
|
8748
|
+
this.cleanupOldArchives();
|
|
8749
|
+
}
|
|
8750
|
+
}
|
|
8751
|
+
}
|
|
8752
|
+
/**
|
|
8753
|
+
* Clean up old session archives, keeping only the most recent ones
|
|
8754
|
+
*/
|
|
8755
|
+
cleanupOldArchives() {
|
|
8756
|
+
try {
|
|
8757
|
+
const files = readdirSync5(this.sessionArchiveDir).filter((f) => f.endsWith(".log")).map((f) => ({
|
|
8758
|
+
name: f,
|
|
8759
|
+
path: join8(this.sessionArchiveDir, f),
|
|
8760
|
+
mtime: statSync4(join8(this.sessionArchiveDir, f)).mtime
|
|
8761
|
+
})).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
8762
|
+
const toDelete = files.slice(10);
|
|
8763
|
+
for (const file of toDelete) {
|
|
8764
|
+
unlinkSync3(file.path);
|
|
8765
|
+
this.logger.debug("Deleted old session archive", { file: file.name });
|
|
8766
|
+
}
|
|
8767
|
+
} catch (err) {
|
|
8768
|
+
this.logger.warn("Failed to cleanup old archives", { error: String(err) });
|
|
8769
|
+
}
|
|
8770
|
+
}
|
|
8675
8771
|
/**
|
|
8676
8772
|
* Configure Claude Code to report insights via workspace CLAUDE.md
|
|
8677
8773
|
*/
|
|
@@ -8686,7 +8782,7 @@ When working on tasks, please report significant findings by writing to the sess
|
|
|
8686
8782
|
- When you find reusable solutions: echo "PATTERN: [approach]" >> ~/.claude/session.log
|
|
8687
8783
|
- When you encounter errors: echo "ERROR: [error details]" >> ~/.claude/session.log
|
|
8688
8784
|
|
|
8689
|
-
Always check
|
|
8785
|
+
Always check .claude-context-current.md in the workspace for task context and requirements.`;
|
|
8690
8786
|
const claudeMdPath = join8(this.workspaceDir, "CLAUDE.md");
|
|
8691
8787
|
writeFileSync8(claudeMdPath, instructions);
|
|
8692
8788
|
this.logger.info("Claude workspace CLAUDE.md configured for context bridge");
|
|
@@ -8940,6 +9036,15 @@ ${memories.join("\n\n")}
|
|
|
8940
9036
|
};
|
|
8941
9037
|
}
|
|
8942
9038
|
}
|
|
9039
|
+
/**
|
|
9040
|
+
* Clean up just the context file (for use after a session)
|
|
9041
|
+
*/
|
|
9042
|
+
cleanupContextFile() {
|
|
9043
|
+
if (existsSync20(this.contextFilePath)) {
|
|
9044
|
+
unlinkSync3(this.contextFilePath);
|
|
9045
|
+
this.logger.info("Cleaned up context file");
|
|
9046
|
+
}
|
|
9047
|
+
}
|
|
8943
9048
|
/**
|
|
8944
9049
|
* Clean up resources
|
|
8945
9050
|
*/
|
|
@@ -8950,6 +9055,7 @@ ${memories.join("\n\n")}
|
|
|
8950
9055
|
}
|
|
8951
9056
|
this.seenPatterns.clear();
|
|
8952
9057
|
this.removeAllListeners();
|
|
9058
|
+
this.cleanupContextFile();
|
|
8953
9059
|
}
|
|
8954
9060
|
};
|
|
8955
9061
|
async function createContextBridge(config) {
|
|
@@ -8958,11 +9064,609 @@ async function createContextBridge(config) {
|
|
|
8958
9064
|
return bridge;
|
|
8959
9065
|
}
|
|
8960
9066
|
|
|
9067
|
+
// packages/runtime/src/sesame-sync.ts
|
|
9068
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync9, existsSync as existsSync21, mkdirSync as mkdirSync16 } from "fs";
|
|
9069
|
+
import { resolve as resolve20, dirname as dirname8 } from "path";
|
|
9070
|
+
|
|
9071
|
+
// packages/runtime/src/sesame-api.ts
|
|
9072
|
+
var SesameAPI = class {
|
|
9073
|
+
baseUrl;
|
|
9074
|
+
apiKey;
|
|
9075
|
+
rateLimitRemaining = 120;
|
|
9076
|
+
rateLimitResetAt = 0;
|
|
9077
|
+
constructor(config) {
|
|
9078
|
+
this.baseUrl = config.api_url.endsWith("/api/v1") ? config.api_url : `${config.api_url}/api/v1`;
|
|
9079
|
+
this.apiKey = config.api_key;
|
|
9080
|
+
}
|
|
9081
|
+
// ── Internal helpers ──
|
|
9082
|
+
async request(method, path, body) {
|
|
9083
|
+
if (this.rateLimitRemaining <= 5 && Date.now() < this.rateLimitResetAt) {
|
|
9084
|
+
const waitMs = this.rateLimitResetAt - Date.now();
|
|
9085
|
+
console.warn(`[sesame-api] Rate limit low (${this.rateLimitRemaining}), waiting ${waitMs}ms`);
|
|
9086
|
+
await new Promise((r) => setTimeout(r, Math.min(waitMs, 5e3)));
|
|
9087
|
+
}
|
|
9088
|
+
const url = `${this.baseUrl}${path}`;
|
|
9089
|
+
const headers = {
|
|
9090
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
9091
|
+
"Content-Type": "application/json"
|
|
9092
|
+
};
|
|
9093
|
+
const options = { method, headers };
|
|
9094
|
+
if (body && method !== "GET") {
|
|
9095
|
+
options.body = JSON.stringify(body);
|
|
9096
|
+
}
|
|
9097
|
+
try {
|
|
9098
|
+
const response = await fetch(url, options);
|
|
9099
|
+
const remaining = response.headers.get("x-ratelimit-remaining");
|
|
9100
|
+
const reset = response.headers.get("x-ratelimit-reset");
|
|
9101
|
+
if (remaining) this.rateLimitRemaining = parseInt(remaining, 10);
|
|
9102
|
+
if (reset) this.rateLimitResetAt = parseInt(reset, 10) * 1e3;
|
|
9103
|
+
if (!response.ok) {
|
|
9104
|
+
const errorBody = await response.text();
|
|
9105
|
+
let parsed;
|
|
9106
|
+
try {
|
|
9107
|
+
parsed = JSON.parse(errorBody);
|
|
9108
|
+
} catch {
|
|
9109
|
+
parsed = { error: errorBody };
|
|
9110
|
+
}
|
|
9111
|
+
const err = new Error(`Sesame API ${method} ${path}: ${response.status} ${parsed.error || errorBody}`);
|
|
9112
|
+
err.status = response.status;
|
|
9113
|
+
err.details = parsed;
|
|
9114
|
+
throw err;
|
|
9115
|
+
}
|
|
9116
|
+
const data = await response.json();
|
|
9117
|
+
return data.data ?? data;
|
|
9118
|
+
} catch (err) {
|
|
9119
|
+
if (err.status) throw err;
|
|
9120
|
+
console.error(`[sesame-api] Network error on ${method} ${path}:`, err.message);
|
|
9121
|
+
throw err;
|
|
9122
|
+
}
|
|
9123
|
+
}
|
|
9124
|
+
// ── Wake ──
|
|
9125
|
+
async wake(agentId) {
|
|
9126
|
+
return this.request("GET", `/agents/${agentId}/wake`);
|
|
9127
|
+
}
|
|
9128
|
+
// ── Agent State ──
|
|
9129
|
+
async getState(agentId, namespace = "default") {
|
|
9130
|
+
try {
|
|
9131
|
+
return await this.request("GET", `/agents/${agentId}/state?namespace=${namespace}`);
|
|
9132
|
+
} catch (err) {
|
|
9133
|
+
if (err.status === 404) return null;
|
|
9134
|
+
throw err;
|
|
9135
|
+
}
|
|
9136
|
+
}
|
|
9137
|
+
async setState(agentId, state, namespace = "default", ttlSeconds = 86400) {
|
|
9138
|
+
return this.request("PUT", `/agents/${agentId}/state`, {
|
|
9139
|
+
namespace,
|
|
9140
|
+
state,
|
|
9141
|
+
ttlSeconds
|
|
9142
|
+
});
|
|
9143
|
+
}
|
|
9144
|
+
// ── Agent Memory ──
|
|
9145
|
+
async getMemory(agentId, category, limit = 50) {
|
|
9146
|
+
const params = new URLSearchParams({ limit: String(limit) });
|
|
9147
|
+
if (category) params.set("category", category);
|
|
9148
|
+
return this.request("GET", `/agents/${agentId}/memory?${params}`);
|
|
9149
|
+
}
|
|
9150
|
+
async setMemory(agentId, category, key, content, metadata) {
|
|
9151
|
+
return this.request("PUT", `/agents/${agentId}/memory`, {
|
|
9152
|
+
category,
|
|
9153
|
+
key,
|
|
9154
|
+
content,
|
|
9155
|
+
metadata
|
|
9156
|
+
});
|
|
9157
|
+
}
|
|
9158
|
+
async deleteMemory(agentId, memoryId) {
|
|
9159
|
+
await this.request("DELETE", `/agents/${agentId}/memory/${memoryId}`);
|
|
9160
|
+
}
|
|
9161
|
+
// ── Agent Focus ──
|
|
9162
|
+
async setFocus(agentId, taskId) {
|
|
9163
|
+
await this.request("PUT", `/agents/${agentId}/focus`, { taskId });
|
|
9164
|
+
}
|
|
9165
|
+
async clearFocus(agentId) {
|
|
9166
|
+
await this.request("DELETE", `/agents/${agentId}/focus`);
|
|
9167
|
+
}
|
|
9168
|
+
// ── Tasks ──
|
|
9169
|
+
async createTask(task) {
|
|
9170
|
+
return this.request("POST", "/tasks", task);
|
|
9171
|
+
}
|
|
9172
|
+
async getTask(taskId) {
|
|
9173
|
+
return this.request("GET", `/tasks/${taskId}`);
|
|
9174
|
+
}
|
|
9175
|
+
async updateTask(taskId, updates) {
|
|
9176
|
+
return this.request("PATCH", `/tasks/${taskId}`, updates);
|
|
9177
|
+
}
|
|
9178
|
+
async getMyTasks(status) {
|
|
9179
|
+
const params = status ? `?status=${status}` : "";
|
|
9180
|
+
return this.request("GET", `/tasks/mine${params}`);
|
|
9181
|
+
}
|
|
9182
|
+
async getNextTask() {
|
|
9183
|
+
try {
|
|
9184
|
+
return await this.request("GET", "/tasks/next");
|
|
9185
|
+
} catch (err) {
|
|
9186
|
+
if (err.status === 404) return null;
|
|
9187
|
+
throw err;
|
|
9188
|
+
}
|
|
9189
|
+
}
|
|
9190
|
+
async logActivity(taskId, type, message, metadata) {
|
|
9191
|
+
return this.request("POST", `/tasks/${taskId}/activity`, {
|
|
9192
|
+
type,
|
|
9193
|
+
message,
|
|
9194
|
+
metadata
|
|
9195
|
+
});
|
|
9196
|
+
}
|
|
9197
|
+
async getTaskContext(taskId) {
|
|
9198
|
+
try {
|
|
9199
|
+
return await this.request("GET", `/tasks/${taskId}/context`);
|
|
9200
|
+
} catch (err) {
|
|
9201
|
+
if (err.status === 404) return null;
|
|
9202
|
+
throw err;
|
|
9203
|
+
}
|
|
9204
|
+
}
|
|
9205
|
+
async updateTaskContext(taskId, context) {
|
|
9206
|
+
return this.request("PATCH", `/tasks/${taskId}/context`, context);
|
|
9207
|
+
}
|
|
9208
|
+
async appendTaskContext(taskId, field, values) {
|
|
9209
|
+
const current = await this.getTaskContext(taskId);
|
|
9210
|
+
const existing = current?.[field] ?? [];
|
|
9211
|
+
const merged = [...existing, ...values.filter((v) => !existing.includes(v))];
|
|
9212
|
+
return this.request("PATCH", `/tasks/${taskId}/context`, {
|
|
9213
|
+
[field]: merged
|
|
9214
|
+
});
|
|
9215
|
+
}
|
|
9216
|
+
async searchTasks(query, scope = "all") {
|
|
9217
|
+
return this.request("GET", `/tasks/search?q=${encodeURIComponent(query)}&scope=${scope}`);
|
|
9218
|
+
}
|
|
9219
|
+
async handoffTask(taskId, toHandle, summary, instructions, reason, state) {
|
|
9220
|
+
return this.request("POST", `/tasks/${taskId}/handoff`, {
|
|
9221
|
+
toHandle,
|
|
9222
|
+
reason: reason || "Handoff",
|
|
9223
|
+
summary,
|
|
9224
|
+
instructions,
|
|
9225
|
+
state
|
|
9226
|
+
});
|
|
9227
|
+
}
|
|
9228
|
+
// ── Projects ──
|
|
9229
|
+
async createProject(project) {
|
|
9230
|
+
return this.request("POST", "/projects", project);
|
|
9231
|
+
}
|
|
9232
|
+
async getProject(projectId) {
|
|
9233
|
+
return this.request("GET", `/projects/${projectId}`);
|
|
9234
|
+
}
|
|
9235
|
+
async getProjectContext(projectId) {
|
|
9236
|
+
try {
|
|
9237
|
+
return await this.request("GET", `/projects/${projectId}/context`);
|
|
9238
|
+
} catch (err) {
|
|
9239
|
+
if (err.status === 404) return null;
|
|
9240
|
+
throw err;
|
|
9241
|
+
}
|
|
9242
|
+
}
|
|
9243
|
+
async updateProjectContext(projectId, context) {
|
|
9244
|
+
return this.request("PUT", `/projects/${projectId}/context`, context);
|
|
9245
|
+
}
|
|
9246
|
+
// ── Schedule ──
|
|
9247
|
+
async syncSchedule(events) {
|
|
9248
|
+
return this.request("PUT", "/schedule/sync", { events });
|
|
9249
|
+
}
|
|
9250
|
+
async recordOccurrence(eventId, scheduledAt, status, result) {
|
|
9251
|
+
return this.request("POST", `/schedule/${eventId}/occurrences`, {
|
|
9252
|
+
scheduledAt,
|
|
9253
|
+
status,
|
|
9254
|
+
result
|
|
9255
|
+
});
|
|
9256
|
+
}
|
|
9257
|
+
// ── Utility ──
|
|
9258
|
+
getRateLimitRemaining() {
|
|
9259
|
+
return this.rateLimitRemaining;
|
|
9260
|
+
}
|
|
9261
|
+
getBaseUrl() {
|
|
9262
|
+
return this.baseUrl;
|
|
9263
|
+
}
|
|
9264
|
+
getApiKey() {
|
|
9265
|
+
return this.apiKey;
|
|
9266
|
+
}
|
|
9267
|
+
};
|
|
9268
|
+
|
|
9269
|
+
// packages/runtime/src/sesame-sync.ts
|
|
9270
|
+
function defaultSyncState() {
|
|
9271
|
+
return {
|
|
9272
|
+
lastStatePush: null,
|
|
9273
|
+
lastL3Sync: null,
|
|
9274
|
+
syncedL3Keys: [],
|
|
9275
|
+
lastMemoryMdHash: null,
|
|
9276
|
+
lastWake: null
|
|
9277
|
+
};
|
|
9278
|
+
}
|
|
9279
|
+
function simpleHash(str) {
|
|
9280
|
+
let hash = 0;
|
|
9281
|
+
for (let i = 0; i < str.length; i++) {
|
|
9282
|
+
const char = str.charCodeAt(i);
|
|
9283
|
+
hash = (hash << 5) - hash + char;
|
|
9284
|
+
hash = hash & hash;
|
|
9285
|
+
}
|
|
9286
|
+
return hash.toString(36);
|
|
9287
|
+
}
|
|
9288
|
+
var SesameSync = class {
|
|
9289
|
+
api;
|
|
9290
|
+
agentId;
|
|
9291
|
+
workspaceDir;
|
|
9292
|
+
dataDir;
|
|
9293
|
+
syncState;
|
|
9294
|
+
syncStatePath;
|
|
9295
|
+
syncInterval = null;
|
|
9296
|
+
constructor(config, agentId, workspaceDir, dataDir) {
|
|
9297
|
+
this.api = new SesameAPI(config);
|
|
9298
|
+
this.agentId = agentId;
|
|
9299
|
+
this.workspaceDir = workspaceDir;
|
|
9300
|
+
this.dataDir = dataDir;
|
|
9301
|
+
this.syncStatePath = resolve20(dataDir, "sesame-sync-state.json");
|
|
9302
|
+
this.syncState = this.loadSyncState();
|
|
9303
|
+
}
|
|
9304
|
+
// ── State Persistence ──
|
|
9305
|
+
loadSyncState() {
|
|
9306
|
+
try {
|
|
9307
|
+
if (existsSync21(this.syncStatePath)) {
|
|
9308
|
+
return JSON.parse(readFileSync16(this.syncStatePath, "utf-8"));
|
|
9309
|
+
}
|
|
9310
|
+
} catch (err) {
|
|
9311
|
+
console.warn("[sesame-sync] Failed to load sync state:", err.message);
|
|
9312
|
+
}
|
|
9313
|
+
return defaultSyncState();
|
|
9314
|
+
}
|
|
9315
|
+
saveSyncState() {
|
|
9316
|
+
try {
|
|
9317
|
+
const dir = dirname8(this.syncStatePath);
|
|
9318
|
+
if (!existsSync21(dir)) mkdirSync16(dir, { recursive: true });
|
|
9319
|
+
writeFileSync9(this.syncStatePath, JSON.stringify(this.syncState, null, 2));
|
|
9320
|
+
} catch (err) {
|
|
9321
|
+
console.warn("[sesame-sync] Failed to save sync state:", err.message);
|
|
9322
|
+
}
|
|
9323
|
+
}
|
|
9324
|
+
// ── Push: MEMORY.md -> Agent State ──
|
|
9325
|
+
/**
|
|
9326
|
+
* Read MEMORY.md from workspace and push to Sesame agent state.
|
|
9327
|
+
* Only pushes if content has changed since last push.
|
|
9328
|
+
*/
|
|
9329
|
+
async pushState() {
|
|
9330
|
+
const memoryMdPath = resolve20(this.workspaceDir, "MEMORY.md");
|
|
9331
|
+
if (!existsSync21(memoryMdPath)) {
|
|
9332
|
+
console.log("[sesame-sync] No MEMORY.md found, skipping state push");
|
|
9333
|
+
return false;
|
|
9334
|
+
}
|
|
9335
|
+
const content = readFileSync16(memoryMdPath, "utf-8");
|
|
9336
|
+
const hash = simpleHash(content);
|
|
9337
|
+
if (hash === this.syncState.lastMemoryMdHash) {
|
|
9338
|
+
return false;
|
|
9339
|
+
}
|
|
9340
|
+
try {
|
|
9341
|
+
await this.api.setState(this.agentId, {
|
|
9342
|
+
memoryMd: content,
|
|
9343
|
+
memoryMdHash: hash,
|
|
9344
|
+
lastSync: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9345
|
+
source: "hivemind-sync"
|
|
9346
|
+
}, "memory", 172800);
|
|
9347
|
+
this.syncState.lastMemoryMdHash = hash;
|
|
9348
|
+
this.syncState.lastStatePush = (/* @__PURE__ */ new Date()).toISOString();
|
|
9349
|
+
this.saveSyncState();
|
|
9350
|
+
console.log("[sesame-sync] Pushed MEMORY.md to agent state");
|
|
9351
|
+
return true;
|
|
9352
|
+
} catch (err) {
|
|
9353
|
+
console.error("[sesame-sync] Failed to push state:", err.message);
|
|
9354
|
+
return false;
|
|
9355
|
+
}
|
|
9356
|
+
}
|
|
9357
|
+
// ── Push: Working Context -> Agent State ──
|
|
9358
|
+
/**
|
|
9359
|
+
* Push current working context (focus, active work, etc.) to agent state.
|
|
9360
|
+
* This is the "default" namespace for quick session recovery.
|
|
9361
|
+
*/
|
|
9362
|
+
async pushWorkingContext(context) {
|
|
9363
|
+
try {
|
|
9364
|
+
await this.api.setState(this.agentId, {
|
|
9365
|
+
...context,
|
|
9366
|
+
lastSync: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9367
|
+
source: "hivemind-sync"
|
|
9368
|
+
}, "default", 86400);
|
|
9369
|
+
console.log("[sesame-sync] Pushed working context to agent state");
|
|
9370
|
+
return true;
|
|
9371
|
+
} catch (err) {
|
|
9372
|
+
console.error("[sesame-sync] Failed to push working context:", err.message);
|
|
9373
|
+
return false;
|
|
9374
|
+
}
|
|
9375
|
+
}
|
|
9376
|
+
// ── L3 Classification ──
|
|
9377
|
+
/**
|
|
9378
|
+
* Classify an L3 entry into a Sesame memory category based on content keywords.
|
|
9379
|
+
* Categories: people, preferences, projects, context-recovery, lessons (default)
|
|
9380
|
+
*/
|
|
9381
|
+
classifyL3Entry(content) {
|
|
9382
|
+
const lower = content.toLowerCase();
|
|
9383
|
+
const peoplePatterns = [
|
|
9384
|
+
/\b(ryan|bailey|caitlin|human|user|agent)\b/,
|
|
9385
|
+
/\bwork(ing|ed|s)?\s+(with|together|alongside)\b/,
|
|
9386
|
+
/\b(collaborat|communicat|partner|teammate)/,
|
|
9387
|
+
/\b(prefers?|expects?|wants?)\s+(me|us|the agent)\b/
|
|
9388
|
+
];
|
|
9389
|
+
if (peoplePatterns.some((p) => p.test(lower))) return "people";
|
|
9390
|
+
const prefPatterns = [
|
|
9391
|
+
/\b(i prefer|always use|never use|best practice|my workflow)\b/,
|
|
9392
|
+
/\b(default to|opt for|choose|convention)\b/,
|
|
9393
|
+
/\b(style|format|approach|pattern)\s+(is|should be|works best)\b/
|
|
9394
|
+
];
|
|
9395
|
+
if (prefPatterns.some((p) => p.test(lower))) return "preferences";
|
|
9396
|
+
const projectPatterns = [
|
|
9397
|
+
/\b(hivemind|sesame|openclaw)\b/,
|
|
9398
|
+
/\b(repo(sitory)?|codebase|project|package)\b/,
|
|
9399
|
+
/\bgithub\.com\b/,
|
|
9400
|
+
/\b(src\/|packages\/|\.ts|\.js|\.py)\b/
|
|
9401
|
+
];
|
|
9402
|
+
if (projectPatterns.some((p) => p.test(lower))) return "projects";
|
|
9403
|
+
const recoveryPatterns = [
|
|
9404
|
+
/\b(recover|restart|compaction|cold start|wake)\b/,
|
|
9405
|
+
/\b(rebuild|restore|flush|checkpoint)\b/,
|
|
9406
|
+
/\b(memory\.md|agent state|sync state)\b/
|
|
9407
|
+
];
|
|
9408
|
+
if (recoveryPatterns.some((p) => p.test(lower))) return "context-recovery";
|
|
9409
|
+
return "lessons";
|
|
9410
|
+
}
|
|
9411
|
+
// ── Push: L3 Knowledge -> Agent Memory ──
|
|
9412
|
+
/**
|
|
9413
|
+
* Sync L3 promoted knowledge to Sesame agent memory.
|
|
9414
|
+
* Classifies entries into categories: people, preferences, projects, context-recovery, lessons.
|
|
9415
|
+
* Only pushes entries that haven't been synced yet.
|
|
9416
|
+
*
|
|
9417
|
+
* @param memoryDaemonUrl - URL of the local memory daemon
|
|
9418
|
+
*/
|
|
9419
|
+
async syncL3ToMemory(memoryDaemonUrl) {
|
|
9420
|
+
let l3Entries;
|
|
9421
|
+
try {
|
|
9422
|
+
const response = await fetch(`${memoryDaemonUrl}/promotion/l3?context=global`);
|
|
9423
|
+
if (!response.ok) {
|
|
9424
|
+
console.warn("[sesame-sync] Failed to fetch L3 knowledge:", response.status);
|
|
9425
|
+
return 0;
|
|
9426
|
+
}
|
|
9427
|
+
const data = await response.json();
|
|
9428
|
+
l3Entries = data.entries || data || [];
|
|
9429
|
+
} catch (err) {
|
|
9430
|
+
console.error("[sesame-sync] Failed to fetch L3 knowledge:", err.message);
|
|
9431
|
+
return 0;
|
|
9432
|
+
}
|
|
9433
|
+
if (!Array.isArray(l3Entries) || l3Entries.length === 0) return 0;
|
|
9434
|
+
let synced = 0;
|
|
9435
|
+
const syncedKeys = new Set(this.syncState.syncedL3Keys);
|
|
9436
|
+
for (const entry of l3Entries) {
|
|
9437
|
+
const key = entry.key || `l3-${simpleHash(entry.content)}`;
|
|
9438
|
+
if (syncedKeys.has(key)) continue;
|
|
9439
|
+
try {
|
|
9440
|
+
const category = this.classifyL3Entry(entry.content);
|
|
9441
|
+
await this.api.setMemory(
|
|
9442
|
+
this.agentId,
|
|
9443
|
+
category,
|
|
9444
|
+
key,
|
|
9445
|
+
entry.content,
|
|
9446
|
+
{
|
|
9447
|
+
source: "hivemind-l3-promotion",
|
|
9448
|
+
l3Score: entry.score,
|
|
9449
|
+
l3Context: entry.context || "global",
|
|
9450
|
+
l3Category: category,
|
|
9451
|
+
syncedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9452
|
+
}
|
|
9453
|
+
);
|
|
9454
|
+
syncedKeys.add(key);
|
|
9455
|
+
synced++;
|
|
9456
|
+
} catch (err) {
|
|
9457
|
+
console.warn(`[sesame-sync] Failed to sync L3 entry "${key}":`, err.message);
|
|
9458
|
+
}
|
|
9459
|
+
}
|
|
9460
|
+
if (synced > 0) {
|
|
9461
|
+
this.syncState.syncedL3Keys = Array.from(syncedKeys);
|
|
9462
|
+
this.syncState.lastL3Sync = (/* @__PURE__ */ new Date()).toISOString();
|
|
9463
|
+
this.saveSyncState();
|
|
9464
|
+
console.log(`[sesame-sync] Synced ${synced} L3 entries to Sesame agent memory`);
|
|
9465
|
+
}
|
|
9466
|
+
return synced;
|
|
9467
|
+
}
|
|
9468
|
+
// ── Pull: Wake -> Rebuild Context ──
|
|
9469
|
+
/**
|
|
9470
|
+
* Call the wake endpoint and return structured data for context rebuilding.
|
|
9471
|
+
* This is the primary recovery mechanism after restart/compaction.
|
|
9472
|
+
*/
|
|
9473
|
+
async pullOnWake() {
|
|
9474
|
+
try {
|
|
9475
|
+
const wake = await this.api.wake(this.agentId);
|
|
9476
|
+
this.syncState.lastWake = (/* @__PURE__ */ new Date()).toISOString();
|
|
9477
|
+
this.saveSyncState();
|
|
9478
|
+
console.log("[sesame-sync] Wake data retrieved successfully");
|
|
9479
|
+
return wake;
|
|
9480
|
+
} catch (err) {
|
|
9481
|
+
console.error("[sesame-sync] Wake failed:", err.message);
|
|
9482
|
+
return null;
|
|
9483
|
+
}
|
|
9484
|
+
}
|
|
9485
|
+
// ── Pull: Recent Channel Messages ──
|
|
9486
|
+
/**
|
|
9487
|
+
* Pull recent messages from unread channels after wake.
|
|
9488
|
+
* Wake gives tasks/state but not conversational context — this fills that gap.
|
|
9489
|
+
*
|
|
9490
|
+
* @param wakeResponse - The response from pullOnWake()
|
|
9491
|
+
* @param limit - Messages per channel (default: 30)
|
|
9492
|
+
* @returns Map of channelId -> messages
|
|
9493
|
+
*/
|
|
9494
|
+
async pullRecentMessages(wakeResponse, limit = 30) {
|
|
9495
|
+
const result = /* @__PURE__ */ new Map();
|
|
9496
|
+
const unreadChannels = wakeResponse.unreads || [];
|
|
9497
|
+
if (unreadChannels.length === 0) {
|
|
9498
|
+
console.log("[sesame-sync] No unread channels to pull messages from");
|
|
9499
|
+
return result;
|
|
9500
|
+
}
|
|
9501
|
+
for (const channel of unreadChannels) {
|
|
9502
|
+
const channelId = channel.channelId || channel.id;
|
|
9503
|
+
if (!channelId) continue;
|
|
9504
|
+
try {
|
|
9505
|
+
const response = await fetch(
|
|
9506
|
+
`${this.api.getBaseUrl()}/channels/${channelId}/messages?limit=${limit}&direction=before`,
|
|
9507
|
+
{
|
|
9508
|
+
headers: {
|
|
9509
|
+
Authorization: `Bearer ${this.api.getApiKey()}`,
|
|
9510
|
+
"Content-Type": "application/json"
|
|
9511
|
+
}
|
|
9512
|
+
}
|
|
9513
|
+
);
|
|
9514
|
+
if (!response.ok) {
|
|
9515
|
+
console.warn(`[sesame-sync] Failed to fetch messages for channel ${channelId}: ${response.status}`);
|
|
9516
|
+
continue;
|
|
9517
|
+
}
|
|
9518
|
+
const data = await response.json();
|
|
9519
|
+
const messages = (data.messages || data.data || []).map((msg) => ({
|
|
9520
|
+
content: msg.content || msg.plaintext || "",
|
|
9521
|
+
sender: msg.senderHandle || msg.sender || "unknown",
|
|
9522
|
+
timestamp: msg.createdAt || msg.timestamp || ""
|
|
9523
|
+
}));
|
|
9524
|
+
if (messages.length > 0) {
|
|
9525
|
+
result.set(channelId, messages);
|
|
9526
|
+
}
|
|
9527
|
+
} catch (err) {
|
|
9528
|
+
console.warn(`[sesame-sync] Failed to pull messages for ${channelId}:`, err.message);
|
|
9529
|
+
}
|
|
9530
|
+
}
|
|
9531
|
+
console.log(`[sesame-sync] Pulled messages from ${result.size} channels`);
|
|
9532
|
+
return result;
|
|
9533
|
+
}
|
|
9534
|
+
// ── Pull: Agent State -> MEMORY.md Recovery ──
|
|
9535
|
+
/**
|
|
9536
|
+
* Pull agent state and restore MEMORY.md if local copy is missing or empty.
|
|
9537
|
+
*/
|
|
9538
|
+
async pullStateToMemoryMd() {
|
|
9539
|
+
const memoryMdPath = resolve20(this.workspaceDir, "MEMORY.md");
|
|
9540
|
+
if (existsSync21(memoryMdPath)) {
|
|
9541
|
+
const content = readFileSync16(memoryMdPath, "utf-8").trim();
|
|
9542
|
+
if (content.length > 0) {
|
|
9543
|
+
return false;
|
|
9544
|
+
}
|
|
9545
|
+
}
|
|
9546
|
+
try {
|
|
9547
|
+
const state = await this.api.getState(this.agentId, "memory");
|
|
9548
|
+
if (!state || !state.state.memoryMd) {
|
|
9549
|
+
console.log("[sesame-sync] No MEMORY.md found in agent state");
|
|
9550
|
+
return false;
|
|
9551
|
+
}
|
|
9552
|
+
writeFileSync9(memoryMdPath, state.state.memoryMd);
|
|
9553
|
+
console.log("[sesame-sync] Restored MEMORY.md from agent state");
|
|
9554
|
+
return true;
|
|
9555
|
+
} catch (err) {
|
|
9556
|
+
console.error("[sesame-sync] Failed to pull state:", err.message);
|
|
9557
|
+
return false;
|
|
9558
|
+
}
|
|
9559
|
+
}
|
|
9560
|
+
// ── Pre-Compaction Flush ──
|
|
9561
|
+
/**
|
|
9562
|
+
* Safety flush before compaction risk.
|
|
9563
|
+
* Pushes MEMORY.md to agent state and updates focused task context.
|
|
9564
|
+
*/
|
|
9565
|
+
async flushBeforeCompaction(focusedTaskId, contextSummary) {
|
|
9566
|
+
console.log("[sesame-sync] Pre-compaction flush starting...");
|
|
9567
|
+
await this.pushState();
|
|
9568
|
+
if (contextSummary) {
|
|
9569
|
+
await this.pushWorkingContext({
|
|
9570
|
+
preCompactionFlush: true,
|
|
9571
|
+
contextSummary,
|
|
9572
|
+
focusedTaskId,
|
|
9573
|
+
flushedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9574
|
+
});
|
|
9575
|
+
}
|
|
9576
|
+
if (focusedTaskId && contextSummary) {
|
|
9577
|
+
try {
|
|
9578
|
+
await this.api.updateTaskContext(focusedTaskId, {
|
|
9579
|
+
notes: `[Pre-compaction flush ${(/* @__PURE__ */ new Date()).toISOString()}] ${contextSummary}`
|
|
9580
|
+
});
|
|
9581
|
+
console.log("[sesame-sync] Updated focused task context");
|
|
9582
|
+
} catch (err) {
|
|
9583
|
+
console.warn("[sesame-sync] Failed to update task context:", err.message);
|
|
9584
|
+
}
|
|
9585
|
+
}
|
|
9586
|
+
console.log("[sesame-sync] Pre-compaction flush complete");
|
|
9587
|
+
}
|
|
9588
|
+
// ── Task Helpers ──
|
|
9589
|
+
/**
|
|
9590
|
+
* Log progress on a Sesame task.
|
|
9591
|
+
*/
|
|
9592
|
+
async logTaskProgress(taskId, message) {
|
|
9593
|
+
try {
|
|
9594
|
+
await this.api.logActivity(taskId, "progress", message);
|
|
9595
|
+
} catch (err) {
|
|
9596
|
+
console.warn("[sesame-sync] Failed to log task progress:", err.message);
|
|
9597
|
+
}
|
|
9598
|
+
}
|
|
9599
|
+
/**
|
|
9600
|
+
* Log a decision on a Sesame task.
|
|
9601
|
+
*/
|
|
9602
|
+
async logTaskDecision(taskId, message) {
|
|
9603
|
+
try {
|
|
9604
|
+
await this.api.logActivity(taskId, "decision", message);
|
|
9605
|
+
} catch (err) {
|
|
9606
|
+
console.warn("[sesame-sync] Failed to log task decision:", err.message);
|
|
9607
|
+
}
|
|
9608
|
+
}
|
|
9609
|
+
/**
|
|
9610
|
+
* Update a task's context block.
|
|
9611
|
+
*/
|
|
9612
|
+
async updateTaskContext(taskId, context) {
|
|
9613
|
+
try {
|
|
9614
|
+
await this.api.updateTaskContext(taskId, context);
|
|
9615
|
+
} catch (err) {
|
|
9616
|
+
console.warn("[sesame-sync] Failed to update task context:", err.message);
|
|
9617
|
+
}
|
|
9618
|
+
}
|
|
9619
|
+
// ── Periodic Sync ──
|
|
9620
|
+
/**
|
|
9621
|
+
* Start periodic background sync.
|
|
9622
|
+
* Pushes state every intervalMs (default: 5 minutes).
|
|
9623
|
+
* Syncs L3 every l3IntervalMs (default: 30 minutes).
|
|
9624
|
+
*/
|
|
9625
|
+
startPeriodicSync(memoryDaemonUrl, intervalMs = 5 * 60 * 1e3, l3IntervalMs = 30 * 60 * 1e3) {
|
|
9626
|
+
if (this.syncInterval) return;
|
|
9627
|
+
let l3Counter = 0;
|
|
9628
|
+
const l3Every = Math.max(1, Math.floor(l3IntervalMs / intervalMs));
|
|
9629
|
+
this.syncInterval = setInterval(async () => {
|
|
9630
|
+
try {
|
|
9631
|
+
await this.pushState();
|
|
9632
|
+
l3Counter++;
|
|
9633
|
+
if (l3Counter >= l3Every) {
|
|
9634
|
+
l3Counter = 0;
|
|
9635
|
+
await this.syncL3ToMemory(memoryDaemonUrl);
|
|
9636
|
+
}
|
|
9637
|
+
} catch (err) {
|
|
9638
|
+
console.error("[sesame-sync] Periodic sync error:", err.message);
|
|
9639
|
+
}
|
|
9640
|
+
}, intervalMs);
|
|
9641
|
+
console.log(`[sesame-sync] Periodic sync started (state: ${intervalMs / 1e3}s, L3: ${l3IntervalMs / 1e3}s)`);
|
|
9642
|
+
}
|
|
9643
|
+
/**
|
|
9644
|
+
* Stop periodic sync.
|
|
9645
|
+
*/
|
|
9646
|
+
stopPeriodicSync() {
|
|
9647
|
+
if (this.syncInterval) {
|
|
9648
|
+
clearInterval(this.syncInterval);
|
|
9649
|
+
this.syncInterval = null;
|
|
9650
|
+
console.log("[sesame-sync] Periodic sync stopped");
|
|
9651
|
+
}
|
|
9652
|
+
}
|
|
9653
|
+
// ── Accessors ──
|
|
9654
|
+
getApi() {
|
|
9655
|
+
return this.api;
|
|
9656
|
+
}
|
|
9657
|
+
getSyncState() {
|
|
9658
|
+
return { ...this.syncState };
|
|
9659
|
+
}
|
|
9660
|
+
getAgentId() {
|
|
9661
|
+
return this.agentId;
|
|
9662
|
+
}
|
|
9663
|
+
};
|
|
9664
|
+
|
|
8961
9665
|
// packages/runtime/src/pipeline.ts
|
|
8962
9666
|
var PACKAGE_VERSION = "unknown";
|
|
8963
9667
|
try {
|
|
8964
|
-
const __dirname2 =
|
|
8965
|
-
const pkg = JSON.parse(
|
|
9668
|
+
const __dirname2 = dirname9(fileURLToPath3(import.meta.url));
|
|
9669
|
+
const pkg = JSON.parse(readFileSync17(resolve21(__dirname2, "../package.json"), "utf-8"));
|
|
8966
9670
|
PACKAGE_VERSION = pkg.version ?? "unknown";
|
|
8967
9671
|
} catch {
|
|
8968
9672
|
}
|
|
@@ -9004,12 +9708,12 @@ function startHealthServer(port) {
|
|
|
9004
9708
|
return server;
|
|
9005
9709
|
}
|
|
9006
9710
|
function writePidFile(path) {
|
|
9007
|
-
|
|
9711
|
+
writeFileSync10(path, String(process.pid));
|
|
9008
9712
|
console.log(`[hivemind] PID file written: ${path}`);
|
|
9009
9713
|
}
|
|
9010
9714
|
function cleanupPidFile(path) {
|
|
9011
9715
|
try {
|
|
9012
|
-
|
|
9716
|
+
unlinkSync4(path);
|
|
9013
9717
|
} catch {
|
|
9014
9718
|
}
|
|
9015
9719
|
}
|
|
@@ -9045,12 +9749,12 @@ async function startPipeline(configPath) {
|
|
|
9045
9749
|
console.log("[hivemind] Global context already exists in memory daemon");
|
|
9046
9750
|
}
|
|
9047
9751
|
}
|
|
9048
|
-
const requestLogger = new RequestLogger(
|
|
9752
|
+
const requestLogger = new RequestLogger(resolve21(dirname9(configPath), "data", "dashboard.db"));
|
|
9049
9753
|
const agent = new Agent(config);
|
|
9050
9754
|
startDashboardServer(requestLogger, config.memory, () => agent.getConversationHistories());
|
|
9051
9755
|
agent.setRequestLogger(requestLogger);
|
|
9052
|
-
const hivemindHome = process.env.HIVEMIND_HOME ||
|
|
9053
|
-
const workspaceDir =
|
|
9756
|
+
const hivemindHome = process.env.HIVEMIND_HOME || resolve21(process.env.HOME || "/root", "hivemind");
|
|
9757
|
+
const workspaceDir = resolve21(hivemindHome, config.agent.workspace || "workspace");
|
|
9054
9758
|
let contextBridge;
|
|
9055
9759
|
if (memoryConnected) {
|
|
9056
9760
|
try {
|
|
@@ -9084,7 +9788,7 @@ async function startPipeline(configPath) {
|
|
|
9084
9788
|
agent.setToolRegistry(toolRegistry);
|
|
9085
9789
|
console.log(`[hivemind] Context manager initialized (active: ${agent.getActiveContext()})`);
|
|
9086
9790
|
if (config.auto_debug?.enabled) {
|
|
9087
|
-
const dataDir2 =
|
|
9791
|
+
const dataDir2 = resolve21(hivemindHome, "data");
|
|
9088
9792
|
const autoDebugConfig = config.auto_debug;
|
|
9089
9793
|
const logWatcher = new LogWatcher(
|
|
9090
9794
|
{
|
|
@@ -9126,7 +9830,7 @@ async function startPipeline(configPath) {
|
|
|
9126
9830
|
process.on("exit", () => processManager.stop());
|
|
9127
9831
|
console.log("[hivemind] Auto-debug processors started");
|
|
9128
9832
|
}
|
|
9129
|
-
const dataDir =
|
|
9833
|
+
const dataDir = resolve21(hivemindHome, "data");
|
|
9130
9834
|
if (config.sesame.api_key) {
|
|
9131
9835
|
await startSesameLoop(config, agent, toolRegistry, dataDir);
|
|
9132
9836
|
} else {
|
|
@@ -9137,6 +9841,7 @@ async function startPipeline(configPath) {
|
|
|
9137
9841
|
async function startSesameLoop(config, agent, toolRegistry, dataDir) {
|
|
9138
9842
|
const sesame = new SesameClient2(config.sesame);
|
|
9139
9843
|
registerMessagingTools(toolRegistry, sesame);
|
|
9844
|
+
let sesameSync = null;
|
|
9140
9845
|
let eventsWatcher = null;
|
|
9141
9846
|
if (dataDir) {
|
|
9142
9847
|
eventsWatcher = new EventsWatcher(dataDir, async (channelId, text, filename, eventType) => {
|
|
@@ -9159,11 +9864,20 @@ async function startSesameLoop(config, agent, toolRegistry, dataDir) {
|
|
|
9159
9864
|
eventsWatcher.start();
|
|
9160
9865
|
}
|
|
9161
9866
|
let shuttingDown = false;
|
|
9162
|
-
const shutdown = (signal) => {
|
|
9867
|
+
const shutdown = async (signal) => {
|
|
9163
9868
|
if (shuttingDown) return;
|
|
9164
9869
|
shuttingDown = true;
|
|
9165
9870
|
console.log(`
|
|
9166
9871
|
[hivemind] Received ${signal}, shutting down...`);
|
|
9872
|
+
if (sesameSync) {
|
|
9873
|
+
try {
|
|
9874
|
+
await sesameSync.flushBeforeCompaction(void 0, `Shutdown via ${signal}`);
|
|
9875
|
+
sesameSync.stopPeriodicSync();
|
|
9876
|
+
console.log("[hivemind] Sesame state flushed before shutdown");
|
|
9877
|
+
} catch (err) {
|
|
9878
|
+
console.warn("[hivemind] Sesame flush failed:", err.message);
|
|
9879
|
+
}
|
|
9880
|
+
}
|
|
9167
9881
|
try {
|
|
9168
9882
|
sesame.updatePresence("offline", { emoji: "\u2B58" });
|
|
9169
9883
|
sesame.disconnect();
|
|
@@ -9253,6 +9967,21 @@ async function startSesameLoop(config, agent, toolRegistry, dataDir) {
|
|
|
9253
9967
|
await sesame.connect();
|
|
9254
9968
|
sesameConnected = true;
|
|
9255
9969
|
console.log("[hivemind] Listening for Sesame messages");
|
|
9970
|
+
const agentId = sesame.getAgentId();
|
|
9971
|
+
if (agentId && dataDir) {
|
|
9972
|
+
const hivemindHome = process.env.HIVEMIND_HOME || resolve21(process.env.HOME || "/root", "hivemind");
|
|
9973
|
+
const workspaceDir = resolve21(hivemindHome, config.agent.workspace || "workspace");
|
|
9974
|
+
sesameSync = new SesameSync(config.sesame, agentId, workspaceDir, dataDir);
|
|
9975
|
+
try {
|
|
9976
|
+
await sesameSync.pushState();
|
|
9977
|
+
console.log("[hivemind] Initial Sesame state sync complete");
|
|
9978
|
+
} catch (err) {
|
|
9979
|
+
console.warn("[hivemind] Initial Sesame state sync failed:", err.message);
|
|
9980
|
+
}
|
|
9981
|
+
sesameSync.startPeriodicSync(config.memory.daemon_url, 5 * 60 * 1e3, 30 * 60 * 1e3);
|
|
9982
|
+
process.on("exit", () => sesameSync?.stopPeriodicSync());
|
|
9983
|
+
console.log("[hivemind] Sesame sync module initialized");
|
|
9984
|
+
}
|
|
9256
9985
|
await new Promise(() => {
|
|
9257
9986
|
});
|
|
9258
9987
|
}
|
|
@@ -9300,8 +10029,8 @@ ${response.content}
|
|
|
9300
10029
|
console.error("Error:", err.message);
|
|
9301
10030
|
}
|
|
9302
10031
|
});
|
|
9303
|
-
return new Promise((
|
|
9304
|
-
rl.on("close",
|
|
10032
|
+
return new Promise((resolve22) => {
|
|
10033
|
+
rl.on("close", resolve22);
|
|
9305
10034
|
});
|
|
9306
10035
|
}
|
|
9307
10036
|
async function runSpawnTask(config, configPath) {
|
|
@@ -9312,7 +10041,7 @@ async function runSpawnTask(config, configPath) {
|
|
|
9312
10041
|
const spawnDir = process.env.SPAWN_DIR;
|
|
9313
10042
|
console.log(`[spawn] Sub-agent starting (id: ${spawnId}, context: ${context})`);
|
|
9314
10043
|
const agent = new Agent(config, context);
|
|
9315
|
-
const hivemindHome = process.env.HIVEMIND_HOME ||
|
|
10044
|
+
const hivemindHome = process.env.HIVEMIND_HOME || resolve21(process.env.HOME || "/root", "hivemind");
|
|
9316
10045
|
const toolRegistry = registerAllTools(hivemindHome, {
|
|
9317
10046
|
enabled: true,
|
|
9318
10047
|
workspace: config.agent.workspace || "workspace",
|
|
@@ -9325,7 +10054,7 @@ async function runSpawnTask(config, configPath) {
|
|
|
9325
10054
|
const result = response.content;
|
|
9326
10055
|
console.log(`[spawn] Task completed (context: ${response.context})`);
|
|
9327
10056
|
if (spawnDir) {
|
|
9328
|
-
|
|
10057
|
+
writeFileSync10(resolve21(spawnDir, "result.txt"), result);
|
|
9329
10058
|
}
|
|
9330
10059
|
if (channelId && config.sesame.api_key) {
|
|
9331
10060
|
try {
|
|
@@ -9342,7 +10071,7 @@ async function runSpawnTask(config, configPath) {
|
|
|
9342
10071
|
const errorMsg = `[SPAWN ERROR] ${err.message}`;
|
|
9343
10072
|
console.error(`[spawn] ${errorMsg}`);
|
|
9344
10073
|
if (spawnDir) {
|
|
9345
|
-
|
|
10074
|
+
writeFileSync10(resolve21(spawnDir, "result.txt"), errorMsg);
|
|
9346
10075
|
}
|
|
9347
10076
|
process.exitCode = 1;
|
|
9348
10077
|
}
|
|
@@ -9373,20 +10102,20 @@ var WorkerServer = class {
|
|
|
9373
10102
|
}
|
|
9374
10103
|
/** Start listening. */
|
|
9375
10104
|
async start() {
|
|
9376
|
-
return new Promise((
|
|
10105
|
+
return new Promise((resolve22, reject) => {
|
|
9377
10106
|
this.server = createServer4((req, res) => this.handleRequest(req, res));
|
|
9378
10107
|
this.server.on("error", reject);
|
|
9379
|
-
this.server.listen(this.port, () =>
|
|
10108
|
+
this.server.listen(this.port, () => resolve22());
|
|
9380
10109
|
});
|
|
9381
10110
|
}
|
|
9382
10111
|
/** Stop the server. */
|
|
9383
10112
|
async stop() {
|
|
9384
|
-
return new Promise((
|
|
10113
|
+
return new Promise((resolve22) => {
|
|
9385
10114
|
if (!this.server) {
|
|
9386
|
-
|
|
10115
|
+
resolve22();
|
|
9387
10116
|
return;
|
|
9388
10117
|
}
|
|
9389
|
-
this.server.close(() =>
|
|
10118
|
+
this.server.close(() => resolve22());
|
|
9390
10119
|
});
|
|
9391
10120
|
}
|
|
9392
10121
|
getPort() {
|
|
@@ -9509,10 +10238,10 @@ var WorkerServer = class {
|
|
|
9509
10238
|
}
|
|
9510
10239
|
};
|
|
9511
10240
|
function readBody2(req) {
|
|
9512
|
-
return new Promise((
|
|
10241
|
+
return new Promise((resolve22, reject) => {
|
|
9513
10242
|
const chunks = [];
|
|
9514
10243
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
9515
|
-
req.on("end", () =>
|
|
10244
|
+
req.on("end", () => resolve22(Buffer.concat(chunks).toString("utf-8")));
|
|
9516
10245
|
req.on("error", reject);
|
|
9517
10246
|
});
|
|
9518
10247
|
}
|
|
@@ -9854,4 +10583,4 @@ smol-toml/dist/index.js:
|
|
|
9854
10583
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
9855
10584
|
*)
|
|
9856
10585
|
*/
|
|
9857
|
-
//# sourceMappingURL=chunk-
|
|
10586
|
+
//# sourceMappingURL=chunk-CEJEBZTB.js.map
|