@deeplake/hivemind 0.7.4 → 0.7.11

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.
Files changed (35) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +97 -0
  4. package/bundle/cli.js +820 -20
  5. package/codex/bundle/capture.js +40 -10
  6. package/codex/bundle/commands/auth-login.js +84 -18
  7. package/codex/bundle/pre-tool-use.js +41 -11
  8. package/codex/bundle/session-start-setup.js +40 -10
  9. package/codex/bundle/session-start.js +27 -3
  10. package/codex/bundle/shell/deeplake-shell.js +41 -11
  11. package/codex/bundle/skilify-worker.js +907 -0
  12. package/codex/bundle/stop.js +373 -51
  13. package/cursor/bundle/capture.js +354 -13
  14. package/cursor/bundle/commands/auth-login.js +84 -18
  15. package/cursor/bundle/pre-tool-use.js +40 -10
  16. package/cursor/bundle/session-end.js +303 -6
  17. package/cursor/bundle/session-start.js +68 -14
  18. package/cursor/bundle/shell/deeplake-shell.js +41 -11
  19. package/cursor/bundle/skilify-worker.js +907 -0
  20. package/hermes/bundle/capture.js +354 -13
  21. package/hermes/bundle/commands/auth-login.js +84 -18
  22. package/hermes/bundle/pre-tool-use.js +40 -10
  23. package/hermes/bundle/session-end.js +305 -7
  24. package/hermes/bundle/session-start.js +68 -14
  25. package/hermes/bundle/shell/deeplake-shell.js +41 -11
  26. package/hermes/bundle/skilify-worker.js +907 -0
  27. package/mcp/bundle/server.js +41 -11
  28. package/openclaw/dist/chunks/{config-G23NI5TV.js → config-ZLH6JFJS.js} +1 -0
  29. package/openclaw/dist/index.js +185 -16
  30. package/openclaw/dist/skilify-worker.js +907 -0
  31. package/openclaw/openclaw.plugin.json +1 -1
  32. package/openclaw/package.json +2 -2
  33. package/openclaw/skills/SKILL.md +19 -0
  34. package/package.json +6 -1
  35. package/pi/extension-source/hivemind.ts +130 -1
@@ -53,9 +53,9 @@ var init_index_marker_store = __esm({
53
53
  });
54
54
 
55
55
  // dist/src/hooks/codex/stop.js
56
- import { readFileSync as readFileSync5, existsSync as existsSync5 } from "node:fs";
57
- import { fileURLToPath as fileURLToPath2 } from "node:url";
58
- import { dirname as dirname2, join as join9 } from "node:path";
56
+ import { readFileSync as readFileSync7, existsSync as existsSync8 } from "node:fs";
57
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
58
+ import { dirname as dirname3, join as join13 } from "node:path";
59
59
 
60
60
  // dist/src/utils/stdin.js
61
61
  function readStdin() {
@@ -102,6 +102,7 @@ function loadConfig() {
102
102
  apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
103
103
  tableName: process.env.HIVEMIND_TABLE ?? "memory",
104
104
  sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
105
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
105
106
  memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join(home, ".deeplake", "memory")
106
107
  };
107
108
  }
@@ -129,6 +130,12 @@ function log(tag, msg) {
129
130
  function sqlStr(value) {
130
131
  return value.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/\0/g, "").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
131
132
  }
133
+ function sqlIdent(name) {
134
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
135
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
136
+ }
137
+ return name;
138
+ }
132
139
 
133
140
  // dist/src/embeddings/columns.js
134
141
  var SUMMARY_EMBEDDING_COL = "summary_embedding";
@@ -501,7 +508,7 @@ var DeeplakeApi = class {
501
508
  }
