@sesamespace/hivemind 0.3.0 → 0.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sesamespace/hivemind",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Cognitive architecture for AI agents with multi-layered memory",
5
5
  "scripts": {
6
6
  "build": "pnpm -r build",
@@ -0,0 +1,158 @@
1
+ import { execSync } from "child_process";
2
+ import { resolve } from "path";
3
+ import { existsSync, readFileSync } from "fs";
4
+
5
+ const PACKAGE_NAME = "@sesamespace/hivemind";
6
+
7
+ interface VersionInfo {
8
+ current: string;
9
+ latest: string;
10
+ updateAvailable: boolean;
11
+ }
12
+
13
+ function getCurrentVersion(): string {
14
+ try {
15
+ // Check the installed package version
16
+ const output = execSync(`npm ls -g ${PACKAGE_NAME} --json 2>/dev/null`, { encoding: "utf-8" });
17
+ const parsed = JSON.parse(output);
18
+ const deps = parsed.dependencies?.[PACKAGE_NAME];
19
+ if (deps?.version) return deps.version;
20
+ } catch {
21
+ // Fallback: read from the package.json in the hivemind dir
22
+ }
23
+
24
+ const hivemindHome = process.env.HIVEMIND_HOME || resolve(process.env.HOME || "~", "hivemind");
25
+ const pkgPath = resolve(hivemindHome, "node_modules", PACKAGE_NAME, "package.json");
26
+ if (existsSync(pkgPath)) {
27
+ try {
28
+ return JSON.parse(readFileSync(pkgPath, "utf-8")).version;
29
+ } catch {
30
+ // fall through
31
+ }
32
+ }
33
+
34
+ return "unknown";
35
+ }
36
+
37
+ function getLatestVersion(target?: string): string {
38
+ try {
39
+ const tag = target || "latest";
40
+ const output = execSync(`npm view ${PACKAGE_NAME}@${tag} version 2>/dev/null`, { encoding: "utf-8" });
41
+ return output.trim();
42
+ } catch {
43
+ throw new Error("Failed to check npm registry for latest version");
44
+ }
45
+ }
46
+
47
+ async function checkVersion(target?: string): Promise<VersionInfo> {
48
+ const current = getCurrentVersion();
49
+ const latest = target || getLatestVersion();
50
+ return {
51
+ current,
52
+ latest,
53
+ updateAvailable: current !== latest && current !== "unknown",
54
+ };
55
+ }
56
+
57
+ export async function runUpgradeCommand(args: string[]): Promise<void> {
58
+ const checkOnly = args.includes("--check") || args.includes("-c");
59
+ const force = args.includes("--force") || args.includes("-f");
60
+ const dryRun = args.includes("--dry-run") || args.includes("-n");
61
+
62
+ // Target version (optional positional arg)
63
+ const targetVersion = args.find((a) => !a.startsWith("-"));
64
+
65
+ if (args.includes("--help") || args.includes("-h")) {
66
+ printHelp();
67
+ return;
68
+ }
69
+
70
+ console.log(`
71
+ ╦ ╦╦╦ ╦╔═╗╔╦╗╦╔╗╔╔╦╗
72
+ ╠═╣║╚╗╔╝║╣ ║║║║║║║ ║║
73
+ ╩ ╩╩ ╚╝ ╚═╝╩ ╩╩╝╚╝═╩╝
74
+ Agent Upgrade
75
+ `);
76
+
77
+ // --- Check versions ---
78
+ console.log("→ Checking versions...");
79
+ const info = await checkVersion(targetVersion);
80
+ console.log(` Current: ${info.current}`);
81
+ console.log(` Latest: ${info.latest}`);
82
+
83
+ if (!info.updateAvailable && !force) {
84
+ console.log("\n ✓ Already up to date!");
85
+ return;
86
+ }
87
+
88
+ if (info.updateAvailable) {
89
+ console.log(`\n ⬆ Update available: ${info.current} → ${info.latest}`);
90
+ }
91
+
92
+ if (checkOnly) {
93
+ return;
94
+ }
95
+
96
+ // --- Perform upgrade ---
97
+ const target = targetVersion ? `${PACKAGE_NAME}@${targetVersion}` : `${PACKAGE_NAME}@latest`;
98
+
99
+ if (dryRun) {
100
+ console.log(`\n→ [dry-run] Would run: npm install -g ${target}`);
101
+ console.log("→ [dry-run] Would restart the agent service");
102
+ return;
103
+ }
104
+
105
+ console.log(`\n→ Installing ${target}...`);
106
+ try {
107
+ execSync(`npm install -g ${target}`, { stdio: "inherit" });
108
+ console.log(" ✓ Package updated");
109
+ } catch (err) {
110
+ console.error(` ✗ npm install failed: ${(err as Error).message}`);
111
+ process.exit(1);
112
+ }
113
+
114
+ // --- Restart agent ---
115
+ console.log("\n→ Restarting agent...");
116
+ try {
117
+ // Try launchctl first (macOS service)
118
+ const plistName = "com.hivemind.agent";
119
+ execSync(`launchctl list ${plistName} 2>/dev/null`, { encoding: "utf-8" });
120
+ // Service exists — restart it
121
+ execSync(`launchctl kickstart -k gui/$(id -u)/${plistName}`, { stdio: "inherit" });
122
+ console.log(" ✓ Agent restarted via launchd");
123
+ } catch {
124
+ // No launchd service — try finding and restarting the process
125
+ try {
126
+ execSync("pkill -f 'hivemind start'", { stdio: "inherit" });
127
+ console.log(" ✓ Old process killed");
128
+ console.log(" ! Start the agent manually: hivemind start");
129
+ } catch {
130
+ console.log(" ! No running agent found — start manually: hivemind start");
131
+ }
132
+ }
133
+
134
+ // --- Verify ---
135
+ const newVersion = getCurrentVersion();
136
+ console.log(`\n ✓ Upgrade complete: ${info.current} → ${newVersion}`);
137
+ }
138
+
139
+ function printHelp(): void {
140
+ console.log(`hivemind upgrade — Upgrade the Hivemind agent runtime
141
+
142
+ Usage: hivemind upgrade [version] [options]
143
+
144
+ Arguments:
145
+ version Target version (default: latest)
146
+
147
+ Options:
148
+ -c, --check Check for updates only (don't install)
149
+ -f, --force Force reinstall even if up to date
150
+ -n, --dry-run Show what would happen without doing it
151
+ -h, --help Show this help
152
+
153
+ Examples:
154
+ hivemind upgrade # Upgrade to latest
155
+ hivemind upgrade 0.3.0 # Upgrade to specific version
156
+ hivemind upgrade --check # Just check if update available
157
+ `);
158
+ }
@@ -4,6 +4,7 @@ import { runFleetCommand } from "./commands/fleet.js";
4
4
  import { runStartCommand } from "./commands/start.js";
5
5
  import { runInitCommand } from "./commands/init.js";
6
6
  import { runServiceCommand } from "./commands/service.js";
7
+ import { runUpgradeCommand } from "./commands/upgrade.js";
7
8
 
8
9
  const [command, ...args] = process.argv.slice(2);
9
10
 
@@ -36,14 +37,22 @@ switch (command) {
36
37
  });
