@deeplake/hivemind 0.7.78 → 0.7.80
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/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/bundle/cli.js +9 -0
- package/codex/bundle/capture.js +176 -43
- package/codex/bundle/pre-tool-use.js +316 -93
- package/codex/bundle/skillopt-worker.js +2057 -0
- package/hermes/bundle/capture.js +168 -45
- package/hermes/bundle/pre-tool-use.js +272 -52
- package/hermes/bundle/skillopt-worker.js +2057 -0
- package/openclaw/dist/index.js +1 -1
- package/openclaw/openclaw.plugin.json +1 -1
- package/openclaw/package.json +1 -1
- package/package.json +1 -1
- package/pi/extension-source/hivemind.ts +93 -0
package/openclaw/dist/index.js
CHANGED
|
@@ -1828,7 +1828,7 @@ function extractLatestVersion(body) {
|
|
|
1828
1828
|
return typeof v === "string" && v.length > 0 ? v : null;
|
|
1829
1829
|
}
|
|
1830
1830
|
function getInstalledVersion() {
|
|
1831
|
-
return "0.7.
|
|
1831
|
+
return "0.7.80".length > 0 ? "0.7.80" : null;
|
|
1832
1832
|
}
|
|
1833
1833
|
function isNewer(latest, current) {
|
|
1834
1834
|
const parse = (v) => v.replace(/-.*$/, "").split(".").map(Number);
|
package/openclaw/package.json
CHANGED
package/package.json
CHANGED
|
@@ -466,6 +466,94 @@ function runAutopullWorker(): void {
|
|
|
466
466
|
}
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
+
// ---------- SkillOpt: arm on org-skill use, react on the next user message ------------
|
|
470
|
+
// Mirrors the CC PreToolUse/UserPromptSubmit wiring, inlined because this extension is raw
|
|
471
|
+
// .ts with zero non-builtin deps (it can't import the skillify trigger). pi has no first-class
|
|
472
|
+
// `Skill` tool — it USES a skill by READING its SKILL.md — so we arm on a tool_result whose
|
|
473
|
+
// path is .../skills/<name--author>/SKILL.md, then on the next user prompt (the reaction) spawn
|
|
474
|
+
// the bundled skillopt-worker to judge + improve. Env-var names are the cross-process contract
|
|
475
|
+
// with the worker (SKILLOPT_ENV in src/skillify/skillopt-env.ts) — kept as literals here since
|
|
476
|
+
// the extension can't import. Fully swallowed; never blocks pi. Both call sites sit AFTER the
|
|
477
|
+
// handler's captureEnabled check, so the worker's own pi-judge subprocess (HIVEMIND_CAPTURE=false)
|
|
478
|
+
// can't re-arm/re-react — that's the recursion guard.
|
|
479
|
+
const PI_SKILLOPT_WORKER_PATH = join(homedir(), ".pi", "agent", "hivemind", "skillopt-worker.js");
|
|
480
|
+
// Mirror getStateDir()'s contract: a non-empty (trimmed) HIVEMIND_STATE_DIR overrides the default
|
|
481
|
+
// ~/.deeplake/state/skillify root, so pi's pending state co-locates with the rest of Skillify
|
|
482
|
+
// (and test-isolation overrides apply here too, not just in the shared trigger).
|
|
483
|
+
const SKILLOPT_STATE_ROOT = (typeof process.env.HIVEMIND_STATE_DIR === "string" && process.env.HIVEMIND_STATE_DIR.trim())
|
|
484
|
+
? process.env.HIVEMIND_STATE_DIR.trim()
|
|
485
|
+
: join(homedir(), ".deeplake", "state", "skillify");
|
|
486
|
+
const SKILLOPT_PENDING_DIR = join(SKILLOPT_STATE_ROOT, "skillopt", "pending");
|
|
487
|
+
const SKILLOPT_JUDGE_WINDOW = 3; // K reactions to keep judging after a skill use (DEFAULT_JUDGE_WINDOW)
|
|
488
|
+
|
|
489
|
+
/** Recover an org-skill ref (name--author) from a path that loads a skill's SKILL.md, else null. */
|
|
490
|
+
function skilloptRefFromPath(p: unknown): string | null {
|
|
491
|
+
if (typeof p !== "string") return null;
|
|
492
|
+
const m = p.match(/\/skills\/([^/]+)\/SKILL\.md$/);
|
|
493
|
+
if (!m) return null;
|
|
494
|
+
const ref = m[1];
|
|
495
|
+
// org shape only: name--author, no plugin namespace / path separators / traversal.
|
|
496
|
+
if (ref.includes(":") || ref.includes("/") || ref.includes("\\") || ref.includes("..")) return null;
|
|
497
|
+
const i = ref.lastIndexOf("--");
|
|
498
|
+
return i > 0 && i + 2 < ref.length ? ref : null;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function skilloptPendingFile(sessionId: string): string {
|
|
502
|
+
const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 200);
|
|
503
|
+
return join(SKILLOPT_PENDING_DIR, `${safe}.json`);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/** tool_result: pi read an org skill's SKILL.md → open a K-message judgment window. */
|
|
507
|
+
function skilloptArm(sessionId: string, toolName: unknown, toolInput: any, toolCallId: unknown): void {
|
|
508
|
+
try {
|
|
509
|
+
if (process.env.HIVEMIND_SKILLOPT_DISABLED === "1") return;
|
|
510
|
+
// Arm only on a READ of the SKILL.md — USING a skill is reading it. An edit/write of a
|
|
511
|
+
// SKILL.md (even one whose input carries a matching path) must NOT open a judgment window.
|
|
512
|
+
if (!/^read/i.test(String(toolName ?? ""))) return;
|
|
513
|
+
const ref = skilloptRefFromPath(toolInput?.path ?? toolInput?.file ?? toolInput?.filePath);
|
|
514
|
+
if (!ref) return;
|
|
515
|
+
mkdirSync(SKILLOPT_PENDING_DIR, { recursive: true });
|
|
516
|
+
const f = skilloptPendingFile(sessionId);
|
|
517
|
+
const tmp = `${f}.${process.pid}.tmp`;
|
|
518
|
+
writeFileSync(tmp, JSON.stringify({ skill: ref, budget: SKILLOPT_JUDGE_WINDOW, toolUseId: typeof toolCallId === "string" ? toolCallId : undefined }));
|
|
519
|
+
renameSync(tmp, f);
|
|
520
|
+
logHm(`skillopt: armed ${ref} for ${sessionId}`);
|
|
521
|
+
} catch (e: any) { logHm(`skillopt arm swallowed: ${e?.message ?? e}`); }
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/** input: the user's reaction → spawn the detached worker to judge the pending skill; spend budget. */
|
|
525
|
+
function skilloptReact(sessionId: string, reaction: string): void {
|
|
526
|
+
try {
|
|
527
|
+
if (process.env.HIVEMIND_SKILLOPT_DISABLED === "1" || process.env.HIVEMIND_WIKI_WORKER === "1") return;
|
|
528
|
+
if (!reaction.trim()) return;
|
|
529
|
+
const f = skilloptPendingFile(sessionId);
|
|
530
|
+
let p: { skill?: string; budget?: number; toolUseId?: string };
|
|
531
|
+
try { p = JSON.parse(readFileSync(f, "utf8")); } catch { return; } // no window open → no-op
|
|
532
|
+
if (!p?.skill || typeof p.budget !== "number") return;
|
|
533
|
+
if (!existsSync(PI_SKILLOPT_WORKER_PATH)) { logHm(`skillopt: worker bundle missing at ${PI_SKILLOPT_WORKER_PATH} — run 'hivemind pi install'`); return; }
|
|
534
|
+
// Spend one message of the budget; close the window when exhausted.
|
|
535
|
+
try {
|
|
536
|
+
if (p.budget - 1 <= 0) { unlinkSync(f); }
|
|
537
|
+
else { const tmp = `${f}.${process.pid}.tmp`; writeFileSync(tmp, JSON.stringify({ ...p, budget: p.budget - 1 })); renameSync(tmp, f); }
|
|
538
|
+
} catch { /* best-effort */ }
|
|
539
|
+
const child = spawn(process.execPath, [PI_SKILLOPT_WORKER_PATH], {
|
|
540
|
+
detached: true,
|
|
541
|
+
stdio: "ignore",
|
|
542
|
+
env: {
|
|
543
|
+
...process.env,
|
|
544
|
+
HIVEMIND_SKILLOPT_WORKER: "1", // recursion guard (worker won't re-fire the trigger)
|
|
545
|
+
HIVEMIND_SKILLOPT_SESSION: sessionId,
|
|
546
|
+
HIVEMIND_SKILLOPT_SKILL: p.skill,
|
|
547
|
+
HIVEMIND_SKILLOPT_REACTION: reaction.slice(0, 8000),
|
|
548
|
+
HIVEMIND_SKILLOPT_AGENT: "pi", // judge/proposer run on pi (the user's own agent)
|
|
549
|
+
...(p.toolUseId ? { HIVEMIND_SKILLOPT_TOOL_USE_ID: p.toolUseId } : {}),
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
child.unref();
|
|
553
|
+
logHm(`skillopt: spawned worker for ${p.skill} in ${sessionId} (agent=pi)`);
|
|
554
|
+
} catch (e: any) { logHm(`skillopt react swallowed: ${e?.message ?? e}`); }
|
|
555
|
+
}
|
|
556
|
+
|
|
469
557
|
interface SummaryState {
|
|
470
558
|
lastSummaryAt: number;
|
|
471
559
|
lastSummaryCount: number;
|
|
@@ -1255,6 +1343,8 @@ export default function hivemindExtension(pi: ExtensionAPI): void {
|
|
|
1255
1343
|
} catch (e: any) {
|
|
1256
1344
|
logHm(`input: writeSessionRow swallowed: ${e?.message ?? e}`);
|
|
1257
1345
|
}
|
|
1346
|
+
// SkillOpt: this prompt is the user's reaction to a recently-used org skill. Swallowed.
|
|
1347
|
+
skilloptReact(sessionId, text);
|
|
1258
1348
|
maybeTriggerPeriodicSummary(creds, sessionId, cwd);
|
|
1259
1349
|
});
|
|
1260
1350
|
|
|
@@ -1286,6 +1376,9 @@ export default function hivemindExtension(pi: ExtensionAPI): void {
|
|
|
1286
1376
|
} catch (e: any) {
|
|
1287
1377
|
logHm(`tool_result: writeSessionRow swallowed: ${e?.message ?? e}`);
|
|
1288
1378
|
}
|
|
1379
|
+
// SkillOpt: pi USES an org skill by reading its SKILL.md — arm the judgment window on
|
|
1380
|
+
// a successful such read (skip errored reads). Swallowed.
|
|
1381
|
+
if (event.isError !== true) skilloptArm(sessionId, event.toolName, event.input, event.toolCallId);
|
|
1289
1382
|
maybeTriggerPeriodicSummary(creds, sessionId, cwd);
|
|
1290
1383
|
});
|
|
1291
1384
|
|