502
509
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
503
510
  async ensureTable(name) {
504
- const tbl = name ?? this.tableName;
511
+ const tbl = sqlIdent(name ?? this.tableName);
505
512
  const tables = await this.listTables();
506
513
  if (!tables.includes(tbl)) {
507
514
  log2(`table "${tbl}" not found, creating`);
@@ -515,17 +522,40 @@ var DeeplakeApi = class {
515
522
  }
516
523
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
517
524
  async ensureSessionsTable(name) {
525
+ const safe = sqlIdent(name);
526
+ const tables = await this.listTables();
527
+ if (!tables.includes(safe)) {
528
+ log2(`table "${safe}" not found, creating`);
529
+ await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
530
+ log2(`table "${safe}" created`);
531
+ if (!tables.includes(safe))
532
+ this._tablesCache = [...tables, safe];
533
+ }
534
+ await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
535
+ await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
536
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
537
+ }
538
+ /**
539
+ * Create the skills table.
540
+ *
541
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
542
+ * MERGE rather than UPDATE-ing in place, so the full version history is
543
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
544
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
545
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
546
+ * worker.
547
+ */
548
+ async ensureSkillsTable(name) {
549
+ const safe = sqlIdent(name);
518
550
  const tables = await this.listTables();
519
- if (!tables.includes(name)) {
520
- log2(`table "${name}" not found, creating`);
521
- await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${name}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, name);
522
- log2(`table "${name}" created`);
523
- if (!tables.includes(name))
524
- this._tablesCache = [...tables, name];
551
+ if (!tables.includes(safe)) {
552
+ log2(`table "${safe}" not found, creating`);
553
+ await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', name TEXT NOT NULL DEFAULT '', project TEXT NOT NULL DEFAULT '', project_key TEXT NOT NULL DEFAULT '', local_path TEXT NOT NULL DEFAULT '', install TEXT NOT NULL DEFAULT 'project', source_sessions TEXT NOT NULL DEFAULT '[]', source_agent TEXT NOT NULL DEFAULT '', scope TEXT NOT NULL DEFAULT 'me', author TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', trigger_text TEXT NOT NULL DEFAULT '', body TEXT NOT NULL DEFAULT '', version BIGINT NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT '', updated_at TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
554
+ log2(`table "${safe}" created`);
555
+ if (!tables.includes(safe))
556
+ this._tablesCache = [...tables, safe];
525
557
  }
526
- await this.ensureEmbeddingColumn(name, MESSAGE_EMBEDDING_COL);
527
- await this.ensureColumn(name, "agent", "TEXT NOT NULL DEFAULT ''");
528
- await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
558
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
529
559
  }
530
560
  };
531
561
 
@@ -649,31 +679,207 @@ function bundleDirFromImportMeta(importMetaUrl) {
649
679
  return dirname(fileURLToPath(importMetaUrl));
650
680
  }
651
681
 
652
- // dist/src/hooks/summary-state.js
653
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, writeSync, mkdirSync as mkdirSync4, renameSync, existsSync as existsSync3, unlinkSync, openSync, closeSync } from "node:fs";
682
+ // dist/src/skilify/spawn-skilify-worker.js
683
+ import { spawn as spawn2 } from "node:child_process";
684
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
685
+ import { dirname as dirname2, join as join7 } from "node:path";
686
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, appendFileSync as appendFileSync3, chmodSync } from "node:fs";
687
+ import { homedir as homedir5, tmpdir as tmpdir3 } from "node:os";
688
+
689
+ // dist/src/skilify/gate-runner.js
690
+ import { execFileSync } from "node:child_process";
691
+ import { existsSync as existsSync3 } from "node:fs";
654
692
  import { homedir as homedir4 } from "node:os";
655
693
  import { join as join6 } from "node:path";
