squadrant 0.26.3 → 0.27.1
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/index.js +3439 -785
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +2610 -415
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
- package/plugin/skills/knowledge-extract/SKILL.md +43 -0
- package/plugin/skills/knowledge-review/SKILL.md +42 -0
package/dist/squadrantd.js
CHANGED
|
@@ -273,6 +273,7 @@ var init_effort = __esm({
|
|
|
273
273
|
});
|
|
274
274
|
|
|
275
275
|
// packages/shared/dist/knowledge.js
|
|
276
|
+
import path4 from "path";
|
|
276
277
|
function validateRuleFrontmatter(fm) {
|
|
277
278
|
if (!isObj(fm))
|
|
278
279
|
return ["frontmatter must be an object"];
|
|
@@ -340,19 +341,58 @@ function validateSourceEntry(e) {
|
|
|
340
341
|
p.push("sensitivity must be 'local-only' when set");
|
|
341
342
|
return p;
|
|
342
343
|
}
|
|
344
|
+
function kbConfigs(cfg) {
|
|
345
|
+
const out = { ...cfg.knowledge };
|
|
346
|
+
for (const [kb, c] of Object.entries(cfg.knowledgeBases ?? {}))
|
|
347
|
+
out[kb] = { ...out[kb], ...c };
|
|
348
|
+
return out;
|
|
349
|
+
}
|
|
350
|
+
function kbPath(cfg, kb) {
|
|
351
|
+
return resolveHome(kbConfigs(cfg)[kb]?.path ?? `${DEFAULT_KB_ROOT}/${kb}`);
|
|
352
|
+
}
|
|
353
|
+
function homeKb(cfg, project) {
|
|
354
|
+
const pc = cfg.projects[project];
|
|
355
|
+
if (pc?.knowledgeHome?.startsWith("kb:"))
|
|
356
|
+
return pc.knowledgeHome.slice(3);
|
|
357
|
+
if (pc?.knowledgeHome)
|
|
358
|
+
return void 0;
|
|
359
|
+
return pc?.group ? cfg.groups?.[pc.group]?.kb : void 0;
|
|
360
|
+
}
|
|
343
361
|
function subscribedKbs(cfg, project) {
|
|
344
|
-
|
|
362
|
+
const pc = cfg.projects[project];
|
|
363
|
+
const group = pc?.group ? cfg.groups?.[pc.group]?.kb : void 0;
|
|
364
|
+
return [...new Set([group, homeKb(cfg, project), ...pc?.knowledge ?? []].filter((k) => !!k))];
|
|
345
365
|
}
|
|
346
|
-
|
|
366
|
+
function projectRulesHome(cfg, project) {
|
|
367
|
+
const pc = cfg.projects[project];
|
|
368
|
+
if (pc?.knowledgeHome === "repo:docs")
|
|
369
|
+
return path4.join(resolveHome(pc.path), "docs", "rules");
|
|
370
|
+
const kb = homeKb(cfg, project);
|
|
371
|
+
return kb ? path4.join(kbPath(cfg, kb), "projects", project, "rules") : void 0;
|
|
372
|
+
}
|
|
373
|
+
function resolveKbConfig(cfg, kb) {
|
|
374
|
+
const explicit = kbConfigs(cfg)[kb] ?? {};
|
|
375
|
+
const firstSubscriber = Object.keys(cfg.projects).find((p) => subscribedKbs(cfg, p).includes(kb));
|
|
376
|
+
return {
|
|
377
|
+
homeProject: explicit.homeProject ?? firstSubscriber,
|
|
378
|
+
domainCap: explicit.domainCap ?? DEFAULT_DOMAIN_CAP
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
var RULE_MODALITIES, RULE_STATUSES, SOURCE_PRIORITIES, RULE_APPROVERS, RULE_ID_RE, KB_NAME_RE, DEFAULT_DOMAIN_CAP, DEFAULT_RECONCILE, isObj, isStrArr, DEFAULT_KB_ROOT;
|
|
347
382
|
var init_knowledge = __esm({
|
|
348
383
|
"packages/shared/dist/knowledge.js"() {
|
|
384
|
+
init_config();
|
|
349
385
|
RULE_MODALITIES = ["must", "must-not", "should", "may"];
|
|
350
386
|
RULE_STATUSES = ["proposed", "active", "stale", "retired"];
|
|
351
387
|
SOURCE_PRIORITIES = ["company", "project", "agent"];
|
|
352
388
|
RULE_APPROVERS = ["auto", "reviewer-agent", "human"];
|
|
353
389
|
RULE_ID_RE = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
|
|
390
|
+
KB_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
391
|
+
DEFAULT_DOMAIN_CAP = 150;
|
|
392
|
+
DEFAULT_RECONCILE = { incrementalAfterDays: 7, fullEveryDays: 30, autoApproveConfidence: 0.8 };
|
|
354
393
|
isObj = (v2) => typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
355
394
|
isStrArr = (v2) => Array.isArray(v2) && v2.every((x) => typeof x === "string");
|
|
395
|
+
DEFAULT_KB_ROOT = "~/squadrant/kb";
|
|
356
396
|
}
|
|
357
397
|
});
|
|
358
398
|
|
|
@@ -423,22 +463,22 @@ function defaultCmuxConfigPath() {
|
|
|
423
463
|
return join(homedir(), ".config", "cmux", "cmux.json");
|
|
424
464
|
}
|
|
425
465
|
function ensureSocketAutomation(opts = {}) {
|
|
426
|
-
const
|
|
427
|
-
if (!existsSync(
|
|
428
|
-
mkdirSync(dirname(
|
|
429
|
-
writeFileSync(
|
|
430
|
-
return { path:
|
|
466
|
+
const path42 = opts.path ?? defaultCmuxConfigPath();
|
|
467
|
+
if (!existsSync(path42)) {
|
|
468
|
+
mkdirSync(dirname(path42), { recursive: true });
|
|
469
|
+
writeFileSync(path42, MINIMAL_TEMPLATE);
|
|
470
|
+
return { path: path42, changed: true, alreadySet: false };
|
|
431
471
|
}
|
|
432
|
-
const text = readFileSync(
|
|
472
|
+
const text = readFileSync(path42, "utf-8");
|
|
433
473
|
const current = parse(text)?.automation?.socketControlMode;
|
|
434
474
|
if (current === AUTOMATION_MODE) {
|
|
435
|
-
return { path:
|
|
475
|
+
return { path: path42, changed: false, alreadySet: true };
|
|
436
476
|
}
|
|
437
477
|
const edits = modify(text, [...SOCKET_CONTROL_MODE_PATH], AUTOMATION_MODE, {
|
|
438
478
|
formattingOptions: { insertSpaces: true, tabSize: 2 }
|
|
439
479
|
});
|
|
440
|
-
writeFileSync(
|
|
441
|
-
return { path:
|
|
480
|
+
writeFileSync(path42, applyEdits(text, edits));
|
|
481
|
+
return { path: path42, changed: true, alreadySet: false };
|
|
442
482
|
}
|
|
443
483
|
var SOCKET_CONTROL_MODE_PATH, AUTOMATION_MODE, MINIMAL_TEMPLATE;
|
|
444
484
|
var init_cmux_config = __esm({
|
|
@@ -588,9 +628,9 @@ import { join as join4 } from "path";
|
|
|
588
628
|
function defaultStatePath() {
|
|
589
629
|
return join4(homedir3(), ".config", "squadrant", "state", "cmux-autoconfig.json");
|
|
590
630
|
}
|
|
591
|
-
function readState(
|
|
631
|
+
function readState(path42) {
|
|
592
632
|
try {
|
|
593
|
-
return JSON.parse(readConfigFileSync(
|
|
633
|
+
return JSON.parse(readConfigFileSync(path42));
|
|
594
634
|
} catch {
|
|
595
635
|
return {};
|
|
596
636
|
}
|
|
@@ -663,13 +703,13 @@ var init_config_version = __esm({
|
|
|
663
703
|
|
|
664
704
|
// packages/shared/dist/lib/update-check.js
|
|
665
705
|
import fs2 from "fs";
|
|
666
|
-
import
|
|
706
|
+
import path5 from "path";
|
|
667
707
|
import os3 from "os";
|
|
668
708
|
import https from "https";
|
|
669
709
|
var UPDATE_CHECK_STATE_PATH, CHECK_INTERVAL_MS, FAILURE_RETRY_MS;
|
|
670
710
|
var init_update_check = __esm({
|
|
671
711
|
"packages/shared/dist/lib/update-check.js"() {
|
|
672
|
-
UPDATE_CHECK_STATE_PATH =
|
|
712
|
+
UPDATE_CHECK_STATE_PATH = path5.join(os3.homedir(), ".config", "squadrant", "update-check.json");
|
|
673
713
|
CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
674
714
|
FAILURE_RETRY_MS = 60 * 60 * 1e3;
|
|
675
715
|
}
|
|
@@ -678,9 +718,9 @@ var init_update_check = __esm({
|
|
|
678
718
|
// packages/shared/dist/lib/git-worktree.js
|
|
679
719
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
680
720
|
import fs3 from "fs";
|
|
681
|
-
import
|
|
721
|
+
import path6 from "path";
|
|
682
722
|
function worktreePath(repoRoot, worktreeDir, project, name) {
|
|
683
|
-
return
|
|
723
|
+
return path6.resolve(repoRoot, worktreeDir, `${project}-${name}`);
|
|
684
724
|
}
|
|
685
725
|
function crewBranch(name) {
|
|
686
726
|
return `crew/${name}`;
|
|
@@ -689,9 +729,9 @@ function ensureSpotlightExcluded(repoRoot, worktreeDir) {
|
|
|
689
729
|
if (process.platform !== "darwin")
|
|
690
730
|
return;
|
|
691
731
|
try {
|
|
692
|
-
const dir =
|
|
732
|
+
const dir = path6.resolve(repoRoot, worktreeDir);
|
|
693
733
|
fs3.mkdirSync(dir, { recursive: true });
|
|
694
|
-
const marker =
|
|
734
|
+
const marker = path6.join(dir, ".metadata_never_index");
|
|
695
735
|
if (!fs3.existsSync(marker))
|
|
696
736
|
fs3.writeFileSync(marker, "");
|
|
697
737
|
} catch {
|
|
@@ -755,15 +795,15 @@ function addWorktree(spec) {
|
|
|
755
795
|
return wt;
|
|
756
796
|
}
|
|
757
797
|
function installWorktreeDependencies(wt) {
|
|
758
|
-
if (!fs3.existsSync(
|
|
798
|
+
if (!fs3.existsSync(path6.join(wt, "package.json")))
|
|
759
799
|
return;
|
|
760
|
-
if (fs3.existsSync(
|
|
800
|
+
if (fs3.existsSync(path6.join(wt, "pnpm-lock.yaml"))) {
|
|
761
801
|
execFileSync2("pnpm", ["-C", wt, "install", "--frozen-lockfile"], { stdio: "pipe" });
|
|
762
|
-
} else if (fs3.existsSync(
|
|
802
|
+
} else if (fs3.existsSync(path6.join(wt, "yarn.lock"))) {
|
|
763
803
|
execFileSync2("yarn", ["install", "--frozen-lockfile"], { cwd: wt, stdio: "pipe" });
|
|
764
|
-
} else if (fs3.existsSync(
|
|
804
|
+
} else if (fs3.existsSync(path6.join(wt, "package-lock.json"))) {
|
|
765
805
|
execFileSync2("npm", ["ci"], { cwd: wt, stdio: "pipe" });
|
|
766
|
-
} else if (fs3.existsSync(
|
|
806
|
+
} else if (fs3.existsSync(path6.join(wt, "bun.lockb"))) {
|
|
767
807
|
execFileSync2("bun", ["install", "--frozen-lockfile"], { cwd: wt, stdio: "pipe" });
|
|
768
808
|
} else {
|
|
769
809
|
process.stderr.write(`worktree ${wt}: package.json present but no lockfile \u2014 dependencies not installed; local typechecks/tests may resolve against the main checkout instead of this worktree.
|
|
@@ -782,7 +822,7 @@ function worktreeDirtyFiles(wtPath) {
|
|
|
782
822
|
}
|
|
783
823
|
function removeGeneratedWorktreeFiles(wtPath) {
|
|
784
824
|
for (const [, file] of worktreeStatus(wtPath).filter(isGeneratedUntracked)) {
|
|
785
|
-
fs3.rmSync(
|
|
825
|
+
fs3.rmSync(path6.join(wtPath, file), { force: true });
|
|
786
826
|
}
|
|
787
827
|
}
|
|
788
828
|
function removeWorktree(repoRoot, wtPath, opts) {
|
|
@@ -806,7 +846,7 @@ var init_resolve_text_input = __esm({
|
|
|
806
846
|
|
|
807
847
|
// packages/shared/dist/lib/runtime-sync.js
|
|
808
848
|
import fs5 from "fs";
|
|
809
|
-
import
|
|
849
|
+
import path7 from "path";
|
|
810
850
|
var init_runtime_sync = __esm({
|
|
811
851
|
"packages/shared/dist/lib/runtime-sync.js"() {
|
|
812
852
|
}
|
|
@@ -849,7 +889,7 @@ var init_tool_compat = __esm({
|
|
|
849
889
|
|
|
850
890
|
// packages/shared/dist/lib/canonical-source.js
|
|
851
891
|
import fs6 from "fs";
|
|
852
|
-
import
|
|
892
|
+
import path8 from "path";
|
|
853
893
|
var init_canonical_source = __esm({
|
|
854
894
|
"packages/shared/dist/lib/canonical-source.js"() {
|
|
855
895
|
}
|
|
@@ -858,7 +898,7 @@ var init_canonical_source = __esm({
|
|
|
858
898
|
// packages/shared/dist/lib/daily-logs.js
|
|
859
899
|
import { execSync } from "child_process";
|
|
860
900
|
import fs7 from "fs";
|
|
861
|
-
import
|
|
901
|
+
import path9 from "path";
|
|
862
902
|
import matter from "gray-matter";
|
|
863
903
|
var init_daily_logs = __esm({
|
|
864
904
|
"packages/shared/dist/lib/daily-logs.js"() {
|
|
@@ -941,47 +981,47 @@ function stampAttempt(rec, patch, now) {
|
|
|
941
981
|
function isStickyAttention(state) {
|
|
942
982
|
return state === "blocked" || state === "review";
|
|
943
983
|
}
|
|
944
|
-
function nextPendingTool(current,
|
|
945
|
-
if (
|
|
946
|
-
return { name:
|
|
947
|
-
if (
|
|
984
|
+
function nextPendingTool(current, ev2, now) {
|
|
985
|
+
if (ev2.note === "agent.hook.PreToolUse")
|
|
986
|
+
return { name: ev2.tool ?? "tool", since: now };
|
|
987
|
+
if (ev2.note === "posttooluse" || ev2.note === "agent.hook.PostToolUse" || ev2.note === "agent.hook.UserPromptSubmit") {
|
|
948
988
|
return void 0;
|
|
949
989
|
}
|
|
950
990
|
return current;
|
|
951
991
|
}
|
|
952
|
-
function nextPendingMonitor(current,
|
|
953
|
-
if (
|
|
992
|
+
function nextPendingMonitor(current, ev2, now) {
|
|
993
|
+
if (ev2.note === "agent.hook.PreToolUse" && ev2.tool === "Monitor")
|
|
954
994
|
return { since: now };
|
|
955
995
|
return current;
|
|
956
996
|
}
|
|
957
|
-
function reduce(rec,
|
|
958
|
-
if (
|
|
959
|
-
return { ...rec, state: "working", question: void 0, error: void 0, lastHeartbeat: now, lastEvent:
|
|
997
|
+
function reduce(rec, ev2, now) {
|
|
998
|
+
if (ev2.type === "task.reopened") {
|
|
999
|
+
return { ...rec, state: "working", question: void 0, error: void 0, lastHeartbeat: now, lastEvent: ev2.type, workingStretchStartedAt: now };
|
|
960
1000
|
}
|
|
961
|
-
if (
|
|
1001
|
+
if (ev2.type === "crew.takeover.started") {
|
|
962
1002
|
if (rec.operatorHold)
|
|
963
|
-
return { ...rec, lastHeartbeat: now, lastEvent:
|
|
1003
|
+
return { ...rec, lastHeartbeat: now, lastEvent: ev2.type };
|
|
964
1004
|
return {
|
|
965
1005
|
...rec,
|
|
966
|
-
operatorHold: { since: now, ...
|
|
1006
|
+
operatorHold: { since: now, ...ev2.note !== void 0 ? { note: ev2.note } : {} },
|
|
967
1007
|
lastHeartbeat: now,
|
|
968
|
-
lastEvent:
|
|
1008
|
+
lastEvent: ev2.type
|
|
969
1009
|
};
|
|
970
1010
|
}
|
|
971
|
-
if (
|
|
1011
|
+
if (ev2.type === "crew.takeover.ended") {
|
|
972
1012
|
const { operatorHold: _dropped, ...rest } = rec;
|
|
973
|
-
return { ...rest, lastHeartbeat: now, lastEvent:
|
|
1013
|
+
return { ...rest, lastHeartbeat: now, lastEvent: ev2.type };
|
|
974
1014
|
}
|
|
975
1015
|
if (TERMINAL_STATES.has(rec.state))
|
|
976
1016
|
return rec;
|
|
977
|
-
const base = { ...rec, lastHeartbeat: now, lastEvent:
|
|
978
|
-
switch (
|
|
1017
|
+
const base = { ...rec, lastHeartbeat: now, lastEvent: ev2.type };
|
|
1018
|
+
switch (ev2.type) {
|
|
979
1019
|
case "task.started":
|
|
980
1020
|
return {
|
|
981
|
-
...stampAttempt(base, { pid:
|
|
1021
|
+
...stampAttempt(base, { pid: ev2.pid }, now),
|
|
982
1022
|
state: "working",
|
|
983
|
-
pid:
|
|
984
|
-
sessionId:
|
|
1023
|
+
pid: ev2.pid ?? rec.pid,
|
|
1024
|
+
sessionId: ev2.sessionId ?? rec.sessionId,
|
|
985
1025
|
question: void 0,
|
|
986
1026
|
// resuming after a blocked→reply clears the question
|
|
987
1027
|
pendingTool: void 0,
|
|
@@ -991,10 +1031,10 @@ function reduce(rec, ev, now) {
|
|
|
991
1031
|
workingStretchStartedAt: now
|
|
992
1032
|
};
|
|
993
1033
|
case "task.progress": {
|
|
994
|
-
const pendingTool = nextPendingTool(rec.pendingTool,
|
|
995
|
-
const pendingMonitor = nextPendingMonitor(rec.pendingMonitor,
|
|
1034
|
+
const pendingTool = nextPendingTool(rec.pendingTool, ev2, now);
|
|
1035
|
+
const pendingMonitor = nextPendingMonitor(rec.pendingMonitor, ev2, now);
|
|
996
1036
|
if (isStickyAttention(rec.state))
|
|
997
|
-
return { ...rec, lastHeartbeat: now, lastEvent:
|
|
1037
|
+
return { ...rec, lastHeartbeat: now, lastEvent: ev2.type, pendingTool, pendingMonitor };
|
|
998
1038
|
const b = { ...base, pendingTool, pendingMonitor };
|
|
999
1039
|
if (rec.state === "awaiting-input" || rec.state === "stalled")
|
|
1000
1040
|
return { ...stampAttempt(b, {}, now), state: "working" };
|
|
@@ -1002,29 +1042,29 @@ function reduce(rec, ev, now) {
|
|
|
1002
1042
|
}
|
|
1003
1043
|
case "heartbeat":
|
|
1004
1044
|
if (isStickyAttention(rec.state))
|
|
1005
|
-
return { ...rec, lastHeartbeat: now, lastEvent:
|
|
1045
|
+
return { ...rec, lastHeartbeat: now, lastEvent: ev2.type };
|
|
1006
1046
|
if (rec.state === "awaiting-input")
|
|
1007
1047
|
return { ...base, state: "working" };
|
|
1008
1048
|
return base;
|
|
1009
1049
|
case "task.blocked":
|
|
1010
1050
|
if (rec.state === "blocked")
|
|
1011
|
-
return { ...rec, lastHeartbeat: now, lastEvent:
|
|
1012
|
-
return { ...base, state: "blocked", question:
|
|
1051
|
+
return { ...rec, lastHeartbeat: now, lastEvent: ev2.type };
|
|
1052
|
+
return { ...base, state: "blocked", question: ev2.question, pendingTool: void 0, pendingMonitor: void 0 };
|
|
1013
1053
|
case "task.review":
|
|
1014
|
-
return { ...base, state: "review", reviewNote:
|
|
1054
|
+
return { ...base, state: "review", reviewNote: ev2.message, pendingTool: void 0, pendingMonitor: void 0 };
|
|
1015
1055
|
case "task.done":
|
|
1016
|
-
if (rec.state === "review" &&
|
|
1017
|
-
return { ...rec, lastHeartbeat: now, lastEvent:
|
|
1056
|
+
if (rec.state === "review" && ev2.source !== "approve") {
|
|
1057
|
+
return { ...rec, lastHeartbeat: now, lastEvent: ev2.type };
|
|
1018
1058
|
}
|
|
1019
|
-
return { ...base, state: "done", resultRef:
|
|
1059
|
+
return { ...base, state: "done", resultRef: ev2.resultRef, parseWarning: ev2.parseWarning };
|
|
1020
1060
|
case "task.failed":
|
|
1021
|
-
return { ...base, state: "failed", error:
|
|
1061
|
+
return { ...base, state: "failed", error: ev2.error, exitCode: ev2.exitCode };
|
|
1022
1062
|
case "task.cancelled":
|
|
1023
1063
|
return { ...base, state: "cancelled" };
|
|
1024
1064
|
case "task.session.ended":
|
|
1025
1065
|
return { ...base, state: "cancelled" };
|
|
1026
1066
|
case "task.session":
|
|
1027
|
-
return stampAttempt(base, { resumeRef:
|
|
1067
|
+
return stampAttempt(base, { resumeRef: ev2.resumeRef }, now);
|
|
1028
1068
|
case "task.turn.started":
|
|
1029
1069
|
return { ...stampAttempt(base, {}, now), state: "working", pendingTool: void 0, pendingMonitor: void 0 };
|
|
1030
1070
|
case "task.turn.failed":
|
|
@@ -1034,7 +1074,7 @@ function reduce(rec, ev, now) {
|
|
|
1034
1074
|
// only the notify message (reduce.ts formatMessage) differs.
|
|
1035
1075
|
case "task.turn.completed":
|
|
1036
1076
|
if (isStickyAttention(rec.state))
|
|
1037
|
-
return { ...rec, lastHeartbeat: now, lastEvent:
|
|
1077
|
+
return { ...rec, lastHeartbeat: now, lastEvent: ev2.type };
|
|
1038
1078
|
if (rec.pendingTool || rec.pendingMonitor)
|
|
1039
1079
|
return stampAttempt(base, {}, now);
|
|
1040
1080
|
return { ...stampAttempt(base, {}, now), state: "awaiting-input", pendingTool: void 0, pendingMonitor: void 0 };
|
|
@@ -1043,14 +1083,14 @@ function reduce(rec, ev, now) {
|
|
|
1043
1083
|
// heartbeat-only
|
|
1044
1084
|
case "task.input.requested":
|
|
1045
1085
|
case "task.approval.requested":
|
|
1046
|
-
return { ...stampAttempt(base, {}, now), state: "blocked", question:
|
|
1086
|
+
return { ...stampAttempt(base, {}, now), state: "blocked", question: ev2.question, pendingTool: void 0, pendingMonitor: void 0 };
|
|
1047
1087
|
case "task.reattached":
|
|
1048
1088
|
return stampAttempt(base, {}, now);
|
|
1049
1089
|
case "task.first-turn.confirmed":
|
|
1050
1090
|
if (rec.firstTurnConfirmedAt) {
|
|
1051
1091
|
return { ...rec, lastEvent: "task.progress" };
|
|
1052
1092
|
}
|
|
1053
|
-
return { ...rec, firstTurnConfirmedAt: now, lastEvent:
|
|
1093
|
+
return { ...rec, firstTurnConfirmedAt: now, lastEvent: ev2.type };
|
|
1054
1094
|
case "task.stalled":
|
|
1055
1095
|
case "task.idle":
|
|
1056
1096
|
case "task.quiet":
|
|
@@ -2537,10 +2577,10 @@ function detectForeignInstall(parsed, thisEntry, registeredEntryExists) {
|
|
|
2537
2577
|
isUpgrade: isVersionUpgrade(parsed.daemonEntry, thisEntry)
|
|
2538
2578
|
};
|
|
2539
2579
|
}
|
|
2540
|
-
function sanitizePathForPlist(
|
|
2580
|
+
function sanitizePathForPlist(path42) {
|
|
2541
2581
|
const seen = /* @__PURE__ */ new Set();
|
|
2542
2582
|
const stable = [];
|
|
2543
|
-
for (const p of
|
|
2583
|
+
for (const p of path42.split(":")) {
|
|
2544
2584
|
if (!p)
|
|
2545
2585
|
continue;
|
|
2546
2586
|
if (p.includes("/.claude/plugins/"))
|
|
@@ -3639,7 +3679,7 @@ function coalesceBacklog(entries, opts) {
|
|
|
3639
3679
|
}
|
|
3640
3680
|
return decisions;
|
|
3641
3681
|
}
|
|
3642
|
-
function decide(entry,
|
|
3682
|
+
function decide(entry, later2, opts) {
|
|
3643
3683
|
if (entry.kind === "captain.message") {
|
|
3644
3684
|
if (entry.payload?.source !== "daemon" || !entry.message)
|
|
3645
3685
|
return { action: "deliver", entry };
|
|
@@ -3648,10 +3688,10 @@ function decide(entry, later, opts) {
|
|
|
3648
3688
|
const prefix = stalePrefix(Date.parse(entry.ts), opts.now);
|
|
3649
3689
|
return { action: "deliver", entry: prefix ? { ...entry, message: `${prefix}${entry.message}` } : entry };
|
|
3650
3690
|
}
|
|
3651
|
-
if (
|
|
3691
|
+
if (later2) {
|
|
3652
3692
|
if (TRANSIENT_KINDS.has(entry.kind))
|
|
3653
3693
|
return { action: "skip", entry, reason: "superseded" };
|
|
3654
|
-
if (entry.kind === "task.review" && (
|
|
3694
|
+
if (entry.kind === "task.review" && (later2.has("task.review") || Object.keys(TERMINAL_EVENT_STATE).some((k) => later2.has(k)))) {
|
|
3655
3695
|
return { action: "skip", entry, reason: "superseded" };
|
|
3656
3696
|
}
|
|
3657
3697
|
}
|
|
@@ -4297,11 +4337,11 @@ var init_auto_gate = __esm({
|
|
|
4297
4337
|
// packages/core/dist/first-turn-spill.js
|
|
4298
4338
|
import fs9 from "fs";
|
|
4299
4339
|
import os4 from "os";
|
|
4300
|
-
import
|
|
4340
|
+
import path10 from "path";
|
|
4301
4341
|
function spillOversizedFirstTurn(text, id) {
|
|
4302
4342
|
if (Buffer.byteLength(text, "utf8") <= FIRST_TURN_INLINE_MAX_BYTES)
|
|
4303
4343
|
return text;
|
|
4304
|
-
const spillFile =
|
|
4344
|
+
const spillFile = path10.join(os4.tmpdir(), `squadrant-task-${id}.md`);
|
|
4305
4345
|
fs9.writeFileSync(spillFile, text, "utf8");
|
|
4306
4346
|
return `Full task is at ${spillFile} \u2014 cat it and follow it exactly.`;
|
|
4307
4347
|
}
|
|
@@ -4411,7 +4451,7 @@ var init_crew_lifecycle = __esm({
|
|
|
4411
4451
|
// packages/core/dist/crew-spawn.js
|
|
4412
4452
|
import fs10 from "fs";
|
|
4413
4453
|
import os5 from "os";
|
|
4414
|
-
import
|
|
4454
|
+
import path11 from "path";
|
|
4415
4455
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
4416
4456
|
function ensureSocksDir(dir = CC_SOCKS_DIR) {
|
|
4417
4457
|
fs10.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
@@ -4549,9 +4589,9 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
4549
4589
|
}) : proj.path;
|
|
4550
4590
|
let firstTurnTask = input.task;
|
|
4551
4591
|
if (input.taskFile && input.taskFile !== "-" && !input.shared) {
|
|
4552
|
-
const absTaskFile =
|
|
4553
|
-
const basename =
|
|
4554
|
-
fs10.copyFileSync(absTaskFile,
|
|
4592
|
+
const absTaskFile = path11.resolve(input.taskFile);
|
|
4593
|
+
const basename = path11.basename(absTaskFile);
|
|
4594
|
+
fs10.copyFileSync(absTaskFile, path11.join(spawnCwd, basename));
|
|
4555
4595
|
firstTurnTask = `Read ./${basename} to get your task brief, then execute it.`;
|
|
4556
4596
|
}
|
|
4557
4597
|
const agentName = requestedAgent;
|
|
@@ -4565,7 +4605,7 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
4565
4605
|
assertBackendUsable({ backend, agent: agent.name, router: config.defaults.router });
|
|
4566
4606
|
deps.onBackendResolved?.({ backend });
|
|
4567
4607
|
if (agentName === "codex") {
|
|
4568
|
-
const codexRoleFile =
|
|
4608
|
+
const codexRoleFile = path11.join(TEMPLATES_DIR, `crew.${agent.templateSuffix}.md`);
|
|
4569
4609
|
const roleInstructions = fs10.existsSync(codexRoleFile) ? fs10.readFileSync(codexRoleFile, "utf8") : void 0;
|
|
4570
4610
|
return runCodexInteractiveSpawn({
|
|
4571
4611
|
project: input.project,
|
|
@@ -4582,7 +4622,7 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
4582
4622
|
sendCodexFirstTurn: deps.sendCodexFirstTurn
|
|
4583
4623
|
});
|
|
4584
4624
|
}
|
|
4585
|
-
const promptFile =
|
|
4625
|
+
const promptFile = path11.join(TEMPLATES_DIR, `crew.${agent.templateSuffix}.md`);
|
|
4586
4626
|
const interactive = agent.name === "claude" || agent.name === "opencode";
|
|
4587
4627
|
const crewRole = config.defaults.roles?.crew;
|
|
4588
4628
|
const configModel = crewRole && crewRole.agent === agent.name ? crewRole.model : void 0;
|
|
@@ -4608,7 +4648,7 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
4608
4648
|
}
|
|
4609
4649
|
}
|
|
4610
4650
|
ensureSocksDir();
|
|
4611
|
-
const messagingSocketPath =
|
|
4651
|
+
const messagingSocketPath = path11.join(CC_SOCKS_DIR, `squadrant-${randomUUID3()}.sock`);
|
|
4612
4652
|
const rec = await deps.dispatchCrew({
|
|
4613
4653
|
provider: "claude",
|
|
4614
4654
|
mode: "interactive",
|
|
@@ -4821,7 +4861,7 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps, o
|
|
|
4821
4861
|
}
|
|
4822
4862
|
} catch {
|
|
4823
4863
|
}
|
|
4824
|
-
const
|
|
4864
|
+
const deliver2 = deps.sendToPane ?? ((pane, msg) => runtime.sendToPane(pane, msg).then(() => ({ delivered: true })));
|
|
4825
4865
|
const agent = task?.provider;
|
|
4826
4866
|
const channel = agent ? deps.controlChannels?.find((c) => c.agent === agent) : void 0;
|
|
4827
4867
|
const mode = channel && agent && deps.controlChannelMode ? deps.controlChannelMode(agent) : "off";
|
|
@@ -4851,7 +4891,7 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps, o
|
|
|
4851
4891
|
channelLog(`crew send ${name}: threw, falling back to pane \u2014 ${e.message}`);
|
|
4852
4892
|
probe = { status: "gone" };
|
|
4853
4893
|
}
|
|
4854
|
-
const { delivered: paneOk, blockedByModal: paneModal } = await
|
|
4894
|
+
const { delivered: paneOk, blockedByModal: paneModal } = await deliver2(crew, message);
|
|
4855
4895
|
const channelWouldSay = probe.status === "reachable" ? "deliverable" : probe.status;
|
|
4856
4896
|
if (paneOk !== (probe.status === "reachable")) {
|
|
4857
4897
|
channelLog(`crew send ${name}: DISAGREEMENT \u2014 pane=${paneOk ? "delivered" : "not delivered"}, channel=${channelWouldSay}`);
|
|
@@ -4865,7 +4905,7 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps, o
|
|
|
4865
4905
|
}
|
|
4866
4906
|
return { reopened };
|
|
4867
4907
|
}
|
|
4868
|
-
const { delivered, blockedByModal } = await
|
|
4908
|
+
const { delivered, blockedByModal } = await deliver2(crew, message);
|
|
4869
4909
|
if (blockedByModal) {
|
|
4870
4910
|
throw new Error(blockedByModalMessage());
|
|
4871
4911
|
}
|
|
@@ -4886,7 +4926,7 @@ function buildRecoveryHint(sessId, provider, worktreeCwd) {
|
|
|
4886
4926
|
return "";
|
|
4887
4927
|
if (provider === "claude") {
|
|
4888
4928
|
const escaped = worktreeCwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
4889
|
-
const transcriptPath =
|
|
4929
|
+
const transcriptPath = path11.join(os5.homedir(), ".claude", "projects", escaped, `${sessId}.jsonl`);
|
|
4890
4930
|
return `
|
|
4891
4931
|
transcript: ${transcriptPath}
|
|
4892
4932
|
resume: claude --resume ${sessId} (run from the worktree path above)
|
|
@@ -4995,8 +5035,8 @@ var init_crew_spawn = __esm({
|
|
|
4995
5035
|
init_crew_protocol();
|
|
4996
5036
|
init_crew_lifecycle();
|
|
4997
5037
|
CC_SOCKS_DIR = "/tmp/cc-socks";
|
|
4998
|
-
TEMPLATES_DIR =
|
|
4999
|
-
STATE_ROOT =
|
|
5038
|
+
TEMPLATES_DIR = path11.join(os5.homedir(), ".config", "squadrant", "templates");
|
|
5039
|
+
STATE_ROOT = path11.join(os5.homedir(), ".config", "squadrant", "state");
|
|
5000
5040
|
FIRST_TURN_HOOK_CONFIRM_GRACE_MS = 15e3;
|
|
5001
5041
|
FIRST_TURN_HOOK_CONFIRM_MAX_MS = 18e4;
|
|
5002
5042
|
FIRST_TURN_HOOK_POLL_INTERVAL_MS = 2e3;
|
|
@@ -5080,11 +5120,11 @@ var init_captain_record = __esm({
|
|
|
5080
5120
|
// packages/core/dist/opencode-session.js
|
|
5081
5121
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
5082
5122
|
async function listSessions(port, fetchImpl = fetch, timeoutMs = 5e3) {
|
|
5083
|
-
for (const
|
|
5123
|
+
for (const path42 of ["/session", "/api/session"]) {
|
|
5084
5124
|
const ac = new AbortController();
|
|
5085
5125
|
const t = setTimeout(() => ac.abort(), timeoutMs);
|
|
5086
5126
|
try {
|
|
5087
|
-
const res = await fetchImpl(`http://127.0.0.1:${port}${
|
|
5127
|
+
const res = await fetchImpl(`http://127.0.0.1:${port}${path42}`, { signal: ac.signal });
|
|
5088
5128
|
if (!res.ok)
|
|
5089
5129
|
continue;
|
|
5090
5130
|
const rows = await res.json();
|
|
@@ -5777,10 +5817,10 @@ function distBuiltAt() {
|
|
|
5777
5817
|
return 0;
|
|
5778
5818
|
}
|
|
5779
5819
|
}
|
|
5780
|
-
function gatherLogStats(
|
|
5820
|
+
function gatherLogStats(path42, now, windowMs) {
|
|
5781
5821
|
let sizeBytes = 0;
|
|
5782
5822
|
try {
|
|
5783
|
-
sizeBytes = statSync3(
|
|
5823
|
+
sizeBytes = statSync3(path42).size;
|
|
5784
5824
|
} catch {
|
|
5785
5825
|
return { errorCount: 0, sizeBytes: 0, windowMs };
|
|
5786
5826
|
}
|
|
@@ -5791,7 +5831,7 @@ function gatherLogStats(path29, now, windowMs) {
|
|
|
5791
5831
|
const len = sizeBytes - start;
|
|
5792
5832
|
let text = "";
|
|
5793
5833
|
try {
|
|
5794
|
-
const fd = openSync2(
|
|
5834
|
+
const fd = openSync2(path42, "r");
|
|
5795
5835
|
try {
|
|
5796
5836
|
const buf = Buffer.alloc(len);
|
|
5797
5837
|
readSync(fd, buf, 0, len, start);
|
|
@@ -6122,6 +6162,7 @@ function startDaemon(ctx, opts, pkgVersion) {
|
|
|
6122
6162
|
const projects = new Set(entries.filter((e) => e.endsWith(".log")).map((e) => e.slice(0, -".log".length)));
|
|
6123
6163
|
for (const project of projects)
|
|
6124
6164
|
await rotateIfNeeded({ stateRoot, project, ...mboxCfg });
|
|
6165
|
+
await opts.knowledgeTick?.();
|
|
6125
6166
|
} catch (e) {
|
|
6126
6167
|
log(`rotation timer error: ${e.message}`);
|
|
6127
6168
|
} finally {
|
|
@@ -6199,7 +6240,7 @@ var init_start = __esm({
|
|
|
6199
6240
|
// packages/core/dist/session-freshness.js
|
|
6200
6241
|
import crypto from "crypto";
|
|
6201
6242
|
import fs11 from "fs";
|
|
6202
|
-
import
|
|
6243
|
+
import path12 from "path";
|
|
6203
6244
|
function loadSessions(sessionsPath) {
|
|
6204
6245
|
try {
|
|
6205
6246
|
return JSON.parse(readConfigFileSync(sessionsPath));
|
|
@@ -6212,17 +6253,17 @@ function saveSessions(sessionsPath, sessions) {
|
|
|
6212
6253
|
}
|
|
6213
6254
|
function computeTemplateHash(role, templatesDir) {
|
|
6214
6255
|
const hash = crypto.createHash("sha256");
|
|
6215
|
-
const roleFile =
|
|
6216
|
-
const legacyRoleFile =
|
|
6256
|
+
const roleFile = path12.join(templatesDir, `${role}.claude.md`);
|
|
6257
|
+
const legacyRoleFile = path12.join(templatesDir, `${role}.CLAUDE.md`);
|
|
6217
6258
|
if (fs11.existsSync(roleFile)) {
|
|
6218
6259
|
hash.update(fs11.readFileSync(roleFile, "utf-8"));
|
|
6219
6260
|
} else if (fs11.existsSync(legacyRoleFile)) {
|
|
6220
6261
|
hash.update(fs11.readFileSync(legacyRoleFile, "utf-8"));
|
|
6221
6262
|
}
|
|
6222
|
-
const pluginSkillsDir =
|
|
6263
|
+
const pluginSkillsDir = path12.join(templatesDir, "..", "plugin", "skills");
|
|
6223
6264
|
if (fs11.existsSync(pluginSkillsDir)) {
|
|
6224
6265
|
for (const skill of fs11.readdirSync(pluginSkillsDir).sort()) {
|
|
6225
|
-
const skillFile =
|
|
6266
|
+
const skillFile = path12.join(pluginSkillsDir, skill, "SKILL.md");
|
|
6226
6267
|
if (fs11.existsSync(skillFile)) {
|
|
6227
6268
|
hash.update(fs11.readFileSync(skillFile, "utf-8"));
|
|
6228
6269
|
}
|
|
@@ -6529,32 +6570,32 @@ var init_ensure_captain = __esm({
|
|
|
6529
6570
|
function topicName(project) {
|
|
6530
6571
|
return project;
|
|
6531
6572
|
}
|
|
6532
|
-
function formatLifecycle(project,
|
|
6533
|
-
switch (
|
|
6573
|
+
function formatLifecycle(project, ev2) {
|
|
6574
|
+
switch (ev2.type) {
|
|
6534
6575
|
case "task.done":
|
|
6535
|
-
return `\u2705 [${project}] CREW DONE \xB7 ${
|
|
6536
|
-
${
|
|
6576
|
+
return `\u2705 [${project}] CREW DONE \xB7 ${ev2.id}` + (ev2.message ? `
|
|
6577
|
+
${ev2.message}` : "");
|
|
6537
6578
|
case "task.blocked":
|
|
6538
|
-
return `\u{1F6A7} [${project}] CREW BLOCKED \xB7 ${
|
|
6539
|
-
${
|
|
6579
|
+
return `\u{1F6A7} [${project}] CREW BLOCKED \xB7 ${ev2.id}
|
|
6580
|
+
${ev2.question}`;
|
|
6540
6581
|
case "task.review":
|
|
6541
|
-
return `\u{1F440} [${project}] CREW REVIEW \xB7 ${
|
|
6542
|
-
${
|
|
6582
|
+
return `\u{1F440} [${project}] CREW REVIEW \xB7 ${ev2.id}` + (ev2.message ? `
|
|
6583
|
+
${ev2.message}` : "");
|
|
6543
6584
|
case "task.idle":
|
|
6544
|
-
return `\u{1F4A4} [${project}] CREW IDLE \xB7 ${
|
|
6585
|
+
return `\u{1F4A4} [${project}] CREW IDLE \xB7 ${ev2.id}`;
|
|
6545
6586
|
case "task.failed":
|
|
6546
|
-
return `\u274C [${project}] CREW FAILED \xB7 ${
|
|
6547
|
-
${
|
|
6587
|
+
return `\u274C [${project}] CREW FAILED \xB7 ${ev2.id}
|
|
6588
|
+
${ev2.error}`;
|
|
6548
6589
|
case "task.approval.requested":
|
|
6549
|
-
return `\u{1F510} [${project}] APPROVAL NEEDED \xB7 ${
|
|
6550
|
-
${
|
|
6590
|
+
return `\u{1F510} [${project}] APPROVAL NEEDED \xB7 ${ev2.id}
|
|
6591
|
+
${ev2.question}`;
|
|
6551
6592
|
case "task.input.requested":
|
|
6552
|
-
return `\u2753 [${project}] INPUT NEEDED \xB7 ${
|
|
6553
|
-
${
|
|
6593
|
+
return `\u2753 [${project}] INPUT NEEDED \xB7 ${ev2.id}
|
|
6594
|
+
${ev2.question}`;
|
|
6554
6595
|
case "task.timeout":
|
|
6555
|
-
return `\u23F1\uFE0F [${project}] CREW TIMEOUT \xB7 ${
|
|
6596
|
+
return `\u23F1\uFE0F [${project}] CREW TIMEOUT \xB7 ${ev2.id}`;
|
|
6556
6597
|
default:
|
|
6557
|
-
return `\u2139\uFE0F [${project}] ${
|
|
6598
|
+
return `\u2139\uFE0F [${project}] ${ev2.type} \xB7 ${ev2.id}`;
|
|
6558
6599
|
}
|
|
6559
6600
|
}
|
|
6560
6601
|
function formatInbound(text) {
|
|
@@ -6608,9 +6649,9 @@ var init_format = __esm({
|
|
|
6608
6649
|
});
|
|
6609
6650
|
|
|
6610
6651
|
// packages/core/dist/telegram/state.js
|
|
6611
|
-
import
|
|
6652
|
+
import path13 from "path";
|
|
6612
6653
|
function statePath(stateRoot) {
|
|
6613
|
-
return
|
|
6654
|
+
return path13.join(stateRoot, "telegram-state.json");
|
|
6614
6655
|
}
|
|
6615
6656
|
function topicKey(project, scope = "project") {
|
|
6616
6657
|
return `${project}::${scope}`;
|
|
@@ -7104,7 +7145,7 @@ var init_tiers = __esm({
|
|
|
7104
7145
|
// packages/core/dist/telegram/bridge.js
|
|
7105
7146
|
import fs12 from "fs";
|
|
7106
7147
|
import os6 from "os";
|
|
7107
|
-
import
|
|
7148
|
+
import path14 from "path";
|
|
7108
7149
|
import crypto2 from "crypto";
|
|
7109
7150
|
function formatInboundReceipt(project, outcome) {
|
|
7110
7151
|
if (!outcome)
|
|
@@ -7123,7 +7164,7 @@ function formatInboundReceipt(project, outcome) {
|
|
|
7123
7164
|
}
|
|
7124
7165
|
function telegramPollLockPath(token) {
|
|
7125
7166
|
const hash = crypto2.createHash("sha256").update(token).digest("hex").slice(0, 16);
|
|
7126
|
-
return
|
|
7167
|
+
return path14.join(os6.tmpdir(), `squadrant-telegram-poll-${hash}.lock`);
|
|
7127
7168
|
}
|
|
7128
7169
|
function pidAlive(pid) {
|
|
7129
7170
|
try {
|
|
@@ -7227,7 +7268,7 @@ function notifyToggle(text) {
|
|
|
7227
7268
|
function createTelegramBridge(opts) {
|
|
7228
7269
|
const { cfg, stateRoot, client, appendCaptainMessage: appendCaptainMessage2, log, ensureCaptainAlive, runCommand, sendReply } = opts;
|
|
7229
7270
|
const lifecycle = opts.lifecycle;
|
|
7230
|
-
const configRoot = opts.configRoot ??
|
|
7271
|
+
const configRoot = opts.configRoot ?? path14.join(os6.homedir(), ".config", "squadrant");
|
|
7231
7272
|
const pollMs = cfg.pollMs ?? 1e3;
|
|
7232
7273
|
const schedule = opts.schedule ?? defaultSchedule2;
|
|
7233
7274
|
const lockToken = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
@@ -7249,16 +7290,16 @@ function createTelegramBridge(opts) {
|
|
|
7249
7290
|
const { topicId } = await runTelegramLink({ project, cfg, client, stateRoot });
|
|
7250
7291
|
await client.sendMessage(cfg.supergroupId, topicId, text);
|
|
7251
7292
|
}
|
|
7252
|
-
async function deliverOutbound(project,
|
|
7293
|
+
async function deliverOutbound(project, ev2) {
|
|
7253
7294
|
const resolved = resolveNotify(cfg.notify, loadProjectOverride(project, configRoot));
|
|
7254
7295
|
const live = loadState(stateRoot).notify[project];
|
|
7255
7296
|
const active = live ?? resolved.active;
|
|
7256
7297
|
if (!active)
|
|
7257
7298
|
return;
|
|
7258
|
-
if (!tierIncludes(resolved.crew,
|
|
7299
|
+
if (!tierIncludes(resolved.crew, ev2.type))
|
|
7259
7300
|
return;
|
|
7260
|
-
let text = formatLifecycle(project,
|
|
7261
|
-
if ((
|
|
7301
|
+
let text = formatLifecycle(project, ev2);
|
|
7302
|
+
if ((ev2.type === "task.done" || ev2.type === "task.failed") && opts.usageFor) {
|
|
7262
7303
|
const usage2 = opts.usageFor(project);
|
|
7263
7304
|
if (usage2 && usage2.requests > 0)
|
|
7264
7305
|
text += `
|
|
@@ -7368,14 +7409,14 @@ ${formatUsageLine(usage2)}`;
|
|
|
7368
7409
|
}
|
|
7369
7410
|
function currentEffort() {
|
|
7370
7411
|
try {
|
|
7371
|
-
return loadConfig(
|
|
7412
|
+
return loadConfig(path14.join(configRoot, "config.json")).defaults.effort ?? "balance";
|
|
7372
7413
|
} catch {
|
|
7373
7414
|
return "balance";
|
|
7374
7415
|
}
|
|
7375
7416
|
}
|
|
7376
7417
|
function projectNames() {
|
|
7377
7418
|
try {
|
|
7378
|
-
return Object.keys(loadConfig(
|
|
7419
|
+
return Object.keys(loadConfig(path14.join(configRoot, "config.json")).projects);
|
|
7379
7420
|
} catch {
|
|
7380
7421
|
return [];
|
|
7381
7422
|
}
|
|
@@ -7677,8 +7718,8 @@ ${formatUsageLine(usage2)}`;
|
|
|
7677
7718
|
releasePollLock?.();
|
|
7678
7719
|
releasePollLock = null;
|
|
7679
7720
|
},
|
|
7680
|
-
pushLifecycle(project,
|
|
7681
|
-
void deliverOutbound(project,
|
|
7721
|
+
pushLifecycle(project, ev2) {
|
|
7722
|
+
void deliverOutbound(project, ev2).catch((e) => {
|
|
7682
7723
|
log(`telegram outbound failed project=${project}: ${e.message}`);
|
|
7683
7724
|
});
|
|
7684
7725
|
},
|
|
@@ -8036,8 +8077,8 @@ var init_stream = __esm({
|
|
|
8036
8077
|
// packages/core/dist/router/shim.js
|
|
8037
8078
|
import { createServer as createServer3 } from "http";
|
|
8038
8079
|
import { Readable } from "stream";
|
|
8039
|
-
function joinUrl(base,
|
|
8040
|
-
return base.replace(/\/+$/, "") +
|
|
8080
|
+
function joinUrl(base, path42) {
|
|
8081
|
+
return base.replace(/\/+$/, "") + path42;
|
|
8041
8082
|
}
|
|
8042
8083
|
async function readBody(req) {
|
|
8043
8084
|
const chunks = [];
|
|
@@ -8213,8 +8254,8 @@ function createRouterShim(opts) {
|
|
|
8213
8254
|
writeJson(res, 200, { input_tokens: Math.max(1, Math.ceil(text.length / 4)) });
|
|
8214
8255
|
}
|
|
8215
8256
|
function handler(req, res) {
|
|
8216
|
-
const
|
|
8217
|
-
if (req.method === "POST" &&
|
|
8257
|
+
const path42 = pathnameOf(req.url);
|
|
8258
|
+
if (req.method === "POST" && path42 === "/v1/messages") {
|
|
8218
8259
|
void handleMessages(req, res).catch((err) => {
|
|
8219
8260
|
log(`router internal error: ${err instanceof Error ? err.message : String(err)}`);
|
|
8220
8261
|
if (!res.headersSent) {
|
|
@@ -8226,18 +8267,18 @@ function createRouterShim(opts) {
|
|
|
8226
8267
|
});
|
|
8227
8268
|
return;
|
|
8228
8269
|
}
|
|
8229
|
-
if (req.method === "POST" &&
|
|
8270
|
+
if (req.method === "POST" && path42 === "/v1/messages/count_tokens") {
|
|
8230
8271
|
void handleCountTokens(req, res).catch(() => {
|
|
8231
8272
|
if (!res.headersSent)
|
|
8232
8273
|
writeJson(res, 502, anthropicError(502, "api_error", "router internal error").body);
|
|
8233
8274
|
});
|
|
8234
8275
|
return;
|
|
8235
8276
|
}
|
|
8236
|
-
if (req.method === "GET" &&
|
|
8277
|
+
if (req.method === "GET" && path42 === "/v1/models") {
|
|
8237
8278
|
writeJson(res, 200, modelsList([...advertisedModels]));
|
|
8238
8279
|
return;
|
|
8239
8280
|
}
|
|
8240
|
-
if (req.method === "GET" &&
|
|
8281
|
+
if (req.method === "GET" && path42 === "/healthz") {
|
|
8241
8282
|
writeJson(res, 200, { ready: server?.listening === true, upstreamReachable, lastError });
|
|
8242
8283
|
return;
|
|
8243
8284
|
}
|
|
@@ -8506,11 +8547,11 @@ async function probeCaptainChannel(project, opts = {}) {
|
|
|
8506
8547
|
}
|
|
8507
8548
|
}
|
|
8508
8549
|
async function httpReachable(port, fetchImpl, timeoutMs) {
|
|
8509
|
-
for (const
|
|
8550
|
+
for (const path42 of ["/session", "/api/session"]) {
|
|
8510
8551
|
const ac = new AbortController();
|
|
8511
8552
|
const t = setTimeout(() => ac.abort(), timeoutMs);
|
|
8512
8553
|
try {
|
|
8513
|
-
const res = await fetchImpl(`http://127.0.0.1:${port}${
|
|
8554
|
+
const res = await fetchImpl(`http://127.0.0.1:${port}${path42}`, { signal: ac.signal });
|
|
8514
8555
|
if (res.ok)
|
|
8515
8556
|
return true;
|
|
8516
8557
|
} catch {
|
|
@@ -9281,8 +9322,8 @@ function createEventsSource(opts) {
|
|
|
9281
9322
|
for (const v2 of checkFact(traceFor(taskId), fact, opts.check ?? {})) {
|
|
9282
9323
|
opts.onViolation(v2);
|
|
9283
9324
|
}
|
|
9284
|
-
for (const
|
|
9285
|
-
opts.emit(
|
|
9325
|
+
for (const ev2 of toControlEvent(fact))
|
|
9326
|
+
opts.emit(ev2);
|
|
9286
9327
|
}
|
|
9287
9328
|
}
|
|
9288
9329
|
};
|
|
@@ -9349,31 +9390,294 @@ var init_peer_first_turn = __esm({
|
|
|
9349
9390
|
});
|
|
9350
9391
|
|
|
9351
9392
|
// packages/core/dist/knowledge/paths.js
|
|
9352
|
-
import
|
|
9353
|
-
function kbDir(
|
|
9354
|
-
return
|
|
9393
|
+
import path15 from "path";
|
|
9394
|
+
function kbDir(cfg, kb) {
|
|
9395
|
+
return kbPath(cfg, kb);
|
|
9355
9396
|
}
|
|
9356
|
-
function kbRulesDir(
|
|
9357
|
-
return
|
|
9397
|
+
function kbRulesDir(cfg, kb) {
|
|
9398
|
+
return path15.join(kbDir(cfg, kb), "shared", "rules");
|
|
9358
9399
|
}
|
|
9359
|
-
function
|
|
9360
|
-
return
|
|
9400
|
+
function legacyKbDir(hubVault, kb) {
|
|
9401
|
+
return path15.join(hubVault, "knowledge", kb);
|
|
9361
9402
|
}
|
|
9362
9403
|
var PROPOSED_DIR;
|
|
9363
9404
|
var init_paths = __esm({
|
|
9364
9405
|
"packages/core/dist/knowledge/paths.js"() {
|
|
9406
|
+
init_dist();
|
|
9365
9407
|
PROPOSED_DIR = "_proposed";
|
|
9366
9408
|
}
|
|
9367
9409
|
});
|
|
9368
9410
|
|
|
9369
|
-
// packages/core/dist/knowledge/
|
|
9411
|
+
// packages/core/dist/knowledge/ingest.js
|
|
9412
|
+
import crypto3 from "crypto";
|
|
9370
9413
|
import fs15 from "fs";
|
|
9371
9414
|
import os7 from "os";
|
|
9372
|
-
import
|
|
9415
|
+
import path16 from "path";
|
|
9416
|
+
import { execFile as execFile2 } from "child_process";
|
|
9417
|
+
import { promisify as promisify2 } from "util";
|
|
9418
|
+
function refFor(kbRoot, file) {
|
|
9419
|
+
const rel = path16.relative(kbRoot, file);
|
|
9420
|
+
if (rel && !rel.startsWith("..") && !path16.isAbsolute(rel))
|
|
9421
|
+
return rel.split(path16.sep).join("/");
|
|
9422
|
+
const home = os7.homedir();
|
|
9423
|
+
return file.startsWith(home + path16.sep) ? `~/${path16.relative(home, file).split(path16.sep).join("/")}` : file;
|
|
9424
|
+
}
|
|
9425
|
+
function readState2(kbRoot, ref) {
|
|
9426
|
+
try {
|
|
9427
|
+
return JSON.parse(fs15.readFileSync(stampFile(kbRoot, ref), "utf8"));
|
|
9428
|
+
} catch {
|
|
9429
|
+
return null;
|
|
9430
|
+
}
|
|
9431
|
+
}
|
|
9432
|
+
function writeState(kbRoot, st) {
|
|
9433
|
+
fs15.mkdirSync(path16.join(kbRoot, CONVERTED_DIR), { recursive: true });
|
|
9434
|
+
fs15.writeFileSync(stampFile(kbRoot, st.ref), JSON.stringify(st) + "\n");
|
|
9435
|
+
}
|
|
9436
|
+
function globToRegExp(glob) {
|
|
9437
|
+
let re = "";
|
|
9438
|
+
for (let i = 0; i < glob.length; i++) {
|
|
9439
|
+
const c = glob[i];
|
|
9440
|
+
if (c === "*") {
|
|
9441
|
+
if (glob[i + 1] === "*") {
|
|
9442
|
+
re += glob[i + 2] === "/" ? "(?:.*/)?" : ".*";
|
|
9443
|
+
i += glob[i + 2] === "/" ? 2 : 1;
|
|
9444
|
+
} else
|
|
9445
|
+
re += "[^/]*";
|
|
9446
|
+
} else if (c === "?")
|
|
9447
|
+
re += "[^/]";
|
|
9448
|
+
else
|
|
9449
|
+
re += c.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
9450
|
+
}
|
|
9451
|
+
return new RegExp(`^${re}$`);
|
|
9452
|
+
}
|
|
9453
|
+
function walkFiles(dir, out = []) {
|
|
9454
|
+
if (!fs15.existsSync(dir))
|
|
9455
|
+
return out;
|
|
9456
|
+
for (const ent of fs15.readdirSync(dir, { withFileTypes: true })) {
|
|
9457
|
+
if (ent.name === ".git" || ent.name === CONVERTED_DIR || ent.name === "node_modules")
|
|
9458
|
+
continue;
|
|
9459
|
+
const full = path16.join(dir, ent.name);
|
|
9460
|
+
if (ent.isDirectory())
|
|
9461
|
+
walkFiles(full, out);
|
|
9462
|
+
else if (ent.isFile())
|
|
9463
|
+
out.push(full);
|
|
9464
|
+
}
|
|
9465
|
+
return out;
|
|
9466
|
+
}
|
|
9467
|
+
function expandSource(kbRoot, entry) {
|
|
9468
|
+
const abs = path16.resolve(kbRoot, expandHome(entry.path));
|
|
9469
|
+
if (!/[*?]/.test(entry.path))
|
|
9470
|
+
return fs15.existsSync(abs) && fs15.statSync(abs).isFile() ? [abs] : [];
|
|
9471
|
+
const base = abs.split(/[*?]/)[0];
|
|
9472
|
+
const root = base.endsWith(path16.sep) ? base : path16.dirname(base);
|
|
9473
|
+
const re = globToRegExp(abs.split(path16.sep).join("/"));
|
|
9474
|
+
return walkFiles(root).filter((f) => re.test(f.split(path16.sep).join("/"))).sort();
|
|
9475
|
+
}
|
|
9476
|
+
function detectChange(kbRoot, file) {
|
|
9477
|
+
const ref = refFor(kbRoot, file);
|
|
9478
|
+
const st = fs15.statSync(file);
|
|
9479
|
+
const prior = readState2(kbRoot, ref);
|
|
9480
|
+
if (prior && prior.size === st.size && prior.mtimeMs === st.mtimeMs) {
|
|
9481
|
+
return { ref, changed: false, stamp: { sha: prior.sha, size: st.size, mtimeMs: st.mtimeMs }, prior };
|
|
9482
|
+
}
|
|
9483
|
+
const sha = sha256(file);
|
|
9484
|
+
return { ref, changed: !prior || prior.sha !== sha, stamp: { sha, size: st.size, mtimeMs: st.mtimeMs }, prior };
|
|
9485
|
+
}
|
|
9486
|
+
function splitSections(text) {
|
|
9487
|
+
const cuts = [];
|
|
9488
|
+
const re = /^#{1,6}[ \t]+(.+)$/gm;
|
|
9489
|
+
for (let m = re.exec(text); m; m = re.exec(text))
|
|
9490
|
+
cuts.push({ at: m.index, heading: m[1].trim() });
|
|
9491
|
+
if (!cuts.length || cuts[0].at > 0)
|
|
9492
|
+
cuts.unshift({ at: 0 });
|
|
9493
|
+
const spans = [];
|
|
9494
|
+
cuts.forEach((c, i) => {
|
|
9495
|
+
const end = i + 1 < cuts.length ? cuts[i + 1].at : text.length;
|
|
9496
|
+
let start = c.at;
|
|
9497
|
+
while (end - start > SECTION_TARGET_CHARS * 1.5) {
|
|
9498
|
+
const brk = text.indexOf("\n\n", start + SECTION_TARGET_CHARS);
|
|
9499
|
+
if (brk < 0 || brk >= end)
|
|
9500
|
+
break;
|
|
9501
|
+
spans.push({ index: 0, start, end: brk, heading: c.heading });
|
|
9502
|
+
start = brk + 2;
|
|
9503
|
+
}
|
|
9504
|
+
if (text.slice(start, end).trim())
|
|
9505
|
+
spans.push({ index: 0, start, end, heading: c.heading });
|
|
9506
|
+
});
|
|
9507
|
+
return spans.map((s, i) => ({ ...s, index: i }));
|
|
9508
|
+
}
|
|
9509
|
+
async function planIngest(kbRoot, opts) {
|
|
9510
|
+
const converter = opts.converter ?? markitdownConverter;
|
|
9511
|
+
const cap = opts.maxSectionsPerPass ?? DEFAULT_MAX_SECTIONS_PER_PASS;
|
|
9512
|
+
const plan = { sources: [], skipped: [], failed: [], carriedSections: 0, sectionCount: 0, estimatedTokens: 0, existing: opts.existing ?? [] };
|
|
9513
|
+
let budget = cap;
|
|
9514
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9515
|
+
for (const entry of opts.sources) {
|
|
9516
|
+
for (const file of expandSource(kbRoot, entry)) {
|
|
9517
|
+
const { ref, changed, stamp, prior } = detectChange(kbRoot, file);
|
|
9518
|
+
if (seen.has(ref))
|
|
9519
|
+
continue;
|
|
9520
|
+
seen.add(ref);
|
|
9521
|
+
const pending = !!prior && prior.cursor < prior.sections;
|
|
9522
|
+
if (!changed && !pending)
|
|
9523
|
+
continue;
|
|
9524
|
+
if (entry.sensitivity === "local-only" && !opts.localModel) {
|
|
9525
|
+
plan.skipped.push({ ref, reason: "sensitivity: local-only and no local model available" });
|
|
9526
|
+
continue;
|
|
9527
|
+
}
|
|
9528
|
+
if (budget <= 0) {
|
|
9529
|
+
plan.carriedSections += changed ? 1 : prior.sections - prior.cursor;
|
|
9530
|
+
continue;
|
|
9531
|
+
}
|
|
9532
|
+
let text;
|
|
9533
|
+
const conv = convertedFile(kbRoot, ref);
|
|
9534
|
+
try {
|
|
9535
|
+
if (!changed && fs15.existsSync(conv))
|
|
9536
|
+
text = fs15.readFileSync(conv, "utf8");
|
|
9537
|
+
else {
|
|
9538
|
+
text = PASS_THROUGH.has(path16.extname(file).toLowerCase()) ? fs15.readFileSync(file, "utf8") : await converter(file);
|
|
9539
|
+
if (!opts.dryRun) {
|
|
9540
|
+
fs15.mkdirSync(path16.dirname(conv), { recursive: true });
|
|
9541
|
+
fs15.writeFileSync(conv, text);
|
|
9542
|
+
}
|
|
9543
|
+
}
|
|
9544
|
+
} catch (e) {
|
|
9545
|
+
plan.failed.push({ ref, error: e.message.split("\n")[0] });
|
|
9546
|
+
continue;
|
|
9547
|
+
}
|
|
9548
|
+
const all = splitSections(text);
|
|
9549
|
+
const from = changed ? 0 : prior.cursor;
|
|
9550
|
+
const take = all.slice(from, from + budget);
|
|
9551
|
+
budget -= take.length;
|
|
9552
|
+
plan.carriedSections += all.length - from - take.length;
|
|
9553
|
+
plan.sectionCount += take.length;
|
|
9554
|
+
plan.estimatedTokens += Math.ceil(take.reduce((n, s) => n + (s.end - s.start), 0) / CHARS_PER_TOKEN);
|
|
9555
|
+
plan.sources.push({
|
|
9556
|
+
ref,
|
|
9557
|
+
file,
|
|
9558
|
+
converted: conv,
|
|
9559
|
+
sha: stamp.sha,
|
|
9560
|
+
priority: entry.priority,
|
|
9561
|
+
domain: entry.domain,
|
|
9562
|
+
sensitivity: entry.sensitivity,
|
|
9563
|
+
sections: take,
|
|
9564
|
+
totalSections: all.length
|
|
9565
|
+
});
|
|
9566
|
+
if (!opts.dryRun)
|
|
9567
|
+
writeState(kbRoot, { ref, ...stamp, sections: all.length, cursor: from + take.length, priority: entry.priority });
|
|
9568
|
+
}
|
|
9569
|
+
}
|
|
9570
|
+
return plan;
|
|
9571
|
+
}
|
|
9572
|
+
var execFileAsync, CONVERTED_DIR, DEFAULT_MAX_SECTIONS_PER_PASS, CHARS_PER_TOKEN, PASS_THROUGH, SECTION_TARGET_CHARS, markitdownConverter, expandHome, fileForRef, cacheKey, convertedFile, stampFile, sha256;
|
|
9573
|
+
var init_ingest = __esm({
|
|
9574
|
+
"packages/core/dist/knowledge/ingest.js"() {
|
|
9575
|
+
execFileAsync = promisify2(execFile2);
|
|
9576
|
+
CONVERTED_DIR = ".converted";
|
|
9577
|
+
DEFAULT_MAX_SECTIONS_PER_PASS = 40;
|
|
9578
|
+
CHARS_PER_TOKEN = 4;
|
|
9579
|
+
PASS_THROUGH = /* @__PURE__ */ new Set([".md", ".markdown", ".txt"]);
|
|
9580
|
+
SECTION_TARGET_CHARS = 4e3;
|
|
9581
|
+
markitdownConverter = async (file) => {
|
|
9582
|
+
const { stdout } = await execFileAsync("markitdown", [file], { maxBuffer: 64 * 1024 * 1024 });
|
|
9583
|
+
return stdout;
|
|
9584
|
+
};
|
|
9585
|
+
expandHome = (p) => p === "~" || p.startsWith("~/") ? path16.join(os7.homedir(), p.slice(1)) : p;
|
|
9586
|
+
fileForRef = (kbRoot, ref) => path16.resolve(kbRoot, expandHome(ref));
|
|
9587
|
+
cacheKey = (ref) => `${ref.replace(/[^A-Za-z0-9._-]+/g, "_")}.${crypto3.createHash("sha1").update(ref).digest("hex").slice(0, 6)}`;
|
|
9588
|
+
convertedFile = (kbRoot, ref) => path16.join(kbRoot, CONVERTED_DIR, `${cacheKey(ref)}.md`);
|
|
9589
|
+
stampFile = (kbRoot, ref) => path16.join(kbRoot, CONVERTED_DIR, `${cacheKey(ref)}.sha`);
|
|
9590
|
+
sha256 = (file) => crypto3.createHash("sha256").update(fs15.readFileSync(file)).digest("hex");
|
|
9591
|
+
}
|
|
9592
|
+
});
|
|
9593
|
+
|
|
9594
|
+
// packages/core/dist/knowledge/verify.js
|
|
9595
|
+
function normalizeWithMap(src, keepBreakHyphen = false) {
|
|
9596
|
+
const out = [];
|
|
9597
|
+
const map2 = [];
|
|
9598
|
+
const push = (s, at) => {
|
|
9599
|
+
for (const ch of s) {
|
|
9600
|
+
out.push(ch);
|
|
9601
|
+
map2.push(at);
|
|
9602
|
+
}
|
|
9603
|
+
};
|
|
9604
|
+
let i = 0;
|
|
9605
|
+
while (i < src.length) {
|
|
9606
|
+
const ch = src[i];
|
|
9607
|
+
if (ch === "-" && /^-[ \t]*\r?\n[ \t]*[a-z]/.test(src.slice(i, i + 40)) && i > 0 && /[A-Za-z]/.test(src[i - 1])) {
|
|
9608
|
+
if (keepBreakHyphen)
|
|
9609
|
+
push("-", i);
|
|
9610
|
+
i++;
|
|
9611
|
+
while (/\s/.test(src[i]))
|
|
9612
|
+
i++;
|
|
9613
|
+
continue;
|
|
9614
|
+
}
|
|
9615
|
+
if (/\s/.test(ch)) {
|
|
9616
|
+
const start = i;
|
|
9617
|
+
while (i < src.length && /\s/.test(src[i]))
|
|
9618
|
+
i++;
|
|
9619
|
+
if (out.length && out[out.length - 1] !== " ")
|
|
9620
|
+
push(" ", start);
|
|
9621
|
+
continue;
|
|
9622
|
+
}
|
|
9623
|
+
if (MARKUP.has(ch)) {
|
|
9624
|
+
i++;
|
|
9625
|
+
continue;
|
|
9626
|
+
}
|
|
9627
|
+
if (LIGATURES[ch])
|
|
9628
|
+
push(LIGATURES[ch], i);
|
|
9629
|
+
else if (QUOTES[ch])
|
|
9630
|
+
push(QUOTES[ch], i);
|
|
9631
|
+
else if (DASHES.has(ch))
|
|
9632
|
+
push("-", i);
|
|
9633
|
+
else
|
|
9634
|
+
push(ch, i);
|
|
9635
|
+
i++;
|
|
9636
|
+
}
|
|
9637
|
+
while (out.length && out[out.length - 1] === " ") {
|
|
9638
|
+
out.pop();
|
|
9639
|
+
map2.pop();
|
|
9640
|
+
}
|
|
9641
|
+
return { text: out.join(""), map: map2 };
|
|
9642
|
+
}
|
|
9643
|
+
function findQuote(source, quote) {
|
|
9644
|
+
const needle = normalizeText(quote);
|
|
9645
|
+
if (!needle)
|
|
9646
|
+
return null;
|
|
9647
|
+
const views = typeof source === "string" ? normalizedViews(source) : source;
|
|
9648
|
+
for (const hay of views) {
|
|
9649
|
+
const at = hay.text.indexOf(needle);
|
|
9650
|
+
if (at >= 0)
|
|
9651
|
+
return [hay.map[at], hay.map[at + needle.length - 1] + 1];
|
|
9652
|
+
}
|
|
9653
|
+
return null;
|
|
9654
|
+
}
|
|
9655
|
+
var LIGATURES, QUOTES, DASHES, MARKUP, normalizeText, normalizedViews, DOCLING_DROP_THRESHOLD, needsDocling, spansOverlap;
|
|
9656
|
+
var init_verify = __esm({
|
|
9657
|
+
"packages/core/dist/knowledge/verify.js"() {
|
|
9658
|
+
LIGATURES = { "\uFB01": "fi", "\uFB02": "fl", "\uFB00": "ff", "\uFB03": "ffi", "\uFB04": "ffl", "\uFB05": "st", "\uFB06": "st" };
|
|
9659
|
+
QUOTES = { "\u2018": "'", "\u2019": "'", "\u201A": "'", "\u201B": "'", "\u201C": '"', "\u201D": '"', "\u201E": '"', "\u201F": '"' };
|
|
9660
|
+
DASHES = /* @__PURE__ */ new Set(["\u2010", "\u2011", "\u2012", "\u2013", "\u2014", "\u2015", "\u2212"]);
|
|
9661
|
+
MARKUP = /* @__PURE__ */ new Set(["*", "_", "`", "\\"]);
|
|
9662
|
+
normalizeText = (s) => normalizeWithMap(s).text;
|
|
9663
|
+
normalizedViews = (src) => [normalizeWithMap(src), normalizeWithMap(src, true)];
|
|
9664
|
+
DOCLING_DROP_THRESHOLD = 0.2;
|
|
9665
|
+
needsDocling = (dropped, total) => total > 0 && dropped / total > DOCLING_DROP_THRESHOLD;
|
|
9666
|
+
spansOverlap = (a, b) => a[0] < b[1] && b[0] < a[1];
|
|
9667
|
+
}
|
|
9668
|
+
});
|
|
9669
|
+
|
|
9670
|
+
// packages/core/dist/knowledge/store.js
|
|
9671
|
+
import fs16 from "fs";
|
|
9672
|
+
import os8 from "os";
|
|
9673
|
+
import path17 from "path";
|
|
9373
9674
|
import matter2 from "gray-matter";
|
|
9374
9675
|
function duplicateIdProblem(id, ignored, used) {
|
|
9375
9676
|
return `duplicate id ${id}: ${ignored} is ignored, ${used} is used (live files win over _proposed, then path order). Fix: rename the id in one file, or delete or move one of them.`;
|
|
9376
9677
|
}
|
|
9678
|
+
function isPendingProposalDup(e, rulesDir) {
|
|
9679
|
+
return e.problems.every((p) => p.startsWith("duplicate id")) && path17.relative(rulesDir, e.file).split(path17.sep).includes(PROPOSED_DIR);
|
|
9680
|
+
}
|
|
9377
9681
|
function splitStatement(body) {
|
|
9378
9682
|
const first = body.trim().split(/\n\s*\n/)[0] ?? "";
|
|
9379
9683
|
return first.replace(/\s+/g, " ").trim();
|
|
@@ -9381,8 +9685,8 @@ function splitStatement(body) {
|
|
|
9381
9685
|
function listMarkdown(dir, includeProposed) {
|
|
9382
9686
|
const out = [];
|
|
9383
9687
|
const walk = (d) => {
|
|
9384
|
-
for (const ent of
|
|
9385
|
-
const full =
|
|
9688
|
+
for (const ent of fs16.readdirSync(d, { withFileTypes: true })) {
|
|
9689
|
+
const full = path17.join(d, ent.name);
|
|
9386
9690
|
if (ent.isDirectory()) {
|
|
9387
9691
|
if (ent.name === PROPOSED_DIR && !includeProposed)
|
|
9388
9692
|
continue;
|
|
@@ -9393,11 +9697,11 @@ function listMarkdown(dir, includeProposed) {
|
|
|
9393
9697
|
}
|
|
9394
9698
|
};
|
|
9395
9699
|
walk(dir);
|
|
9396
|
-
const isProposed = (f) =>
|
|
9700
|
+
const isProposed = (f) => path17.relative(dir, f).split(path17.sep).includes(PROPOSED_DIR);
|
|
9397
9701
|
return out.sort((a, b) => Number(isProposed(a)) - Number(isProposed(b)) || (a < b ? -1 : a > b ? 1 : 0));
|
|
9398
9702
|
}
|
|
9399
9703
|
function loadRulesDir(dir, layer, opts = {}) {
|
|
9400
|
-
if (!
|
|
9704
|
+
if (!fs16.existsSync(dir))
|
|
9401
9705
|
return { rules: [], errors: [] };
|
|
9402
9706
|
const rules = [];
|
|
9403
9707
|
const errors = [];
|
|
@@ -9405,7 +9709,7 @@ function loadRulesDir(dir, layer, opts = {}) {
|
|
|
9405
9709
|
for (const file of listMarkdown(dir, opts.includeProposed ?? false)) {
|
|
9406
9710
|
let parsed;
|
|
9407
9711
|
try {
|
|
9408
|
-
parsed = matter2(
|
|
9712
|
+
parsed = matter2(fs16.readFileSync(file, "utf8"), NO_JS_FRONTMATTER);
|
|
9409
9713
|
} catch (e) {
|
|
9410
9714
|
errors.push({ file, problems: [`frontmatter parse error: ${e.message}`] });
|
|
9411
9715
|
continue;
|
|
@@ -9429,22 +9733,23 @@ function loadRulesDir(dir, layer, opts = {}) {
|
|
|
9429
9733
|
function resolveSourceRef(ref, kbRoot, sources) {
|
|
9430
9734
|
const bases = /* @__PURE__ */ new Set([kbRoot]);
|
|
9431
9735
|
for (const s of sources) {
|
|
9432
|
-
let d =
|
|
9433
|
-
for (let i = 0; i < 6 && d !==
|
|
9434
|
-
d =
|
|
9736
|
+
let d = path17.resolve(kbRoot, expandHome2(s.path.split(/[*?[{]/)[0]));
|
|
9737
|
+
for (let i = 0; i < 6 && d !== path17.dirname(d); i++) {
|
|
9738
|
+
d = path17.dirname(d);
|
|
9435
9739
|
bases.add(d);
|
|
9436
9740
|
}
|
|
9437
9741
|
}
|
|
9438
|
-
const cands = [
|
|
9439
|
-
return cands.find((c) =>
|
|
9742
|
+
const cands = [path17.resolve(kbRoot, expandHome2(ref)), ...[...bases].map((b) => path17.join(b, ref))];
|
|
9743
|
+
return cands.find((c) => fs16.existsSync(c) && fs16.statSync(c).isFile());
|
|
9440
9744
|
}
|
|
9441
9745
|
function validateKb(opts) {
|
|
9442
9746
|
const { rules, errors } = loadRulesDir(opts.rulesDir, "kb", { includeProposed: true });
|
|
9443
9747
|
const checks = [];
|
|
9444
|
-
const dups = errors.filter((e) => e.problems.some((p) => p.startsWith("duplicate id")));
|
|
9445
|
-
const
|
|
9446
|
-
const
|
|
9447
|
-
|
|
9748
|
+
const dups = errors.filter((e) => e.problems.some((p) => p.startsWith("duplicate id")) && !isPendingProposalDup(e, opts.rulesDir));
|
|
9749
|
+
const pendingDups = errors.filter((e) => isPendingProposalDup(e, opts.rulesDir)).length;
|
|
9750
|
+
const invalid = errors.filter((e) => !dups.includes(e) && !isPendingProposalDup(e, opts.rulesDir));
|
|
9751
|
+
const proposed = rules.filter((r) => path17.relative(opts.rulesDir, r.file).split(path17.sep).includes(PROPOSED_DIR)).length;
|
|
9752
|
+
checks.push({ level: "ok", message: `${rules.length + errors.length} rule files read (${rules.length - proposed} live, ${proposed + pendingDups} proposed parsed OK)` });
|
|
9448
9753
|
if (invalid.length)
|
|
9449
9754
|
for (const e of invalid)
|
|
9450
9755
|
checks.push({ level: "fail", message: `invalid ${e.file}: ${e.problems.join("; ")}` });
|
|
@@ -9456,7 +9761,7 @@ function validateKb(opts) {
|
|
|
9456
9761
|
else
|
|
9457
9762
|
checks.push({ level: "ok", message: "no duplicate ids" });
|
|
9458
9763
|
const cache = /* @__PURE__ */ new Map();
|
|
9459
|
-
let verified = 0, unresolved = 0;
|
|
9764
|
+
let verified = 0, unresolved = 0, notIngested = 0;
|
|
9460
9765
|
for (const r of rules) {
|
|
9461
9766
|
r.sources.forEach((src, i) => {
|
|
9462
9767
|
const file = resolveSourceRef(src.ref, opts.kbRoot, opts.sources);
|
|
@@ -9464,16 +9769,28 @@ function validateKb(opts) {
|
|
|
9464
9769
|
unresolved++;
|
|
9465
9770
|
return;
|
|
9466
9771
|
}
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
|
|
9772
|
+
const key = `${src.ref}\0${file}`;
|
|
9773
|
+
if (!cache.has(key)) {
|
|
9774
|
+
const conv = readState2(opts.kbRoot, src.ref) ? convertedFile(opts.kbRoot, src.ref) : null;
|
|
9775
|
+
const cached = !!conv && fs16.existsSync(conv);
|
|
9776
|
+
cache.set(key, {
|
|
9777
|
+
hay: normalizedViews(fs16.readFileSync(cached ? conv : file, "utf8")),
|
|
9778
|
+
convertible: !cached && !PASS_THROUGH.has(path17.extname(file).toLowerCase())
|
|
9779
|
+
});
|
|
9780
|
+
}
|
|
9781
|
+
const { hay, convertible } = cache.get(key);
|
|
9782
|
+
if (findQuote(hay, src.quote))
|
|
9470
9783
|
verified++;
|
|
9784
|
+
else if (convertible)
|
|
9785
|
+
notIngested++;
|
|
9471
9786
|
else
|
|
9472
9787
|
checks.push({ level: "fail", message: `quote not found in ${file}: ${r.id} sources[${i}] (${r.file})` });
|
|
9473
9788
|
});
|
|
9474
9789
|
}
|
|
9475
9790
|
if (!checks.some((c) => c.message.startsWith("quote not found")))
|
|
9476
9791
|
checks.push({ level: "ok", message: `${verified} source quotes found in their source files` });
|
|
9792
|
+
if (notIngested)
|
|
9793
|
+
checks.push({ level: "skip", message: `${notIngested} source refs need conversion but have no .converted cache, quote not checked (run \`squadrant knowledge ingest <kb>\`)` });
|
|
9477
9794
|
if (unresolved)
|
|
9478
9795
|
checks.push({ level: "skip", message: `${unresolved} source refs not resolvable on this machine, quote not checked` });
|
|
9479
9796
|
if (opts.domains) {
|
|
@@ -9484,24 +9801,26 @@ function validateKb(opts) {
|
|
|
9484
9801
|
}
|
|
9485
9802
|
return { checks, ok: !checks.some((c) => c.level === "fail") };
|
|
9486
9803
|
}
|
|
9487
|
-
var NO_JS_FRONTMATTER,
|
|
9804
|
+
var NO_JS_FRONTMATTER, expandHome2;
|
|
9488
9805
|
var init_store2 = __esm({
|
|
9489
9806
|
"packages/core/dist/knowledge/store.js"() {
|
|
9490
9807
|
init_dist();
|
|
9808
|
+
init_ingest();
|
|
9491
9809
|
init_paths();
|
|
9810
|
+
init_verify();
|
|
9492
9811
|
NO_JS_FRONTMATTER = {
|
|
9493
9812
|
engines: { javascript: { parse: () => {
|
|
9494
9813
|
throw new Error("only YAML front matter is allowed");
|
|
9495
9814
|
} } }
|
|
9496
9815
|
};
|
|
9497
|
-
|
|
9816
|
+
expandHome2 = (p) => p === "~" || p.startsWith("~/") ? path17.join(os8.homedir(), p.slice(1)) : p;
|
|
9498
9817
|
}
|
|
9499
9818
|
});
|
|
9500
9819
|
|
|
9501
9820
|
// packages/core/dist/knowledge/layers.js
|
|
9502
|
-
import
|
|
9821
|
+
import fs17 from "fs";
|
|
9503
9822
|
function loadKbRules(cfg, kb, opts = {}) {
|
|
9504
|
-
return loadRulesDir(kbRulesDir(
|
|
9823
|
+
return loadRulesDir(kbRulesDir(cfg, kb), `kb:${kb}`, opts);
|
|
9505
9824
|
}
|
|
9506
9825
|
function resolveProjectRules(cfg, project, opts = {}) {
|
|
9507
9826
|
const pc = cfg.projects[project];
|
|
@@ -9511,7 +9830,7 @@ function resolveProjectRules(cfg, project, opts = {}) {
|
|
|
9511
9830
|
const errors = [];
|
|
9512
9831
|
const warnings = [];
|
|
9513
9832
|
for (const kb of subscribedKbs(cfg, project)) {
|
|
9514
|
-
if (!
|
|
9833
|
+
if (!fs17.existsSync(kbDir(cfg, kb))) {
|
|
9515
9834
|
warnings.push(`knowledge base '${kb}' not found (run: squadrant knowledge init ${kb})`);
|
|
9516
9835
|
continue;
|
|
9517
9836
|
}
|
|
@@ -9526,10 +9845,13 @@ function resolveProjectRules(cfg, project, opts = {}) {
|
|
|
9526
9845
|
byId.set(r.id, r);
|
|
9527
9846
|
}
|
|
9528
9847
|
}
|
|
9529
|
-
const
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
|
|
9848
|
+
const projDir = projectRulesHome(cfg, project);
|
|
9849
|
+
if (projDir) {
|
|
9850
|
+
const proj = loadRulesDir(projDir, `project:${project}`, opts);
|
|
9851
|
+
errors.push(...proj.errors);
|
|
9852
|
+
for (const r of proj.rules)
|
|
9853
|
+
byId.set(r.id, r);
|
|
9854
|
+
}
|
|
9533
9855
|
for (const id of pc.rulesDisabled ?? []) {
|
|
9534
9856
|
if (!byId.delete(id))
|
|
9535
9857
|
warnings.push(`rulesDisabled: unknown rule id '${id}'`);
|
|
@@ -9546,8 +9868,8 @@ var init_layers = __esm({
|
|
|
9546
9868
|
});
|
|
9547
9869
|
|
|
9548
9870
|
// packages/core/dist/knowledge/index-file.js
|
|
9549
|
-
import
|
|
9550
|
-
import
|
|
9871
|
+
import fs18 from "fs";
|
|
9872
|
+
import path18 from "path";
|
|
9551
9873
|
function compileIndex(kb, rules, now = /* @__PURE__ */ new Date()) {
|
|
9552
9874
|
return {
|
|
9553
9875
|
kb,
|
|
@@ -9555,19 +9877,19 @@ function compileIndex(kb, rules, now = /* @__PURE__ */ new Date()) {
|
|
|
9555
9877
|
rules: rules.map(({ id, domain, modality, status, statement, triggers, anchors, file }) => ({ id, domain, modality, status, statement, triggers, anchors, file }))
|
|
9556
9878
|
};
|
|
9557
9879
|
}
|
|
9558
|
-
function writeIndex(
|
|
9559
|
-
const file =
|
|
9880
|
+
function writeIndex(kbRoot, index) {
|
|
9881
|
+
const file = path18.join(kbRoot, "index.json");
|
|
9560
9882
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
9561
|
-
|
|
9562
|
-
|
|
9883
|
+
fs18.writeFileSync(tmp, JSON.stringify(index, null, 2) + "\n");
|
|
9884
|
+
fs18.renameSync(tmp, file);
|
|
9563
9885
|
return file;
|
|
9564
9886
|
}
|
|
9565
|
-
function readIndex(
|
|
9566
|
-
const file =
|
|
9567
|
-
if (!
|
|
9887
|
+
function readIndex(kbRoot, kb) {
|
|
9888
|
+
const file = path18.join(kbRoot, "index.json");
|
|
9889
|
+
if (!fs18.existsSync(file))
|
|
9568
9890
|
return { index: null, problem: `index.json missing for kb '${kb}'` };
|
|
9569
9891
|
try {
|
|
9570
|
-
const index = JSON.parse(
|
|
9892
|
+
const index = JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
9571
9893
|
if (!Array.isArray(index.rules))
|
|
9572
9894
|
return { index: null, problem: `index.json corrupt for kb '${kb}' (no rules array)` };
|
|
9573
9895
|
return { index };
|
|
@@ -9577,14 +9899,13 @@ function readIndex(hubVault, kb) {
|
|
|
9577
9899
|
}
|
|
9578
9900
|
var init_index_file = __esm({
|
|
9579
9901
|
"packages/core/dist/knowledge/index-file.js"() {
|
|
9580
|
-
init_paths();
|
|
9581
9902
|
}
|
|
9582
9903
|
});
|
|
9583
9904
|
|
|
9584
9905
|
// packages/core/dist/knowledge/search.js
|
|
9585
9906
|
function searchRules(rules, query, opts = {}) {
|
|
9586
|
-
const
|
|
9587
|
-
if (
|
|
9907
|
+
const terms2 = [...new Set(query.toLowerCase().split(/\s+/).filter(Boolean))];
|
|
9908
|
+
if (terms2.length === 0)
|
|
9588
9909
|
return [];
|
|
9589
9910
|
const statuses = opts.statuses ?? DEFAULT_SEARCH_STATUSES;
|
|
9590
9911
|
const hits = [];
|
|
@@ -9600,7 +9921,7 @@ function searchRules(rules, query, opts = {}) {
|
|
|
9600
9921
|
let score = 0;
|
|
9601
9922
|
let matched = 0;
|
|
9602
9923
|
let curated = false;
|
|
9603
|
-
for (const t of
|
|
9924
|
+
for (const t of terms2) {
|
|
9604
9925
|
let s = 0;
|
|
9605
9926
|
if (id.includes(t))
|
|
9606
9927
|
s += 3;
|
|
@@ -9624,8 +9945,8 @@ function searchRules(rules, query, opts = {}) {
|
|
|
9624
9945
|
if (score > 0)
|
|
9625
9946
|
hits.push({ rule, score, matched, curated });
|
|
9626
9947
|
}
|
|
9627
|
-
const
|
|
9628
|
-
const out =
|
|
9948
|
+
const strong2 = terms2.length >= 2 ? hits.filter((h) => h.matched >= 2 || h.curated) : hits;
|
|
9949
|
+
const out = strong2.length ? strong2 : hits;
|
|
9629
9950
|
return out.sort((a, b) => b.score - a.score || a.rule.id.localeCompare(b.rule.id));
|
|
9630
9951
|
}
|
|
9631
9952
|
var DEFAULT_SEARCH_STATUSES, lower, rationaleOf;
|
|
@@ -12663,15 +12984,15 @@ var init_js_yaml = __esm({
|
|
|
12663
12984
|
});
|
|
12664
12985
|
|
|
12665
12986
|
// packages/core/dist/knowledge/sources.js
|
|
12666
|
-
import
|
|
12667
|
-
import
|
|
12668
|
-
function loadSources(
|
|
12669
|
-
const file =
|
|
12670
|
-
if (!
|
|
12987
|
+
import fs19 from "fs";
|
|
12988
|
+
import path19 from "path";
|
|
12989
|
+
function loadSources(kbRoot) {
|
|
12990
|
+
const file = path19.join(kbRoot, "sources.yaml");
|
|
12991
|
+
if (!fs19.existsSync(file))
|
|
12671
12992
|
return { sources: [], errors: [] };
|
|
12672
12993
|
let data;
|
|
12673
12994
|
try {
|
|
12674
|
-
data = yaml.load(
|
|
12995
|
+
data = yaml.load(fs19.readFileSync(file, "utf8"));
|
|
12675
12996
|
} catch (e) {
|
|
12676
12997
|
return { sources: [], errors: [`YAML parse error: ${e.message.split("\n")[0]}`] };
|
|
12677
12998
|
}
|
|
@@ -12695,7 +13016,6 @@ var init_sources = __esm({
|
|
|
12695
13016
|
"packages/core/dist/knowledge/sources.js"() {
|
|
12696
13017
|
init_js_yaml();
|
|
12697
13018
|
init_dist();
|
|
12698
|
-
init_paths();
|
|
12699
13019
|
SOURCES_TEMPLATE = `# Trusted sources for this knowledge base (spec \xA73). Only these can produce rules.
|
|
12700
13020
|
# priority: company > project > agent (decides conflicts).
|
|
12701
13021
|
# sensitivity: local-only \u2192 never sent to a hosted model (D12).
|
|
@@ -12710,9 +13030,113 @@ var init_sources = __esm({
|
|
|
12710
13030
|
}
|
|
12711
13031
|
});
|
|
12712
13032
|
|
|
13033
|
+
// packages/core/dist/knowledge/match.js
|
|
13034
|
+
import path20 from "path";
|
|
13035
|
+
function globMatches(glob, relPath) {
|
|
13036
|
+
const g = glob.replace(/^\.\//, "");
|
|
13037
|
+
if (globToRegExp(g).test(relPath))
|
|
13038
|
+
return true;
|
|
13039
|
+
return !g.includes("/") && globToRegExp(g).test(path20.posix.basename(relPath));
|
|
13040
|
+
}
|
|
13041
|
+
function repoRelativePaths(file, cwd, projectRoot) {
|
|
13042
|
+
const out = /* @__PURE__ */ new Set();
|
|
13043
|
+
const add = (base) => {
|
|
13044
|
+
const rel = path20.relative(base, file).split(path20.sep).join("/");
|
|
13045
|
+
if (!rel || rel.startsWith("..") || path20.isAbsolute(rel))
|
|
13046
|
+
return;
|
|
13047
|
+
out.add(rel);
|
|
13048
|
+
const wt = /^\.worktrees\/[^/]+\/(.+)$/.exec(rel);
|
|
13049
|
+
if (wt)
|
|
13050
|
+
out.add(wt[1]);
|
|
13051
|
+
};
|
|
13052
|
+
if (path20.isAbsolute(file)) {
|
|
13053
|
+
add(cwd);
|
|
13054
|
+
add(projectRoot);
|
|
13055
|
+
} else
|
|
13056
|
+
out.add(file.replace(/^\.\//, ""));
|
|
13057
|
+
return [...out];
|
|
13058
|
+
}
|
|
13059
|
+
function matchPaths(rules, project, relPaths) {
|
|
13060
|
+
const hits = [];
|
|
13061
|
+
for (const rule of rules) {
|
|
13062
|
+
if (rule.status !== "active")
|
|
13063
|
+
continue;
|
|
13064
|
+
if (rule.triggers?.globs?.some((g) => relPaths.some((p) => globMatches(g, p))))
|
|
13065
|
+
hits.push({ rule, via: "glob" });
|
|
13066
|
+
else if (rule.anchors?.[project]?.paths?.some((a) => relPaths.includes(a)))
|
|
13067
|
+
hits.push({ rule, via: "anchor" });
|
|
13068
|
+
}
|
|
13069
|
+
return hits;
|
|
13070
|
+
}
|
|
13071
|
+
function matchSymbols(rules, project, text) {
|
|
13072
|
+
const idents = new Set(text.match(/[A-Za-z_$][\w$]{2,}/g) ?? []);
|
|
13073
|
+
return rules.filter((r) => r.status === "active" && r.anchors?.[project]?.symbols?.some((s) => idents.has(s))).map((rule) => ({ rule, via: "anchor" }));
|
|
13074
|
+
}
|
|
13075
|
+
function mentionedPaths(text) {
|
|
13076
|
+
const found = text.match(/[\w@.\-/]*[\w-]\.[A-Za-z]{1,5}\b|[\w.-]+\/[\w./-]+/g) ?? [];
|
|
13077
|
+
return [...new Set(found.map((f) => f.replace(/^\.\//, "")))].slice(0, 20);
|
|
13078
|
+
}
|
|
13079
|
+
function applyBudget(items, lineLen, maxRules = INJECT_MAX_RULES, maxChars = INJECT_MAX_CHARS) {
|
|
13080
|
+
const shown = [];
|
|
13081
|
+
let chars = 0;
|
|
13082
|
+
for (const it of items) {
|
|
13083
|
+
const n = lineLen(it.rule);
|
|
13084
|
+
if (shown.length >= maxRules || shown.length > 0 && chars + n > maxChars)
|
|
13085
|
+
break;
|
|
13086
|
+
shown.push(it);
|
|
13087
|
+
chars += n;
|
|
13088
|
+
}
|
|
13089
|
+
return { shown, overflow: items.slice(shown.length) };
|
|
13090
|
+
}
|
|
13091
|
+
var INJECT_MAX_RULES, INJECT_MAX_CHARS;
|
|
13092
|
+
var init_match = __esm({
|
|
13093
|
+
"packages/core/dist/knowledge/match.js"() {
|
|
13094
|
+
init_ingest();
|
|
13095
|
+
INJECT_MAX_RULES = 5;
|
|
13096
|
+
INJECT_MAX_CHARS = 3200;
|
|
13097
|
+
}
|
|
13098
|
+
});
|
|
13099
|
+
|
|
13100
|
+
// packages/core/dist/knowledge/audit.js
|
|
13101
|
+
import fs20 from "fs";
|
|
13102
|
+
import os9 from "os";
|
|
13103
|
+
import path21 from "path";
|
|
13104
|
+
function appendAudit(events, sink = {}) {
|
|
13105
|
+
if (!events.length)
|
|
13106
|
+
return;
|
|
13107
|
+
try {
|
|
13108
|
+
const ts = (sink.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
13109
|
+
const dir = sink.dir ?? defaultAuditDir();
|
|
13110
|
+
const file = path21.join(dir, `${ts.slice(0, 7)}.${sink.machineId ?? auditMachineId()}.jsonl`);
|
|
13111
|
+
fs20.mkdirSync(dir, { recursive: true });
|
|
13112
|
+
const fd = fs20.openSync(file, "a");
|
|
13113
|
+
try {
|
|
13114
|
+
for (const e of events) {
|
|
13115
|
+
const q = e.query === void 0 ? {} : { query: e.query.slice(0, AUDIT_QUERY_CHARS) };
|
|
13116
|
+
fs20.writeSync(fd, JSON.stringify({ ts, ...e, ...q }) + "\n");
|
|
13117
|
+
}
|
|
13118
|
+
} finally {
|
|
13119
|
+
fs20.closeSync(fd);
|
|
13120
|
+
}
|
|
13121
|
+
} catch (err) {
|
|
13122
|
+
console.error(`squadrant audit: log write failed: ${err.message}`);
|
|
13123
|
+
}
|
|
13124
|
+
}
|
|
13125
|
+
function ruleAuditScope(rule) {
|
|
13126
|
+
return rule.layer.startsWith("kb:") ? { kb: rule.layer.slice(3), level: "group" } : { kb: "", level: "project" };
|
|
13127
|
+
}
|
|
13128
|
+
var AUDIT_QUERY_CHARS, defaultAuditDir, auditMachineId;
|
|
13129
|
+
var init_audit = __esm({
|
|
13130
|
+
"packages/core/dist/knowledge/audit.js"() {
|
|
13131
|
+
AUDIT_QUERY_CHARS = 200;
|
|
13132
|
+
defaultAuditDir = () => process.env.SQUADRANT_AUDIT_DIR || path21.join(os9.homedir(), ".local", "state", "squadrant", "audit");
|
|
13133
|
+
auditMachineId = () => os9.hostname().replace(/[^A-Za-z0-9_-]/g, "_") || "host";
|
|
13134
|
+
}
|
|
13135
|
+
});
|
|
13136
|
+
|
|
12713
13137
|
// packages/core/dist/knowledge/inject.js
|
|
12714
|
-
import
|
|
12715
|
-
import
|
|
13138
|
+
import fs21 from "fs";
|
|
13139
|
+
import path22 from "path";
|
|
12716
13140
|
function sessionOrder(a, b) {
|
|
12717
13141
|
const proj = (r) => r.layer.startsWith("project:") ? 0 : 1;
|
|
12718
13142
|
const mod = (r) => r.modality === "must-not" ? 0 : 1;
|
|
@@ -12720,7 +13144,7 @@ function sessionOrder(a, b) {
|
|
|
12720
13144
|
}
|
|
12721
13145
|
function formatSessionContext(kbs, rules, cap = SESSION_RULE_CAP) {
|
|
12722
13146
|
const hard = rules.filter((r) => r.status === "active" && (r.modality === "must" || r.modality === "must-not")).sort(sessionOrder);
|
|
12723
|
-
const shown = hard.
|
|
13147
|
+
const shown = applyBudget(hard.map((rule) => ({ rule })), (r) => line(r).length, cap, INJECT_MAX_CHARS).shown.map((x) => x.rule);
|
|
12724
13148
|
const lines = [
|
|
12725
13149
|
`<squadrant-project-rules kb="${kbs.join(",")}">`,
|
|
12726
13150
|
`Rules KB: ${kbs.join(", ")}. ${DATA_NOTE}`,
|
|
@@ -12729,16 +13153,17 @@ function formatSessionContext(kbs, rules, cap = SESSION_RULE_CAP) {
|
|
|
12729
13153
|
if (hard.length > shown.length)
|
|
12730
13154
|
lines.push(`+${hard.length - shown.length} more: squadrant rules list --brief`);
|
|
12731
13155
|
lines.push("Look up rules for a task with `squadrant rules search <terms>` / `squadrant rules show <id>` (rules-ops skill).");
|
|
13156
|
+
lines.push(CITE_NOTE);
|
|
12732
13157
|
lines.push("</squadrant-project-rules>");
|
|
12733
|
-
return { text: lines.join("\n"), ids: shown.map((r) => r.id) };
|
|
13158
|
+
return { text: lines.join("\n"), ids: shown.map((r) => r.id), hiddenIds: hard.slice(shown.length).map((r) => r.id) };
|
|
12734
13159
|
}
|
|
12735
13160
|
function cleanPromptQuery(prompt) {
|
|
12736
13161
|
const text = prompt.replace(/```[\s\S]*?(```|$)/g, " ").split("\n").filter((l) => l.length <= MAX_LINE_CHARS).join("\n").slice(0, MAX_PROMPT_CHARS).toLowerCase().replace(/https?:\/\/\S+/g, " ");
|
|
12737
|
-
const
|
|
13162
|
+
const terms2 = [];
|
|
12738
13163
|
const add = (t) => {
|
|
12739
|
-
if (t.length < 3 && !SHORT_TERMS.has(t) || /^\d+$/.test(t) || STOPWORDS.has(t) ||
|
|
13164
|
+
if (t.length < 3 && !SHORT_TERMS.has(t) || /^\d+$/.test(t) || STOPWORDS.has(t) || terms2.includes(t))
|
|
12740
13165
|
return;
|
|
12741
|
-
|
|
13166
|
+
terms2.push(t);
|
|
12742
13167
|
};
|
|
12743
13168
|
for (const raw of text.split(/[^a-z0-9._-]+/)) {
|
|
12744
13169
|
const t = raw.replace(/^[._-]+|[._-]+$/g, "");
|
|
@@ -12748,12 +13173,12 @@ function cleanPromptQuery(prompt) {
|
|
|
12748
13173
|
const parts = t.split(/[._-]+/);
|
|
12749
13174
|
if (parts.length > 1 && parts.every((x) => x.length >= 3 && !PREFIXES.has(x)))
|
|
12750
13175
|
parts.forEach(add);
|
|
12751
|
-
if (
|
|
13176
|
+
if (terms2.length >= MAX_TERMS)
|
|
12752
13177
|
break;
|
|
12753
13178
|
}
|
|
12754
|
-
return
|
|
13179
|
+
return terms2.slice(0, MAX_TERMS).join(" ");
|
|
12755
13180
|
}
|
|
12756
|
-
function wholeWordTerms(rule,
|
|
13181
|
+
function wholeWordTerms(rule, terms2) {
|
|
12757
13182
|
const text = [
|
|
12758
13183
|
rule.id,
|
|
12759
13184
|
...rule.triggers?.keywords ?? [],
|
|
@@ -12761,18 +13186,18 @@ function wholeWordTerms(rule, terms) {
|
|
|
12761
13186
|
rule.triggers?.when ?? "",
|
|
12762
13187
|
rule.statement
|
|
12763
13188
|
].join("\n").toLowerCase();
|
|
12764
|
-
return
|
|
13189
|
+
return terms2.filter((t) => {
|
|
12765
13190
|
const base = t.length > 4 ? t.replace(/(es|s)$/, "") : t;
|
|
12766
13191
|
return new RegExp(`(^|[^a-z0-9])(?:${escapeRe(t)}|${escapeRe(base)}(?:s|es)?)($|[^a-z0-9])`).test(text);
|
|
12767
13192
|
});
|
|
12768
13193
|
}
|
|
12769
13194
|
function selectPromptHits(hits, query, seen, opts = {}) {
|
|
12770
13195
|
const minScore = opts.minScore ?? PROMPT_MIN_SCORE;
|
|
12771
|
-
const
|
|
13196
|
+
const terms2 = query.split(" ").filter(Boolean);
|
|
12772
13197
|
return hits.filter((h) => {
|
|
12773
13198
|
if (seen.has(h.rule.id))
|
|
12774
13199
|
return false;
|
|
12775
|
-
const n = wholeWordTerms(h.rule,
|
|
13200
|
+
const n = wholeWordTerms(h.rule, terms2).length;
|
|
12776
13201
|
return n >= 2 && h.score >= minScore || n === 1 && h.score >= PROMPT_SINGLE_TERM_MIN_SCORE;
|
|
12777
13202
|
}).slice(0, opts.cap ?? PROMPT_RULE_CAP);
|
|
12778
13203
|
}
|
|
@@ -12785,25 +13210,56 @@ function promptText(prompt) {
|
|
|
12785
13210
|
if (!m)
|
|
12786
13211
|
return prompt;
|
|
12787
13212
|
try {
|
|
12788
|
-
return
|
|
13213
|
+
return fs21.readFileSync(m[1], "utf8").slice(0, MAX_PROMPT_CHARS * 2);
|
|
12789
13214
|
} catch {
|
|
12790
13215
|
return prompt;
|
|
12791
13216
|
}
|
|
12792
13217
|
}
|
|
12793
|
-
function formatPromptContext(hits) {
|
|
13218
|
+
function formatPromptContext(hits, match = "prompt", overflowIds = []) {
|
|
12794
13219
|
return [
|
|
12795
|
-
|
|
12796
|
-
`Rules that may apply to this prompt. ${DATA_NOTE}`,
|
|
13220
|
+
`<squadrant-project-rules match="${match}">`,
|
|
13221
|
+
`Rules that may apply to this ${match === "read" ? "file" : "prompt"}. ${DATA_NOTE}`,
|
|
12797
13222
|
...hits.map((h) => line(h.rule)),
|
|
13223
|
+
...overflowIds.length ? [`+${overflowIds.length} more: squadrant rules show ${overflowIds.join(" ")}`] : [],
|
|
13224
|
+
CITE_NOTE,
|
|
12798
13225
|
"</squadrant-project-rules>"
|
|
12799
13226
|
].join("\n");
|
|
12800
13227
|
}
|
|
13228
|
+
function readTargetPath(payload) {
|
|
13229
|
+
const q = typeof payload === "object" && payload !== null ? payload : {};
|
|
13230
|
+
const tool = typeof q.tool_name === "string" ? q.tool_name.toLowerCase() : "";
|
|
13231
|
+
const input = typeof q.tool_input === "object" && q.tool_input !== null ? q.tool_input : {};
|
|
13232
|
+
if (READ_TOOLS.has(tool)) {
|
|
13233
|
+
for (const k of ["file_path", "absolute_path", "filePath", "path"]) {
|
|
13234
|
+
if (typeof input[k] === "string" && input[k])
|
|
13235
|
+
return input[k];
|
|
13236
|
+
}
|
|
13237
|
+
return null;
|
|
13238
|
+
}
|
|
13239
|
+
const cmd = SHELL_TOOLS.has(tool) ? input.command ?? input.cmd : void 0;
|
|
13240
|
+
const text = Array.isArray(cmd) ? cmd.filter((x) => typeof x === "string").join(" ") : cmd;
|
|
13241
|
+
if (typeof text !== "string" || SHELL_META.test(text))
|
|
13242
|
+
return null;
|
|
13243
|
+
const words = text.trim().split(/\s+/);
|
|
13244
|
+
if (words.length < 2 || !/^(cat|head|tail|nl|bat|sed)$/.test(words[0]))
|
|
13245
|
+
return null;
|
|
13246
|
+
const last = words[words.length - 1].replace(/^["']|["']$/g, "");
|
|
13247
|
+
return last && !last.startsWith("-") ? last : null;
|
|
13248
|
+
}
|
|
13249
|
+
function rulesFallbackPointer(kbs) {
|
|
13250
|
+
return [
|
|
13251
|
+
"## Project rules (squadrant)",
|
|
13252
|
+
"",
|
|
13253
|
+
`This project subscribes to the rules KB: ${kbs.join(", ")}. ${DATA_NOTE}`,
|
|
13254
|
+
"Before changing code, look up the rules that apply with `squadrant rules search <terms>`, then `squadrant rules show <id>`."
|
|
13255
|
+
].join("\n");
|
|
13256
|
+
}
|
|
12801
13257
|
function injectStateDir(stateRoot) {
|
|
12802
|
-
return
|
|
13258
|
+
return path22.join(stateRoot, "rules-inject");
|
|
12803
13259
|
}
|
|
12804
13260
|
function readSeenRules(stateRoot, sessionId) {
|
|
12805
13261
|
try {
|
|
12806
|
-
const ids = JSON.parse(
|
|
13262
|
+
const ids = JSON.parse(fs21.readFileSync(sessionFile(stateRoot, sessionId), "utf8"));
|
|
12807
13263
|
return new Set(Array.isArray(ids) ? ids.filter((x) => typeof x === "string") : []);
|
|
12808
13264
|
} catch {
|
|
12809
13265
|
return /* @__PURE__ */ new Set();
|
|
@@ -12815,22 +13271,22 @@ function recordSeenRules(stateRoot, sessionId, ids) {
|
|
|
12815
13271
|
const seen = readSeenRules(stateRoot, sessionId);
|
|
12816
13272
|
for (const id of ids)
|
|
12817
13273
|
seen.add(id);
|
|
12818
|
-
|
|
12819
|
-
|
|
13274
|
+
fs21.mkdirSync(injectStateDir(stateRoot), { recursive: true });
|
|
13275
|
+
fs21.writeFileSync(sessionFile(stateRoot, sessionId), JSON.stringify([...seen]));
|
|
12820
13276
|
}
|
|
12821
13277
|
function pruneSeenRules(stateRoot, now = Date.now()) {
|
|
12822
13278
|
const dir = injectStateDir(stateRoot);
|
|
12823
13279
|
let names;
|
|
12824
13280
|
try {
|
|
12825
|
-
names =
|
|
13281
|
+
names = fs21.readdirSync(dir);
|
|
12826
13282
|
} catch {
|
|
12827
13283
|
return;
|
|
12828
13284
|
}
|
|
12829
13285
|
for (const n of names) {
|
|
12830
|
-
const f =
|
|
13286
|
+
const f = path22.join(dir, n);
|
|
12831
13287
|
try {
|
|
12832
|
-
if (now -
|
|
12833
|
-
|
|
13288
|
+
if (now - fs21.statSync(f).mtimeMs > SEEN_TTL_MS)
|
|
13289
|
+
fs21.unlinkSync(f);
|
|
12834
13290
|
} catch {
|
|
12835
13291
|
}
|
|
12836
13292
|
}
|
|
@@ -12858,13 +13314,27 @@ function resolveInjectProject(cfg, cwd, env, project) {
|
|
|
12858
13314
|
let bestLen = -1;
|
|
12859
13315
|
for (const [name, pc] of Object.entries(cfg.projects)) {
|
|
12860
13316
|
const root = resolveHome(pc.path);
|
|
12861
|
-
if ((cwd === root || cwd.startsWith(root +
|
|
13317
|
+
if ((cwd === root || cwd.startsWith(root + path22.sep)) && root.length > bestLen) {
|
|
12862
13318
|
best = name;
|
|
12863
13319
|
bestLen = root.length;
|
|
12864
13320
|
}
|
|
12865
13321
|
}
|
|
12866
13322
|
return best;
|
|
12867
13323
|
}
|
|
13324
|
+
function audit(i, project, session, trigger, items) {
|
|
13325
|
+
appendAudit(items.map(({ rule, event, score }) => ({
|
|
13326
|
+
...ruleAuditScope(rule),
|
|
13327
|
+
project,
|
|
13328
|
+
domain: "rules",
|
|
13329
|
+
itemId: rule.id,
|
|
13330
|
+
event,
|
|
13331
|
+
trigger,
|
|
13332
|
+
...score === void 0 ? {} : { score },
|
|
13333
|
+
chars: line(rule).length,
|
|
13334
|
+
agent: i.agent ?? "claude",
|
|
13335
|
+
...session ? { session } : {}
|
|
13336
|
+
})), i.audit);
|
|
13337
|
+
}
|
|
12868
13338
|
function computeRulesInjection(i) {
|
|
12869
13339
|
if (i.env.SQUADRANT_RULES_INJECT === "0")
|
|
12870
13340
|
return null;
|
|
@@ -12878,32 +13348,60 @@ function computeRulesInjection(i) {
|
|
|
12878
13348
|
return null;
|
|
12879
13349
|
const sessionId = typeof p.session_id === "string" && p.session_id ? p.session_id : null;
|
|
12880
13350
|
if (i.event === "session-start") {
|
|
12881
|
-
const
|
|
13351
|
+
const rules2 = resolveProjectRules(i.cfg, project).rules;
|
|
13352
|
+
const { text: text2, ids, hiddenIds } = formatSessionContext(kbs, rules2);
|
|
13353
|
+
const byId = new Map(rules2.map((r) => [r.id, r]));
|
|
13354
|
+
audit(i, project, sessionId, "session", [
|
|
13355
|
+
...ids.map((id) => ({ id, event: "item.surfaced" })),
|
|
13356
|
+
...hiddenIds.map((id) => ({ id, event: "item.suppressed" }))
|
|
13357
|
+
].map((e) => ({ rule: byId.get(e.id), event: e.event, score: void 0 })));
|
|
12882
13358
|
if (sessionId) {
|
|
12883
13359
|
pruneSeenRules(i.stateRoot);
|
|
12884
13360
|
recordSeenRules(i.stateRoot, sessionId, ids);
|
|
12885
13361
|
}
|
|
12886
|
-
return
|
|
13362
|
+
return text2;
|
|
13363
|
+
}
|
|
13364
|
+
const rules = resolveProjectRules(i.cfg, project).rules;
|
|
13365
|
+
const seen = sessionId ? readSeenRules(i.stateRoot, sessionId) : /* @__PURE__ */ new Set();
|
|
13366
|
+
if (i.event === "post-read") {
|
|
13367
|
+
const file = readTargetPath(p);
|
|
13368
|
+
if (!file)
|
|
13369
|
+
return null;
|
|
13370
|
+
const rels = repoRelativePaths(file, cwd, resolveHome(i.cfg.projects[project].path));
|
|
13371
|
+
const hits = matchPaths(rules, project, rels).map((h) => ({ rule: h.rule, score: h.via === "glob" ? 100 : 90, matched: 1, curated: true }));
|
|
13372
|
+
return deliver(i, project, sessionId, "tool", "read", hits, seen, []);
|
|
12887
13373
|
}
|
|
12888
13374
|
if (typeof p.prompt !== "string" || isSystemPrompt(p.prompt))
|
|
12889
13375
|
return null;
|
|
12890
|
-
const
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
const
|
|
12894
|
-
const picked = selectPromptHits(
|
|
12895
|
-
|
|
13376
|
+
const text = promptText(p.prompt);
|
|
13377
|
+
const query = cleanPromptQuery(text);
|
|
13378
|
+
const structural = [...matchPaths(rules, project, mentionedPaths(text)), ...matchSymbols(rules, project, text)].map((h) => ({ rule: h.rule, score: 100, matched: 1, curated: true }));
|
|
13379
|
+
const lexical = query ? searchRules(rules, query) : [];
|
|
13380
|
+
const picked = selectPromptHits(lexical, query, seen);
|
|
13381
|
+
const all = [...new Map([...structural, ...picked].map((h) => [h.rule.id, h])).values()];
|
|
13382
|
+
return deliver(i, project, sessionId, "prompt", "prompt", all, seen, lexical.filter((h) => !picked.includes(h)));
|
|
13383
|
+
}
|
|
13384
|
+
function deliver(i, project, sessionId, trigger, match, candidates, seen, belowBar) {
|
|
13385
|
+
const fresh = candidates.filter((h) => !seen.has(h.rule.id));
|
|
13386
|
+
const { shown, overflow } = applyBudget(fresh, (r) => line(r).length);
|
|
13387
|
+
audit(i, project, sessionId, trigger, [
|
|
13388
|
+
...shown.map((h) => ({ rule: h.rule, event: "item.surfaced", score: h.score })),
|
|
13389
|
+
...[...overflow, ...belowBar].slice(0, MAX_SUPPRESSED_LOGGED).map((h) => ({ rule: h.rule, event: "item.suppressed", score: h.score }))
|
|
13390
|
+
]);
|
|
13391
|
+
if (!shown.length)
|
|
12896
13392
|
return null;
|
|
12897
13393
|
if (sessionId)
|
|
12898
|
-
recordSeenRules(i.stateRoot, sessionId,
|
|
12899
|
-
return formatPromptContext(
|
|
13394
|
+
recordSeenRules(i.stateRoot, sessionId, shown.map((h) => h.rule.id));
|
|
13395
|
+
return formatPromptContext(shown, match, overflow.map((h) => h.rule.id));
|
|
12900
13396
|
}
|
|
12901
|
-
var SESSION_RULE_CAP, PROMPT_RULE_CAP, PROMPT_MIN_SCORE, PROMPT_SINGLE_TERM_MIN_SCORE, INJECT_BUDGET_MS, MAX_PROMPT_CHARS, MAX_LINE_CHARS, MAX_TERMS, SHORT_TERMS, PREFIXES, SEEN_TTL_MS, STOPWORDS, DATA_NOTE, line, escapeRe, BRIEF_POINTER_RE, sessionFile;
|
|
13397
|
+
var SESSION_RULE_CAP, PROMPT_RULE_CAP, PROMPT_MIN_SCORE, PROMPT_SINGLE_TERM_MIN_SCORE, INJECT_BUDGET_MS, MAX_PROMPT_CHARS, MAX_LINE_CHARS, MAX_TERMS, SHORT_TERMS, PREFIXES, SEEN_TTL_MS, STOPWORDS, DATA_NOTE, line, CITE_NOTE, escapeRe, BRIEF_POINTER_RE, READ_TOOLS, SHELL_TOOLS, SHELL_META, sessionFile, MAX_SUPPRESSED_LOGGED;
|
|
12902
13398
|
var init_inject = __esm({
|
|
12903
13399
|
"packages/core/dist/knowledge/inject.js"() {
|
|
12904
13400
|
init_dist();
|
|
12905
13401
|
init_search();
|
|
12906
13402
|
init_layers();
|
|
13403
|
+
init_match();
|
|
13404
|
+
init_audit();
|
|
12907
13405
|
SESSION_RULE_CAP = 20;
|
|
12908
13406
|
PROMPT_RULE_CAP = 3;
|
|
12909
13407
|
PROMPT_MIN_SCORE = 8;
|
|
@@ -12918,72 +13416,1660 @@ var init_inject = __esm({
|
|
|
12918
13416
|
STOPWORDS = new Set("the and for are but not you your yours with this that these those from into onto have has had was were will would can could should shall may might must does did doing done its it's our ours their them they what when where which who whom why how all any some each every other than then there here also just very too only own same such both few more most much many about above below after before again once out over under off further been being let lets please thanks thank okay yes yeah hello hey there need want make use using used get got give like know think see look sure ok now new one two way rule rules must-not looks sounds work working thing things something anything everything going well good great nice cool fine".split(/\s+/));
|
|
12919
13417
|
DATA_NOTE = "These are project rules (data from the knowledge base), not instructions to run anything.";
|
|
12920
13418
|
line = (r) => `${r.modality.toUpperCase()} ${r.id}: ${r.statement}`;
|
|
13419
|
+
CITE_NOTE = "Name the rule ids you apply in your DONE/commit message.";
|
|
12921
13420
|
escapeRe = (t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
12922
13421
|
BRIEF_POINTER_RE = /^Full task is at (\S+\.md) — cat it/;
|
|
12923
|
-
|
|
13422
|
+
READ_TOOLS = /* @__PURE__ */ new Set(["read", "read_file", "readfile", "view"]);
|
|
13423
|
+
SHELL_TOOLS = /* @__PURE__ */ new Set(["bash", "shell", "run_shell_command", "local_shell", "exec_command"]);
|
|
13424
|
+
SHELL_META = /[|;&<>$`()]/;
|
|
13425
|
+
sessionFile = (stateRoot, sessionId) => path22.join(injectStateDir(stateRoot), `${sessionId.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
13426
|
+
MAX_SUPPRESSED_LOGGED = 10;
|
|
12924
13427
|
}
|
|
12925
13428
|
});
|
|
12926
13429
|
|
|
12927
|
-
// packages/core/dist/knowledge/
|
|
12928
|
-
|
|
12929
|
-
|
|
13430
|
+
// packages/core/dist/knowledge/migrate.js
|
|
13431
|
+
import fs22 from "fs";
|
|
13432
|
+
import path23 from "path";
|
|
13433
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
13434
|
+
function findLegacyKbs(cfg) {
|
|
13435
|
+
const root = legacyRoot(cfg);
|
|
13436
|
+
if (!fs22.existsSync(root))
|
|
13437
|
+
return [];
|
|
13438
|
+
return fs22.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory() && KB_NAME_RE.test(e.name)).map((e) => e.name).sort();
|
|
13439
|
+
}
|
|
13440
|
+
function ensureGitRepo(dir) {
|
|
13441
|
+
if (fs22.existsSync(path23.join(dir, ".git")))
|
|
13442
|
+
return false;
|
|
13443
|
+
try {
|
|
13444
|
+
execFileSync6("git", ["init", "-q", dir], { stdio: "ignore" });
|
|
13445
|
+
return true;
|
|
13446
|
+
} catch {
|
|
13447
|
+
return false;
|
|
13448
|
+
}
|
|
13449
|
+
}
|
|
13450
|
+
function ensureKbGitignore(dir) {
|
|
13451
|
+
const file = path23.join(dir, ".gitignore");
|
|
13452
|
+
if (fs22.existsSync(file))
|
|
13453
|
+
return false;
|
|
13454
|
+
fs22.writeFileSync(file, KB_GITIGNORE);
|
|
13455
|
+
return true;
|
|
13456
|
+
}
|
|
13457
|
+
function findLegacyOverlays(cfg) {
|
|
13458
|
+
return Object.keys(cfg.projects).filter((p) => fs22.existsSync(legacyOverlay(cfg, p))).sort();
|
|
13459
|
+
}
|
|
13460
|
+
function moveLegacyOverlays(cfg, opts = {}) {
|
|
13461
|
+
return findLegacyOverlays(cfg).map((project) => {
|
|
13462
|
+
const from = legacyOverlay(cfg, project);
|
|
13463
|
+
const to = projectRulesHome(cfg, project);
|
|
13464
|
+
const kb = `overlay:${project}`;
|
|
13465
|
+
if (!to)
|
|
13466
|
+
return { kb, from, to: "", action: "skipped", note: "no group KB or knowledgeHome for this project; set one, then re-run" };
|
|
13467
|
+
if (fs22.existsSync(to))
|
|
13468
|
+
return { kb, from, to, action: "skipped", note: "target already exists; nothing moved" };
|
|
13469
|
+
if (opts.dryRun)
|
|
13470
|
+
return { kb, from, to, action: "would-move" };
|
|
13471
|
+
fs22.mkdirSync(path23.dirname(to), { recursive: true });
|
|
13472
|
+
try {
|
|
13473
|
+
fs22.renameSync(from, to);
|
|
13474
|
+
return { kb, from, to, action: "moved" };
|
|
13475
|
+
} catch (e) {
|
|
13476
|
+
if (e.code !== "EXDEV")
|
|
13477
|
+
throw e;
|
|
13478
|
+
fs22.cpSync(from, to, { recursive: true });
|
|
13479
|
+
return { kb, from, to, action: "copied", note: "cross-device: original left in place, delete it yourself once verified" };
|
|
13480
|
+
}
|
|
13481
|
+
});
|
|
13482
|
+
}
|
|
13483
|
+
function moveLegacyKbs(cfg, opts = {}) {
|
|
13484
|
+
const hub = resolveHome(cfg.hubVault);
|
|
13485
|
+
return findLegacyKbs(cfg).map((kb) => {
|
|
13486
|
+
const from = legacyKbDir(hub, kb);
|
|
13487
|
+
const to = kbDir(cfg, kb);
|
|
13488
|
+
if (fs22.existsSync(to))
|
|
13489
|
+
return { kb, from, to, action: "skipped", note: "target already exists; nothing moved" };
|
|
13490
|
+
if (opts.dryRun)
|
|
13491
|
+
return { kb, from, to, action: "would-move" };
|
|
13492
|
+
fs22.mkdirSync(path23.dirname(to), { recursive: true });
|
|
13493
|
+
let action = "moved";
|
|
13494
|
+
try {
|
|
13495
|
+
fs22.renameSync(from, to);
|
|
13496
|
+
} catch (e) {
|
|
13497
|
+
if (e.code !== "EXDEV")
|
|
13498
|
+
throw e;
|
|
13499
|
+
fs22.cpSync(from, to, { recursive: true });
|
|
13500
|
+
action = "copied";
|
|
13501
|
+
}
|
|
13502
|
+
const notes = [];
|
|
13503
|
+
const oldRules = path23.join(to, "rules"), newRules = path23.join(to, "shared", "rules");
|
|
13504
|
+
if (fs22.existsSync(oldRules) && !fs22.existsSync(newRules)) {
|
|
13505
|
+
fs22.mkdirSync(path23.dirname(newRules), { recursive: true });
|
|
13506
|
+
fs22.renameSync(oldRules, newRules);
|
|
13507
|
+
notes.push("rules/ -> shared/rules/");
|
|
13508
|
+
}
|
|
13509
|
+
if (ensureGitRepo(to))
|
|
13510
|
+
notes.push("git init");
|
|
13511
|
+
if (ensureKbGitignore(to))
|
|
13512
|
+
notes.push("wrote .gitignore (raw/, .converted/)");
|
|
13513
|
+
if (fs22.existsSync(path23.join(to, "index.json")))
|
|
13514
|
+
notes.push(`index.json holds old paths: run squadrant knowledge reindex ${kb}`);
|
|
13515
|
+
if (action === "copied")
|
|
13516
|
+
notes.push("cross-device: original left in place, delete it yourself once verified");
|
|
13517
|
+
return { kb, from, to, action, ...notes.length ? { note: notes.join("; ") } : {} };
|
|
13518
|
+
});
|
|
13519
|
+
}
|
|
13520
|
+
var legacyRoot, KB_GITIGNORE, legacyOverlay;
|
|
13521
|
+
var init_migrate = __esm({
|
|
13522
|
+
"packages/core/dist/knowledge/migrate.js"() {
|
|
13523
|
+
init_dist();
|
|
12930
13524
|
init_paths();
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
|
|
12934
|
-
init_search();
|
|
12935
|
-
init_sources();
|
|
12936
|
-
init_inject();
|
|
13525
|
+
legacyRoot = (cfg) => path23.join(resolveHome(cfg.hubVault), "knowledge");
|
|
13526
|
+
KB_GITIGNORE = "# squadrant KB defaults: source documents stay local\nraw/\n.converted/\n";
|
|
13527
|
+
legacyOverlay = (cfg, project) => path23.join(resolveHome(cfg.projects[project].spokeVault), "knowledge", "rules");
|
|
12937
13528
|
}
|
|
12938
13529
|
});
|
|
12939
13530
|
|
|
12940
|
-
// packages/core/dist/
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
|
|
12948
|
-
|
|
12949
|
-
|
|
12950
|
-
|
|
12951
|
-
|
|
12952
|
-
|
|
12953
|
-
|
|
12954
|
-
|
|
12955
|
-
|
|
12956
|
-
|
|
12957
|
-
|
|
12958
|
-
|
|
12959
|
-
|
|
12960
|
-
|
|
12961
|
-
|
|
12962
|
-
|
|
12963
|
-
|
|
12964
|
-
|
|
12965
|
-
|
|
12966
|
-
|
|
12967
|
-
|
|
12968
|
-
|
|
12969
|
-
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
|
|
12976
|
-
|
|
12977
|
-
|
|
12978
|
-
|
|
12979
|
-
|
|
12980
|
-
|
|
12981
|
-
|
|
12982
|
-
|
|
12983
|
-
|
|
12984
|
-
|
|
12985
|
-
|
|
12986
|
-
|
|
13531
|
+
// packages/core/dist/knowledge/anchor.js
|
|
13532
|
+
import fs23 from "fs";
|
|
13533
|
+
import path24 from "path";
|
|
13534
|
+
function* repoFiles(root) {
|
|
13535
|
+
let count = 0;
|
|
13536
|
+
const stack = [root];
|
|
13537
|
+
while (stack.length) {
|
|
13538
|
+
const dir = stack.pop();
|
|
13539
|
+
let ents;
|
|
13540
|
+
try {
|
|
13541
|
+
ents = fs23.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name < b.name ? 1 : -1);
|
|
13542
|
+
} catch {
|
|
13543
|
+
continue;
|
|
13544
|
+
}
|
|
13545
|
+
for (const ent of ents) {
|
|
13546
|
+
if (ent.isDirectory()) {
|
|
13547
|
+
if (!SKIP_DIRS.has(ent.name))
|
|
13548
|
+
stack.push(path24.join(dir, ent.name));
|
|
13549
|
+
} else if (ent.isFile() && CODE_EXT.has(path24.extname(ent.name))) {
|
|
13550
|
+
if (++count > MAX_FILES)
|
|
13551
|
+
return;
|
|
13552
|
+
yield path24.join(dir, ent.name);
|
|
13553
|
+
}
|
|
13554
|
+
}
|
|
13555
|
+
}
|
|
13556
|
+
}
|
|
13557
|
+
function computeAnchor(repoRoot, triggers) {
|
|
13558
|
+
const terms2 = anchorTerms(triggers);
|
|
13559
|
+
if (!terms2.length || !fs23.existsSync(repoRoot))
|
|
13560
|
+
return null;
|
|
13561
|
+
const hits = [];
|
|
13562
|
+
for (const file of repoFiles(repoRoot)) {
|
|
13563
|
+
let text;
|
|
13564
|
+
try {
|
|
13565
|
+
if (fs23.statSync(file).size > MAX_FILE_BYTES)
|
|
13566
|
+
continue;
|
|
13567
|
+
text = fs23.readFileSync(file, "utf8");
|
|
13568
|
+
} catch {
|
|
13569
|
+
continue;
|
|
13570
|
+
}
|
|
13571
|
+
const hay = `${path24.relative(repoRoot, file)}
|
|
13572
|
+
${text}`.toLowerCase();
|
|
13573
|
+
const score = terms2.filter((t) => hay.includes(t)).length;
|
|
13574
|
+
if (!score)
|
|
13575
|
+
continue;
|
|
13576
|
+
const symbols2 = [...text.matchAll(SYMBOL_RE)].map((m) => m[1]).filter((s) => terms2.some((t) => s.toLowerCase().includes(t)));
|
|
13577
|
+
hits.push({ rel: path24.relative(repoRoot, file).split(path24.sep).join("/"), score, symbols: symbols2 });
|
|
13578
|
+
}
|
|
13579
|
+
if (!hits.length)
|
|
13580
|
+
return null;
|
|
13581
|
+
hits.sort((a, b) => b.score - a.score || (a.rel < b.rel ? -1 : 1));
|
|
13582
|
+
const top = hits.slice(0, MAX_PATHS);
|
|
13583
|
+
const symbols = [...new Set(top.flatMap((h) => h.symbols))].sort().slice(0, MAX_SYMBOLS);
|
|
13584
|
+
return { paths: top.map((h) => h.rel).sort(), ...symbols.length ? { symbols } : {} };
|
|
13585
|
+
}
|
|
13586
|
+
var SKIP_DIRS, CODE_EXT, MAX_FILES, MAX_FILE_BYTES, MAX_PATHS, MAX_SYMBOLS, SYMBOL_RE, anchorTerms;
|
|
13587
|
+
var init_anchor = __esm({
|
|
13588
|
+
"packages/core/dist/knowledge/anchor.js"() {
|
|
13589
|
+
SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", ".next", ".worktrees", ".planning", ".converted"]);
|
|
13590
|
+
CODE_EXT = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs", ".java", ".kt", ".swift", ".rb", ".php", ".cs", ".sol", ".sql", ".md"]);
|
|
13591
|
+
MAX_FILES = 5e3;
|
|
13592
|
+
MAX_FILE_BYTES = 256 * 1024;
|
|
13593
|
+
MAX_PATHS = 5;
|
|
13594
|
+
MAX_SYMBOLS = 10;
|
|
13595
|
+
SYMBOL_RE = /\b(?:function|class|interface|type|const|let|var|enum|def|fn|struct)\s+([A-Za-z_]\w*)/g;
|
|
13596
|
+
anchorTerms = (t) => [...new Set([...t?.keywords ?? [], ...t?.expanded ?? []].map((s) => s.toLowerCase().trim()).filter((s) => s.length >= 3))].sort();
|
|
13597
|
+
}
|
|
13598
|
+
});
|
|
13599
|
+
|
|
13600
|
+
// packages/core/dist/knowledge/extract.js
|
|
13601
|
+
import fs24 from "fs";
|
|
13602
|
+
import path25 from "path";
|
|
13603
|
+
import matter3 from "gray-matter";
|
|
13604
|
+
function serialize(fm, body) {
|
|
13605
|
+
return matter3.stringify(body.trim() + "\n", clean(fm));
|
|
13606
|
+
}
|
|
13607
|
+
function validateCandidate(c) {
|
|
13608
|
+
const p = [];
|
|
13609
|
+
if (typeof c.source !== "string" || !c.source)
|
|
13610
|
+
p.push("source must be a non-empty string");
|
|
13611
|
+
if (typeof c.id !== "string" || !RULE_ID_RE.test(c.id))
|
|
13612
|
+
p.push(`id must match ${RULE_ID_RE}`);
|
|
13613
|
+
if (typeof c.domain !== "string" || !/^[a-z][a-z0-9-]*$/.test(c.domain))
|
|
13614
|
+
p.push("domain must be a lowercase slug");
|
|
13615
|
+
if (!RULE_MODALITIES.includes(c.modality))
|
|
13616
|
+
p.push(`modality must be one of ${RULE_MODALITIES.join("|")}`);
|
|
13617
|
+
if (typeof c.statement !== "string" || !c.statement.trim())
|
|
13618
|
+
p.push("statement must be non-empty");
|
|
13619
|
+
if (typeof c.quote !== "string" || !c.quote.trim())
|
|
13620
|
+
p.push("quote must be non-empty");
|
|
13621
|
+
const t = c.triggers;
|
|
13622
|
+
if (t !== void 0) {
|
|
13623
|
+
for (const k of ["globs", "keywords", "expanded"]) {
|
|
13624
|
+
const v2 = t[k];
|
|
13625
|
+
if (v2 !== void 0 && !(Array.isArray(v2) && v2.every((x) => typeof x === "string")))
|
|
13626
|
+
p.push(`triggers.${k} must be a string array`);
|
|
13627
|
+
}
|
|
13628
|
+
for (const g of t.globs ?? []) {
|
|
13629
|
+
if (typeof g === "string" && /^\s*$/.test(g))
|
|
13630
|
+
p.push("triggers.globs entries must be non-empty");
|
|
13631
|
+
}
|
|
13632
|
+
}
|
|
13633
|
+
return p;
|
|
13634
|
+
}
|
|
13635
|
+
function upsertSource(fm, src) {
|
|
13636
|
+
const i = fm.sources.findIndex((s) => s.ref === src.ref);
|
|
13637
|
+
if (i < 0) {
|
|
13638
|
+
fm.sources.push(src);
|
|
13639
|
+
return true;
|
|
13640
|
+
}
|
|
13641
|
+
if (JSON.stringify(fm.sources[i]) === JSON.stringify(src))
|
|
13642
|
+
return false;
|
|
13643
|
+
fm.sources[i] = src;
|
|
13644
|
+
return true;
|
|
13645
|
+
}
|
|
13646
|
+
function applyCandidates(input, opts) {
|
|
13647
|
+
const { kb, kbRoot, rulesDir } = opts;
|
|
13648
|
+
const res = { created: [], merged: [], proposed: [], superseded: [], stale: [], rejected: [], doclingFlags: [], idOverrides: [] };
|
|
13649
|
+
const events = [];
|
|
13650
|
+
const ev2 = (event, itemId, domain, extra = {}) => events.push({ kb, level: "group", project: "", domain, itemId, event, ...extra });
|
|
13651
|
+
const loaded = loadRulesDir(rulesDir, `kb:${kb}`);
|
|
13652
|
+
const work = /* @__PURE__ */ new Map();
|
|
13653
|
+
for (const r of loaded.rules) {
|
|
13654
|
+
const { statement: _s, body, file, layer: _l, ...fm } = r;
|
|
13655
|
+
work.set(r.id, { fm, body, file, dirty: false, isNew: false });
|
|
13656
|
+
}
|
|
13657
|
+
const norm = /* @__PURE__ */ new Map();
|
|
13658
|
+
const totals = /* @__PURE__ */ new Map();
|
|
13659
|
+
const touched = /* @__PURE__ */ new Set();
|
|
13660
|
+
const complete = new Set(input.complete ?? []);
|
|
13661
|
+
for (const cand of input.candidates) {
|
|
13662
|
+
const problems = validateCandidate(cand);
|
|
13663
|
+
if (problems.length) {
|
|
13664
|
+
res.rejected.push({ id: String(cand.id), source: String(cand.source), reason: `invalid: ${problems.join("; ")}` });
|
|
13665
|
+
continue;
|
|
13666
|
+
}
|
|
13667
|
+
const st = readState2(kbRoot, cand.source);
|
|
13668
|
+
const convFile = st ? convertedFile(kbRoot, cand.source) : null;
|
|
13669
|
+
if (!st || !fs24.existsSync(convFile)) {
|
|
13670
|
+
res.rejected.push({ id: cand.id, source: cand.source, reason: "unknown source (not ingested)" });
|
|
13671
|
+
continue;
|
|
13672
|
+
}
|
|
13673
|
+
let hay = norm.get(cand.source);
|
|
13674
|
+
if (!hay) {
|
|
13675
|
+
hay = normalizedViews(fs24.readFileSync(convFile, "utf8"));
|
|
13676
|
+
norm.set(cand.source, hay);
|
|
13677
|
+
}
|
|
13678
|
+
const tot = totals.get(cand.source) ?? { total: 0, dropped: 0 };
|
|
13679
|
+
tot.total++;
|
|
13680
|
+
totals.set(cand.source, tot);
|
|
13681
|
+
const offset = findQuote(hay, cand.quote);
|
|
13682
|
+
if (!offset) {
|
|
13683
|
+
tot.dropped++;
|
|
13684
|
+
res.rejected.push({ id: cand.id, source: cand.source, reason: "ungrounded" });
|
|
13685
|
+
ev2("item.rejected", cand.id, cand.domain, { reason: "ungrounded" });
|
|
13686
|
+
continue;
|
|
13687
|
+
}
|
|
13688
|
+
let id = cand.id;
|
|
13689
|
+
for (const [wid, w] of work) {
|
|
13690
|
+
const span = (s) => s.sha === st.sha ? s.offset : findQuote(hay, s.quote) ?? s.offset;
|
|
13691
|
+
if (w.fm.sources.some((s) => s.ref === cand.source && s.offset && ((o) => !!o && spansOverlap(o, offset))(span(s)))) {
|
|
13692
|
+
id = wid;
|
|
13693
|
+
break;
|
|
13694
|
+
}
|
|
13695
|
+
}
|
|
13696
|
+
if (id !== cand.id)
|
|
13697
|
+
res.idOverrides.push({ from: cand.id, to: id });
|
|
13698
|
+
touched.add(id);
|
|
13699
|
+
const src = { ref: cand.source, sha: st.sha, ...cand.loc ? { loc: cand.loc } : {}, offset, quote: cand.quote.trim() };
|
|
13700
|
+
const target = work.get(id);
|
|
13701
|
+
const contradicted = (cand.contradicts ?? []).filter((x) => work.has(x) && x !== id);
|
|
13702
|
+
const bodyOf = () => cand.statement.trim() + (cand.rationale?.trim() ? `
|
|
13703
|
+
|
|
13704
|
+
${cand.rationale.trim()}` : "");
|
|
13705
|
+
if (target && contradicted.length === 0 && sameMeaning(target, cand)) {
|
|
13706
|
+
let changed = upsertSource(target.fm, src);
|
|
13707
|
+
if (target.fm.status === "stale") {
|
|
13708
|
+
target.fm.status = "active";
|
|
13709
|
+
changed = true;
|
|
13710
|
+
}
|
|
13711
|
+
if (changed) {
|
|
13712
|
+
target.dirty = true;
|
|
13713
|
+
res.merged.push(id);
|
|
13714
|
+
}
|
|
13715
|
+
continue;
|
|
13716
|
+
}
|
|
13717
|
+
if (!target && contradicted.length === 0) {
|
|
13718
|
+
const fm2 = {
|
|
13719
|
+
id,
|
|
13720
|
+
domain: cand.domain,
|
|
13721
|
+
modality: cand.modality,
|
|
13722
|
+
status: "active",
|
|
13723
|
+
...cand.triggers ? { triggers: cand.triggers } : {},
|
|
13724
|
+
sources: [src],
|
|
13725
|
+
approvedBy: "auto",
|
|
13726
|
+
justification: "",
|
|
13727
|
+
supersedes: [],
|
|
13728
|
+
conflictsWith: []
|
|
13729
|
+
};
|
|
13730
|
+
work.set(id, { fm: fm2, body: bodyOf(), file: path25.join(rulesDir, cand.domain, `${id}.md`), dirty: true, isNew: true });
|
|
13731
|
+
res.created.push(id);
|
|
13732
|
+
ev2("item.applied", id, cand.domain);
|
|
13733
|
+
continue;
|
|
13734
|
+
}
|
|
13735
|
+
const fm = {
|
|
13736
|
+
id,
|
|
13737
|
+
domain: target?.fm.domain ?? cand.domain,
|
|
13738
|
+
modality: cand.modality,
|
|
13739
|
+
status: "proposed",
|
|
13740
|
+
...cand.triggers ?? target?.fm.triggers ? { triggers: cand.triggers ?? target?.fm.triggers } : {},
|
|
13741
|
+
sources: [src],
|
|
13742
|
+
approvedBy: "reviewer-agent",
|
|
13743
|
+
justification: "",
|
|
13744
|
+
supersedes: [],
|
|
13745
|
+
conflictsWith: contradicted
|
|
13746
|
+
};
|
|
13747
|
+
const dir = path25.join(rulesDir, PROPOSED_DIR);
|
|
13748
|
+
const file = path25.join(dir, `${id}@${st.sha.slice(0, SHA_LEN)}.md`);
|
|
13749
|
+
const text = serialize(fm, bodyOf());
|
|
13750
|
+
if (fs24.existsSync(file) && fs24.readFileSync(file, "utf8") === text)
|
|
13751
|
+
continue;
|
|
13752
|
+
fs24.mkdirSync(dir, { recursive: true });
|
|
13753
|
+
for (const other of fs24.readdirSync(dir)) {
|
|
13754
|
+
if (!other.startsWith(`${id}@`) || !other.endsWith(".md") || path25.join(dir, other) === file)
|
|
13755
|
+
continue;
|
|
13756
|
+
const old = matter3(fs24.readFileSync(path25.join(dir, other), "utf8"));
|
|
13757
|
+
if (!old.data.sources?.some((s) => s.ref === cand.source))
|
|
13758
|
+
continue;
|
|
13759
|
+
fs24.writeFileSync(path25.join(dir, other.replace(/\.md$/, ".superseded")), matter3.stringify(old.content, { ...old.data, supersededBy: path25.basename(file) }));
|
|
13760
|
+
fs24.rmSync(path25.join(dir, other));
|
|
13761
|
+
res.superseded.push(other.replace(/\.md$/, ""));
|
|
13762
|
+
ev2("item.superseded", id, fm.domain);
|
|
13763
|
+
}
|
|
13764
|
+
fs24.writeFileSync(file, text);
|
|
13765
|
+
res.proposed.push(`${id}@${st.sha.slice(0, SHA_LEN)}`);
|
|
13766
|
+
ev2("item.proposed", id, fm.domain);
|
|
13767
|
+
}
|
|
13768
|
+
for (const [id, w] of work) {
|
|
13769
|
+
if (touched.has(id) || w.fm.status !== "active" || w.isNew)
|
|
13770
|
+
continue;
|
|
13771
|
+
if (w.fm.sources.length && w.fm.sources.every((s) => complete.has(s.ref))) {
|
|
13772
|
+
w.fm.status = "stale";
|
|
13773
|
+
w.dirty = true;
|
|
13774
|
+
res.stale.push(id);
|
|
13775
|
+
}
|
|
13776
|
+
}
|
|
13777
|
+
for (const w of work.values()) {
|
|
13778
|
+
if (!w.dirty || !opts.projects?.length)
|
|
13779
|
+
continue;
|
|
13780
|
+
const anchors = { ...w.fm.anchors ?? {} };
|
|
13781
|
+
for (const p of opts.projects) {
|
|
13782
|
+
const a = computeAnchor(p.root, w.fm.triggers);
|
|
13783
|
+
if (a)
|
|
13784
|
+
anchors[p.name] = a;
|
|
13785
|
+
}
|
|
13786
|
+
if (Object.keys(anchors).length)
|
|
13787
|
+
w.fm.anchors = anchors;
|
|
13788
|
+
}
|
|
13789
|
+
for (const w of work.values()) {
|
|
13790
|
+
if (!w.dirty)
|
|
13791
|
+
continue;
|
|
13792
|
+
fs24.mkdirSync(path25.dirname(w.file), { recursive: true });
|
|
13793
|
+
const text = serialize(w.fm, w.body);
|
|
13794
|
+
if (!fs24.existsSync(w.file) || fs24.readFileSync(w.file, "utf8") !== text)
|
|
13795
|
+
fs24.writeFileSync(w.file, text);
|
|
13796
|
+
}
|
|
13797
|
+
for (const [ref, t] of totals)
|
|
13798
|
+
if (needsDocling(t.dropped, t.total))
|
|
13799
|
+
res.doclingFlags.push({ ref, ...t });
|
|
13800
|
+
const live = loadRulesDir(rulesDir, `kb:${kb}`).rules;
|
|
13801
|
+
writeIndex(kbRoot, compileIndex(kb, live, opts.now?.()));
|
|
13802
|
+
appendAudit(events, opts.audit);
|
|
13803
|
+
return res;
|
|
13804
|
+
}
|
|
13805
|
+
var SHA_LEN, clean, sameMeaning;
|
|
13806
|
+
var init_extract = __esm({
|
|
13807
|
+
"packages/core/dist/knowledge/extract.js"() {
|
|
13808
|
+
init_dist();
|
|
13809
|
+
init_audit();
|
|
13810
|
+
init_anchor();
|
|
13811
|
+
init_index_file();
|
|
13812
|
+
init_ingest();
|
|
13813
|
+
init_paths();
|
|
13814
|
+
init_store2();
|
|
13815
|
+
init_verify();
|
|
13816
|
+
SHA_LEN = 12;
|
|
13817
|
+
clean = (v2) => JSON.parse(JSON.stringify(v2));
|
|
13818
|
+
sameMeaning = (existing, c) => {
|
|
13819
|
+
if (existing.fm.modality !== c.modality)
|
|
13820
|
+
return false;
|
|
13821
|
+
if (normalizeText(existing.body.trim().split(/\n\s*\n/)[0] ?? "") === normalizeText(c.statement))
|
|
13822
|
+
return true;
|
|
13823
|
+
return c.meaning === "same";
|
|
13824
|
+
};
|
|
13825
|
+
}
|
|
13826
|
+
});
|
|
13827
|
+
|
|
13828
|
+
// packages/core/dist/knowledge/schedule.js
|
|
13829
|
+
import fs25 from "fs";
|
|
13830
|
+
import path26 from "path";
|
|
13831
|
+
function readSchedule(stateRoot) {
|
|
13832
|
+
try {
|
|
13833
|
+
return JSON.parse(fs25.readFileSync(scheduleFile(stateRoot), "utf8"));
|
|
13834
|
+
} catch {
|
|
13835
|
+
return {};
|
|
13836
|
+
}
|
|
13837
|
+
}
|
|
13838
|
+
function writeSchedule(stateRoot, s) {
|
|
13839
|
+
fs25.mkdirSync(stateRoot, { recursive: true });
|
|
13840
|
+
const tmp = `${scheduleFile(stateRoot)}.${process.pid}.tmp`;
|
|
13841
|
+
fs25.writeFileSync(tmp, JSON.stringify(s, null, 2) + "\n");
|
|
13842
|
+
fs25.renameSync(tmp, scheduleFile(stateRoot));
|
|
13843
|
+
}
|
|
13844
|
+
function snapshotSources(kbRoot, sources) {
|
|
13845
|
+
const out = {};
|
|
13846
|
+
for (const e of sources)
|
|
13847
|
+
for (const f of expandSource(kbRoot, e)) {
|
|
13848
|
+
const d = detectChange(kbRoot, f);
|
|
13849
|
+
out[d.ref] = d.stamp.sha;
|
|
13850
|
+
}
|
|
13851
|
+
return out;
|
|
13852
|
+
}
|
|
13853
|
+
function changedRefs(kbRoot, sources) {
|
|
13854
|
+
const out = [];
|
|
13855
|
+
for (const e of sources) {
|
|
13856
|
+
for (const f of expandSource(kbRoot, e)) {
|
|
13857
|
+
const d = detectChange(kbRoot, f);
|
|
13858
|
+
const st = readState2(kbRoot, d.ref);
|
|
13859
|
+
if (d.changed || st && st.cursor < st.sections)
|
|
13860
|
+
out.push(d.ref);
|
|
13861
|
+
}
|
|
13862
|
+
}
|
|
13863
|
+
return out;
|
|
13864
|
+
}
|
|
13865
|
+
function duePass(entry, o) {
|
|
13866
|
+
const now = o.now.getTime();
|
|
13867
|
+
if (o.empty && !entry.lastFullAt)
|
|
13868
|
+
return "full";
|
|
13869
|
+
const fullBase = entry.lastFullAt ?? entry.createdAt;
|
|
13870
|
+
if (fullBase && now >= Date.parse(fullBase) + o.rc.fullEveryDays * DAY_MS)
|
|
13871
|
+
return "full";
|
|
13872
|
+
if (entry.firstChangeAt && entry.incrementalDueAt && now >= Date.parse(entry.incrementalDueAt))
|
|
13873
|
+
return "incremental";
|
|
13874
|
+
return null;
|
|
13875
|
+
}
|
|
13876
|
+
function acquirePass(entry, pass, now, snapshot, log) {
|
|
13877
|
+
if (entry.running) {
|
|
13878
|
+
if (now.getTime() - Date.parse(entry.running.startedAt) <= RUNNING_STALE_MS)
|
|
13879
|
+
return false;
|
|
13880
|
+
log?.(`knowledge: clearing stale ${entry.running.pass} pass started ${entry.running.startedAt}`);
|
|
13881
|
+
}
|
|
13882
|
+
entry.running = { pass, startedAt: now.toISOString(), sourceSnapshot: snapshot };
|
|
13883
|
+
return true;
|
|
13884
|
+
}
|
|
13885
|
+
function requestText(kb, pass) {
|
|
13886
|
+
const absorbed = pass === "full" ? " (absorbs any pending incremental pass)" : "";
|
|
13887
|
+
return `KB ${kb}: ${pass} reconcile due${absorbed}. Spawn a crew with the knowledge-review skill: it extracts changed sources, reviews proposals, then runs \`squadrant knowledge reconcile ${kb}${pass === "full" ? " --full" : ""} --finish\`.`;
|
|
13888
|
+
}
|
|
13889
|
+
async function requestPass(kb, pass, d, opts = {}) {
|
|
13890
|
+
const now = d.now?.() ?? /* @__PURE__ */ new Date();
|
|
13891
|
+
const home = resolveKbConfig(d.cfg, kb).homeProject;
|
|
13892
|
+
if (!home)
|
|
13893
|
+
return { kb, pass, requested: false, reason: "no homeProject configured" };
|
|
13894
|
+
const schedule = readSchedule(d.stateRoot);
|
|
13895
|
+
const entry = schedule[kb] ??= { createdAt: now.toISOString() };
|
|
13896
|
+
const root = kbDir(d.cfg, kb);
|
|
13897
|
+
if (opts.dryRun) {
|
|
13898
|
+
const busy = entry.running && now.getTime() - Date.parse(entry.running.startedAt) <= RUNNING_STALE_MS;
|
|
13899
|
+
return { kb, pass, requested: false, reason: busy ? `a ${entry.running.pass} pass is already running` : `dry run: would enqueue to ${home}` };
|
|
13900
|
+
}
|
|
13901
|
+
const snapshot = snapshotSources(root, loadSources(root).sources);
|
|
13902
|
+
if (!acquirePass(entry, pass, now, snapshot, d.log))
|
|
13903
|
+
return { kb, pass, requested: false, reason: `a ${entry.running.pass} pass is already running` };
|
|
13904
|
+
writeSchedule(d.stateRoot, schedule);
|
|
13905
|
+
try {
|
|
13906
|
+
await d.enqueue(home, requestText(kb, pass));
|
|
13907
|
+
} catch (e) {
|
|
13908
|
+
delete entry.running;
|
|
13909
|
+
writeSchedule(d.stateRoot, schedule);
|
|
13910
|
+
throw e;
|
|
13911
|
+
}
|
|
13912
|
+
return { kb, pass, requested: true };
|
|
13913
|
+
}
|
|
13914
|
+
async function tickKnowledgeSchedule(d) {
|
|
13915
|
+
const out = [];
|
|
13916
|
+
const now = d.now?.() ?? /* @__PURE__ */ new Date();
|
|
13917
|
+
for (const kb of Object.keys(kbConfigs(d.cfg))) {
|
|
13918
|
+
if (!resolveKbConfig(d.cfg, kb).homeProject)
|
|
13919
|
+
continue;
|
|
13920
|
+
try {
|
|
13921
|
+
const root = kbDir(d.cfg, kb);
|
|
13922
|
+
if (!fs25.existsSync(root))
|
|
13923
|
+
continue;
|
|
13924
|
+
const { sources } = loadSources(root);
|
|
13925
|
+
if (!sources.length)
|
|
13926
|
+
continue;
|
|
13927
|
+
const schedule = readSchedule(d.stateRoot);
|
|
13928
|
+
const entry = schedule[kb] ??= { createdAt: now.toISOString() };
|
|
13929
|
+
const rc = reconcileConfig(d.cfg, kb);
|
|
13930
|
+
let dirty = false;
|
|
13931
|
+
if (entry.running && now.getTime() - Date.parse(entry.running.startedAt) > RUNNING_STALE_MS) {
|
|
13932
|
+
d.log?.(`knowledge: ${kb} pass stale, cleared`);
|
|
13933
|
+
delete entry.running;
|
|
13934
|
+
dirty = true;
|
|
13935
|
+
}
|
|
13936
|
+
if (!entry.running && !entry.firstChangeAt && changedRefs(root, sources).length) {
|
|
13937
|
+
entry.firstChangeAt = now.toISOString();
|
|
13938
|
+
entry.incrementalDueAt = new Date(now.getTime() + rc.incrementalAfterDays * DAY_MS).toISOString();
|
|
13939
|
+
dirty = true;
|
|
13940
|
+
}
|
|
13941
|
+
if (dirty)
|
|
13942
|
+
writeSchedule(d.stateRoot, schedule);
|
|
13943
|
+
if (entry.running)
|
|
13944
|
+
continue;
|
|
13945
|
+
const empty = loadKbRules(d.cfg, kb, { includeProposed: true }).rules.length === 0;
|
|
13946
|
+
const pass = duePass(entry, { now, rc, empty });
|
|
13947
|
+
if (pass)
|
|
13948
|
+
out.push(await requestPass(kb, pass, d));
|
|
13949
|
+
} catch (e) {
|
|
13950
|
+
d.log?.(`knowledge schedule error (${kb}): ${e.message}`);
|
|
13951
|
+
}
|
|
13952
|
+
}
|
|
13953
|
+
return out;
|
|
13954
|
+
}
|
|
13955
|
+
function completePass(kb, d) {
|
|
13956
|
+
const now = d.now?.() ?? /* @__PURE__ */ new Date();
|
|
13957
|
+
const schedule = readSchedule(d.stateRoot);
|
|
13958
|
+
const entry = schedule[kb] ??= { createdAt: now.toISOString() };
|
|
13959
|
+
const pass = d.pass ?? entry.running?.pass ?? "incremental";
|
|
13960
|
+
const snap = entry.running?.sourceSnapshot;
|
|
13961
|
+
const root = kbDir(d.cfg, kb);
|
|
13962
|
+
delete entry.running;
|
|
13963
|
+
if (pass === "full")
|
|
13964
|
+
entry.lastFullAt = now.toISOString();
|
|
13965
|
+
delete entry.firstChangeAt;
|
|
13966
|
+
delete entry.incrementalDueAt;
|
|
13967
|
+
if (snap) {
|
|
13968
|
+
const nowSnap = snapshotSources(root, loadSources(root).sources);
|
|
13969
|
+
if (Object.entries(nowSnap).some(([ref, sha]) => snap[ref] !== sha)) {
|
|
13970
|
+
entry.firstChangeAt = now.toISOString();
|
|
13971
|
+
entry.incrementalDueAt = new Date(now.getTime() + reconcileConfig(d.cfg, kb).incrementalAfterDays * DAY_MS).toISOString();
|
|
13972
|
+
}
|
|
13973
|
+
}
|
|
13974
|
+
writeSchedule(d.stateRoot, schedule);
|
|
13975
|
+
return pass;
|
|
13976
|
+
}
|
|
13977
|
+
var RUNNING_STALE_MS, DAY_MS, scheduleFile, reconcileConfig;
|
|
13978
|
+
var init_schedule = __esm({
|
|
13979
|
+
"packages/core/dist/knowledge/schedule.js"() {
|
|
13980
|
+
init_dist();
|
|
13981
|
+
init_ingest();
|
|
13982
|
+
init_layers();
|
|
13983
|
+
init_paths();
|
|
13984
|
+
init_sources();
|
|
13985
|
+
RUNNING_STALE_MS = 6 * 60 * 60 * 1e3;
|
|
13986
|
+
DAY_MS = 24 * 60 * 60 * 1e3;
|
|
13987
|
+
scheduleFile = (stateRoot) => path26.join(stateRoot, "knowledge-schedule.json");
|
|
13988
|
+
reconcileConfig = (cfg, kb) => ({ ...DEFAULT_RECONCILE, ...kbConfigs(cfg)[kb]?.reconcile });
|
|
13989
|
+
}
|
|
13990
|
+
});
|
|
13991
|
+
|
|
13992
|
+
// packages/core/dist/knowledge/review.js
|
|
13993
|
+
import crypto4 from "crypto";
|
|
13994
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
13995
|
+
import fs26 from "fs";
|
|
13996
|
+
import path27 from "path";
|
|
13997
|
+
import matter4 from "gray-matter";
|
|
13998
|
+
function readEscalations(kbRoot) {
|
|
13999
|
+
try {
|
|
14000
|
+
return JSON.parse(fs26.readFileSync(escalationsFile(kbRoot), "utf8")).items;
|
|
14001
|
+
} catch {
|
|
14002
|
+
return [];
|
|
14003
|
+
}
|
|
14004
|
+
}
|
|
14005
|
+
function writeEscalations(kbRoot, items) {
|
|
14006
|
+
fs26.mkdirSync(kbRoot, { recursive: true });
|
|
14007
|
+
fs26.writeFileSync(escalationsFile(kbRoot), JSON.stringify({ items }, null, 2) + "\n");
|
|
14008
|
+
}
|
|
14009
|
+
function addEscalation(kbRoot, e) {
|
|
14010
|
+
const items = readEscalations(kbRoot);
|
|
14011
|
+
if (items.some((x) => x.key === e.key))
|
|
14012
|
+
return false;
|
|
14013
|
+
writeEscalations(kbRoot, [...items, e]);
|
|
14014
|
+
return true;
|
|
14015
|
+
}
|
|
14016
|
+
function appendVerdict(kbRoot, v2, now) {
|
|
14017
|
+
fs26.mkdirSync(kbRoot, { recursive: true });
|
|
14018
|
+
fs26.appendFileSync(verdictsFile(kbRoot), JSON.stringify({ ts: now.toISOString(), ...v2 }) + "\n");
|
|
14019
|
+
}
|
|
14020
|
+
function priorityOfRef(c, ref) {
|
|
14021
|
+
if (ref.startsWith("agent:"))
|
|
14022
|
+
return "agent";
|
|
14023
|
+
const root = kbDir(c.cfg, c.kb);
|
|
14024
|
+
for (const e of c.sources)
|
|
14025
|
+
for (const f of expandSource(root, e))
|
|
14026
|
+
if (refFor(root, f) === ref)
|
|
14027
|
+
return e.priority;
|
|
14028
|
+
return "project";
|
|
14029
|
+
}
|
|
14030
|
+
function priorityOfRule(c, r) {
|
|
14031
|
+
return r.sources.map((s) => priorityOfRef(c, s.ref)).sort((a, b) => PRIORITY_RANK[b] - PRIORITY_RANK[a])[0] ?? "project";
|
|
14032
|
+
}
|
|
14033
|
+
function loadProposals(c) {
|
|
14034
|
+
const dir = kbRulesDir(c.cfg, c.kb);
|
|
14035
|
+
const live = loadRulesDir(dir, `kb:${c.kb}`).rules;
|
|
14036
|
+
const byId = new Map(live.map((r) => [r.id, r]));
|
|
14037
|
+
const proposals = [];
|
|
14038
|
+
const pdirs = /* @__PURE__ */ new Set();
|
|
14039
|
+
for (const f of fs26.existsSync(dir) ? walkDirs(dir) : [])
|
|
14040
|
+
if (path27.basename(f) === PROPOSED_DIR)
|
|
14041
|
+
pdirs.add(f);
|
|
14042
|
+
for (const pd of pdirs) {
|
|
14043
|
+
for (const rule of loadRulesDir(pd, `kb:${c.kb}`, { includeProposed: true }).rules) {
|
|
14044
|
+
proposals.push({ key: path27.basename(rule.file).replace(/\.md$/, ""), rule, live: byId.get(rule.id) });
|
|
14045
|
+
}
|
|
14046
|
+
}
|
|
14047
|
+
return { proposals, live };
|
|
14048
|
+
}
|
|
14049
|
+
function walkDirs(dir, out = []) {
|
|
14050
|
+
for (const e of fs26.readdirSync(dir, { withFileTypes: true }))
|
|
14051
|
+
if (e.isDirectory()) {
|
|
14052
|
+
const f = path27.join(dir, e.name);
|
|
14053
|
+
out.push(f);
|
|
14054
|
+
walkDirs(f, out);
|
|
14055
|
+
}
|
|
14056
|
+
return out;
|
|
14057
|
+
}
|
|
14058
|
+
function reindex(c) {
|
|
14059
|
+
const live = loadRulesDir(kbRulesDir(c.cfg, c.kb), `kb:${c.kb}`).rules;
|
|
14060
|
+
writeIndex(kbDir(c.cfg, c.kb), compileIndex(c.kb, live, nowOf(c)));
|
|
14061
|
+
}
|
|
14062
|
+
function anchorsFor(c, fm) {
|
|
14063
|
+
const anchors = { ...fm.anchors ?? {} };
|
|
14064
|
+
for (const p of c.projects ?? []) {
|
|
14065
|
+
const a = computeAnchor(p.root, fm.triggers);
|
|
14066
|
+
if (a)
|
|
14067
|
+
anchors[p.name] = a;
|
|
14068
|
+
}
|
|
14069
|
+
return Object.keys(anchors).length ? anchors : void 0;
|
|
14070
|
+
}
|
|
14071
|
+
function promoteProposal(c, p, by, justification, events) {
|
|
14072
|
+
const dir = kbRulesDir(c.cfg, c.kb);
|
|
14073
|
+
const { fm, body } = readFm(p.rule.file);
|
|
14074
|
+
const retired = [];
|
|
14075
|
+
for (const id of fm.conflictsWith ?? []) {
|
|
14076
|
+
const other = loadRulesDir(dir, `kb:${c.kb}`).rules.find((r) => r.id === id);
|
|
14077
|
+
if (!other || other.id === fm.id)
|
|
14078
|
+
continue;
|
|
14079
|
+
const o = readFm(other.file);
|
|
14080
|
+
o.fm.status = "retired";
|
|
14081
|
+
fs26.writeFileSync(other.file, fileText(o.fm, o.body));
|
|
14082
|
+
retired.push(id);
|
|
14083
|
+
events.push(ev(c, "item.superseded", id, o.fm.domain));
|
|
14084
|
+
}
|
|
14085
|
+
const next = { ...fm, status: "active", approvedBy: by, justification, supersedes: [.../* @__PURE__ */ new Set([...fm.supersedes ?? [], ...retired])], conflictsWith: [] };
|
|
14086
|
+
if (p.live?.decision)
|
|
14087
|
+
next.decision = p.live.decision;
|
|
14088
|
+
const anchors = anchorsFor(c, { ...next, anchors: p.live?.anchors ?? fm.anchors });
|
|
14089
|
+
if (anchors)
|
|
14090
|
+
next.anchors = anchors;
|
|
14091
|
+
const target = p.live?.file ?? path27.join(dir, next.domain, `${next.id}.md`);
|
|
14092
|
+
fs26.mkdirSync(path27.dirname(target), { recursive: true });
|
|
14093
|
+
fs26.writeFileSync(target, fileText(next, body));
|
|
14094
|
+
fs26.rmSync(p.rule.file);
|
|
14095
|
+
events.push(ev(c, "item.approved", next.id, next.domain), ev(c, "item.applied", next.id, next.domain));
|
|
14096
|
+
if (p.live)
|
|
14097
|
+
events.push(ev(c, "item.superseded", next.id, next.domain));
|
|
14098
|
+
}
|
|
14099
|
+
function dropProposal(c, p, events) {
|
|
14100
|
+
fs26.rmSync(p.rule.file);
|
|
14101
|
+
events.push(ev(c, "item.rejected", p.rule.id, p.rule.domain, { reason: "reviewer" }));
|
|
14102
|
+
}
|
|
14103
|
+
function mergeInto(c, p, targetId, live, events) {
|
|
14104
|
+
const t = live.find((r) => r.id === targetId);
|
|
14105
|
+
if (!t)
|
|
14106
|
+
return false;
|
|
14107
|
+
const tf = readFm(t.file);
|
|
14108
|
+
for (const s of p.rule.sources)
|
|
14109
|
+
if (!tf.fm.sources.some((x) => x.ref === s.ref))
|
|
14110
|
+
tf.fm.sources.push(s);
|
|
14111
|
+
fs26.writeFileSync(t.file, fileText(tf.fm, tf.body));
|
|
14112
|
+
fs26.rmSync(p.rule.file);
|
|
14113
|
+
events.push(ev(c, "item.superseded", p.rule.id, p.rule.domain), ev(c, "item.applied", targetId, t.domain));
|
|
14114
|
+
return true;
|
|
14115
|
+
}
|
|
14116
|
+
function evaluateCodeConflict(c, rule, cc) {
|
|
14117
|
+
const g = c.git ?? realGit;
|
|
14118
|
+
const proj = (c.projects ?? []).find((p) => p.name === cc.project);
|
|
14119
|
+
const root = proj?.root;
|
|
14120
|
+
const code = root ? g.lastCommit(root, cc.files) : null;
|
|
14121
|
+
const kbRoot = kbDir(c.cfg, c.kb);
|
|
14122
|
+
const docDates = rule.sources.map((s) => g.docDate(fileForRef(kbRoot, s.ref))).filter((d2) => !!d2).sort();
|
|
14123
|
+
const docDate = docDates[docDates.length - 1];
|
|
14124
|
+
const docRev = docRevOf(rule);
|
|
14125
|
+
const evidence = { docRev, codeRev: code?.rev ?? "unknown", files: cc.files, project: cc.project };
|
|
14126
|
+
const doc = rule.sources.map((s) => `${s.ref} (${docDate ?? "date unknown"})`).join(", ");
|
|
14127
|
+
if (!code || !docDate) {
|
|
14128
|
+
return { action: "escalate", evidence, explanation: `Rule ${rule.id} ("${rule.statement}") may conflict with code in ${cc.project} (${cc.files.join(", ")}): ${cc.summary}. Dates could not be verified (doc ${doc}; code ${code?.date ?? "unknown"}), so a human must judge it.` };
|
|
14129
|
+
}
|
|
14130
|
+
if (Date.parse(docDate) >= Date.parse(code.date)) {
|
|
14131
|
+
return { action: "flag-violation", explanation: `Code in ${cc.project} (${cc.files.join(", ")}, last commit ${code.date}) contradicts rule ${rule.id}, but the doc (${doc}) is newer: treat the code as the violation. ${cc.summary}` };
|
|
14132
|
+
}
|
|
14133
|
+
const d = rule.decision;
|
|
14134
|
+
if (d && d.docRev === docRev && !g.changedSince(root, cc.files, d.at))
|
|
14135
|
+
return { action: "skip-decided" };
|
|
14136
|
+
const reopened = d ? ` An earlier decision (${d.verdict}, ${d.at}) is re-opened because ${d.docRev !== docRev ? "the doc has a new revision" : "the anchored code changed after it"}.` : "";
|
|
14137
|
+
return {
|
|
14138
|
+
action: "escalate",
|
|
14139
|
+
evidence,
|
|
14140
|
+
explanation: `Rule ${rule.id} ("${rule.statement}") from ${doc} is contradicted by newer code: ${cc.project} ${cc.files.join(", ")} (last commit ${code.date}). ${cc.summary} Keeping the doc leaves the code in violation; superseding with the code retires this rule and any code-derived replacement inherits its priority unless you say otherwise.${reopened}`
|
|
14141
|
+
};
|
|
14142
|
+
}
|
|
14143
|
+
function applyDecisions(file, c) {
|
|
14144
|
+
const now = nowOf(c);
|
|
14145
|
+
const kbRoot = kbDir(c.cfg, c.kb);
|
|
14146
|
+
const threshold = reconcileConfig(c.cfg, c.kb).autoApproveConfidence;
|
|
14147
|
+
const res = { applied: [], escalated: [], violations: [], skipped: [] };
|
|
14148
|
+
const events = [];
|
|
14149
|
+
let { proposals, live } = loadProposals(c);
|
|
14150
|
+
const escalate = (e) => {
|
|
14151
|
+
addEscalation(kbRoot, { createdAt: now.toISOString(), needsYou: true, ...e });
|
|
14152
|
+
res.escalated.push({ key: e.key, reasons: e.reasons });
|
|
14153
|
+
};
|
|
14154
|
+
const verdict = (d, outcome, extra = {}) => appendVerdict(kbRoot, { kb: c.kb, kind: "review", item: d.item, outcome, ...extra }, now);
|
|
14155
|
+
for (const d of file.decisions ?? []) {
|
|
14156
|
+
const conf = Number.isFinite(d.confidence) ? d.confidence : 0;
|
|
14157
|
+
const stale = d.item.startsWith("stale:");
|
|
14158
|
+
const prop = stale ? void 0 : proposals.find((p2) => p2.key === d.item);
|
|
14159
|
+
const rule = stale ? live.find((r) => r.id === d.item.slice(6)) : prop?.rule ?? live.find((r) => r.id === d.item);
|
|
14160
|
+
if (!rule) {
|
|
14161
|
+
res.skipped.push({ item: d.item, reason: "no such proposal or rule" });
|
|
14162
|
+
continue;
|
|
14163
|
+
}
|
|
14164
|
+
const reasons = [];
|
|
14165
|
+
if (d.codeConflict) {
|
|
14166
|
+
const target = prop?.live ?? live.find((r) => r.id === rule.id) ?? rule;
|
|
14167
|
+
const v2 = evaluateCodeConflict(c, target, d.codeConflict);
|
|
14168
|
+
if (v2.action === "flag-violation") {
|
|
14169
|
+
escalate({ key: `violation:${target.id}`, kind: "code-violation", ruleId: target.id, reasons: ["doc newer than code"], explanation: v2.explanation, needsYou: false });
|
|
14170
|
+
res.violations.push(target.id);
|
|
14171
|
+
} else if (v2.action === "escalate") {
|
|
14172
|
+
escalate({ key: `code-vs-doc:${target.id}`, kind: "code-vs-doc", ruleId: target.id, reasons: ["code-vs-doc"], explanation: v2.explanation, evidence: v2.evidence, confidence: conf });
|
|
14173
|
+
verdict(d, "escalated", { reasons: ["code-vs-doc"], confidence: conf });
|
|
14174
|
+
}
|
|
14175
|
+
if (!prop && !stale)
|
|
14176
|
+
continue;
|
|
14177
|
+
if (v2.action === "escalate") {
|
|
14178
|
+
reasons.push("code-vs-doc");
|
|
14179
|
+
}
|
|
14180
|
+
}
|
|
14181
|
+
if (stale) {
|
|
14182
|
+
if (d.decision === "reject") {
|
|
14183
|
+
const f = readFm(rule.file);
|
|
14184
|
+
f.fm.status = "active";
|
|
14185
|
+
if (d.loc && f.fm.sources[0])
|
|
14186
|
+
f.fm.sources[0].loc = d.loc;
|
|
14187
|
+
if (conf >= threshold && !d.flag && !reasons.length) {
|
|
14188
|
+
fs26.writeFileSync(rule.file, fileText(f.fm, f.body));
|
|
14189
|
+
res.applied.push(d.item);
|
|
14190
|
+
events.push(ev(c, "item.applied", rule.id, rule.domain));
|
|
14191
|
+
verdict(d, "applied", { confidence: conf });
|
|
14192
|
+
continue;
|
|
14193
|
+
}
|
|
14194
|
+
reasons.push(conf < threshold ? "low-confidence" : "reviewer-flagged");
|
|
14195
|
+
} else if (d.decision === "approve") {
|
|
14196
|
+
if (strong(rule.modality))
|
|
14197
|
+
reasons.push("retiring must/must-not");
|
|
14198
|
+
if (conf < threshold)
|
|
14199
|
+
reasons.push("low-confidence");
|
|
14200
|
+
if (d.flag)
|
|
14201
|
+
reasons.push("reviewer-flagged");
|
|
14202
|
+
if (!reasons.length) {
|
|
14203
|
+
const f = readFm(rule.file);
|
|
14204
|
+
f.fm.status = "retired";
|
|
14205
|
+
fs26.writeFileSync(rule.file, fileText(f.fm, f.body));
|
|
14206
|
+
res.applied.push(d.item);
|
|
14207
|
+
events.push(ev(c, "item.archived", rule.id, rule.domain));
|
|
14208
|
+
verdict(d, "applied", { confidence: conf });
|
|
14209
|
+
continue;
|
|
14210
|
+
}
|
|
14211
|
+
} else
|
|
14212
|
+
reasons.push("reviewer-flagged");
|
|
14213
|
+
escalate({ key: d.item, kind: "stale-retire", ruleId: rule.id, item: d.item, reasons: [...new Set(reasons)], explanation: `Rule ${rule.id} ("${rule.statement}") lost its source support. Reviewer: ${d.justification}`, confidence: conf });
|
|
14214
|
+
verdict(d, "escalated", { reasons, confidence: conf });
|
|
14215
|
+
continue;
|
|
14216
|
+
}
|
|
14217
|
+
const p = prop;
|
|
14218
|
+
if (d.decision === "escalate" || d.flag)
|
|
14219
|
+
reasons.push("reviewer-flagged");
|
|
14220
|
+
if (conf < threshold)
|
|
14221
|
+
reasons.push("low-confidence");
|
|
14222
|
+
if (d.decision === "approve") {
|
|
14223
|
+
const myPri = priorityOfRule(c, p.rule);
|
|
14224
|
+
const conflicted = (p.rule.conflictsWith ?? []).map((id) => live.find((r) => r.id === id)).filter((r) => !!r);
|
|
14225
|
+
if (myPri === "company" && conflicted.some((r) => priorityOfRule(c, r) === "company"))
|
|
14226
|
+
reasons.push("company-vs-company conflict");
|
|
14227
|
+
const retired = [...conflicted, ...p.live && p.live.modality !== p.rule.modality ? [p.live] : []];
|
|
14228
|
+
if (retired.some((r) => strong(r.modality)))
|
|
14229
|
+
reasons.push("retiring must/must-not");
|
|
14230
|
+
}
|
|
14231
|
+
if (reasons.length) {
|
|
14232
|
+
escalate({ key: p.key, kind: "proposal", ruleId: p.rule.id, item: p.key, reasons: [...new Set(reasons)], explanation: `Proposal ${p.key}: "${p.rule.statement}"${p.live ? ` (replaces live "${p.live.statement}")` : ""}. Reviewer (${d.decision}, ${conf}): ${d.justification}`, confidence: conf });
|
|
14233
|
+
verdict(d, "escalated", { reasons: [...new Set(reasons)], confidence: conf, decision: d.decision });
|
|
14234
|
+
continue;
|
|
14235
|
+
}
|
|
14236
|
+
if (d.decision === "approve")
|
|
14237
|
+
promoteProposal(c, p, "reviewer-agent", d.justification, events);
|
|
14238
|
+
else if (d.decision === "reject")
|
|
14239
|
+
dropProposal(c, p, events);
|
|
14240
|
+
else if (d.decision.startsWith("merge-into:")) {
|
|
14241
|
+
if (!mergeInto(c, p, d.decision.slice("merge-into:".length), live, events)) {
|
|
14242
|
+
res.skipped.push({ item: d.item, reason: `merge target ${d.decision.slice(11)} not found` });
|
|
14243
|
+
continue;
|
|
14244
|
+
}
|
|
14245
|
+
} else {
|
|
14246
|
+
res.skipped.push({ item: d.item, reason: `unknown decision '${d.decision}'` });
|
|
14247
|
+
continue;
|
|
14248
|
+
}
|
|
14249
|
+
res.applied.push(d.item);
|
|
14250
|
+
verdict(d, "applied", { decision: d.decision, confidence: conf, justification: d.justification });
|
|
14251
|
+
({ proposals, live } = loadProposals(c));
|
|
14252
|
+
}
|
|
14253
|
+
for (const pd of file.pairs ?? [])
|
|
14254
|
+
applyPair(pd, c, res, threshold, events, kbRoot, now);
|
|
14255
|
+
reindex(c);
|
|
14256
|
+
appendAudit(events, c.audit);
|
|
14257
|
+
return res;
|
|
14258
|
+
}
|
|
14259
|
+
function applyPair(pd, c, res, threshold, events, kbRoot, now) {
|
|
14260
|
+
const { live } = loadProposals(c);
|
|
14261
|
+
const a = live.find((r) => r.id === pd.a), b = live.find((r) => r.id === pd.b);
|
|
14262
|
+
const key = `pair:${[pd.a, pd.b].sort().join("+")}`;
|
|
14263
|
+
if (!a || !b) {
|
|
14264
|
+
res.skipped.push({ item: key, reason: "rule not found" });
|
|
14265
|
+
return;
|
|
14266
|
+
}
|
|
14267
|
+
const esc = (reasons2) => {
|
|
14268
|
+
addEscalation(kbRoot, { key, kind: "pair", reasons: reasons2, explanation: `${a.id} ("${a.statement}") vs ${b.id} ("${b.statement}"): ${pd.relation}. ${pd.justification}`, confidence: pd.confidence, createdAt: now.toISOString(), needsYou: true });
|
|
14269
|
+
res.escalated.push({ key, reasons: reasons2 });
|
|
14270
|
+
};
|
|
14271
|
+
appendVerdict(kbRoot, { kb: c.kb, kind: "pair", item: key, relation: pd.relation, confidence: pd.confidence }, now);
|
|
14272
|
+
if (pd.relation === "unrelated" || pd.relation === "refines")
|
|
14273
|
+
return;
|
|
14274
|
+
if (pd.relation === "conflicts") {
|
|
14275
|
+
const reasons2 = priorityOfRule(c, a) === "company" && priorityOfRule(c, b) === "company" ? ["company-vs-company conflict"] : pd.confidence < threshold ? ["low-confidence"] : [];
|
|
14276
|
+
if (reasons2.length) {
|
|
14277
|
+
esc(reasons2);
|
|
14278
|
+
return;
|
|
14279
|
+
}
|
|
14280
|
+
for (const [x, y] of [[a, b], [b, a]]) {
|
|
14281
|
+
const f = readFm(x.file);
|
|
14282
|
+
f.fm.conflictsWith = [.../* @__PURE__ */ new Set([...f.fm.conflictsWith ?? [], y.id])];
|
|
14283
|
+
fs26.writeFileSync(x.file, fileText(f.fm, f.body));
|
|
14284
|
+
}
|
|
14285
|
+
return;
|
|
14286
|
+
}
|
|
14287
|
+
const keep = pd.keep === b.id ? b : a, drop = keep === a ? b : a;
|
|
14288
|
+
const reasons = [...pd.confidence < threshold ? ["low-confidence"] : [], ...strong(drop.modality) && drop.modality !== keep.modality ? ["retiring must/must-not"] : []];
|
|
14289
|
+
if (reasons.length) {
|
|
14290
|
+
esc(reasons);
|
|
14291
|
+
return;
|
|
14292
|
+
}
|
|
14293
|
+
const kf = readFm(keep.file);
|
|
14294
|
+
for (const s of drop.sources)
|
|
14295
|
+
if (!kf.fm.sources.some((x) => x.ref === s.ref))
|
|
14296
|
+
kf.fm.sources.push(s);
|
|
14297
|
+
kf.fm.supersedes = [.../* @__PURE__ */ new Set([...kf.fm.supersedes ?? [], drop.id])];
|
|
14298
|
+
fs26.writeFileSync(keep.file, fileText(kf.fm, kf.body));
|
|
14299
|
+
const df = readFm(drop.file);
|
|
14300
|
+
df.fm.status = "retired";
|
|
14301
|
+
fs26.writeFileSync(drop.file, fileText(df.fm, df.body));
|
|
14302
|
+
events.push(ev(c, "item.superseded", drop.id, drop.domain), ev(c, "item.applied", keep.id, keep.domain));
|
|
14303
|
+
res.applied.push(key);
|
|
14304
|
+
}
|
|
14305
|
+
function resolveEscalation(c, key, r, by, reason) {
|
|
14306
|
+
const now = nowOf(c);
|
|
14307
|
+
const kbRoot = kbDir(c.cfg, c.kb);
|
|
14308
|
+
const items = readEscalations(kbRoot);
|
|
14309
|
+
const esc = items.find((e) => e.key === key);
|
|
14310
|
+
if (!esc)
|
|
14311
|
+
throw new Error(`No open escalation '${key}'`);
|
|
14312
|
+
const events = [];
|
|
14313
|
+
const { proposals, live } = loadProposals(c);
|
|
14314
|
+
const done = (msg) => {
|
|
14315
|
+
writeEscalations(kbRoot, items.filter((e) => e.key !== key));
|
|
14316
|
+
appendVerdict(kbRoot, { kb: c.kb, kind: "operator", item: key, resolution: r, by, reason }, now);
|
|
14317
|
+
reindex(c);
|
|
14318
|
+
appendAudit(events, c.audit);
|
|
14319
|
+
return msg;
|
|
14320
|
+
};
|
|
14321
|
+
if (esc.kind === "code-vs-doc") {
|
|
14322
|
+
if (r.kind !== "verdict")
|
|
14323
|
+
throw new Error("code-vs-doc needs --keep-doc or --supersede-with-code");
|
|
14324
|
+
const rule = live.find((x) => x.id === esc.ruleId);
|
|
14325
|
+
if (!rule || !esc.evidence)
|
|
14326
|
+
throw new Error(`rule ${esc.ruleId} or its evidence is gone`);
|
|
14327
|
+
const f = readFm(rule.file);
|
|
14328
|
+
const decision = { by, at: now.toISOString(), verdict: r.verdict, docRev: esc.evidence.docRev, codeRev: esc.evidence.codeRev, files: esc.evidence.files, reason };
|
|
14329
|
+
f.fm.decision = decision;
|
|
14330
|
+
if (r.verdict === "supersede-with-code") {
|
|
14331
|
+
f.fm.status = "retired";
|
|
14332
|
+
events.push(ev(c, "item.superseded", rule.id, rule.domain));
|
|
14333
|
+
}
|
|
14334
|
+
fs26.writeFileSync(rule.file, fileText(f.fm, f.body));
|
|
14335
|
+
return done(`${rule.id}: ${r.verdict}`);
|
|
14336
|
+
}
|
|
14337
|
+
if (esc.kind === "proposal") {
|
|
14338
|
+
const p = proposals.find((x) => x.key === esc.item);
|
|
14339
|
+
if (!p)
|
|
14340
|
+
throw new Error(`proposal ${esc.item} no longer exists`);
|
|
14341
|
+
if (r.kind === "approve")
|
|
14342
|
+
promoteProposal(c, p, "human", reason || "approved by operator", events);
|
|
14343
|
+
else if (r.kind === "reject")
|
|
14344
|
+
dropProposal(c, p, events);
|
|
14345
|
+
else
|
|
14346
|
+
throw new Error("proposal needs approve or reject");
|
|
14347
|
+
return done(`${p.key}: ${r.kind}`);
|
|
14348
|
+
}
|
|
14349
|
+
if (esc.kind === "stale-retire") {
|
|
14350
|
+
const rule = live.find((x) => x.id === esc.ruleId);
|
|
14351
|
+
if (rule) {
|
|
14352
|
+
const f = readFm(rule.file);
|
|
14353
|
+
f.fm.status = r.kind === "approve" ? "retired" : "active";
|
|
14354
|
+
fs26.writeFileSync(rule.file, fileText(f.fm, f.body));
|
|
14355
|
+
events.push(ev(c, r.kind === "approve" ? "item.archived" : "item.applied", rule.id, rule.domain));
|
|
14356
|
+
}
|
|
14357
|
+
return done(`${esc.ruleId}: ${r.kind === "approve" ? "retired" : "kept"}`);
|
|
14358
|
+
}
|
|
14359
|
+
if (esc.kind === "override-base-changed") {
|
|
14360
|
+
const ov = esc.item && fs26.existsSync(esc.item) ? esc.item : void 0;
|
|
14361
|
+
if (ov) {
|
|
14362
|
+
if (r.kind === "reject")
|
|
14363
|
+
fs26.rmSync(ov);
|
|
14364
|
+
else {
|
|
14365
|
+
const f = readFm(ov);
|
|
14366
|
+
f.fm.overridesBase = esc.evidence?.docRev;
|
|
14367
|
+
fs26.writeFileSync(ov, fileText(f.fm, f.body));
|
|
14368
|
+
}
|
|
14369
|
+
}
|
|
14370
|
+
return done(`${esc.ruleId}: override ${r.kind === "reject" ? "dropped" : "kept"}`);
|
|
14371
|
+
}
|
|
14372
|
+
return done(`${key}: acknowledged`);
|
|
14373
|
+
}
|
|
14374
|
+
function proposeAgentRule(c, o) {
|
|
14375
|
+
const sha = crypto4.createHash("sha256").update(`${o.statement}
|
|
14376
|
+
${o.evidence}`).digest("hex");
|
|
14377
|
+
const slug = o.statement.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").split("-").slice(0, 6).join("-") || "proposal";
|
|
14378
|
+
const id = o.id ?? `agent.${slug}`;
|
|
14379
|
+
const domain = o.domain ?? "general";
|
|
14380
|
+
const fm = {
|
|
14381
|
+
id,
|
|
14382
|
+
domain,
|
|
14383
|
+
modality: o.modality ?? "should",
|
|
14384
|
+
status: "proposed",
|
|
14385
|
+
sources: [{ ref: `agent:${o.agent ?? "unknown"}`, sha, quote: o.evidence.trim() }],
|
|
14386
|
+
approvedBy: "reviewer-agent",
|
|
14387
|
+
justification: "",
|
|
14388
|
+
supersedes: [],
|
|
14389
|
+
conflictsWith: []
|
|
14390
|
+
};
|
|
14391
|
+
const dir = path27.join(kbRulesDir(c.cfg, c.kb), PROPOSED_DIR);
|
|
14392
|
+
fs26.mkdirSync(dir, { recursive: true });
|
|
14393
|
+
const file = path27.join(dir, `${id}@${sha.slice(0, SHA_LEN2)}.md`);
|
|
14394
|
+
fs26.writeFileSync(file, fileText(fm, o.statement));
|
|
14395
|
+
appendAudit([ev(c, "item.proposed", id, domain)], c.audit);
|
|
14396
|
+
return file;
|
|
14397
|
+
}
|
|
14398
|
+
function buildReviewPacket(c) {
|
|
14399
|
+
const { proposals, live } = loadProposals(c);
|
|
14400
|
+
return {
|
|
14401
|
+
kb: c.kb,
|
|
14402
|
+
autoApproveConfidence: reconcileConfig(c.cfg, c.kb).autoApproveConfidence,
|
|
14403
|
+
proposals: proposals.map((p) => ({
|
|
14404
|
+
item: p.key,
|
|
14405
|
+
id: p.rule.id,
|
|
14406
|
+
domain: p.rule.domain,
|
|
14407
|
+
modality: p.rule.modality,
|
|
14408
|
+
statement: p.rule.statement,
|
|
14409
|
+
priority: priorityOfRule(c, p.rule),
|
|
14410
|
+
sources: p.rule.sources,
|
|
14411
|
+
...p.live ? { replaces: { statement: p.live.statement, modality: p.live.modality } } : {},
|
|
14412
|
+
conflictsWith: (p.rule.conflictsWith ?? []).flatMap((id) => {
|
|
14413
|
+
const r = live.find((x) => x.id === id);
|
|
14414
|
+
return r ? [{ id, statement: r.statement, modality: r.modality, priority: priorityOfRule(c, r) }] : [];
|
|
14415
|
+
})
|
|
14416
|
+
})),
|
|
14417
|
+
stale: live.filter((r) => r.status === "stale").map((r) => ({ item: `stale:${r.id}`, id: r.id, modality: r.modality, statement: r.statement, sources: r.sources })),
|
|
14418
|
+
decided: live.filter((r) => r.decision).map((r) => ({ id: r.id, verdict: r.decision.verdict, at: r.decision.at })),
|
|
14419
|
+
open: readEscalations(kbDir(c.cfg, c.kb))
|
|
14420
|
+
};
|
|
14421
|
+
}
|
|
14422
|
+
var PRIORITY_RANK, SHA_LEN2, strong, escalationsFile, verdictsFile, needsYouCount, git, realGit, nowOf, fileText, readFm, ev, docRevOf;
|
|
14423
|
+
var init_review = __esm({
|
|
14424
|
+
"packages/core/dist/knowledge/review.js"() {
|
|
14425
|
+
init_audit();
|
|
14426
|
+
init_anchor();
|
|
14427
|
+
init_index_file();
|
|
14428
|
+
init_ingest();
|
|
14429
|
+
init_paths();
|
|
14430
|
+
init_schedule();
|
|
14431
|
+
init_store2();
|
|
14432
|
+
PRIORITY_RANK = { company: 3, project: 2, agent: 1 };
|
|
14433
|
+
SHA_LEN2 = 12;
|
|
14434
|
+
strong = (m) => m === "must" || m === "must-not";
|
|
14435
|
+
escalationsFile = (kbRoot) => path27.join(kbRoot, "escalations.json");
|
|
14436
|
+
verdictsFile = (kbRoot) => path27.join(kbRoot, "verdicts.jsonl");
|
|
14437
|
+
needsYouCount = (kbRoot) => readEscalations(kbRoot).filter((e) => e.needsYou).length;
|
|
14438
|
+
git = (root, args) => execFileSync7("git", ["-C", root, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
14439
|
+
realGit = {
|
|
14440
|
+
lastCommit(root, files) {
|
|
14441
|
+
try {
|
|
14442
|
+
const [rev, date] = git(root, ["log", "-1", "--format=%H%x09%cI", "--", ...files]).split(" ");
|
|
14443
|
+
return rev && date ? { rev, date } : null;
|
|
14444
|
+
} catch {
|
|
14445
|
+
return null;
|
|
14446
|
+
}
|
|
14447
|
+
},
|
|
14448
|
+
changedSince(root, files, since) {
|
|
14449
|
+
try {
|
|
14450
|
+
return git(root, ["log", "--since", since, "--format=%H", "--", ...files]) !== "";
|
|
14451
|
+
} catch {
|
|
14452
|
+
return false;
|
|
14453
|
+
}
|
|
14454
|
+
},
|
|
14455
|
+
docDate(file) {
|
|
14456
|
+
try {
|
|
14457
|
+
const d = git(path27.dirname(file), ["log", "-1", "--format=%cI", "--", path27.basename(file)]);
|
|
14458
|
+
if (d)
|
|
14459
|
+
return d;
|
|
14460
|
+
} catch {
|
|
14461
|
+
}
|
|
14462
|
+
try {
|
|
14463
|
+
return fs26.statSync(file).mtime.toISOString();
|
|
14464
|
+
} catch {
|
|
14465
|
+
return null;
|
|
14466
|
+
}
|
|
14467
|
+
}
|
|
14468
|
+
};
|
|
14469
|
+
nowOf = (c) => c.now?.() ?? /* @__PURE__ */ new Date();
|
|
14470
|
+
fileText = (fm, body) => matter4.stringify(body.trim() + "\n", JSON.parse(JSON.stringify(fm)));
|
|
14471
|
+
readFm = (file) => {
|
|
14472
|
+
const m = matter4(fs26.readFileSync(file, "utf8"));
|
|
14473
|
+
return { fm: m.data, body: m.content };
|
|
14474
|
+
};
|
|
14475
|
+
ev = (c, event, itemId, domain, extra = {}) => ({ kb: c.kb, level: "group", project: "", domain, itemId, event, ...extra });
|
|
14476
|
+
docRevOf = (r) => r.sources.map((s) => `${s.ref}@${s.sha.slice(0, SHA_LEN2)}`).join(",");
|
|
14477
|
+
}
|
|
14478
|
+
});
|
|
14479
|
+
|
|
14480
|
+
// packages/core/dist/knowledge/fullpass.js
|
|
14481
|
+
import crypto5 from "crypto";
|
|
14482
|
+
import fs27 from "fs";
|
|
14483
|
+
import path28 from "path";
|
|
14484
|
+
import matter5 from "gray-matter";
|
|
14485
|
+
function dedupPairs(rules) {
|
|
14486
|
+
const active = rules.filter((r) => r.status === "active").sort((x, y) => x.id.localeCompare(y.id));
|
|
14487
|
+
const out = [];
|
|
14488
|
+
for (let i = 0; i < active.length; i++) {
|
|
14489
|
+
for (let j = i + 1; j < active.length; j++) {
|
|
14490
|
+
const ta = terms(active[i]), shared2 = [...terms(active[j])].filter((t) => ta.has(t));
|
|
14491
|
+
const pa = new Set(Object.values(active[i].anchors ?? {}).flatMap((a) => a.paths ?? []));
|
|
14492
|
+
const sharedPaths = Object.values(active[j].anchors ?? {}).flatMap((a) => a.paths ?? []).filter((p) => pa.has(p));
|
|
14493
|
+
if (shared2.length >= 2 || sharedPaths.length)
|
|
14494
|
+
out.push({ a: active[i].id, b: active[j].id, shared: [...shared2, ...sharedPaths] });
|
|
14495
|
+
}
|
|
14496
|
+
}
|
|
14497
|
+
return out.slice(0, MAX_DEDUP_PAIRS);
|
|
14498
|
+
}
|
|
14499
|
+
function readAuditRecords(kb, since, dir = defaultAuditDir()) {
|
|
14500
|
+
if (!fs27.existsSync(dir))
|
|
14501
|
+
return [];
|
|
14502
|
+
const sinceMonth = since.toISOString().slice(0, 7);
|
|
14503
|
+
const out = [];
|
|
14504
|
+
for (const f of fs27.readdirSync(dir).filter((n) => n.endsWith(".jsonl") && n.slice(0, 7) >= sinceMonth).sort()) {
|
|
14505
|
+
for (const line2 of fs27.readFileSync(path28.join(dir, f), "utf8").split("\n")) {
|
|
14506
|
+
if (!line2.trim())
|
|
14507
|
+
continue;
|
|
14508
|
+
try {
|
|
14509
|
+
const e = JSON.parse(line2);
|
|
14510
|
+
if ((kb === void 0 || e.kb === kb) && Date.parse(e.ts) >= since.getTime())
|
|
14511
|
+
out.push(e);
|
|
14512
|
+
} catch {
|
|
14513
|
+
}
|
|
14514
|
+
}
|
|
14515
|
+
}
|
|
14516
|
+
return out;
|
|
14517
|
+
}
|
|
14518
|
+
function usageReview(rules, events) {
|
|
14519
|
+
const surfaced = {};
|
|
14520
|
+
const noise = {};
|
|
14521
|
+
const violated = /* @__PURE__ */ new Set();
|
|
14522
|
+
for (const e of events) {
|
|
14523
|
+
if (!e.itemId)
|
|
14524
|
+
continue;
|
|
14525
|
+
if (e.event === "item.surfaced")
|
|
14526
|
+
surfaced[e.itemId] = (surfaced[e.itemId] ?? 0) + 1;
|
|
14527
|
+
if (e.event === "item.outcome") {
|
|
14528
|
+
if (e.outcome === "noise")
|
|
14529
|
+
noise[e.itemId] = (noise[e.itemId] ?? 0) + 1;
|
|
14530
|
+
if (e.outcome === "violated")
|
|
14531
|
+
violated.add(e.itemId);
|
|
14532
|
+
}
|
|
14533
|
+
}
|
|
14534
|
+
const active = rules.filter((r) => r.status === "active");
|
|
14535
|
+
return {
|
|
14536
|
+
surfaced,
|
|
14537
|
+
neverSurfaced: active.filter((r) => !surfaced[r.id]).map((r) => r.id).sort(),
|
|
14538
|
+
noisy: Object.keys(noise).filter((id) => (surfaced[id] ?? 0) >= NOISY_MIN_SURFACED && noise[id] / surfaced[id] >= NOISY_RATE).sort(),
|
|
14539
|
+
violated: [...violated].sort()
|
|
14540
|
+
};
|
|
14541
|
+
}
|
|
14542
|
+
function domainCapExcess(rules, cap, surfaced) {
|
|
14543
|
+
const byDomain = /* @__PURE__ */ new Map();
|
|
14544
|
+
for (const r of rules.filter((x) => x.status === "active"))
|
|
14545
|
+
byDomain.set(r.domain, [...byDomain.get(r.domain) ?? [], r]);
|
|
14546
|
+
return [...byDomain].filter(([, rs]) => rs.length > cap).map(([domain, rs]) => ({
|
|
14547
|
+
domain,
|
|
14548
|
+
count: rs.length,
|
|
14549
|
+
cap,
|
|
14550
|
+
candidates: [...rs].sort((a, b) => (surfaced[a.id] ?? 0) - (surfaced[b.id] ?? 0) || a.id.localeCompare(b.id)).slice(0, rs.length - cap).map((r) => r.id)
|
|
14551
|
+
}));
|
|
14552
|
+
}
|
|
14553
|
+
function checkOverrides(cfg, kb) {
|
|
14554
|
+
const base = new Map(loadKbRules(cfg, kb).rules.map((r) => [r.id, r]));
|
|
14555
|
+
const out = [];
|
|
14556
|
+
for (const project of Object.keys(cfg.projects)) {
|
|
14557
|
+
if (!subscribedKbs(cfg, project).includes(kb))
|
|
14558
|
+
continue;
|
|
14559
|
+
const dir = projectRulesHome(cfg, project);
|
|
14560
|
+
if (!dir)
|
|
14561
|
+
continue;
|
|
14562
|
+
for (const o of loadRulesDir(dir, `project:${project}`).rules) {
|
|
14563
|
+
const b = base.get(o.id);
|
|
14564
|
+
if (!b)
|
|
14565
|
+
continue;
|
|
14566
|
+
const h = baseHash(b);
|
|
14567
|
+
if (!o.overridesBase) {
|
|
14568
|
+
const m = matter5(fs27.readFileSync(o.file, "utf8"));
|
|
14569
|
+
fs27.writeFileSync(o.file, matter5.stringify(m.content, { ...m.data, overridesBase: h }));
|
|
14570
|
+
continue;
|
|
14571
|
+
}
|
|
14572
|
+
out.push({ project, ruleId: o.id, file: o.file, baseHash: h, changed: o.overridesBase !== h });
|
|
14573
|
+
}
|
|
14574
|
+
}
|
|
14575
|
+
return out;
|
|
14576
|
+
}
|
|
14577
|
+
function runFullChecks(cfg, kb, o) {
|
|
14578
|
+
const rules = loadKbRules(cfg, kb).rules;
|
|
14579
|
+
const kbRoot = kbDir(cfg, kb);
|
|
14580
|
+
const usage2 = usageReview(rules, readAuditRecords(kb, new Date(o.now.getTime() - NEVER_SURFACED_DAYS * DAY_MS2), o.auditDir));
|
|
14581
|
+
const caps = domainCapExcess(rules, resolveKbConfig(cfg, kb).domainCap, usage2.surfaced);
|
|
14582
|
+
const overrides = checkOverrides(cfg, kb).filter((f) => f.changed);
|
|
14583
|
+
const base = { createdAt: o.now.toISOString(), needsYou: true };
|
|
14584
|
+
for (const c of caps) {
|
|
14585
|
+
addEscalation(kbRoot, { ...base, key: `domain-cap:${c.domain}`, kind: "domain-cap", reasons: ["domain cap"], explanation: `Domain '${c.domain}' has ${c.count} active rules (cap ${c.cap}). Archive candidates (least surfaced): ${c.candidates.join(", ")}.` });
|
|
14586
|
+
}
|
|
14587
|
+
for (const f of overrides) {
|
|
14588
|
+
addEscalation(kbRoot, { ...base, key: `override:${f.project}:${f.ruleId}`, kind: "override-base-changed", ruleId: f.ruleId, item: f.file, reasons: ["override.base-changed"], explanation: `Project ${f.project} overrides ${f.ruleId}, but the KB rule changed since the override was confirmed. Keep the override (approve) or drop it (reject).`, evidence: { docRev: f.baseHash, codeRev: "", files: [], project: f.project } });
|
|
14589
|
+
}
|
|
14590
|
+
return { usage: usage2, caps, overrides };
|
|
14591
|
+
}
|
|
14592
|
+
var DAY_MS2, NEVER_SURFACED_DAYS, NOISY_MIN_SURFACED, NOISY_RATE, MAX_DEDUP_PAIRS, terms, baseHash;
|
|
14593
|
+
var init_fullpass = __esm({
|
|
14594
|
+
"packages/core/dist/knowledge/fullpass.js"() {
|
|
14595
|
+
init_dist();
|
|
14596
|
+
init_audit();
|
|
14597
|
+
init_layers();
|
|
14598
|
+
init_review();
|
|
14599
|
+
init_paths();
|
|
14600
|
+
init_store2();
|
|
14601
|
+
DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
14602
|
+
NEVER_SURFACED_DAYS = 30;
|
|
14603
|
+
NOISY_MIN_SURFACED = 5;
|
|
14604
|
+
NOISY_RATE = 0.5;
|
|
14605
|
+
MAX_DEDUP_PAIRS = 50;
|
|
14606
|
+
terms = (r) => new Set([...r.triggers?.keywords ?? [], ...r.triggers?.expanded ?? []].map((s) => s.toLowerCase()));
|
|
14607
|
+
baseHash = (r) => crypto5.createHash("sha256").update(JSON.stringify([r.modality, r.statement, r.sources.map((s) => s.sha)])).digest("hex").slice(0, 12);
|
|
14608
|
+
}
|
|
14609
|
+
});
|
|
14610
|
+
|
|
14611
|
+
// packages/core/dist/knowledge/scores.js
|
|
14612
|
+
import fs28 from "fs";
|
|
14613
|
+
import path29 from "path";
|
|
14614
|
+
function tally(records, into = emptyTally()) {
|
|
14615
|
+
for (const e of records) {
|
|
14616
|
+
const domain = typeof e.domain === "string" && e.domain ? e.domain : "rules";
|
|
14617
|
+
const d = into.domains[domain] ??= { writes: 0, reads: 0 };
|
|
14618
|
+
if (WRITE_EVENTS.has(e.event ?? ""))
|
|
14619
|
+
d.writes++;
|
|
14620
|
+
if (READ_EVENTS.has(e.event ?? ""))
|
|
14621
|
+
d.reads++;
|
|
14622
|
+
if (!e.itemId)
|
|
14623
|
+
continue;
|
|
14624
|
+
const c = into.items[`${domain}|${e.itemId}`] ??= { ...zero(), domain, itemId: e.itemId };
|
|
14625
|
+
switch (e.event) {
|
|
14626
|
+
case "item.surfaced": {
|
|
14627
|
+
c.surfaced++;
|
|
14628
|
+
c.chars += Number(e.chars) || 0;
|
|
14629
|
+
c.lastSurfaced = later(c.lastSurfaced, e.ts);
|
|
14630
|
+
const agent = typeof e.agent === "string" ? e.agent : "unknown";
|
|
14631
|
+
into.agents[agent] = (into.agents[agent] ?? 0) + 1;
|
|
14632
|
+
const tier = typeof e.tier === "string" ? e.tier : typeof e.trigger === "string" ? e.trigger : "unknown";
|
|
14633
|
+
into.tiers[tier] = (into.tiers[tier] ?? 0) + 1;
|
|
14634
|
+
break;
|
|
14635
|
+
}
|
|
14636
|
+
case "item.suppressed":
|
|
14637
|
+
c.suppressed++;
|
|
14638
|
+
break;
|
|
14639
|
+
case "item.searched":
|
|
14640
|
+
c.searched++;
|
|
14641
|
+
break;
|
|
14642
|
+
case "item.shown":
|
|
14643
|
+
c.shown++;
|
|
14644
|
+
break;
|
|
14645
|
+
case "item.cited":
|
|
14646
|
+
c.cited++;
|
|
14647
|
+
c.lastUsed = later(c.lastUsed, e.ts);
|
|
14648
|
+
break;
|
|
14649
|
+
case "item.outcome":
|
|
14650
|
+
if (OUTCOMES.has(String(e.outcome)))
|
|
14651
|
+
c[e.outcome]++;
|
|
14652
|
+
break;
|
|
14653
|
+
}
|
|
14654
|
+
}
|
|
14655
|
+
return into;
|
|
14656
|
+
}
|
|
14657
|
+
function parseLines(file) {
|
|
14658
|
+
const out = [];
|
|
14659
|
+
for (const line2 of fs28.readFileSync(file, "utf8").split("\n")) {
|
|
14660
|
+
if (!line2.trim())
|
|
14661
|
+
continue;
|
|
14662
|
+
try {
|
|
14663
|
+
out.push(JSON.parse(line2));
|
|
14664
|
+
} catch {
|
|
14665
|
+
}
|
|
14666
|
+
}
|
|
14667
|
+
return out;
|
|
14668
|
+
}
|
|
14669
|
+
function compactAudit(o) {
|
|
14670
|
+
const dir = o.dir ?? defaultAuditDir();
|
|
14671
|
+
if (!fs28.existsSync(dir))
|
|
14672
|
+
return [];
|
|
14673
|
+
const cutoffMonth = new Date(o.now.getTime() - SCORE_WINDOW_DAYS * DAY_MS3).toISOString().slice(0, 7);
|
|
14674
|
+
const done = [];
|
|
14675
|
+
for (const f of fs28.readdirSync(dir).sort()) {
|
|
14676
|
+
const m = MONTH_FILE_RE.exec(f);
|
|
14677
|
+
if (!m || m[1] >= cutoffMonth)
|
|
14678
|
+
continue;
|
|
14679
|
+
const [, month, machine] = m;
|
|
14680
|
+
const byKb = /* @__PURE__ */ new Map();
|
|
14681
|
+
for (const r of parseLines(path29.join(dir, f)))
|
|
14682
|
+
byKb.set(r.kb ?? "", [...byKb.get(r.kb ?? "") ?? [], r]);
|
|
14683
|
+
const statsPath = path29.join(dir, statsFileName(month, machine));
|
|
14684
|
+
const prior = fs28.existsSync(statsPath) ? JSON.parse(fs28.readFileSync(statsPath, "utf8")) : null;
|
|
14685
|
+
const kbs = prior?.kbs ?? {};
|
|
14686
|
+
for (const [kb, recs] of byKb)
|
|
14687
|
+
kbs[kb] = tally(recs, kbs[kb]);
|
|
14688
|
+
fs28.writeFileSync(`${statsPath}.tmp`, JSON.stringify({ month, machine, kbs }));
|
|
14689
|
+
fs28.renameSync(`${statsPath}.tmp`, statsPath);
|
|
14690
|
+
fs28.unlinkSync(path29.join(dir, f));
|
|
14691
|
+
done.push(month);
|
|
14692
|
+
}
|
|
14693
|
+
return done;
|
|
14694
|
+
}
|
|
14695
|
+
function readCompacted(kb, dir) {
|
|
14696
|
+
const t = emptyTally();
|
|
14697
|
+
if (!fs28.existsSync(dir))
|
|
14698
|
+
return t;
|
|
14699
|
+
for (const f of fs28.readdirSync(dir).filter((n) => n.endsWith(".stats.json"))) {
|
|
14700
|
+
try {
|
|
14701
|
+
const k = JSON.parse(fs28.readFileSync(path29.join(dir, f), "utf8")).kbs[kb];
|
|
14702
|
+
if (k)
|
|
14703
|
+
mergeTally(t, k);
|
|
14704
|
+
} catch {
|
|
14705
|
+
}
|
|
14706
|
+
}
|
|
14707
|
+
return t;
|
|
14708
|
+
}
|
|
14709
|
+
function mergeTally(into, from) {
|
|
14710
|
+
for (const [k, c] of Object.entries(from.items)) {
|
|
14711
|
+
const t = into.items[k] ??= { ...zero(), domain: c.domain, itemId: c.itemId };
|
|
14712
|
+
t.lastSurfaced = later(t.lastSurfaced, c.lastSurfaced);
|
|
14713
|
+
t.lastUsed = later(t.lastUsed, c.lastUsed);
|
|
14714
|
+
for (const f of Object.keys(zero()))
|
|
14715
|
+
if (typeof c[f] === "number")
|
|
14716
|
+
t[f] += c[f];
|
|
14717
|
+
}
|
|
14718
|
+
for (const [k, v2] of Object.entries(from.domains)) {
|
|
14719
|
+
const d = into.domains[k] ??= { writes: 0, reads: 0 };
|
|
14720
|
+
d.writes += v2.writes;
|
|
14721
|
+
d.reads += v2.reads;
|
|
14722
|
+
}
|
|
14723
|
+
for (const [k, v2] of Object.entries(from.agents))
|
|
14724
|
+
into.agents[k] = (into.agents[k] ?? 0) + v2;
|
|
14725
|
+
for (const [k, v2] of Object.entries(from.tiers))
|
|
14726
|
+
into.tiers[k] = (into.tiers[k] ?? 0) + v2;
|
|
14727
|
+
}
|
|
14728
|
+
function computeScores(i) {
|
|
14729
|
+
const since = i.now.getTime() - SCORE_WINDOW_DAYS * DAY_MS3;
|
|
14730
|
+
const win = tally(i.records.filter((r) => Date.parse(r.ts) >= since));
|
|
14731
|
+
const life = tally(i.records, i.compacted ? structuredClone(i.compacted) : void 0);
|
|
14732
|
+
const knownActive = new Set((i.known ?? []).filter((k) => k.status === "active").map((k) => `${k.domain}|${k.id}`));
|
|
14733
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(win.items), ...Object.keys(life.items), ...knownActive]);
|
|
14734
|
+
const items = [...keys].sort().map((key) => {
|
|
14735
|
+
const [domain, itemId] = [key.slice(0, key.indexOf("|")), key.slice(key.indexOf("|") + 1)];
|
|
14736
|
+
const w = win.items[key] ?? { ...zero(), domain, itemId };
|
|
14737
|
+
const l = life.items[key];
|
|
14738
|
+
return {
|
|
14739
|
+
domain,
|
|
14740
|
+
itemId,
|
|
14741
|
+
surfaced: w.surfaced,
|
|
14742
|
+
suppressed: w.suppressed,
|
|
14743
|
+
searched: w.searched,
|
|
14744
|
+
shown: w.shown,
|
|
14745
|
+
cited: w.cited,
|
|
14746
|
+
followed: w.followed,
|
|
14747
|
+
violated: w.violated,
|
|
14748
|
+
noise: w.noise,
|
|
14749
|
+
wrong: w.wrong,
|
|
14750
|
+
fireRate: w.surfaced,
|
|
14751
|
+
pullRate: w.shown,
|
|
14752
|
+
useRate: ratio(w.cited, w.surfaced),
|
|
14753
|
+
noiseRate: ratio(w.noise, w.surfaced),
|
|
14754
|
+
violationRate: ratio(w.violated, w.surfaced),
|
|
14755
|
+
lastSurfaced: l?.lastSurfaced,
|
|
14756
|
+
lastUsed: l?.lastUsed,
|
|
14757
|
+
neverFired30d: w.surfaced === 0 && (knownActive.has(key) || (l?.surfaced ?? 0) > 0),
|
|
14758
|
+
lifetimeSurfaced: l?.surfaced ?? 0
|
|
14759
|
+
};
|
|
14760
|
+
});
|
|
14761
|
+
const domainNames = /* @__PURE__ */ new Set([...Object.keys(win.domains), ...items.map((x) => x.domain)]);
|
|
14762
|
+
const domains = [...domainNames].sort().map((domain) => {
|
|
14763
|
+
const d = win.domains[domain] ?? { writes: 0, reads: 0 };
|
|
14764
|
+
const rows = items.filter((x) => x.domain === domain);
|
|
14765
|
+
const chars = Object.values(win.items).filter((x) => x.domain === domain).reduce((a, x) => a + x.chars, 0);
|
|
14766
|
+
const cited = rows.reduce((a, x) => a + x.cited, 0);
|
|
14767
|
+
return {
|
|
14768
|
+
domain,
|
|
14769
|
+
writesPerMonth: d.writes,
|
|
14770
|
+
readsPerMonth: d.reads,
|
|
14771
|
+
deadDomain: d.writes === 0 && d.reads === 0,
|
|
14772
|
+
charsPerCitation: cited ? Math.round(chars / cited) : null,
|
|
14773
|
+
surfaced: rows.reduce((a, x) => a + x.surfaced, 0),
|
|
14774
|
+
cited
|
|
14775
|
+
};
|
|
14776
|
+
});
|
|
14777
|
+
const operator = (i.verdicts ?? []).filter((v2) => v2.kind === "operator");
|
|
14778
|
+
const approved = operator.filter((v2) => v2.resolution?.kind === "approve").length;
|
|
14779
|
+
return {
|
|
14780
|
+
kb: i.kb,
|
|
14781
|
+
generatedAt: i.now.toISOString(),
|
|
14782
|
+
windowDays: SCORE_WINDOW_DAYS,
|
|
14783
|
+
items,
|
|
14784
|
+
domains,
|
|
14785
|
+
perAgentCoverage: win.agents,
|
|
14786
|
+
tierContribution: win.tiers,
|
|
14787
|
+
reviewerAgreement: { resolved: operator.length, approved, rate: ratio(approved, operator.length) }
|
|
14788
|
+
};
|
|
14789
|
+
}
|
|
14790
|
+
function scoreKb(o) {
|
|
14791
|
+
const dir = o.auditDir ?? defaultAuditDir();
|
|
14792
|
+
const records = [];
|
|
14793
|
+
if (fs28.existsSync(dir)) {
|
|
14794
|
+
for (const f of fs28.readdirSync(dir).filter((n) => MONTH_FILE_RE.test(n)).sort()) {
|
|
14795
|
+
records.push(...parseLines(path29.join(dir, f)).filter((r) => r.kb === o.kb));
|
|
14796
|
+
}
|
|
14797
|
+
}
|
|
14798
|
+
let verdicts = [];
|
|
14799
|
+
try {
|
|
14800
|
+
verdicts = fs28.readFileSync(path29.join(o.kbRoot, "verdicts.jsonl"), "utf8").split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
14801
|
+
} catch {
|
|
14802
|
+
}
|
|
14803
|
+
return computeScores({ kb: o.kb, records, now: o.now, known: o.known, compacted: readCompacted(o.kb, dir), verdicts });
|
|
14804
|
+
}
|
|
14805
|
+
function writeScores(kbRoot, s) {
|
|
14806
|
+
fs28.mkdirSync(kbRoot, { recursive: true });
|
|
14807
|
+
fs28.writeFileSync(scoresFile(kbRoot), JSON.stringify(s, null, 2) + "\n");
|
|
14808
|
+
}
|
|
14809
|
+
function renderScoresSection(s) {
|
|
14810
|
+
const pct = (x) => x === null ? "n/a" : `${Math.round(x * 100)}%`;
|
|
14811
|
+
const used = s.items.filter((x) => x.surfaced).sort((a, b) => b.surfaced - a.surfaced || a.itemId.localeCompare(b.itemId)).slice(0, 10);
|
|
14812
|
+
const never = s.items.filter((x) => x.neverFired30d).map((x) => x.itemId);
|
|
14813
|
+
return [
|
|
14814
|
+
"",
|
|
14815
|
+
`## Scores (${s.windowDays} days)`,
|
|
14816
|
+
"",
|
|
14817
|
+
...used.length ? [
|
|
14818
|
+
"| Item | Surfaced | Pulled | Cited | Use | Noise | Violated |",
|
|
14819
|
+
"|---|---|---|---|---|---|---|",
|
|
14820
|
+
...used.map((x) => `| ${x.itemId} | ${x.surfaced} | ${x.shown} | ${x.cited} | ${pct(x.useRate)} | ${pct(x.noiseRate)} | ${pct(x.violationRate)} |`)
|
|
14821
|
+
] : ["No items surfaced."],
|
|
14822
|
+
"",
|
|
14823
|
+
`- Never fired (${never.length}): ${never.slice(0, 20).join(", ") || "none"}${never.length > 20 ? ", \u2026" : ""}`,
|
|
14824
|
+
`- Tier contribution: ${Object.entries(s.tierContribution).map(([k, v2]) => `${k} ${v2}`).join(", ") || "none"}`,
|
|
14825
|
+
`- Per-agent coverage (surfaced): ${Object.entries(s.perAgentCoverage).map(([k, v2]) => `${k} ${v2}`).join(", ") || "none"}`,
|
|
14826
|
+
`- Reviewer agreement: ${pct(s.reviewerAgreement.rate)} (${s.reviewerAgreement.approved}/${s.reviewerAgreement.resolved} operator resolutions approved)`,
|
|
14827
|
+
"",
|
|
14828
|
+
"| Domain | Writes/mo | Reads/mo | Chars per citation | Alarm |",
|
|
14829
|
+
"|---|---|---|---|---|",
|
|
14830
|
+
...s.domains.map((d) => `| ${d.domain} | ${d.writesPerMonth} | ${d.readsPerMonth} | ${d.charsPerCitation ?? "n/a"} | ${d.deadDomain ? "DEAD (no activity 30d)" : ""} |`)
|
|
14831
|
+
];
|
|
14832
|
+
}
|
|
14833
|
+
var DAY_MS3, SCORE_WINDOW_DAYS, WRITE_EVENTS, READ_EVENTS, OUTCOMES, zero, later, emptyTally, MONTH_FILE_RE, statsFileName, ratio, scoresFile;
|
|
14834
|
+
var init_scores = __esm({
|
|
14835
|
+
"packages/core/dist/knowledge/scores.js"() {
|
|
14836
|
+
init_audit();
|
|
14837
|
+
DAY_MS3 = 24 * 60 * 60 * 1e3;
|
|
14838
|
+
SCORE_WINDOW_DAYS = 30;
|
|
14839
|
+
WRITE_EVENTS = /* @__PURE__ */ new Set(["item.proposed", "item.approved", "item.rejected", "item.applied", "item.promoted", "item.superseded", "item.archived"]);
|
|
14840
|
+
READ_EVENTS = /* @__PURE__ */ new Set(["item.surfaced", "item.shown", "item.searched"]);
|
|
14841
|
+
OUTCOMES = /* @__PURE__ */ new Set(["followed", "violated", "noise", "wrong"]);
|
|
14842
|
+
zero = () => ({ surfaced: 0, suppressed: 0, searched: 0, shown: 0, cited: 0, followed: 0, violated: 0, noise: 0, wrong: 0, chars: 0 });
|
|
14843
|
+
later = (a, b) => a && b ? a > b ? a : b : a ?? b;
|
|
14844
|
+
emptyTally = () => ({ items: {}, domains: {}, agents: {}, tiers: {} });
|
|
14845
|
+
MONTH_FILE_RE = /^(\d{4}-\d{2})\.(.+)\.jsonl$/;
|
|
14846
|
+
statsFileName = (month, machine) => `${month}.${machine}.stats.json`;
|
|
14847
|
+
ratio = (n, d) => d ? Math.round(n / d * 1e3) / 1e3 : null;
|
|
14848
|
+
scoresFile = (kbRoot) => path29.join(kbRoot, "scores.json");
|
|
14849
|
+
}
|
|
14850
|
+
});
|
|
14851
|
+
|
|
14852
|
+
// packages/core/dist/knowledge/report.js
|
|
14853
|
+
import fs29 from "fs";
|
|
14854
|
+
import path30 from "path";
|
|
14855
|
+
function renderReport(cfg, kb, o) {
|
|
14856
|
+
const { rules } = loadKbRules(cfg, kb, { includeProposed: true });
|
|
14857
|
+
const count = (s) => rules.filter((r) => r.status === s).length;
|
|
14858
|
+
const esc = readEscalations(kbDir(cfg, kb));
|
|
14859
|
+
const need = esc.filter((e) => e.needsYou);
|
|
14860
|
+
const L = [
|
|
14861
|
+
`# ${kb} knowledge base: reconcile report`,
|
|
14862
|
+
"",
|
|
14863
|
+
`${o.pass} pass, ${o.now.toISOString()}`,
|
|
14864
|
+
"",
|
|
14865
|
+
`Rules: ${count("active")} active, ${count("proposed")} proposed, ${count("stale")} stale, ${count("retired")} retired.`,
|
|
14866
|
+
"",
|
|
14867
|
+
`## Needs you (${need.length})`,
|
|
14868
|
+
"",
|
|
14869
|
+
...need.length ? need.map((e) => `- **${e.key}** [${e.kind}] ${e.reasons.join(", ")}: ${e.explanation}`) : ["Nothing."],
|
|
14870
|
+
"",
|
|
14871
|
+
"Answer with `squadrant knowledge review " + kb + "`."
|
|
14872
|
+
];
|
|
14873
|
+
const info = esc.filter((e) => !e.needsYou);
|
|
14874
|
+
if (info.length)
|
|
14875
|
+
L.push("", `## For information (${info.length})`, "", ...info.map((e) => `- **${e.key}**: ${e.explanation}`));
|
|
14876
|
+
if (o.full) {
|
|
14877
|
+
const { usage: usage2, caps } = o.full;
|
|
14878
|
+
L.push("", "## Usage (30 days)", "", `- Surfaced: ${Object.values(usage2.surfaced).reduce((a, b) => a + b, 0)} times across ${Object.keys(usage2.surfaced).length} rules.`, `- Never surfaced (re-check triggers): ${usage2.neverSurfaced.join(", ") || "none"}`, `- Noisy (narrow triggers): ${usage2.noisy.join(", ") || "none"}`, `- Violation feedback (reword): ${usage2.violated.join(", ") || "none"}`, `- Domains over cap: ${caps.map((c) => `${c.domain} (${c.count}/${c.cap})`).join(", ") || "none"}`);
|
|
14879
|
+
}
|
|
14880
|
+
if (o.scores)
|
|
14881
|
+
L.push(...renderScoresSection(o.scores));
|
|
14882
|
+
return L.join("\n") + "\n";
|
|
14883
|
+
}
|
|
14884
|
+
function writeReport(cfg, kb, o) {
|
|
14885
|
+
const file = path30.join(kbDir(cfg, kb), "REPORT.md");
|
|
14886
|
+
fs29.writeFileSync(file, renderReport(cfg, kb, o));
|
|
14887
|
+
return file;
|
|
14888
|
+
}
|
|
14889
|
+
var init_report = __esm({
|
|
14890
|
+
"packages/core/dist/knowledge/report.js"() {
|
|
14891
|
+
init_layers();
|
|
14892
|
+
init_paths();
|
|
14893
|
+
init_review();
|
|
14894
|
+
init_scores();
|
|
14895
|
+
}
|
|
14896
|
+
});
|
|
14897
|
+
|
|
14898
|
+
// packages/core/dist/knowledge/reconcile.js
|
|
14899
|
+
function finishMessage(kb, needsYou) {
|
|
14900
|
+
return needsYou ? `${kb} KB: ${needsYou} item${needsYou === 1 ? "" : "s"} need you (squadrant knowledge review ${kb})` : `${kb} KB: reconcile finished, nothing needs you`;
|
|
14901
|
+
}
|
|
14902
|
+
async function finishReconcile(kb, d) {
|
|
14903
|
+
const now = d.now?.() ?? /* @__PURE__ */ new Date();
|
|
14904
|
+
const pass = completePass(kb, { ...d, now: () => now });
|
|
14905
|
+
let full;
|
|
14906
|
+
if (pass === "full")
|
|
14907
|
+
full = runFullChecks(d.cfg, kb, { now, auditDir: d.auditDir });
|
|
14908
|
+
if (pass === "full")
|
|
14909
|
+
compactAudit({ now, dir: d.auditDir });
|
|
14910
|
+
const scores = scoreKb({ kb, now, auditDir: d.auditDir, kbRoot: kbDir(d.cfg, kb), known: loadKbRules(d.cfg, kb).rules.map((r) => ({ id: r.id, domain: "rules", status: r.status })) });
|
|
14911
|
+
writeScores(kbDir(d.cfg, kb), scores);
|
|
14912
|
+
const report = writeReport(d.cfg, kb, { pass, now, full, scores });
|
|
14913
|
+
const needsYou = needsYouCount(kbDir(d.cfg, kb));
|
|
14914
|
+
const home = resolveKbConfig(d.cfg, kb).homeProject;
|
|
14915
|
+
if (home)
|
|
14916
|
+
await d.enqueue(home, finishMessage(kb, needsYou));
|
|
14917
|
+
return { pass, report, needsYou };
|
|
14918
|
+
}
|
|
14919
|
+
var init_reconcile = __esm({
|
|
14920
|
+
"packages/core/dist/knowledge/reconcile.js"() {
|
|
14921
|
+
init_dist();
|
|
14922
|
+
init_scores();
|
|
14923
|
+
init_layers();
|
|
14924
|
+
init_fullpass();
|
|
14925
|
+
init_paths();
|
|
14926
|
+
init_report();
|
|
14927
|
+
init_review();
|
|
14928
|
+
init_schedule();
|
|
14929
|
+
}
|
|
14930
|
+
});
|
|
14931
|
+
|
|
14932
|
+
// packages/core/dist/knowledge/cite.js
|
|
14933
|
+
function scanAndLogCitations(i) {
|
|
14934
|
+
const at = i.at ?? i.sink?.now?.() ?? /* @__PURE__ */ new Date();
|
|
14935
|
+
const since = new Date(at.getTime() - (i.lookbackDays ?? CITE_LOOKBACK_DAYS) * DAY_MS4);
|
|
14936
|
+
const dir = i.sink?.dir ?? defaultAuditDir();
|
|
14937
|
+
const delivered = /* @__PURE__ */ new Map();
|
|
14938
|
+
const already = /* @__PURE__ */ new Set();
|
|
14939
|
+
for (const r of readAuditRecords(void 0, since, dir)) {
|
|
14940
|
+
if (!r.itemId || r.project !== i.project)
|
|
14941
|
+
continue;
|
|
14942
|
+
if (r.event === "item.cited" && r.ref === i.ref)
|
|
14943
|
+
already.add(r.itemId);
|
|
14944
|
+
if (!(r.event === "item.surfaced" || r.event === "item.shown") || Date.parse(r.ts) > at.getTime())
|
|
14945
|
+
continue;
|
|
14946
|
+
if (i.session && r.session && r.session !== i.session)
|
|
14947
|
+
continue;
|
|
14948
|
+
delivered.set(r.itemId, r);
|
|
14949
|
+
}
|
|
14950
|
+
const hits = [...delivered].filter(([id]) => !already.has(id) && mentionsId(i.text, id));
|
|
14951
|
+
appendAudit(hits.map(([itemId, r]) => ({
|
|
14952
|
+
kb: String(r.kb ?? ""),
|
|
14953
|
+
level: String(r.level ?? "project"),
|
|
14954
|
+
project: i.project,
|
|
14955
|
+
domain: String(r.domain ?? "rules"),
|
|
14956
|
+
itemId,
|
|
14957
|
+
event: "item.cited",
|
|
14958
|
+
ref: i.ref,
|
|
14959
|
+
...i.agent ? { agent: i.agent } : {},
|
|
14960
|
+
...i.session ? { session: i.session } : {}
|
|
14961
|
+
})), i.sink);
|
|
14962
|
+
return hits.map(([id]) => id);
|
|
14963
|
+
}
|
|
14964
|
+
function readGitMessages(repo, since, run) {
|
|
14965
|
+
const out = run(["-C", repo, "log", `--since=${since.toISOString()}`, "--format=%x1e%H%x1f%cI%x1f%B"]);
|
|
14966
|
+
return out.split("").filter((c) => c.trim()).map((c) => {
|
|
14967
|
+
const [sha, at, ...rest] = c.split("");
|
|
14968
|
+
return { sha: sha.trim(), at: new Date(at), text: rest.join("") };
|
|
14969
|
+
});
|
|
14970
|
+
}
|
|
14971
|
+
var DAY_MS4, CITE_LOOKBACK_DAYS, escapeRe2, mentionsId;
|
|
14972
|
+
var init_cite = __esm({
|
|
14973
|
+
"packages/core/dist/knowledge/cite.js"() {
|
|
14974
|
+
init_audit();
|
|
14975
|
+
init_fullpass();
|
|
14976
|
+
DAY_MS4 = 24 * 60 * 60 * 1e3;
|
|
14977
|
+
CITE_LOOKBACK_DAYS = 7;
|
|
14978
|
+
escapeRe2 = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14979
|
+
mentionsId = (text, id) => new RegExp(`(?<![\\w.-])${escapeRe2(id)}(?![\\w-])`).test(text);
|
|
14980
|
+
}
|
|
14981
|
+
});
|
|
14982
|
+
|
|
14983
|
+
// packages/core/dist/knowledge/index.js
|
|
14984
|
+
var init_knowledge2 = __esm({
|
|
14985
|
+
"packages/core/dist/knowledge/index.js"() {
|
|
14986
|
+
init_paths();
|
|
14987
|
+
init_store2();
|
|
14988
|
+
init_layers();
|
|
14989
|
+
init_index_file();
|
|
14990
|
+
init_search();
|
|
14991
|
+
init_sources();
|
|
14992
|
+
init_inject();
|
|
14993
|
+
init_audit();
|
|
14994
|
+
init_migrate();
|
|
14995
|
+
init_verify();
|
|
14996
|
+
init_ingest();
|
|
14997
|
+
init_anchor();
|
|
14998
|
+
init_extract();
|
|
14999
|
+
init_match();
|
|
15000
|
+
init_schedule();
|
|
15001
|
+
init_review();
|
|
15002
|
+
init_fullpass();
|
|
15003
|
+
init_report();
|
|
15004
|
+
init_reconcile();
|
|
15005
|
+
init_scores();
|
|
15006
|
+
init_cite();
|
|
15007
|
+
}
|
|
15008
|
+
});
|
|
15009
|
+
|
|
15010
|
+
// packages/core/dist/index.js
|
|
15011
|
+
var dist_exports = {};
|
|
15012
|
+
__export(dist_exports, {
|
|
15013
|
+
AGENT_BINS: () => AGENT_BINS,
|
|
15014
|
+
ANTHROPIC_ONLY_REQUEST_FIELDS: () => ANTHROPIC_ONLY_REQUEST_FIELDS,
|
|
15015
|
+
AUDIT_QUERY_CHARS: () => AUDIT_QUERY_CHARS,
|
|
15016
|
+
BOT_COMMANDS: () => BOT_COMMANDS,
|
|
15017
|
+
CAPTAIN_RECEIVED_ACK: () => CAPTAIN_RECEIVED_ACK,
|
|
15018
|
+
CC_SOCKS_DIR: () => CC_SOCKS_DIR,
|
|
15019
|
+
CHARS_PER_TOKEN: () => CHARS_PER_TOKEN,
|
|
15020
|
+
CITE_LOOKBACK_DAYS: () => CITE_LOOKBACK_DAYS,
|
|
15021
|
+
CITE_NOTE: () => CITE_NOTE,
|
|
15022
|
+
CMUX_PRESERVE_CLAUDE_AUTH_ENV: () => CMUX_PRESERVE_CLAUDE_AUTH_ENV,
|
|
15023
|
+
CONFLICT_409_SELF_HEAL_MS: () => CONFLICT_409_SELF_HEAL_MS,
|
|
15024
|
+
CONVERTED_DIR: () => CONVERTED_DIR,
|
|
15025
|
+
CREW_GONE_MS: () => CREW_GONE_MS,
|
|
15026
|
+
CREW_STALE_MS: () => CREW_STALE_MS,
|
|
15027
|
+
CaptainDelivery: () => CaptainDelivery,
|
|
15028
|
+
DEFAULT_BLOCKED_RENUDGE_MS: () => DEFAULT_BLOCKED_RENUDGE_MS,
|
|
15029
|
+
DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS: () => DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS,
|
|
15030
|
+
DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS: () => DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS,
|
|
15031
|
+
DEFAULT_GATE_DENY_RULES: () => DEFAULT_GATE_DENY_RULES,
|
|
15032
|
+
DEFAULT_GATE_TOOLS: () => DEFAULT_GATE_TOOLS,
|
|
15033
|
+
DEFAULT_MAX_SECTIONS_PER_PASS: () => DEFAULT_MAX_SECTIONS_PER_PASS,
|
|
15034
|
+
DEFAULT_SEARCH_STATUSES: () => DEFAULT_SEARCH_STATUSES,
|
|
15035
|
+
DEFAULT_TASK_TIMEOUT_MS: () => DEFAULT_TASK_TIMEOUT_MS,
|
|
15036
|
+
DOCLING_DROP_THRESHOLD: () => DOCLING_DROP_THRESHOLD,
|
|
15037
|
+
DeferDelivery: () => DeferDelivery,
|
|
15038
|
+
FIRST_TURN_INLINE_MAX_BYTES: () => FIRST_TURN_INLINE_MAX_BYTES,
|
|
15039
|
+
FactLog: () => FactLog,
|
|
15040
|
+
GROUP_DISPATCH_WARMUP_POLL_MS: () => GROUP_DISPATCH_WARMUP_POLL_MS,
|
|
15041
|
+
GROUP_DISPATCH_WARMUP_TIMEOUT_MS: () => GROUP_DISPATCH_WARMUP_TIMEOUT_MS,
|
|
15042
|
+
GateDecisionCache: () => GateDecisionCache,
|
|
15043
|
+
IDLE_DEBOUNCE_MS: () => IDLE_DEBOUNCE_MS,
|
|
15044
|
+
INJECT_BUDGET_MS: () => INJECT_BUDGET_MS,
|
|
15045
|
+
INJECT_MAX_CHARS: () => INJECT_MAX_CHARS,
|
|
15046
|
+
INJECT_MAX_RULES: () => INJECT_MAX_RULES,
|
|
15047
|
+
LABEL: () => LABEL,
|
|
15048
|
+
LivenessRegistry: () => LivenessRegistry,
|
|
15049
|
+
MAX_DEDUP_PAIRS: () => MAX_DEDUP_PAIRS,
|
|
15050
|
+
MONITOR_STALL_BUDGET_MS: () => MONITOR_STALL_BUDGET_MS,
|
|
15051
|
+
NEVER_SURFACED_DAYS: () => NEVER_SURFACED_DAYS,
|
|
15052
|
+
NOISY_MIN_SURFACED: () => NOISY_MIN_SURFACED,
|
|
15053
|
+
NOISY_RATE: () => NOISY_RATE,
|
|
15054
|
+
OPERATOR_INITIATED_COMMANDS: () => OPERATOR_INITIATED_COMMANDS,
|
|
15055
|
+
PANE_IDLE_AFTER_MS: () => PANE_IDLE_AFTER_MS,
|
|
15056
|
+
PASS_THROUGH: () => PASS_THROUGH,
|
|
15057
|
+
PROBE_QUIET_MS: () => PROBE_QUIET_MS,
|
|
15058
|
+
PROMPT_MIN_SCORE: () => PROMPT_MIN_SCORE,
|
|
15059
|
+
PROMPT_RULE_CAP: () => PROMPT_RULE_CAP,
|
|
15060
|
+
PROMPT_SINGLE_TERM_MIN_SCORE: () => PROMPT_SINGLE_TERM_MIN_SCORE,
|
|
15061
|
+
PROPOSED_DIR: () => PROPOSED_DIR,
|
|
15062
|
+
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
15063
|
+
READ_ONLY_CREW_SUBCOMMANDS: () => READ_ONLY_CREW_SUBCOMMANDS,
|
|
15064
|
+
READ_ONLY_TOP_LEVEL_COMMANDS: () => READ_ONLY_TOP_LEVEL_COMMANDS,
|
|
15065
|
+
ROUTER_READY_ATTEMPTS: () => ROUTER_READY_ATTEMPTS,
|
|
15066
|
+
ROUTER_READY_DELAY_MS: () => ROUTER_READY_DELAY_MS,
|
|
15067
|
+
RUNNING_STALE_MS: () => RUNNING_STALE_MS,
|
|
15068
|
+
SCORE_WINDOW_DAYS: () => SCORE_WINDOW_DAYS,
|
|
15069
|
+
SERVER_TOOL_TYPE_RE: () => SERVER_TOOL_TYPE_RE,
|
|
15070
|
+
SESSION_RULE_CAP: () => SESSION_RULE_CAP,
|
|
15071
|
+
SIDE_SESSION_ENV: () => SIDE_SESSION_ENV,
|
|
15072
|
+
SOURCES_TEMPLATE: () => SOURCES_TEMPLATE,
|
|
12987
15073
|
STALE_THRESHOLD_MS: () => STALE_THRESHOLD_MS,
|
|
12988
15074
|
STUCK_ALERT_TEXT: () => STUCK_ALERT_TEXT,
|
|
12989
15075
|
TERMINAL_RECORD_KEEP_PER_PROJECT: () => TERMINAL_RECORD_KEEP_PER_PROJECT,
|
|
@@ -12996,12 +15082,22 @@ __export(dist_exports, {
|
|
|
12996
15082
|
WORK_ITEM_TTL_MS: () => WORK_ITEM_TTL_MS,
|
|
12997
15083
|
WRITABLE_CONFIG_KEYS: () => WRITABLE_CONFIG_KEYS,
|
|
12998
15084
|
_resetRestartInFlightForTest: () => _resetRestartInFlightForTest,
|
|
15085
|
+
acquirePass: () => acquirePass,
|
|
15086
|
+
addEscalation: () => addEscalation,
|
|
12999
15087
|
ageText: () => ageText,
|
|
15088
|
+
anchorTerms: () => anchorTerms,
|
|
13000
15089
|
anthropicError: () => anthropicError,
|
|
15090
|
+
appendAudit: () => appendAudit,
|
|
13001
15091
|
appendCaptainMessage: () => appendCaptainMessage,
|
|
13002
15092
|
appendToMailbox: () => appendToMailbox,
|
|
15093
|
+
appendVerdict: () => appendVerdict,
|
|
15094
|
+
applyBudget: () => applyBudget,
|
|
15095
|
+
applyCandidates: () => applyCandidates,
|
|
15096
|
+
applyDecisions: () => applyDecisions,
|
|
13003
15097
|
assembleDaemonSnapshot: () => assembleDaemonSnapshot,
|
|
13004
15098
|
assertBackendUsable: () => assertBackendUsable,
|
|
15099
|
+
auditMachineId: () => auditMachineId,
|
|
15100
|
+
baseHash: () => baseHash,
|
|
13005
15101
|
bootWorkspace: () => bootWorkspace,
|
|
13006
15102
|
buildClassifierInput: () => buildClassifierInput,
|
|
13007
15103
|
buildCompletionProtocol: () => buildCompletionProtocol,
|
|
@@ -13009,6 +15105,7 @@ __export(dist_exports, {
|
|
|
13009
15105
|
buildDaemonPath: () => buildDaemonPath,
|
|
13010
15106
|
buildFreshness: () => buildFreshness,
|
|
13011
15107
|
buildOpencodeGateRunCommand: () => buildOpencodeGateRunCommand,
|
|
15108
|
+
buildReviewPacket: () => buildReviewPacket,
|
|
13012
15109
|
buildRouterCredentialsRequest: () => buildRouterCredentialsRequest,
|
|
13013
15110
|
buildRouterEnv: () => buildRouterEnv,
|
|
13014
15111
|
buildSideFirstTurn: () => buildSideFirstTurn,
|
|
@@ -13018,17 +15115,24 @@ __export(dist_exports, {
|
|
|
13018
15115
|
captainAliveStateFromHealth: () => captainAliveStateFromHealth,
|
|
13019
15116
|
captainRecordPath: () => captainRecordPath,
|
|
13020
15117
|
captainSocketPath: () => captainSocketPath,
|
|
15118
|
+
changedRefs: () => changedRefs,
|
|
13021
15119
|
checkFact: () => checkFact,
|
|
15120
|
+
checkOverrides: () => checkOverrides,
|
|
13022
15121
|
classifyCaptainPane: () => classifyCaptainPane,
|
|
13023
15122
|
classifyHealth: () => classifyHealth,
|
|
13024
15123
|
claudeEnvShadowsRouter: () => claudeEnvShadowsRouter,
|
|
13025
15124
|
cleanPromptQuery: () => cleanPromptQuery,
|
|
13026
15125
|
clearPending: () => clearPending,
|
|
13027
15126
|
closeWorkItem: () => closeWorkItem,
|
|
15127
|
+
compactAudit: () => compactAudit,
|
|
13028
15128
|
compileIndex: () => compileIndex,
|
|
15129
|
+
completePass: () => completePass,
|
|
15130
|
+
computeAnchor: () => computeAnchor,
|
|
13029
15131
|
computeRulesInjection: () => computeRulesInjection,
|
|
15132
|
+
computeScores: () => computeScores,
|
|
13030
15133
|
computeTemplateHash: () => computeTemplateHash,
|
|
13031
15134
|
consumeExitMarker: () => consumeExitMarker,
|
|
15135
|
+
convertedFile: () => convertedFile,
|
|
13032
15136
|
createAttach: () => createAttach,
|
|
13033
15137
|
createCaptainPaneReader: () => createCaptainPaneReader,
|
|
13034
15138
|
createCrewPaneReader: () => createCrewPaneReader,
|
|
@@ -13062,6 +15166,8 @@ __export(dist_exports, {
|
|
|
13062
15166
|
decideForeignInstall: () => decideForeignInstall,
|
|
13063
15167
|
decideOpencodeGateWrap: () => decideOpencodeGateWrap,
|
|
13064
15168
|
decodeFrames: () => decodeFrames,
|
|
15169
|
+
dedupPairs: () => dedupPairs,
|
|
15170
|
+
defaultAuditDir: () => defaultAuditDir,
|
|
13065
15171
|
defaultGateCachePath: () => defaultGateCachePath,
|
|
13066
15172
|
defaultGateDenyRules: () => defaultGateDenyRules,
|
|
13067
15173
|
defaultIsPidAlive: () => defaultIsPidAlive,
|
|
@@ -13073,27 +15179,41 @@ __export(dist_exports, {
|
|
|
13073
15179
|
deliverable: () => deliverable,
|
|
13074
15180
|
deriveCaptainState: () => deriveCaptainState,
|
|
13075
15181
|
describeOutcome: () => describeOutcome,
|
|
15182
|
+
detectChange: () => detectChange,
|
|
13076
15183
|
detectForeignInstall: () => detectForeignInstall,
|
|
13077
15184
|
detectGroupAndUser: () => detectGroupAndUser,
|
|
13078
15185
|
detectGroupId: () => detectGroupId,
|
|
13079
15186
|
discoverCaptainSurface: () => discoverCaptainSurface,
|
|
13080
15187
|
discoverLiveOpencodeServer: () => discoverLiveOpencodeServer,
|
|
13081
15188
|
dispatchToSibling: () => dispatchToSibling,
|
|
15189
|
+
domainCapExcess: () => domainCapExcess,
|
|
15190
|
+
duePass: () => duePass,
|
|
13082
15191
|
duplicateIdProblem: () => duplicateIdProblem,
|
|
13083
15192
|
encodeFrame: () => encodeFrame,
|
|
13084
15193
|
encodeMsg: () => encodeMsg,
|
|
13085
15194
|
ensureDaemon: () => ensureDaemon,
|
|
15195
|
+
ensureGitRepo: () => ensureGitRepo,
|
|
15196
|
+
ensureKbGitignore: () => ensureKbGitignore,
|
|
13086
15197
|
ensureSocksDir: () => ensureSocksDir,
|
|
15198
|
+
escalationsFile: () => escalationsFile,
|
|
15199
|
+
evaluateCodeConflict: () => evaluateCodeConflict,
|
|
13087
15200
|
evaluatePermissionRequest: () => evaluatePermissionRequest,
|
|
13088
15201
|
evaluateStall: () => evaluateStall,
|
|
13089
15202
|
exitMarkerPath: () => exitMarkerPath,
|
|
15203
|
+
expandSource: () => expandSource,
|
|
13090
15204
|
extractToolPayload: () => extractToolPayload,
|
|
13091
15205
|
extractUserIntentFromTranscript: () => extractUserIntentFromTranscript,
|
|
13092
15206
|
fallsBackToPane: () => fallsBackToPane,
|
|
15207
|
+
fileForRef: () => fileForRef,
|
|
13093
15208
|
findCrewPane: () => findCrewPane,
|
|
15209
|
+
findLegacyKbs: () => findLegacyKbs,
|
|
15210
|
+
findLegacyOverlays: () => findLegacyOverlays,
|
|
13094
15211
|
findOpenChildren: () => findOpenChildren,
|
|
13095
15212
|
findProjectByThread: () => findProjectByThread,
|
|
15213
|
+
findQuote: () => findQuote,
|
|
13096
15214
|
findWorkItemById: () => findWorkItemById,
|
|
15215
|
+
finishMessage: () => finishMessage,
|
|
15216
|
+
finishReconcile: () => finishReconcile,
|
|
13097
15217
|
forceKickstartAndVerify: () => forceKickstartAndVerify,
|
|
13098
15218
|
formatCustomHeaders: () => formatCustomHeaders,
|
|
13099
15219
|
formatInbound: () => formatInbound,
|
|
@@ -13108,6 +15228,8 @@ __export(dist_exports, {
|
|
|
13108
15228
|
freshTrace: () => freshTrace,
|
|
13109
15229
|
gateCacheKey: () => gateCacheKey,
|
|
13110
15230
|
getDaemonPid: () => getDaemonPid,
|
|
15231
|
+
globMatches: () => globMatches,
|
|
15232
|
+
globToRegExp: () => globToRegExp,
|
|
13111
15233
|
hasAutoGateCredential: () => hasAutoGateCredential,
|
|
13112
15234
|
hasModalOptionList: () => hasModalOptionList,
|
|
13113
15235
|
healCmdFor: () => healCmdFor,
|
|
@@ -13124,6 +15246,7 @@ __export(dist_exports, {
|
|
|
13124
15246
|
isGateSession: () => isGateSession,
|
|
13125
15247
|
isNotifyActive: () => isNotifyActive,
|
|
13126
15248
|
isOperatorInitiatedCommand: () => isOperatorInitiatedCommand,
|
|
15249
|
+
isPendingProposalDup: () => isPendingProposalDup,
|
|
13127
15250
|
isReadOnlyCrewCommand: () => isReadOnlyCrewCommand,
|
|
13128
15251
|
isReadOnlyTopLevelCommand: () => isReadOnlyTopLevelCommand,
|
|
13129
15252
|
isSideTitle: () => isSideTitle,
|
|
@@ -13136,9 +15259,11 @@ __export(dist_exports, {
|
|
|
13136
15259
|
kbRulesDir: () => kbRulesDir,
|
|
13137
15260
|
kickstartArgv: () => kickstartArgv,
|
|
13138
15261
|
launchOneWorkspace: () => launchOneWorkspace,
|
|
15262
|
+
legacyKbDir: () => legacyKbDir,
|
|
13139
15263
|
listSessions: () => listSessions,
|
|
13140
15264
|
loadKbRules: () => loadKbRules,
|
|
13141
15265
|
loadPending: () => loadPending,
|
|
15266
|
+
loadProposals: () => loadProposals,
|
|
13142
15267
|
loadRulesDir: () => loadRulesDir,
|
|
13143
15268
|
loadSessions: () => loadSessions,
|
|
13144
15269
|
loadSources: () => loadSources,
|
|
@@ -13146,16 +15271,28 @@ __export(dist_exports, {
|
|
|
13146
15271
|
mailboxStats: () => mailboxStats,
|
|
13147
15272
|
makeGate: () => makeGate,
|
|
13148
15273
|
markPendingWarned: () => markPendingWarned,
|
|
15274
|
+
markitdownConverter: () => markitdownConverter,
|
|
13149
15275
|
maskToken: () => maskToken,
|
|
15276
|
+
matchPaths: () => matchPaths,
|
|
15277
|
+
matchSymbols: () => matchSymbols,
|
|
13150
15278
|
matchTier1Deny: () => matchTier1Deny,
|
|
13151
15279
|
mediaKind: () => mediaKind,
|
|
13152
15280
|
mediaMarker: () => mediaMarker,
|
|
15281
|
+
mentionedPaths: () => mentionedPaths,
|
|
15282
|
+
mentionsId: () => mentionsId,
|
|
13153
15283
|
mergeClaudeEnvRouterSettings: () => mergeClaudeEnvRouterSettings,
|
|
13154
15284
|
mintToken: () => mintToken,
|
|
15285
|
+
moveLegacyKbs: () => moveLegacyKbs,
|
|
15286
|
+
moveLegacyOverlays: () => moveLegacyOverlays,
|
|
13155
15287
|
nameFromTitle: () => nameFromTitle,
|
|
15288
|
+
needsDocling: () => needsDocling,
|
|
15289
|
+
needsYouCount: () => needsYouCount,
|
|
13156
15290
|
newestSessionInDirectory: () => newestSessionInDirectory,
|
|
13157
15291
|
nextAutoName: () => nextAutoName,
|
|
13158
15292
|
niceCrewCommand: () => niceCrewCommand,
|
|
15293
|
+
normalizeText: () => normalizeText,
|
|
15294
|
+
normalizeWithMap: () => normalizeWithMap,
|
|
15295
|
+
normalizedViews: () => normalizedViews,
|
|
13159
15296
|
notePaneScreen: () => notePaneScreen,
|
|
13160
15297
|
notifyToggle: () => notifyToggle,
|
|
13161
15298
|
parseBearer: () => parseBearer,
|
|
@@ -13164,39 +15301,57 @@ __export(dist_exports, {
|
|
|
13164
15301
|
parseLiveOpencodeServers: () => parseLiveOpencodeServers,
|
|
13165
15302
|
parseNotifyPref: () => parseNotifyPref,
|
|
13166
15303
|
parseProgramArgs: () => parseProgramArgs,
|
|
15304
|
+
planIngest: () => planIngest,
|
|
13167
15305
|
plistPath: () => plistPath,
|
|
13168
15306
|
pollBackoffMs: () => pollBackoffMs,
|
|
13169
15307
|
pollNewestSessionInDirectory: () => pollNewestSessionInDirectory,
|
|
13170
15308
|
prepareCaptainRoute: () => prepareCaptainRoute,
|
|
13171
15309
|
printForeignInstallError: () => printForeignInstallError,
|
|
13172
15310
|
printVersionUpgradeNotice: () => printVersionUpgradeNotice,
|
|
15311
|
+
priorityOfRef: () => priorityOfRef,
|
|
15312
|
+
priorityOfRule: () => priorityOfRule,
|
|
13173
15313
|
probeCaptainChannel: () => probeCaptainChannel,
|
|
13174
15314
|
programArgsBlock: () => programArgsBlock,
|
|
13175
15315
|
projectHealth: () => projectHealth,
|
|
13176
|
-
|
|
15316
|
+
promoteProposal: () => promoteProposal,
|
|
15317
|
+
proposeAgentRule: () => proposeAgentRule,
|
|
13177
15318
|
pruneSeenRules: () => pruneSeenRules,
|
|
13178
15319
|
pruneTopics: () => pruneTopics,
|
|
13179
15320
|
purgeExpiredWorkItems: () => purgeExpiredWorkItems,
|
|
15321
|
+
readAuditRecords: () => readAuditRecords,
|
|
13180
15322
|
readCaptainAddress: () => readCaptainAddress,
|
|
13181
15323
|
readCursor: () => readCursor,
|
|
15324
|
+
readEscalations: () => readEscalations,
|
|
13182
15325
|
readFromCursor: () => readFromCursor,
|
|
15326
|
+
readGitMessages: () => readGitMessages,
|
|
13183
15327
|
readIndex: () => readIndex,
|
|
13184
15328
|
readRunningMarker: () => readRunningMarker,
|
|
15329
|
+
readSchedule: () => readSchedule,
|
|
13185
15330
|
readSeenRules: () => readSeenRules,
|
|
15331
|
+
readState: () => readState2,
|
|
15332
|
+
readTargetPath: () => readTargetPath,
|
|
13186
15333
|
readUserIntent: () => readUserIntent,
|
|
15334
|
+
realGit: () => realGit,
|
|
13187
15335
|
realpathOrSelf: () => realpathOrSelf,
|
|
13188
15336
|
reapCrewChildren: () => reapCrewChildren,
|
|
13189
15337
|
reapOrphanedCrews: () => reapOrphanedCrews,
|
|
15338
|
+
reconcileConfig: () => reconcileConfig,
|
|
13190
15339
|
reconcileLiveness: () => reconcileLiveness,
|
|
13191
15340
|
recordSeenRules: () => recordSeenRules,
|
|
13192
15341
|
recordSession: () => recordSession,
|
|
13193
15342
|
recoverStall: () => recoverStall,
|
|
13194
15343
|
reduce: () => reduce,
|
|
13195
15344
|
reduceLifecycle: () => reduceLifecycle,
|
|
15345
|
+
refFor: () => refFor,
|
|
13196
15346
|
releaseDaemonLock: () => releaseDaemonLock,
|
|
13197
15347
|
removeRunningMarker: () => removeRunningMarker,
|
|
13198
15348
|
renderEnvAssignments: () => renderEnvAssignments,
|
|
13199
15349
|
renderPlist: () => renderPlist,
|
|
15350
|
+
renderReport: () => renderReport,
|
|
15351
|
+
renderScoresSection: () => renderScoresSection,
|
|
15352
|
+
repoRelativePaths: () => repoRelativePaths,
|
|
15353
|
+
requestPass: () => requestPass,
|
|
15354
|
+
requestText: () => requestText,
|
|
13200
15355
|
reregisterDaemon: () => reregisterDaemon,
|
|
13201
15356
|
resolveAgentBinDirs: () => resolveAgentBinDirs,
|
|
13202
15357
|
resolveAndPersistOpencodeCaptain: () => resolveAndPersistOpencodeCaptain,
|
|
@@ -13204,6 +15359,7 @@ __export(dist_exports, {
|
|
|
13204
15359
|
resolveClassifierModel: () => resolveClassifierModel,
|
|
13205
15360
|
resolveCrewRoute: () => resolveCrewRoute,
|
|
13206
15361
|
resolveCurrentProject: () => resolveCurrentProject,
|
|
15362
|
+
resolveEscalation: () => resolveEscalation,
|
|
13207
15363
|
resolveGate: () => resolveGate,
|
|
13208
15364
|
resolveGateDenyRules: () => resolveGateDenyRules,
|
|
13209
15365
|
resolveGateEngine: () => resolveGateEngine,
|
|
@@ -13220,6 +15376,8 @@ __export(dist_exports, {
|
|
|
13220
15376
|
resolveSetupUserId: () => resolveSetupUserId,
|
|
13221
15377
|
restartDaemonIfRunning: () => restartDaemonIfRunning,
|
|
13222
15378
|
rotateIfNeeded: () => rotateIfNeeded,
|
|
15379
|
+
ruleAuditScope: () => ruleAuditScope,
|
|
15380
|
+
rulesFallbackPointer: () => rulesFallbackPointer,
|
|
13223
15381
|
runAdapterConformance: () => runAdapterConformance,
|
|
13224
15382
|
runCrewAnswer: () => runCrewAnswer,
|
|
13225
15383
|
runCrewClose: () => runCrewClose,
|
|
@@ -13227,6 +15385,7 @@ __export(dist_exports, {
|
|
|
13227
15385
|
runCrewRead: () => runCrewRead,
|
|
13228
15386
|
runCrewSend: () => runCrewSend,
|
|
13229
15387
|
runCrewSpawn: () => runCrewSpawn,
|
|
15388
|
+
runFullChecks: () => runFullChecks,
|
|
13230
15389
|
runLivenessTick: () => runLivenessTick,
|
|
13231
15390
|
runNotifyConfirmation: () => runNotifyConfirmation,
|
|
13232
15391
|
runRegisterCommands: () => runRegisterCommands,
|
|
@@ -13247,6 +15406,10 @@ __export(dist_exports, {
|
|
|
13247
15406
|
sanitizeRequest: () => sanitizeRequest,
|
|
13248
15407
|
saveSessions: () => saveSessions,
|
|
13249
15408
|
saveState: () => saveState,
|
|
15409
|
+
scanAndLogCitations: () => scanAndLogCitations,
|
|
15410
|
+
scheduleFile: () => scheduleFile,
|
|
15411
|
+
scoreKb: () => scoreKb,
|
|
15412
|
+
scoresFile: () => scoresFile,
|
|
13250
15413
|
screenHasSplashMarker: () => screenHasSplashMarker,
|
|
13251
15414
|
screenHash: () => screenHash,
|
|
13252
15415
|
screenShowsTrustPanel: () => screenShowsTrustPanel,
|
|
@@ -13263,13 +15426,18 @@ __export(dist_exports, {
|
|
|
13263
15426
|
sideNameFromTitle: () => sideNameFromTitle,
|
|
13264
15427
|
sideNextAutoName: () => sideNextAutoName,
|
|
13265
15428
|
sideTitleFor: () => sideTitleFor,
|
|
15429
|
+
snapshotSources: () => snapshotSources,
|
|
15430
|
+
spansOverlap: () => spansOverlap,
|
|
15431
|
+
splitSections: () => splitSections,
|
|
13266
15432
|
splitStatement: () => splitStatement,
|
|
13267
15433
|
stampFact: () => stampFact,
|
|
13268
15434
|
startDaemon: () => startDaemon,
|
|
13269
15435
|
startServer: () => startServer,
|
|
13270
15436
|
stripBotMention: () => stripBotMention,
|
|
13271
15437
|
surfaceVerdict: () => surfaceVerdict,
|
|
15438
|
+
tally: () => tally,
|
|
13272
15439
|
telegramPollLockPath: () => telegramPollLockPath,
|
|
15440
|
+
tickKnowledgeSchedule: () => tickKnowledgeSchedule,
|
|
13273
15441
|
timeoutGate: () => timeoutGate,
|
|
13274
15442
|
titleFor: () => titleFor,
|
|
13275
15443
|
toControlEvent: () => toControlEvent,
|
|
@@ -13278,16 +15446,23 @@ __export(dist_exports, {
|
|
|
13278
15446
|
trustPanelAcceptKeys: () => trustPanelAcceptKeys,
|
|
13279
15447
|
tryAcquireDaemonLock: () => tryAcquireDaemonLock,
|
|
13280
15448
|
usageFromJson: () => usageFromJson,
|
|
15449
|
+
usageReview: () => usageReview,
|
|
15450
|
+
validateCandidate: () => validateCandidate,
|
|
13281
15451
|
validateKb: () => validateKb,
|
|
15452
|
+
verdictsFile: () => verdictsFile,
|
|
13282
15453
|
waitForCaptainDelivery: () => waitForCaptainDelivery,
|
|
13283
15454
|
waitForWarmup: () => waitForWarmup,
|
|
13284
15455
|
wholeWordTerms: () => wholeWordTerms,
|
|
13285
15456
|
withBudget: () => withBudget,
|
|
13286
15457
|
writeCaptainAddress: () => writeCaptainAddress,
|
|
13287
15458
|
writeCursor: () => writeCursor,
|
|
15459
|
+
writeEscalations: () => writeEscalations,
|
|
13288
15460
|
writeExitMarker: () => writeExitMarker,
|
|
13289
15461
|
writeIndex: () => writeIndex,
|
|
15462
|
+
writeReport: () => writeReport,
|
|
13290
15463
|
writeRunningMarker: () => writeRunningMarker,
|
|
15464
|
+
writeSchedule: () => writeSchedule,
|
|
15465
|
+
writeScores: () => writeScores,
|
|
13291
15466
|
writeTelegramConfig: () => writeTelegramConfig
|
|
13292
15467
|
});
|
|
13293
15468
|
var init_dist2 = __esm({
|
|
@@ -13386,7 +15561,7 @@ import { readdirSync as readdirSync4, readFileSync as readFileSync12 } from "fs"
|
|
|
13386
15561
|
import { join as join18 } from "path";
|
|
13387
15562
|
|
|
13388
15563
|
// packages/agents/dist/claude/registry.js
|
|
13389
|
-
import
|
|
15564
|
+
import fs30 from "fs";
|
|
13390
15565
|
import { join as join17 } from "path";
|
|
13391
15566
|
import { homedir as homedir7 } from "os";
|
|
13392
15567
|
var CLAUDE_SESSIONS_DIR = join17(homedir7(), ".claude", "sessions");
|
|
@@ -13431,11 +15606,11 @@ function toLifecycleSnapshot(entry, taskId, alive, now) {
|
|
|
13431
15606
|
function readClaudeStatusBySocketPath(socketPath) {
|
|
13432
15607
|
let files;
|
|
13433
15608
|
try {
|
|
13434
|
-
files =
|
|
15609
|
+
files = fs30.readdirSync(CLAUDE_SESSIONS_DIR);
|
|
13435
15610
|
} catch {
|
|
13436
15611
|
return void 0;
|
|
13437
15612
|
}
|
|
13438
|
-
const entries = parseRegistryDir(files, (name) =>
|
|
15613
|
+
const entries = parseRegistryDir(files, (name) => fs30.readFileSync(join17(CLAUDE_SESSIONS_DIR, name), "utf8"));
|
|
13439
15614
|
const entry = entries.find((e) => e.messagingSocketPath === socketPath);
|
|
13440
15615
|
if (!entry)
|
|
13441
15616
|
return void 0;
|
|
@@ -13458,38 +15633,38 @@ import { homedir as homedir8 } from "os";
|
|
|
13458
15633
|
var SQUADRANT_STATE_DIR = join19(homedir8(), ".config", "squadrant", "state");
|
|
13459
15634
|
|
|
13460
15635
|
// packages/agents/dist/drivers/launch-cmd.js
|
|
13461
|
-
import
|
|
13462
|
-
import
|
|
15636
|
+
import fs31 from "fs";
|
|
15637
|
+
import path31 from "path";
|
|
13463
15638
|
|
|
13464
15639
|
// packages/agents/dist/projection/cursor.js
|
|
13465
15640
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
13466
|
-
import
|
|
13467
|
-
import
|
|
15641
|
+
import path32 from "path";
|
|
15642
|
+
import os10 from "os";
|
|
13468
15643
|
|
|
13469
15644
|
// packages/agents/dist/projection/codex.js
|
|
13470
15645
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
13471
|
-
import
|
|
13472
|
-
import
|
|
15646
|
+
import path33 from "path";
|
|
15647
|
+
import os11 from "os";
|
|
13473
15648
|
|
|
13474
15649
|
// packages/agents/dist/projection/gemini.js
|
|
13475
15650
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
13476
|
-
import
|
|
13477
|
-
import
|
|
15651
|
+
import path34 from "path";
|
|
15652
|
+
import os12 from "os";
|
|
13478
15653
|
|
|
13479
15654
|
// packages/agents/dist/projection/opencode.js
|
|
13480
15655
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
13481
|
-
import
|
|
13482
|
-
import
|
|
15656
|
+
import path35 from "path";
|
|
15657
|
+
import os13 from "os";
|
|
13483
15658
|
|
|
13484
15659
|
// packages/agents/dist/projection/opencode-skills.js
|
|
13485
|
-
import
|
|
13486
|
-
import
|
|
13487
|
-
import
|
|
15660
|
+
import fs32 from "fs";
|
|
15661
|
+
import os14 from "os";
|
|
15662
|
+
import path36 from "path";
|
|
13488
15663
|
var OPENCODE_SKILL_MARKER = ".squadrant-managed";
|
|
13489
15664
|
var MARKER_BODY = "squadrant\n";
|
|
13490
15665
|
function defaultOpencodeSkillsRoot(env = process.env) {
|
|
13491
|
-
const configHome = env.XDG_CONFIG_HOME ||
|
|
13492
|
-
return
|
|
15666
|
+
const configHome = env.XDG_CONFIG_HOME || path36.join(os14.homedir(), ".config");
|
|
15667
|
+
return path36.join(configHome, "opencode", "skills");
|
|
13493
15668
|
}
|
|
13494
15669
|
var SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
13495
15670
|
function parseSkillName(raw) {
|
|
@@ -13504,62 +15679,62 @@ function parseSkillName(raw) {
|
|
|
13504
15679
|
}
|
|
13505
15680
|
function listSourceSkills(sourceSkillsDir) {
|
|
13506
15681
|
const out = /* @__PURE__ */ new Map();
|
|
13507
|
-
if (!
|
|
15682
|
+
if (!fs32.existsSync(sourceSkillsDir))
|
|
13508
15683
|
return out;
|
|
13509
|
-
for (const entry of
|
|
15684
|
+
for (const entry of fs32.readdirSync(sourceSkillsDir, { withFileTypes: true })) {
|
|
13510
15685
|
if (!entry.isDirectory())
|
|
13511
15686
|
continue;
|
|
13512
|
-
const skillPath =
|
|
13513
|
-
if (!
|
|
15687
|
+
const skillPath = path36.join(sourceSkillsDir, entry.name, "SKILL.md");
|
|
15688
|
+
if (!fs32.existsSync(skillPath))
|
|
13514
15689
|
continue;
|
|
13515
|
-
const name = parseSkillName(
|
|
15690
|
+
const name = parseSkillName(fs32.readFileSync(skillPath, "utf-8"));
|
|
13516
15691
|
if (name && SAFE_NAME.test(name))
|
|
13517
15692
|
out.set(name, skillPath);
|
|
13518
15693
|
}
|
|
13519
15694
|
return out;
|
|
13520
15695
|
}
|
|
13521
15696
|
function hasMarker(dir) {
|
|
13522
|
-
return
|
|
15697
|
+
return fs32.existsSync(path36.join(dir, OPENCODE_SKILL_MARKER));
|
|
13523
15698
|
}
|
|
13524
15699
|
function syncOpencodeSkills(opts) {
|
|
13525
15700
|
const result = { written: [], unchanged: [], removed: [], skipped: [] };
|
|
13526
15701
|
const wanted = listSourceSkills(opts.sourceSkillsDir);
|
|
13527
|
-
if (wanted.size === 0 && !
|
|
15702
|
+
if (wanted.size === 0 && !fs32.existsSync(opts.sourceSkillsDir))
|
|
13528
15703
|
return result;
|
|
13529
15704
|
if (!opts.dryRun)
|
|
13530
|
-
|
|
15705
|
+
fs32.mkdirSync(opts.skillsRoot, { recursive: true });
|
|
13531
15706
|
for (const [name, srcPath] of wanted) {
|
|
13532
|
-
const targetDir =
|
|
13533
|
-
const targetSkill =
|
|
15707
|
+
const targetDir = path36.join(opts.skillsRoot, name);
|
|
15708
|
+
const targetSkill = path36.join(targetDir, "SKILL.md");
|
|
13534
15709
|
const managed = hasMarker(targetDir);
|
|
13535
|
-
if (
|
|
15710
|
+
if (fs32.existsSync(targetSkill) && !managed) {
|
|
13536
15711
|
result.skipped.push({ name, reason: "foreign-collision" });
|
|
13537
15712
|
continue;
|
|
13538
15713
|
}
|
|
13539
|
-
const current =
|
|
13540
|
-
if (current && current.equals(
|
|
15714
|
+
const current = fs32.existsSync(targetSkill) ? fs32.readFileSync(targetSkill) : null;
|
|
15715
|
+
if (current && current.equals(fs32.readFileSync(srcPath))) {
|
|
13541
15716
|
if (!managed && !opts.dryRun) {
|
|
13542
|
-
|
|
15717
|
+
fs32.writeFileSync(path36.join(targetDir, OPENCODE_SKILL_MARKER), MARKER_BODY);
|
|
13543
15718
|
}
|
|
13544
15719
|
result.unchanged.push(name);
|
|
13545
15720
|
continue;
|
|
13546
15721
|
}
|
|
13547
15722
|
if (!opts.dryRun) {
|
|
13548
|
-
|
|
13549
|
-
|
|
13550
|
-
|
|
15723
|
+
fs32.mkdirSync(targetDir, { recursive: true });
|
|
15724
|
+
fs32.copyFileSync(srcPath, targetSkill);
|
|
15725
|
+
fs32.writeFileSync(path36.join(targetDir, OPENCODE_SKILL_MARKER), MARKER_BODY);
|
|
13551
15726
|
}
|
|
13552
15727
|
result.written.push(name);
|
|
13553
15728
|
}
|
|
13554
|
-
if (
|
|
13555
|
-
for (const entry of
|
|
15729
|
+
if (fs32.existsSync(opts.skillsRoot)) {
|
|
15730
|
+
for (const entry of fs32.readdirSync(opts.skillsRoot, { withFileTypes: true })) {
|
|
13556
15731
|
if (!entry.isDirectory() || wanted.has(entry.name))
|
|
13557
15732
|
continue;
|
|
13558
|
-
const dir =
|
|
15733
|
+
const dir = path36.join(opts.skillsRoot, entry.name);
|
|
13559
15734
|
if (!hasMarker(dir))
|
|
13560
15735
|
continue;
|
|
13561
15736
|
if (!opts.dryRun)
|
|
13562
|
-
|
|
15737
|
+
fs32.rmSync(dir, { recursive: true, force: true });
|
|
13563
15738
|
result.removed.push(entry.name);
|
|
13564
15739
|
}
|
|
13565
15740
|
}
|
|
@@ -13567,7 +15742,7 @@ function syncOpencodeSkills(opts) {
|
|
|
13567
15742
|
}
|
|
13568
15743
|
function syncShippedOpencodeSkills(opts) {
|
|
13569
15744
|
return syncOpencodeSkills({
|
|
13570
|
-
sourceSkillsDir:
|
|
15745
|
+
sourceSkillsDir: path36.join(opts.pkgRoot, "plugin", "skills"),
|
|
13571
15746
|
skillsRoot: opts.skillsRoot ?? defaultOpencodeSkillsRoot(),
|
|
13572
15747
|
dryRun: opts.dryRun
|
|
13573
15748
|
});
|
|
@@ -13575,8 +15750,8 @@ function syncShippedOpencodeSkills(opts) {
|
|
|
13575
15750
|
|
|
13576
15751
|
// packages/agents/dist/projection/claude.js
|
|
13577
15752
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
13578
|
-
import
|
|
13579
|
-
import
|
|
15753
|
+
import path37 from "path";
|
|
15754
|
+
import os15 from "os";
|
|
13580
15755
|
|
|
13581
15756
|
// packages/agents/dist/codex/app-server-client.js
|
|
13582
15757
|
import { EventEmitter } from "events";
|
|
@@ -13794,51 +15969,51 @@ var CodexAppServerSource = class {
|
|
|
13794
15969
|
* are ignored — terminal state still comes exclusively from `squadrant crew signal`
|
|
13795
15970
|
* (anti-#2576 invariant).
|
|
13796
15971
|
*/
|
|
13797
|
-
observe(
|
|
13798
|
-
const snap = toSnapshot(
|
|
15972
|
+
observe(ev2) {
|
|
15973
|
+
const snap = toSnapshot(ev2);
|
|
13799
15974
|
if (!snap || !this.deps)
|
|
13800
15975
|
return;
|
|
13801
15976
|
this.cache.set(snap.taskId, snap);
|
|
13802
15977
|
this.deps.report(snap);
|
|
13803
15978
|
}
|
|
13804
15979
|
};
|
|
13805
|
-
function toSnapshot(
|
|
15980
|
+
function toSnapshot(ev2) {
|
|
13806
15981
|
const now = Date.now();
|
|
13807
|
-
switch (
|
|
15982
|
+
switch (ev2.type) {
|
|
13808
15983
|
// ── running: a turn is live ──────────────────────────────────────────────
|
|
13809
15984
|
case "task.started":
|
|
13810
15985
|
case "task.reattached":
|
|
13811
15986
|
case "task.turn.started":
|
|
13812
15987
|
case "task.delta":
|
|
13813
15988
|
case "task.progress":
|
|
13814
|
-
return { taskId:
|
|
15989
|
+
return { taskId: ev2.id, state: "running", alive: true, origin: "agent", at: now };
|
|
13815
15990
|
// ── idle: turn ended, crew alive, awaiting next input ────────────────────
|
|
13816
15991
|
// task.failed: the turn ended with an error, but the crew process is alive.
|
|
13817
15992
|
// task.session.ended: process is gone (alive:false) — signals liveness loss.
|
|
13818
15993
|
case "task.turn.completed":
|
|
13819
|
-
return { taskId:
|
|
15994
|
+
return { taskId: ev2.id, state: "idle", alive: true, origin: "agent", at: now };
|
|
13820
15995
|
case "task.failed":
|
|
13821
|
-
return { taskId:
|
|
15996
|
+
return { taskId: ev2.id, state: "idle", alive: true, origin: "agent", at: now };
|
|
13822
15997
|
case "task.session.ended":
|
|
13823
|
-
return { taskId:
|
|
15998
|
+
return { taskId: ev2.id, state: "idle", alive: false, origin: "agent", at: now };
|
|
13824
15999
|
// ── needsInput: crew is blocked on a human ───────────────────────────────
|
|
13825
16000
|
case "task.approval.requested":
|
|
13826
16001
|
return {
|
|
13827
|
-
taskId:
|
|
16002
|
+
taskId: ev2.id,
|
|
13828
16003
|
state: "needsInput",
|
|
13829
16004
|
alive: true,
|
|
13830
16005
|
origin: "agent",
|
|
13831
16006
|
at: now,
|
|
13832
|
-
detail: { note:
|
|
16007
|
+
detail: { note: ev2.question, reason: ev2.kind }
|
|
13833
16008
|
};
|
|
13834
16009
|
case "task.input.requested":
|
|
13835
16010
|
return {
|
|
13836
|
-
taskId:
|
|
16011
|
+
taskId: ev2.id,
|
|
13837
16012
|
state: "needsInput",
|
|
13838
16013
|
alive: true,
|
|
13839
16014
|
origin: "agent",
|
|
13840
16015
|
at: now,
|
|
13841
|
-
detail: { note:
|
|
16016
|
+
detail: { note: ev2.question }
|
|
13842
16017
|
};
|
|
13843
16018
|
// ── terminal / notify-only — ignored ────────────────────────────────────
|
|
13844
16019
|
// task.done, task.blocked, task.cancelled: terminal state from crew signal only.
|
|
@@ -14123,9 +16298,9 @@ var CodexInteractiveDriver = class {
|
|
|
14123
16298
|
const taskId = tid ? this.taskByThread.get(tid) : void 0;
|
|
14124
16299
|
if (!taskId)
|
|
14125
16300
|
return;
|
|
14126
|
-
const
|
|
14127
|
-
if (
|
|
14128
|
-
this.deps.emit(
|
|
16301
|
+
const ev2 = normalizeAppServerNotification(taskId, n);
|
|
16302
|
+
if (ev2)
|
|
16303
|
+
this.deps.emit(ev2);
|
|
14129
16304
|
}
|
|
14130
16305
|
onServerRequest(r) {
|
|
14131
16306
|
const tid = r.params?.threadId ?? r.params?.thread_id;
|
|
@@ -14920,9 +17095,9 @@ var ClaudeReceiptListener = class {
|
|
|
14920
17095
|
};
|
|
14921
17096
|
|
|
14922
17097
|
// packages/agents/dist/claude/api-key-approval.js
|
|
14923
|
-
import
|
|
14924
|
-
import
|
|
14925
|
-
import
|
|
17098
|
+
import fs33 from "fs";
|
|
17099
|
+
import os16 from "os";
|
|
17100
|
+
import path38 from "path";
|
|
14926
17101
|
|
|
14927
17102
|
// packages/agents/dist/opencode/http-channel.js
|
|
14928
17103
|
init_dist2();
|
|
@@ -15014,10 +17189,10 @@ var OpencodeHttpChannel = class {
|
|
|
15014
17189
|
return cached;
|
|
15015
17190
|
const directory = this.directoryFor?.(taskId);
|
|
15016
17191
|
const createdAfterMs = this.createdAfterFor?.(taskId);
|
|
15017
|
-
for (const
|
|
17192
|
+
for (const path42 of ["/session?", "/api/session?"]) {
|
|
15018
17193
|
let res;
|
|
15019
17194
|
try {
|
|
15020
|
-
res = await this.request(`http://127.0.0.1:${port}${
|
|
17195
|
+
res = await this.request(`http://127.0.0.1:${port}${path42}`, { method: "GET" });
|
|
15021
17196
|
} catch {
|
|
15022
17197
|
return void 0;
|
|
15023
17198
|
}
|
|
@@ -15090,13 +17265,18 @@ function createOpencodeFactAdapter(deps) {
|
|
|
15090
17265
|
};
|
|
15091
17266
|
}
|
|
15092
17267
|
|
|
17268
|
+
// packages/agents/dist/rules-hooks/install.js
|
|
17269
|
+
import fs34 from "fs";
|
|
17270
|
+
import os17 from "os";
|
|
17271
|
+
import path39 from "path";
|
|
17272
|
+
|
|
15093
17273
|
// packages/workspaces/dist/runtimes/cmux.js
|
|
15094
17274
|
init_dist();
|
|
15095
17275
|
init_dist();
|
|
15096
17276
|
init_dist();
|
|
15097
17277
|
init_dist2();
|
|
15098
17278
|
init_dist2();
|
|
15099
|
-
import { execFile as
|
|
17279
|
+
import { execFile as execFile3, execFileSync as execFileSync8 } from "child_process";
|
|
15100
17280
|
var CMUX_TIMEOUT = 15e3;
|
|
15101
17281
|
var CmuxTimeoutError = class extends Error {
|
|
15102
17282
|
constructor(cmd) {
|
|
@@ -15106,7 +17286,7 @@ var CmuxTimeoutError = class extends Error {
|
|
|
15106
17286
|
};
|
|
15107
17287
|
function cmux(args) {
|
|
15108
17288
|
return new Promise((resolve4, reject) => {
|
|
15109
|
-
|
|
17289
|
+
execFile3(
|
|
15110
17290
|
resolveCmuxBin(),
|
|
15111
17291
|
args,
|
|
15112
17292
|
// CMUX_QUIET=1 silences cmux 0.64's one-time deprecation hints (e.g. the
|
|
@@ -15126,7 +17306,7 @@ function cmux(args) {
|
|
|
15126
17306
|
}
|
|
15127
17307
|
function cmuxStdin(args, input) {
|
|
15128
17308
|
return new Promise((resolve4, reject) => {
|
|
15129
|
-
const child =
|
|
17309
|
+
const child = execFile3(resolveCmuxBin(), args, { encoding: "utf-8", timeout: CMUX_TIMEOUT, env: { ...process.env, CMUX_QUIET: "1" } }, (err, stdout) => {
|
|
15130
17310
|
if (err) {
|
|
15131
17311
|
reject(err.code === "ETIMEDOUT" ? new CmuxTimeoutError(args.join(" ")) : err);
|
|
15132
17312
|
return;
|
|
@@ -15549,7 +17729,7 @@ function createCmuxDriver() {
|
|
|
15549
17729
|
async sendToSurface(surface, text, opts) {
|
|
15550
17730
|
const ws = surface.workspaceId;
|
|
15551
17731
|
const sf = surface.surfaceId;
|
|
15552
|
-
const
|
|
17732
|
+
const deliver2 = async () => {
|
|
15553
17733
|
const dbg = sendDebugEnabled();
|
|
15554
17734
|
let preBox = null;
|
|
15555
17735
|
if (dbg) {
|
|
@@ -15579,7 +17759,7 @@ function createCmuxDriver() {
|
|
|
15579
17759
|
if (state === "dialog")
|
|
15580
17760
|
throw new DeferDelivery(null, "modal");
|
|
15581
17761
|
if (state === "empty") {
|
|
15582
|
-
await
|
|
17762
|
+
await deliver2();
|
|
15583
17763
|
return;
|
|
15584
17764
|
}
|
|
15585
17765
|
}
|
|
@@ -15597,7 +17777,7 @@ function createCmuxDriver() {
|
|
|
15597
17777
|
if (hasModalOptionList2(screen))
|
|
15598
17778
|
throw new DeferDelivery(null, "modal");
|
|
15599
17779
|
if (draft === "") {
|
|
15600
|
-
await
|
|
17780
|
+
await deliver2();
|
|
15601
17781
|
return;
|
|
15602
17782
|
}
|
|
15603
17783
|
if (!opts?.probe)
|
|
@@ -15625,7 +17805,7 @@ function createCmuxDriver() {
|
|
|
15625
17805
|
throw new DeferDelivery(draft);
|
|
15626
17806
|
}
|
|
15627
17807
|
if (liveness === "no-draft") {
|
|
15628
|
-
await
|
|
17808
|
+
await deliver2();
|
|
15629
17809
|
return;
|
|
15630
17810
|
}
|
|
15631
17811
|
if (rawBefore !== null && rawAfter !== null) {
|
|
@@ -15635,7 +17815,7 @@ function createCmuxDriver() {
|
|
|
15635
17815
|
await restoreGrapheme(lastGrapheme);
|
|
15636
17816
|
throw new DeferDelivery(draft);
|
|
15637
17817
|
}
|
|
15638
|
-
await
|
|
17818
|
+
await deliver2();
|
|
15639
17819
|
return;
|
|
15640
17820
|
}
|
|
15641
17821
|
throw new DeferDelivery(draft);
|
|
@@ -15770,15 +17950,15 @@ var RuntimeRegistry = class {
|
|
|
15770
17950
|
|
|
15771
17951
|
// packages/workspaces/dist/notifiers/cmux.js
|
|
15772
17952
|
init_dist();
|
|
15773
|
-
import { execFile as execFileCb, execFileSync as
|
|
15774
|
-
import { promisify as
|
|
15775
|
-
var
|
|
17953
|
+
import { execFile as execFileCb, execFileSync as execFileSync9 } from "child_process";
|
|
17954
|
+
import { promisify as promisify3 } from "util";
|
|
17955
|
+
var execFile4 = promisify3(execFileCb);
|
|
15776
17956
|
function createCmuxNotifier(_scope) {
|
|
15777
17957
|
return {
|
|
15778
17958
|
name: "cmux",
|
|
15779
17959
|
async probe() {
|
|
15780
17960
|
try {
|
|
15781
|
-
|
|
17961
|
+
execFileSync9(resolveCmuxBin(), ["capabilities"], { encoding: "utf-8", stdio: "pipe", timeout: CMUX_TIMEOUT });
|
|
15782
17962
|
return { installed: true, reachable: true };
|
|
15783
17963
|
} catch (err) {
|
|
15784
17964
|
const code = err.code;
|
|
@@ -15789,7 +17969,7 @@ function createCmuxNotifier(_scope) {
|
|
|
15789
17969
|
}
|
|
15790
17970
|
},
|
|
15791
17971
|
async notify(message) {
|
|
15792
|
-
await
|
|
17972
|
+
await execFile4("squadrant", ["runtime", "send", "--command", message], { encoding: "utf-8", timeout: CMUX_TIMEOUT });
|
|
15793
17973
|
}
|
|
15794
17974
|
};
|
|
15795
17975
|
}
|
|
@@ -15826,9 +18006,9 @@ var NotifierRegistry = class {
|
|
|
15826
18006
|
};
|
|
15827
18007
|
|
|
15828
18008
|
// packages/workspaces/dist/workspaces/obsidian.js
|
|
15829
|
-
import
|
|
18009
|
+
import fs35 from "fs/promises";
|
|
15830
18010
|
import { existsSync as existsSync11 } from "fs";
|
|
15831
|
-
import
|
|
18011
|
+
import path40 from "path";
|
|
15832
18012
|
|
|
15833
18013
|
// packages/workspaces/dist/workspaces/registry.js
|
|
15834
18014
|
init_dist();
|
|
@@ -15988,9 +18168,9 @@ import { homedir as homedir11 } from "os";
|
|
|
15988
18168
|
// packages/workspaces/dist/cmux-daemon/store-fingerprint.js
|
|
15989
18169
|
init_dist();
|
|
15990
18170
|
init_dist2();
|
|
15991
|
-
import { execFileSync as
|
|
18171
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
15992
18172
|
function defaultExecPs(pid) {
|
|
15993
|
-
return
|
|
18173
|
+
return execFileSync10("ps", ["-o", "command=", "-p", String(pid)], {
|
|
15994
18174
|
encoding: "utf-8",
|
|
15995
18175
|
stdio: ["ignore", "pipe", "ignore"]
|
|
15996
18176
|
});
|
|
@@ -16378,9 +18558,9 @@ function defaultListFiles(dir) {
|
|
|
16378
18558
|
return [];
|
|
16379
18559
|
}
|
|
16380
18560
|
}
|
|
16381
|
-
function defaultReadFile(
|
|
18561
|
+
function defaultReadFile(path42) {
|
|
16382
18562
|
try {
|
|
16383
|
-
return readFileSync17(
|
|
18563
|
+
return readFileSync17(path42, "utf-8");
|
|
16384
18564
|
} catch {
|
|
16385
18565
|
return void 0;
|
|
16386
18566
|
}
|
|
@@ -16412,7 +18592,9 @@ var CLAUDE_HOOK_EVENTS = [
|
|
|
16412
18592
|
["PermissionRequest", "permission-request"],
|
|
16413
18593
|
// #763: the turn died on an API error — today squadrant has no source for
|
|
16414
18594
|
// this at all; without it the watchdog reports a plain stall (wrong story).
|
|
16415
|
-
["StopFailure", "stop-failure"]
|
|
18595
|
+
["StopFailure", "stop-failure"],
|
|
18596
|
+
// #899: rules whose globs/anchors match the file just read are injected as context.
|
|
18597
|
+
["PostToolUse", "post-tool-use", "Read"]
|
|
16416
18598
|
];
|
|
16417
18599
|
var DEFAULT_CLI_CMD = "squadrant";
|
|
16418
18600
|
function hookCommandFor(eventName, sub, hookCmd, gateCmd) {
|
|
@@ -16758,16 +18940,16 @@ function extractDetail(sub, payload) {
|
|
|
16758
18940
|
}
|
|
16759
18941
|
return void 0;
|
|
16760
18942
|
}
|
|
16761
|
-
function defaultReadFile2(
|
|
18943
|
+
function defaultReadFile2(path42) {
|
|
16762
18944
|
try {
|
|
16763
|
-
return readFileSync18(
|
|
18945
|
+
return readFileSync18(path42, "utf-8");
|
|
16764
18946
|
} catch {
|
|
16765
18947
|
return void 0;
|
|
16766
18948
|
}
|
|
16767
18949
|
}
|
|
16768
|
-
function defaultWriteFile(
|
|
16769
|
-
mkdirSync8(
|
|
16770
|
-
writeFileSync11(
|
|
18950
|
+
function defaultWriteFile(path42, content) {
|
|
18951
|
+
mkdirSync8(path42.replace(/\/[^/]+$/, ""), { recursive: true });
|
|
18952
|
+
writeFileSync11(path42, content, "utf-8");
|
|
16771
18953
|
}
|
|
16772
18954
|
|
|
16773
18955
|
// packages/workspaces/dist/crew-pane.js
|
|
@@ -16852,9 +19034,9 @@ init_dist();
|
|
|
16852
19034
|
|
|
16853
19035
|
// packages/cli/src/lib/daemon-restart-broadcast.ts
|
|
16854
19036
|
init_dist();
|
|
16855
|
-
import
|
|
19037
|
+
import path41 from "path";
|
|
16856
19038
|
function statePath2(stateRoot) {
|
|
16857
|
-
return
|
|
19039
|
+
return path41.join(stateRoot, "daemon-restart-state.json");
|
|
16858
19040
|
}
|
|
16859
19041
|
function computeRestartSignature(version, buildMtimeMs) {
|
|
16860
19042
|
return `${version}::${buildMtimeMs}`;
|
|
@@ -16903,7 +19085,7 @@ async function maybeBroadcastDaemonRestart(opts) {
|
|
|
16903
19085
|
|
|
16904
19086
|
// packages/cli/src/lib/captain-channel-factory.ts
|
|
16905
19087
|
import { createServer as createServer4, connect as netConnect } from "net";
|
|
16906
|
-
import
|
|
19088
|
+
import fs36 from "fs";
|
|
16907
19089
|
import { join as join26 } from "path";
|
|
16908
19090
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
16909
19091
|
import chalk2 from "chalk";
|
|
@@ -16912,15 +19094,15 @@ var shared;
|
|
|
16912
19094
|
var registryEntryPath = () => join26(CLAUDE_SESSIONS_DIR, `${process.pid}.json`);
|
|
16913
19095
|
function unregisterSenderIdentity() {
|
|
16914
19096
|
try {
|
|
16915
|
-
|
|
19097
|
+
fs36.unlinkSync(registryEntryPath());
|
|
16916
19098
|
} catch {
|
|
16917
19099
|
}
|
|
16918
19100
|
}
|
|
16919
19101
|
function registerSenderIdentity(socketPath) {
|
|
16920
19102
|
try {
|
|
16921
19103
|
unregisterSenderIdentity();
|
|
16922
|
-
|
|
16923
|
-
|
|
19104
|
+
fs36.mkdirSync(CLAUDE_SESSIONS_DIR, { recursive: true });
|
|
19105
|
+
fs36.writeFileSync(
|
|
16924
19106
|
registryEntryPath(),
|
|
16925
19107
|
JSON.stringify({
|
|
16926
19108
|
pid: process.pid,
|
|
@@ -16946,7 +19128,7 @@ async function sharedReceiptListener() {
|
|
|
16946
19128
|
// must never be the reason we refuse to start.
|
|
16947
19129
|
unlinkStale: (p) => {
|
|
16948
19130
|
try {
|
|
16949
|
-
|
|
19131
|
+
fs36.unlinkSync(p);
|
|
16950
19132
|
} catch {
|
|
16951
19133
|
}
|
|
16952
19134
|
},
|
|
@@ -16971,7 +19153,7 @@ async function buildCaptainChannel() {
|
|
|
16971
19153
|
// in /tmp/x registers cwd "/private/tmp/x" on macOS, so cwd matching silently
|
|
16972
19154
|
// missed and every captain ping reported accepted-unconfirmed.
|
|
16973
19155
|
statusFor: (taskId) => readClaudeStatusBySocketPath(captainSocketPath(taskId)),
|
|
16974
|
-
wire: (p, e) => writeLine(p, e, { connect: (
|
|
19156
|
+
wire: (p, e) => writeLine(p, e, { connect: (path42) => netConnect(path42) }),
|
|
16975
19157
|
receipts,
|
|
16976
19158
|
newMsgId: () => randomUUID5(),
|
|
16977
19159
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
@@ -17137,25 +19319,25 @@ function startSquadrantd(opts = {}) {
|
|
|
17137
19319
|
ctx.cancelPromotionsFor = cancelPromotionsFor;
|
|
17138
19320
|
const codexAppServerSource = new CodexAppServerSource();
|
|
17139
19321
|
const codexDriver = opts.codexDriver ?? new CodexInteractiveDriver({
|
|
17140
|
-
emit: (
|
|
17141
|
-
const found = ctx.store.listAll().find((r) => r.id ===
|
|
19322
|
+
emit: (ev2) => {
|
|
19323
|
+
const found = ctx.store.listAll().find((r) => r.id === ev2.id);
|
|
17142
19324
|
if (!found) return;
|
|
17143
|
-
void ctx.d.handle({ kind: "event", project: found.project, event:
|
|
17144
|
-
if (
|
|
17145
|
-
ctx.broadcast(
|
|
17146
|
-
else if (
|
|
17147
|
-
ctx.broadcast(
|
|
17148
|
-
else if (
|
|
17149
|
-
ctx.broadcast(
|
|
17150
|
-
else if (
|
|
17151
|
-
ctx.broadcast(
|
|
17152
|
-
ctx.schedulePromotion(
|
|
17153
|
-
} else if (
|
|
17154
|
-
ctx.broadcast(
|
|
17155
|
-
ctx.schedulePromotion(
|
|
17156
|
-
} else if (
|
|
17157
|
-
ctx.broadcast(
|
|
17158
|
-
codexAppServerSource.observe(
|
|
19325
|
+
void ctx.d.handle({ kind: "event", project: found.project, event: ev2 });
|
|
19326
|
+
if (ev2.type === "task.delta")
|
|
19327
|
+
ctx.broadcast(ev2.id, { type: "delta", taskId: ev2.id, text: ev2.chunk });
|
|
19328
|
+
else if (ev2.type === "task.turn.started")
|
|
19329
|
+
ctx.broadcast(ev2.id, { type: "turn-started", taskId: ev2.id });
|
|
19330
|
+
else if (ev2.type === "task.turn.completed")
|
|
19331
|
+
ctx.broadcast(ev2.id, { type: "turn-completed", taskId: ev2.id });
|
|
19332
|
+
else if (ev2.type === "task.input.requested") {
|
|
19333
|
+
ctx.broadcast(ev2.id, { type: "input-requested", taskId: ev2.id, requestId: ev2.requestId, question: ev2.question });
|
|
19334
|
+
ctx.schedulePromotion(ev2.id, ev2.requestId, "input", ev2.question);
|
|
19335
|
+
} else if (ev2.type === "task.approval.requested") {
|
|
19336
|
+
ctx.broadcast(ev2.id, { type: "approval-requested", taskId: ev2.id, requestId: ev2.requestId, question: ev2.question, kind: ev2.kind });
|
|
19337
|
+
ctx.schedulePromotion(ev2.id, ev2.requestId, "approval", ev2.question);
|
|
19338
|
+
} else if (ev2.type === "task.reattached")
|
|
19339
|
+
ctx.broadcast(ev2.id, { type: "reattached", taskId: ev2.id });
|
|
19340
|
+
codexAppServerSource.observe(ev2);
|
|
17159
19341
|
}
|
|
17160
19342
|
});
|
|
17161
19343
|
const violationLogState = /* @__PURE__ */ new Map();
|
|
@@ -17179,23 +19361,23 @@ function startSquadrantd(opts = {}) {
|
|
|
17179
19361
|
const opencodeRequestIds = { n: 1 };
|
|
17180
19362
|
const eventsSource = createEventsSource({
|
|
17181
19363
|
adapters: [createOpencodeFactAdapter({ nextRequestId: () => opencodeRequestIds.n++ })],
|
|
17182
|
-
emit: (
|
|
17183
|
-
const found = store.listAll().find((r) => r.id ===
|
|
19364
|
+
emit: (ev2) => {
|
|
19365
|
+
const found = store.listAll().find((r) => r.id === ev2.id);
|
|
17184
19366
|
if (!found) return;
|
|
17185
|
-
void ctx.d.handle({ kind: "event", project: found.project, event:
|
|
17186
|
-
if (
|
|
17187
|
-
ctx.schedulePromotion(
|
|
19367
|
+
void ctx.d.handle({ kind: "event", project: found.project, event: ev2 });
|
|
19368
|
+
if (ev2.type === "task.approval.requested")
|
|
19369
|
+
ctx.schedulePromotion(ev2.id, ev2.requestId, "approval", ev2.question);
|
|
17188
19370
|
},
|
|
17189
19371
|
onViolation: onEventsViolation,
|
|
17190
19372
|
check: { stallBudgetMs: 6e4, disagreeWindowMs: 5e3 }
|
|
17191
19373
|
});
|
|
17192
19374
|
const opencodeBridge = opts.opencodeBridge ?? new OpencodeSseBridge({
|
|
17193
|
-
emit: (
|
|
17194
|
-
const found = store.listAll().find((r) => r.id ===
|
|
19375
|
+
emit: (ev2) => {
|
|
19376
|
+
const found = store.listAll().find((r) => r.id === ev2.id);
|
|
17195
19377
|
if (!found) return;
|
|
17196
|
-
void ctx.d.handle({ kind: "event", project: found.project, event:
|
|
17197
|
-
if (
|
|
17198
|
-
ctx.schedulePromotion(
|
|
19378
|
+
void ctx.d.handle({ kind: "event", project: found.project, event: ev2 });
|
|
19379
|
+
if (ev2.type === "task.approval.requested")
|
|
19380
|
+
ctx.schedulePromotion(ev2.id, ev2.requestId, "approval", ev2.question);
|
|
17199
19381
|
},
|
|
17200
19382
|
ingest: (raw, taskId) => eventsSource.ingest("opencode-sse", raw, { taskId }),
|
|
17201
19383
|
// #828: a crew wrapped by auto-gate has BOTH bash and edit at "ask" in its
|
|
@@ -17215,10 +19397,10 @@ function startSquadrantd(opts = {}) {
|
|
|
17215
19397
|
log
|
|
17216
19398
|
});
|
|
17217
19399
|
const cmuxEventsBridge = opts.cmuxEventsBridge ?? new CmuxEventsBridge({
|
|
17218
|
-
emit: (
|
|
17219
|
-
const found = store.listAll().find((r) => r.id ===
|
|
19400
|
+
emit: (ev2) => {
|
|
19401
|
+
const found = store.listAll().find((r) => r.id === ev2.id);
|
|
17220
19402
|
if (!found) return;
|
|
17221
|
-
void ctx.d.handle({ kind: "event", project: found.project, event:
|
|
19403
|
+
void ctx.d.handle({ kind: "event", project: found.project, event: ev2 });
|
|
17222
19404
|
},
|
|
17223
19405
|
resolve: (hook) => {
|
|
17224
19406
|
if (!hook.cwd) return void 0;
|
|
@@ -17574,7 +19756,20 @@ if (process.argv[1] && process.argv[1].endsWith("squadrantd.js")) {
|
|
|
17574
19756
|
`);
|
|
17575
19757
|
process.exit(0);
|
|
17576
19758
|
}
|
|
17577
|
-
const h = startSquadrantd({
|
|
19759
|
+
const h = startSquadrantd({
|
|
19760
|
+
sweepMs: 3e4,
|
|
19761
|
+
// #898: KB reconcile scheduler on the 60s tick. Enqueues to the home captain's mailbox; never spawns.
|
|
19762
|
+
knowledgeTick: async () => {
|
|
19763
|
+
const stateRoot = join27(homedir14(), ".config", "squadrant", "state");
|
|
19764
|
+
await tickKnowledgeSchedule({
|
|
19765
|
+
cfg: loadConfig(),
|
|
19766
|
+
stateRoot,
|
|
19767
|
+
log: (m) => process.stderr.write(`[squadrantd] ${m}
|
|
19768
|
+
`),
|
|
19769
|
+
enqueue: (project, text) => appendCaptainMessage({ stateRoot, project, text, source: "daemon" })
|
|
19770
|
+
});
|
|
19771
|
+
}
|
|
19772
|
+
});
|
|
17578
19773
|
const shutdown = (signal) => {
|
|
17579
19774
|
void h.stop(signal).finally(() => process.exit(0));
|
|
17580
19775
|
};
|