37
38
  break;
38
39
 
40
+ case "upgrade":
41
+ runUpgradeCommand(args).catch((err) => {
42
+ console.error(err instanceof Error ? err.message : String(err));
43
+ process.exit(1);
44
+ });
45
+ break;
46
+
39
47
  default:
40
- console.log(`hivemind cli v0.1.0
48
+ console.log(`hivemind cli v0.3.0
41
49
 
42
50
  Usage: hivemind <command> [args]
43
51
 
44
52
  Commands:
45
53
  init Initialize agent from Sesame API key
46
54
  start Start the Hivemind agent
55
+ upgrade Upgrade the Hivemind runtime
47
56
  service Manage launchd services (install/uninstall/status/logs)
48
57
  fleet Manage the worker fleet
49
58
  `);
@@ -1,7 +1,9 @@
1
+ import { execSync } from "child_process";
1
2
  import { Agent } from "./agent.js";
2
3
  import type { HivemindConfig } from "./config.js";
3
4
  import { loadConfig } from "./config.js";
4
5
  import { SesameClient } from "./sesame.js";
6
+ import type { UpgradeRequest } from "./sesame.js";
5
7
  import { MemoryClient } from "./memory-client.js";
6
8
 
7
9
  export async function startPipeline(configPath: string): Promise<void> {
@@ -53,6 +55,40 @@ async function startSesameLoop(config: HivemindConfig, agent: Agent): Promise<vo
53
55
  process.on("SIGTERM", () => shutdown("SIGTERM"));
54
56
  process.on("SIGINT", () => shutdown("SIGINT"));
55
57
 
58
+ // ── Native upgrade handler (no LLM involved) ──
59
+ sesame.onUpgrade(async (req: UpgradeRequest) => {
60
+ console.log(`[hivemind] Upgrade requested: ${req.packageName}@${req.targetVersion} (by ${req.requestedBy})`);
61
+ sesame.updatePresence("working", { detail: `Upgrading to ${req.targetVersion}`, emoji: "⬆️" });
62
+
63
+ try {
64
+ const target = req.targetVersion === "latest"
65
+ ? req.packageName
66
+ : `${req.packageName}@${req.targetVersion}`;
67
+
68
+ console.log(`[hivemind] Running: npm install -g ${target}`);
69
+ execSync(`npm install -g ${target}`, { stdio: "inherit", timeout: 120_000 });
70
+ console.log("[hivemind] Package updated successfully");
71
+
72
+ // Send confirmation message to the requesting human's DM
73
+ // (For now, log it — we'd need the human's channel ID for a proper message)
74
+ console.log(`[hivemind] Upgrade to ${req.targetVersion} complete. Restarting...`);
75
+
76
+ sesame.updatePresence("working", { detail: "Restarting after upgrade", emoji: "🔄" });
77
+
78
+ // Graceful restart — try launchd kickstart, fall back to process.exit
79
+ try {
80
+ execSync("launchctl kickstart -k gui/$(id -u)/com.hivemind.agent", { timeout: 10_000 });
81
+ } catch {
82
+ // If launchd isn't managing us, just exit (the wrapper script will restart)
83
+ console.log("[hivemind] Exiting for restart...");
84
+ process.exit(0);
85
+ }
86
+ } catch (err) {
87
+ console.error(`[hivemind] Upgrade failed: ${(err as Error).message}`);
88
+ sesame.updatePresence("online", { detail: "Upgrade failed", emoji: "❌" });
89
+ }
90
+ });
91
+
56
92
  sesame.onMessage(async (msg) => {
57
93
  if (shuttingDown) return;
58
94
  console.log(`[sesame] ${msg.author.handle} (${msg.channelKind}): ${msg.content}`);
@@ -1,6 +1,20 @@
1
1
  import { SesameClient as SesameSDK } from "@sesamespace/sdk";
2
+ import { execSync } from "child_process";
3
+ import { readFileSync } from "fs";
4
+ import { resolve, dirname } from "path";
5
+ import { fileURLToPath } from "url";
2
6
  import type { SesameConfig } from "./config.js";
3
7
 
8
+ // Read version at module load time
9
+ let RUNTIME_VERSION = "unknown";
10
+ try {
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const pkg = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf-8"));
13
+ RUNTIME_VERSION = pkg.version ?? "unknown";
14
+ } catch {
15
+ // give up
16
+ }
17
+
4
18
  export interface SesameMessage {
5
19
  id: string;
6
20
  channelId: string;
@@ -14,6 +28,14 @@ export interface SesameMessage {
14
28
  }
15
29
 
16
30
  type MessageHandler = (message: SesameMessage) => void | Promise<void>;
31
+ type UpgradeHandler = (event: UpgradeRequest) => void | Promise<void>;
32
+
33
+ export interface UpgradeRequest {
34
+ targetVersion: string;
35
+ packageName: string;
36
+ requestedBy: string;
37
+ requestId: string;
38
+ }
17
39
 
18
40
  interface ChannelInfo {
19
41
  kind: "dm" | "group" | "topic";
@@ -24,6 +46,7 @@ export class SesameClient {
24
46
  private config: SesameConfig;
25
47
  private sdk: SesameSDK;
26
48
  private messageHandler: MessageHandler | null = null;
49
+ private upgradeHandler: UpgradeHandler | null = null;
27
50
  private agentId: string | null = null;
28
51
  private channels: Map<string, ChannelInfo> = new Map();
29
52
  private typingIntervals: Map<string, ReturnType<typeof setInterval>> = new Map();
@@ -41,6 +64,10 @@ export class SesameClient {
41
64
  this.messageHandler = handler;
42
65
  }
43
66
 
67
+ onUpgrade(handler: UpgradeHandler): void {
68
+ this.upgradeHandler = handler;
69
+ }
70
+
44
71
  async connect(): Promise<void> {
45
72
  const manifest = await this.sdk.getManifest();
46
73
  this.agentId = manifest.agent.id;
@@ -56,6 +83,24 @@ export class SesameClient {
56
83
  // Set presence to online
57
84
  this.updatePresence("online", { emoji: "🟢" });
58
85
 
86
+ // Listen for upgrade.request control events
87
+ this.sdk.on("upgrade.request", (event: any) => {
88
+ console.log(`[sesame] Upgrade request received: ${event.packageName}@${event.targetVersion}`);
89
+ if (this.upgradeHandler) {
90
+ this.upgradeHandler({
91
+ targetVersion: event.targetVersion,
92
+ packageName: event.packageName,
93
+ requestedBy: event.requestedBy,
94
+ requestId: event.requestId,
95
+ });
96
+ }
97
+ });
98
+
99
+ // Listen for generic control events
100
+ this.sdk.on("control", (event: any) => {
101
+ console.log(`[sesame] Control event: ${event.action}`, event.payload);
102
+ });
103
+
59
104
  // Listen for message events
60
105
  this.sdk.on("message", (event: any) => {
61
106
  const msg = event.data || event.message || event;
@@ -83,6 +128,13 @@ export class SesameClient {
83
128
 
84
129
  await this.sdk.connect();
85
130
  console.log("[sesame] WebSocket connected");
131
+
132
+ // Report runtime metadata
133
+ const ws = (this.sdk as any).ws;
134
+ if (ws?.readyState === 1) {
135
+ ws.send(JSON.stringify({ type: "meta", runtime: "hivemind", version: RUNTIME_VERSION }));
136
+ console.log(`[sesame] Reported version: hivemind@${RUNTIME_VERSION}`);
137
+ }
86
138
  }
87
139
 
88
140
  // ── Typing Indicators ──