656
- var dlog = (msg) => log("summary-state", msg);
657
- var STATE_DIR = join6(homedir4(), ".claude", "hooks", "summary-state");
694
+ function findAgentBin(agent) {
695
+ const which = (name) => {
696
+ try {
697
+ const out = execFileSync("which", [name], {
698
+ encoding: "utf-8",
699
+ stdio: ["ignore", "pipe", "ignore"]
700
+ });
701
+ return out.trim() || null;
702
+ } catch {
703
+ return null;
704
+ }
705
+ };
706
+ switch (agent) {
707
+ case "claude_code":
708
+ return which("claude") ?? join6(homedir4(), ".claude", "local", "claude");
709
+ case "codex":
710
+ return which("codex") ?? "/usr/local/bin/codex";
711
+ case "cursor":
712
+ return which("cursor-agent") ?? "/usr/local/bin/cursor-agent";
713
+ case "hermes":
714
+ return which("hermes") ?? join6(homedir4(), ".local", "bin", "hermes");
715
+ case "pi":
716
+ return which("pi") ?? join6(homedir4(), ".local", "bin", "pi");
717
+ }
718
+ }
719
+
720
+ // dist/src/skilify/spawn-skilify-worker.js
721
+ var HOME2 = homedir5();
722
+ var SKILIFY_LOG = join7(HOME2, ".claude", "hooks", "skilify.log");
723
+ function skilifyLog(msg) {
724
+ try {
725
+ mkdirSync4(dirname2(SKILIFY_LOG), { recursive: true });
726
+ appendFileSync3(SKILIFY_LOG, `[${utcTimestamp()}] ${msg}
727
+ `);
728
+ } catch {
729
+ }
730
+ }
731
+ function spawnSkilifyWorker(opts) {
732
+ const { config, cwd, projectKey, project, bundleDir, agent, scopeConfig, currentSessionId, reason } = opts;
733
+ const tmpDir = join7(tmpdir3(), `deeplake-skilify-${projectKey}-${Date.now()}`);
734
+ mkdirSync4(tmpDir, { recursive: true, mode: 448 });
735
+ const gateBin = findAgentBin(agent);
736
+ const configFile = join7(tmpDir, "config.json");
737
+ writeFileSync3(configFile, JSON.stringify({
738
+ apiUrl: config.apiUrl,
739
+ token: config.token,
740
+ orgId: config.orgId,
741
+ workspaceId: config.workspaceId,
742
+ sessionsTable: config.sessionsTableName,
743
+ skillsTable: config.skillsTableName,
744
+ userName: config.userName,
745
+ cwd,
746
+ projectKey,
747
+ project,
748
+ agent,
749
+ scope: scopeConfig.scope,
750
+ team: scopeConfig.team,
751
+ install: scopeConfig.install,
752
+ tmpDir,
753
+ gateBin,
754
+ cursorModel: process.env.HIVEMIND_CURSOR_MODEL,
755
+ hermesProvider: process.env.HIVEMIND_HERMES_PROVIDER,
756
+ hermesModel: process.env.HIVEMIND_HERMES_MODEL,
757
+ piProvider: process.env.HIVEMIND_PI_PROVIDER,
758
+ piModel: process.env.HIVEMIND_PI_MODEL,
759
+ skilifyLog: SKILIFY_LOG,
760
+ currentSessionId
761
+ }), { mode: 384 });
762
+ try {
763
+ chmodSync(configFile, 384);
764
+ } catch {
765
+ }
766
+ skilifyLog(`${reason}: spawning skilify worker for project=${project} key=${projectKey}`);
767
+ const workerPath = join7(bundleDir, "skilify-worker.js");
768
+ spawn2("nohup", ["node", workerPath, configFile], {
769
+ detached: true,
770
+ stdio: ["ignore", "ignore", "ignore"]
771
+ }).unref();
772
+ skilifyLog(`${reason}: spawned skilify worker for ${projectKey}`);
773
+ }
774
+
775
+ // dist/src/skilify/state.js
776
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, writeSync, mkdirSync as mkdirSync5, renameSync, existsSync as existsSync4, unlinkSync, openSync, closeSync } from "node:fs";
777
+ import { execSync as execSync2 } from "node:child_process";
778
+ import { homedir as homedir6 } from "node:os";
779
+ import { createHash } from "node:crypto";
780
+ import { join as join8, basename } from "node:path";
781
+ var dlog = (msg) => log("skilify-state", msg);
782
+ var STATE_DIR = join8(homedir6(), ".deeplake", "state", "skilify");
658
783
  var YIELD_BUF = new Int32Array(new SharedArrayBuffer(4));
659
- function lockPath(sessionId) {
660
- return join6(STATE_DIR, `${sessionId}.lock`);
784
+ var TRIGGER_THRESHOLD = (() => {
785
+ const n = Number(process.env.HIVEMIND_SKILIFY_EVERY_N_TURNS ?? "");
786
+ return Number.isInteger(n) && n > 0 ? n : 20;
787
+ })();
788
+ function statePath(projectKey) {
789
+ return join8(STATE_DIR, `${projectKey}.json`);
661
790
  }
662
- function tryAcquireLock(sessionId, maxAgeMs = 10 * 60 * 1e3) {
663
- mkdirSync4(STATE_DIR, { recursive: true });
664
- const p = lockPath(sessionId);
665
- if (existsSync3(p)) {
791
+ function lockPath(projectKey) {
792
+ return join8(STATE_DIR, `${projectKey}.lock`);
793
+ }
794
+ function deriveProjectKey(cwd) {
795
+ const project = basename(cwd) || "unknown";
796
+ let signature = null;
797
+ try {
798
+ signature = execSync2("git config --get remote.origin.url", {
799
+ cwd,
800
+ encoding: "utf-8",
801
+ stdio: ["ignore", "pipe", "ignore"]
802
+ }).trim() || null;
803
+ } catch {
804
+ }
805
+ const input = signature ?? cwd;
806
+ const key = createHash("sha1").update(input).digest("hex").slice(0, 16);
807
+ return { key, project };
808
+ }
809
+ function readState(projectKey) {
810
+ const p = statePath(projectKey);
811
+ if (!existsSync4(p))
812
+ return null;
813
+ try {
814
+ return JSON.parse(readFileSync3(p, "utf-8"));
815
+ } catch {
816
+ return null;
817
+ }
818
+ }
819
+ function writeState(projectKey, state) {
820
+ mkdirSync5(STATE_DIR, { recursive: true });
821
+ const p = statePath(projectKey);
822
+ const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;
823
+ writeFileSync4(tmp, JSON.stringify(state, null, 2));
824
+ renameSync(tmp, p);
825
+ }
826
+ function withRmwLock(projectKey, fn) {
827
+ mkdirSync5(STATE_DIR, { recursive: true });
828
+ const rmw = lockPath(projectKey) + ".rmw";
829
+ const deadline = Date.now() + 2e3;
830
+ let fd = null;
831
+ while (fd === null) {
832
+ try {
833
+ fd = openSync(rmw, "wx");
834
+ } catch (e) {
835
+ if (e.code !== "EEXIST")
836
+ throw e;
837
+ if (Date.now() > deadline) {
838
+ dlog(`rmw lock deadline exceeded for ${projectKey}, reclaiming stale lock`);
839
+ try {
840
+ unlinkSync(rmw);
841
+ } catch (unlinkErr) {
842
+ dlog(`stale rmw lock unlink failed for ${projectKey}: ${unlinkErr.message}`);
843
+ }
844
+ continue;
845
+ }
846
+ Atomics.wait(YIELD_BUF, 0, 0, 10);
847
+ }
848
+ }
849
+ try {
850
+ return fn();
851
+ } finally {
852
+ closeSync(fd);
853
+ try {
854
+ unlinkSync(rmw);
855
+ } catch (unlinkErr) {
856
+ dlog(`rmw lock cleanup failed for ${projectKey}: ${unlinkErr.message}`);
857
+ }
858
+ }
859
+ }
860
+ function resetCounter(projectKey) {
861
+ withRmwLock(projectKey, () => {
862
+ const s = readState(projectKey);
863
+ if (!s)
864
+ return;
865
+ writeState(projectKey, { ...s, counter: 0, updatedAt: Date.now() });
866
+ });
867
+ }
868
+ function tryAcquireWorkerLock(projectKey, maxAgeMs = 10 * 60 * 1e3) {
869
+ mkdirSync5(STATE_DIR, { recursive: true });
870
+ const p = lockPath(projectKey);
871
+ if (existsSync4(p)) {
666
872
  try {
667
873
  const ageMs = Date.now() - parseInt(readFileSync3(p, "utf-8"), 10);
668
874
  if (Number.isFinite(ageMs) && ageMs < maxAgeMs)
669
875
  return false;
670
876
  } catch (readErr) {
671
- dlog(`lock file unreadable for ${sessionId}, treating as stale: ${readErr.message}`);
877
+ dlog(`worker lock unreadable for ${projectKey}, treating as stale: ${readErr.message}`);
672
878
  }
673
879
  try {
674
880
  unlinkSync(p);
675
881
  } catch (unlinkErr) {
676
- dlog(`could not unlink stale lock for ${sessionId}: ${unlinkErr.message}`);
882
+ dlog(`could not unlink stale worker lock for ${projectKey}: ${unlinkErr.message}`);
677
883
  return false;
678
884
  }
679
885
  }
@@ -685,6 +891,115 @@ function tryAcquireLock(sessionId, maxAgeMs = 10 * 60 * 1e3) {
685
891
  closeSync(fd);
686
892
  }
687
893
  return true;
894
+ } catch {
895
+ return false;
896
+ }
897
+ }
898
+ function releaseWorkerLock(projectKey) {
899
+ const p = lockPath(projectKey);
900
+ try {
901
+ unlinkSync(p);
902
+ } catch {
903
+ }
904
+ }
905
+
906
+ // dist/src/skilify/scope-config.js
907
+ import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
908
+ import { homedir as homedir7 } from "node:os";
909
+ import { join as join9 } from "node:path";
910
+ var STATE_DIR2 = join9(homedir7(), ".deeplake", "state", "skilify");
911
+ var CONFIG_PATH = join9(STATE_DIR2, "config.json");
912
+ var DEFAULT = { scope: "me", team: [], install: "project" };
913
+ function loadScopeConfig() {
914
+ if (!existsSync5(CONFIG_PATH))
915
+ return DEFAULT;
916
+ try {
917
+ const raw = JSON.parse(readFileSync4(CONFIG_PATH, "utf-8"));
918
+ const scope = raw.scope === "team" || raw.scope === "org" ? raw.scope : "me";
919
+ const team = Array.isArray(raw.team) ? raw.team.filter((s) => typeof s === "string") : [];
920
+ const install = raw.install === "global" ? "global" : "project";
921
+ return { scope, team, install };
922
+ } catch {
923
+ return DEFAULT;
924
+ }
925
+ }
926
+
927
+ // dist/src/skilify/triggers.js
928
+ function forceSessionEndTrigger(opts) {
929
+ if (process.env.HIVEMIND_SKILIFY_WORKER === "1")
930
+ return;
931
+ if (!opts.cwd)
932
+ return;
933
+ try {
934
+ const { key: projectKey, project } = deriveProjectKey(opts.cwd);
935
+ if (!tryAcquireWorkerLock(projectKey)) {
936
+ skilifyLog(`SessionEnd: skilify worker already running for ${projectKey}, skipping`);
937
+ return;
938
+ }
939
+ if (readState(projectKey)) {
940
+ resetCounter(projectKey);
941
+ }
942
+ skilifyLog(`SessionEnd: spawning skilify worker for project=${project} agent=${opts.agent}`);
943
+ try {
944
+ spawnSkilifyWorker({
945
+ config: opts.config,
946
+ cwd: opts.cwd,
947
+ projectKey,
948
+ project,
949
+ bundleDir: opts.bundleDir,
950
+ agent: opts.agent,
951
+ scopeConfig: loadScopeConfig(),
952
+ currentSessionId: opts.sessionId,
953
+ reason: "SessionEnd"
954
+ });
955
+ } catch (e) {
956
+ skilifyLog(`SessionEnd spawn failed: ${e?.message ?? e}`);
957
+ try {
958
+ releaseWorkerLock(projectKey);
959
+ } catch {
960
+ }
961
+ }
962
+ } catch (e) {
963
+ skilifyLog(`SessionEnd trigger error: ${e?.message ?? e}`);
964
+ }
965
+ }
966
+
967
+ // dist/src/hooks/summary-state.js
968
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync6, writeSync as writeSync2, mkdirSync as mkdirSync7, renameSync as renameSync2, existsSync as existsSync6, unlinkSync as unlinkSync2, openSync as openSync2, closeSync as closeSync2 } from "node:fs";
969
+ import { homedir as homedir8 } from "node:os";
970
+ import { join as join10 } from "node:path";
971
+ var dlog2 = (msg) => log("summary-state", msg);
972
+ var STATE_DIR3 = join10(homedir8(), ".claude", "hooks", "summary-state");
973
+ var YIELD_BUF2 = new Int32Array(new SharedArrayBuffer(4));
974
+ function lockPath2(sessionId) {
975
+ return join10(STATE_DIR3, `${sessionId}.lock`);
976
+ }
977
+ function tryAcquireLock(sessionId, maxAgeMs = 10 * 60 * 1e3) {
978
+ mkdirSync7(STATE_DIR3, { recursive: true });
979
+ const p = lockPath2(sessionId);
980
+ if (existsSync6(p)) {
981
+ try {
982
+ const ageMs = Date.now() - parseInt(readFileSync5(p, "utf-8"), 10);
983
+ if (Number.isFinite(ageMs) && ageMs < maxAgeMs)
984
+ return false;
985
+ } catch (readErr) {
986
+ dlog2(`lock file unreadable for ${sessionId}, treating as stale: ${readErr.message}`);
987
+ }
988
+ try {
989
+ unlinkSync2(p);
990
+ } catch (unlinkErr) {
991
+ dlog2(`could not unlink stale lock for ${sessionId}: ${unlinkErr.message}`);
992
+ return false;
993
+ }
994
+ }
995
+ try {
996
+ const fd = openSync2(p, "wx");
997
+ try {
998
+ writeSync2(fd, String(Date.now()));
999
+ } finally {
1000
+ closeSync2(fd);
1001
+ }
1002
+ return true;
688
1003
  } catch (e) {
689
1004
  if (e.code === "EEXIST")
690
1005
  return false;
@@ -693,10 +1008,10 @@ function tryAcquireLock(sessionId, maxAgeMs = 10 * 60 * 1e3) {
693
1008
  }
694
1009
  function releaseLock(sessionId) {
695
1010
  try {
696
- unlinkSync(lockPath(sessionId));
1011
+ unlinkSync2(lockPath2(sessionId));
697
1012
  } catch (e) {
698
1013
  if (e?.code !== "ENOENT") {
699
- dlog(`releaseLock unlink failed for ${sessionId}: ${e.message}`);
1014
+ dlog2(`releaseLock unlink failed for ${sessionId}: ${e.message}`);
700
1015
  }
701
1016
  }
702
1017
  }
@@ -709,10 +1024,10 @@ function buildSessionPath(config, sessionId) {
709
1024
 
710
1025
  // dist/src/embeddings/client.js
711
1026
  import { connect } from "node:net";
712
- import { spawn as spawn2 } from "node:child_process";
713
- import { openSync as openSync2, closeSync as closeSync2, writeSync as writeSync2, unlinkSync as unlinkSync2, existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
714
- import { homedir as homedir5 } from "node:os";
715
- import { join as join7 } from "node:path";
1027
+ import { spawn as spawn3 } from "node:child_process";
1028
+ import { openSync as openSync3, closeSync as closeSync3, writeSync as writeSync3, unlinkSync as unlinkSync3, existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
1029
+ import { homedir as homedir9 } from "node:os";
1030
+ import { join as join11 } from "node:path";
716
1031
 
717
1032
  // dist/src/embeddings/protocol.js
718
1033
  var DEFAULT_SOCKET_DIR = "/tmp";
@@ -726,7 +1041,7 @@ function pidPathFor(uid, dir = DEFAULT_SOCKET_DIR) {
726
1041
  }
727
1042
 
728
1043
  // dist/src/embeddings/client.js
729
- var SHARED_DAEMON_PATH = join7(homedir5(), ".hivemind", "embed-deps", "embed-daemon.js");
1044
+ var SHARED_DAEMON_PATH = join11(homedir9(), ".hivemind", "embed-deps", "embed-daemon.js");
730
1045
  var log3 = (m) => log("embed-client", m);
731
1046
  function getUid() {
732
1047
  const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
@@ -746,7 +1061,7 @@ var EmbedClient = class {
746
1061
  this.socketPath = socketPathFor(uid, dir);
747
1062
  this.pidPath = pidPathFor(uid, dir);
748
1063
  this.timeoutMs = opts.timeoutMs ?? DEFAULT_CLIENT_TIMEOUT_MS;
749
- this.daemonEntry = opts.daemonEntry ?? process.env.HIVEMIND_EMBED_DAEMON ?? (existsSync4(SHARED_DAEMON_PATH) ? SHARED_DAEMON_PATH : void 0);
1064
+ this.daemonEntry = opts.daemonEntry ?? process.env.HIVEMIND_EMBED_DAEMON ?? (existsSync7(SHARED_DAEMON_PATH) ? SHARED_DAEMON_PATH : void 0);
750
1065
  this.autoSpawn = opts.autoSpawn ?? true;
751
1066
  this.spawnWaitMs = opts.spawnWaitMs ?? 5e3;
752
1067
  }
@@ -828,17 +1143,17 @@ var EmbedClient = class {
828
1143
  trySpawnDaemon() {
829
1144
  let fd;
830
1145
  try {
831
- fd = openSync2(this.pidPath, "wx", 384);
832
- writeSync2(fd, String(process.pid));
1146
+ fd = openSync3(this.pidPath, "wx", 384);
1147
+ writeSync3(fd, String(process.pid));
833
1148
  } catch (e) {
834
1149
  if (this.isPidFileStale()) {
835
1150
  try {
836
- unlinkSync2(this.pidPath);
1151
+ unlinkSync3(this.pidPath);
837
1152
  } catch {
838
1153
  }
839
1154
  try {
840
- fd = openSync2(this.pidPath, "wx", 384);
841
- writeSync2(fd, String(process.pid));
1155
+ fd = openSync3(this.pidPath, "wx", 384);
1156
+ writeSync3(fd, String(process.pid));
842
1157
  } catch {
843
1158
  return;
844
1159
  }
@@ -846,17 +1161,17 @@ var EmbedClient = class {
846
1161
  return;
847
1162
  }
848
1163
  }
849
- if (!this.daemonEntry || !existsSync4(this.daemonEntry)) {
1164
+ if (!this.daemonEntry || !existsSync7(this.daemonEntry)) {
850
1165
  log3(`daemonEntry not configured or missing: ${this.daemonEntry}`);
851
1166
  try {
852
- closeSync2(fd);
853
- unlinkSync2(this.pidPath);
1167
+ closeSync3(fd);
1168
+ unlinkSync3(this.pidPath);
854
1169
  } catch {
855
1170
  }
856
1171
  return;
857
1172
  }
858
1173
  try {
859
- const child = spawn2(process.execPath, [this.daemonEntry], {
1174
+ const child = spawn3(process.execPath, [this.daemonEntry], {
860
1175
  detached: true,
861
1176
  stdio: "ignore",
862
1177
  env: process.env
@@ -864,12 +1179,12 @@ var EmbedClient = class {
864
1179
  child.unref();
865
1180
  log3(`spawned daemon pid=${child.pid}`);
866
1181
  } finally {
867
- closeSync2(fd);
1182
+ closeSync3(fd);
868
1183
  }
869
1184
  }
870
1185
  isPidFileStale() {
871
1186
  try {
872
- const raw = readFileSync4(this.pidPath, "utf-8").trim();
1187
+ const raw = readFileSync6(this.pidPath, "utf-8").trim();
873
1188
  const pid = Number(raw);
874
1189
  if (!pid || Number.isNaN(pid))
875
1190
  return true;
@@ -889,7 +1204,7 @@ var EmbedClient = class {
889
1204
  while (Date.now() < deadline) {
890
1205
  await sleep2(delay);
891
1206
  delay = Math.min(delay * 1.5, 300);
892
- if (!existsSync4(this.socketPath))
1207
+ if (!existsSync7(this.socketPath))
893
1208
  continue;
894
1209
  try {
895
1210
  return await this.connectOnce();
@@ -950,8 +1265,8 @@ function embeddingSqlLiteral(vec) {
950
1265
 
951
1266
  // dist/src/embeddings/disable.js
952
1267
  import { createRequire } from "node:module";
953
- import { homedir as homedir6 } from "node:os";
954
- import { join as join8 } from "node:path";
1268
+ import { homedir as homedir10 } from "node:os";
1269
+ import { join as join12 } from "node:path";
955
1270
  import { pathToFileURL } from "node:url";
956
1271
  var cachedStatus = null;
957
1272
  function defaultResolveTransformers() {
@@ -960,7 +1275,7 @@ function defaultResolveTransformers() {
960
1275
  return;
961
1276
  } catch {
962
1277
  }
963
- const sharedDir = join8(homedir6(), ".hivemind", "embed-deps");
1278
+ const sharedDir = join12(homedir10(), ".hivemind", "embed-deps");
964
1279
  createRequire(pathToFileURL(`${sharedDir}/`).href).resolve("@huggingface/transformers");
965
1280
  }
966
1281
  var _resolve = defaultResolveTransformers;
@@ -987,7 +1302,7 @@ function embeddingsDisabled() {
987
1302
  // dist/src/hooks/codex/stop.js
988
1303
  var log4 = (msg) => log("codex-stop", msg);
989
1304
  function resolveEmbedDaemonPath() {
990
- return join9(dirname2(fileURLToPath2(import.meta.url)), "embeddings", "embed-daemon.js");
1305
+ return join13(dirname3(fileURLToPath3(import.meta.url)), "embeddings", "embed-daemon.js");
991
1306
  }
992
1307
  var CAPTURE = process.env.HIVEMIND_CAPTURE !== "false";
993
1308
  async function main() {
@@ -1011,8 +1326,8 @@ async function main() {
1011
1326
  if (input.transcript_path) {
1012
1327
  try {
1013
1328
  const transcriptPath = input.transcript_path;
1014
- if (existsSync5(transcriptPath)) {
1015
- const transcript = readFileSync5(transcriptPath, "utf-8");
1329
+ if (existsSync8(transcriptPath)) {
1330
+ const transcript = readFileSync7(transcriptPath, "utf-8");
1016
1331
  const lines = transcript.trim().split("\n").reverse();
1017
1332
  for (const line2 of lines) {
1018
1333
  try {
@@ -1084,6 +1399,13 @@ async function main() {
1084
1399
  }
1085
1400
  throw e;
1086
1401
  }
1402
+ forceSessionEndTrigger({
1403
+ config,
1404
+ cwd: input.cwd ?? "",
1405
+ bundleDir: bundleDirFromImportMeta(import.meta.url),
1406
+ agent: "codex",
1407
+ sessionId
1408
+ });
1087
1409
  }
1088
1410
  main().catch((e) => {
1089
1411
  log4(`fatal: ${e.message}`);