@hiveai/mcp 0.10.9 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +394 -290
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +7 -3
- package/dist/server.js +394 -290
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
package/dist/server.js
CHANGED
|
@@ -151,6 +151,13 @@ var MemSaveInputSchema = {
|
|
|
151
151
|
commit: z4.string().optional().describe("Anchor commit SHA (for staleness detection later)"),
|
|
152
152
|
topic: z4.string().optional().describe(
|
|
153
153
|
"Stable key for this memory. If a memory with the same topic already exists in this scope, it is updated in-place (revision_count++). Use for knowledge that evolves over time."
|
|
154
|
+
),
|
|
155
|
+
activation: z4.object({
|
|
156
|
+
keywords: z4.array(z4.string()).default([]),
|
|
157
|
+
globs: z4.array(z4.string()).default([]),
|
|
158
|
+
always: z4.boolean().default(false)
|
|
159
|
+
}).optional().describe(
|
|
160
|
+
"Only for type='skill'. Progressive-disclosure triggers: the skill is surfaced ONLY when a keyword matches the task or a glob matches the edited files (or always=true). Omit to keep the skill always-eligible."
|
|
154
161
|
)
|
|
155
162
|
};
|
|
156
163
|
function bodyHash(body) {
|
|
@@ -269,7 +276,8 @@ async function memSave(input, ctx) {
|
|
|
269
276
|
commit: input.commit,
|
|
270
277
|
topic: input.topic,
|
|
271
278
|
status: haiveConfig.defaultStatus === "validated" ? "validated" : void 0,
|
|
272
|
-
sensor: suggestSensorForSavedMemory(input.type, input.body, input.paths) ?? void 0
|
|
279
|
+
sensor: suggestSensorForSavedMemory(input.type, input.body, input.paths) ?? void 0,
|
|
280
|
+
activation: input.type === "skill" ? input.activation : void 0
|
|
273
281
|
});
|
|
274
282
|
const file = memoryFilePath(
|
|
275
283
|
ctx.paths,
|
|
@@ -658,28 +666,78 @@ async function memReject(input, ctx) {
|
|
|
658
666
|
};
|
|
659
667
|
}
|
|
660
668
|
|
|
669
|
+
// src/tools/mem-feedback.ts
|
|
670
|
+
import {
|
|
671
|
+
computeImpact,
|
|
672
|
+
getUsage as getUsage2,
|
|
673
|
+
loadMemoriesFromDir as loadMemoriesFromDir6,
|
|
674
|
+
loadUsageIndex as loadUsageIndex3,
|
|
675
|
+
recordApplied,
|
|
676
|
+
recordRejection as recordRejection2,
|
|
677
|
+
saveUsageIndex as saveUsageIndex2
|
|
678
|
+
} from "@hiveai/core";
|
|
679
|
+
import { existsSync as existsSync8 } from "fs";
|
|
680
|
+
import { z as z8 } from "zod";
|
|
681
|
+
var MemFeedbackInputSchema = {
|
|
682
|
+
id: z8.string().min(1).describe("Full memory id the feedback is about"),
|
|
683
|
+
outcome: z8.enum(["applied", "rejected"]).describe(
|
|
684
|
+
"'applied' = this memory changed what you did (strong positive utility signal); 'rejected' = it was wrong/outdated/unhelpful (negative signal, blocks auto-promotion)."
|
|
685
|
+
),
|
|
686
|
+
reason: z8.string().optional().describe("Why it was rejected (stored on the memory's usage record). Only used for outcome='rejected'.")
|
|
687
|
+
};
|
|
688
|
+
async function memFeedback(input, ctx) {
|
|
689
|
+
if (!existsSync8(ctx.paths.memoriesDir)) {
|
|
690
|
+
return { ok: false, id: input.id, error: "No .ai/memories \u2014 run `haive init` first." };
|
|
691
|
+
}
|
|
692
|
+
const all = await loadMemoriesFromDir6(ctx.paths.memoriesDir);
|
|
693
|
+
const target = all.find((m) => m.memory.frontmatter.id === input.id);
|
|
694
|
+
if (!target) {
|
|
695
|
+
return { ok: false, id: input.id, error: `No memory with id '${input.id}'.` };
|
|
696
|
+
}
|
|
697
|
+
const index = await loadUsageIndex3(ctx.paths);
|
|
698
|
+
if (input.outcome === "applied") {
|
|
699
|
+
recordApplied(index, input.id);
|
|
700
|
+
} else {
|
|
701
|
+
recordRejection2(index, input.id, input.reason ?? null);
|
|
702
|
+
}
|
|
703
|
+
await saveUsageIndex2(ctx.paths, index);
|
|
704
|
+
const usage = getUsage2(index, input.id);
|
|
705
|
+
const impact = computeImpact(target.memory.frontmatter, usage);
|
|
706
|
+
return {
|
|
707
|
+
ok: true,
|
|
708
|
+
id: input.id,
|
|
709
|
+
outcome: input.outcome,
|
|
710
|
+
usage: {
|
|
711
|
+
read_count: usage.read_count,
|
|
712
|
+
applied_count: usage.applied_count,
|
|
713
|
+
rejected_count: usage.rejected_count
|
|
714
|
+
},
|
|
715
|
+
impact: { score: impact.score, tier: impact.tier, signals: impact.signals }
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
|
|
661
719
|
// src/tools/mem-for-files.ts
|
|
662
720
|
import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
|
|
663
|
-
import { existsSync as
|
|
721
|
+
import { existsSync as existsSync9 } from "fs";
|
|
664
722
|
import path4 from "path";
|
|
665
723
|
import {
|
|
666
724
|
deriveConfidence as deriveConfidence2,
|
|
667
|
-
getUsage as
|
|
725
|
+
getUsage as getUsage3,
|
|
668
726
|
inferModulesFromPaths,
|
|
669
|
-
loadMemoriesFromDir as
|
|
670
|
-
loadUsageIndex as
|
|
727
|
+
loadMemoriesFromDir as loadMemoriesFromDir7,
|
|
728
|
+
loadUsageIndex as loadUsageIndex4,
|
|
671
729
|
memoryMatchesAnchorPaths,
|
|
672
730
|
trackReads as trackReads2
|
|
673
731
|
} from "@hiveai/core";
|
|
674
|
-
import { z as
|
|
732
|
+
import { z as z9 } from "zod";
|
|
675
733
|
var MemForFilesInputSchema = {
|
|
676
|
-
files:
|
|
677
|
-
include_module_contexts:
|
|
678
|
-
track:
|
|
734
|
+
files: z9.array(z9.string()).min(1).describe("Project-relative file paths the agent is currently working on"),
|
|
735
|
+
include_module_contexts: z9.boolean().default(true).describe("Inline the matching .ai/modules/<name>/context.md contents"),
|
|
736
|
+
track: z9.boolean().default(true).describe("Increment read_count on returned memories")
|
|
679
737
|
};
|
|
680
738
|
async function memForFiles(input, ctx) {
|
|
681
739
|
const inferred = inferModulesFromPaths(input.files);
|
|
682
|
-
if (!
|
|
740
|
+
if (!existsSync9(ctx.paths.memoriesDir)) {
|
|
683
741
|
return {
|
|
684
742
|
inferred_modules: inferred,
|
|
685
743
|
by_anchor: [],
|
|
@@ -688,8 +746,8 @@ async function memForFiles(input, ctx) {
|
|
|
688
746
|
module_contexts: await loadModuleContexts(ctx, inferred, input.include_module_contexts)
|
|
689
747
|
};
|
|
690
748
|
}
|
|
691
|
-
const all = await
|
|
692
|
-
const usage = await
|
|
749
|
+
const all = await loadMemoriesFromDir7(ctx.paths.memoriesDir);
|
|
750
|
+
const usage = await loadUsageIndex4(ctx.paths);
|
|
693
751
|
const seen = /* @__PURE__ */ new Set();
|
|
694
752
|
const byAnchor = [];
|
|
695
753
|
const byModule = [];
|
|
@@ -737,7 +795,7 @@ async function memForFiles(input, ctx) {
|
|
|
737
795
|
}
|
|
738
796
|
function toMatch(loaded, reason, usage) {
|
|
739
797
|
const fm = loaded.memory.frontmatter;
|
|
740
|
-
const u =
|
|
798
|
+
const u = getUsage3(usage, fm.id);
|
|
741
799
|
return {
|
|
742
800
|
id: fm.id,
|
|
743
801
|
scope: fm.scope,
|
|
@@ -801,7 +859,7 @@ function extractPathSegments(files) {
|
|
|
801
859
|
}
|
|
802
860
|
async function loadModuleContexts(ctx, modules, enabled) {
|
|
803
861
|
if (!enabled || modules.length === 0) return [];
|
|
804
|
-
if (!
|
|
862
|
+
if (!existsSync9(ctx.paths.modulesContextDir)) return [];
|
|
805
863
|
const available = new Set(
|
|
806
864
|
(await readdir2(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
807
865
|
);
|
|
@@ -809,7 +867,7 @@ async function loadModuleContexts(ctx, modules, enabled) {
|
|
|
809
867
|
for (const m of modules) {
|
|
810
868
|
if (!available.has(m)) continue;
|
|
811
869
|
const file = path4.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
812
|
-
if (
|
|
870
|
+
if (existsSync9(file)) {
|
|
813
871
|
out.push({ name: m, content: await readFile2(file, "utf8") });
|
|
814
872
|
}
|
|
815
873
|
}
|
|
@@ -817,26 +875,26 @@ async function loadModuleContexts(ctx, modules, enabled) {
|
|
|
817
875
|
}
|
|
818
876
|
|
|
819
877
|
// src/tools/mem-get.ts
|
|
820
|
-
import { existsSync as
|
|
878
|
+
import { existsSync as existsSync10 } from "fs";
|
|
821
879
|
import {
|
|
822
880
|
deriveConfidence as deriveConfidence3,
|
|
823
|
-
getUsage as
|
|
824
|
-
loadMemoriesFromDir as
|
|
825
|
-
loadUsageIndex as
|
|
881
|
+
getUsage as getUsage4,
|
|
882
|
+
loadMemoriesFromDir as loadMemoriesFromDir8,
|
|
883
|
+
loadUsageIndex as loadUsageIndex5
|
|
826
884
|
} from "@hiveai/core";
|
|
827
|
-
import { z as
|
|
885
|
+
import { z as z10 } from "zod";
|
|
828
886
|
var MemGetInputSchema = {
|
|
829
|
-
id:
|
|
887
|
+
id: z10.string().min(1).describe("Memory id to fetch")
|
|
830
888
|
};
|
|
831
889
|
async function memGet(input, ctx) {
|
|
832
|
-
if (!
|
|
890
|
+
if (!existsSync10(ctx.paths.memoriesDir)) {
|
|
833
891
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
834
892
|
}
|
|
835
|
-
const all = await
|
|
893
|
+
const all = await loadMemoriesFromDir8(ctx.paths.memoriesDir);
|
|
836
894
|
const found = all.find((m) => m.memory.frontmatter.id === input.id);
|
|
837
895
|
if (!found) throw new Error(`No memory with id "${input.id}".`);
|
|
838
896
|
const fm = found.memory.frontmatter;
|
|
839
|
-
const u =
|
|
897
|
+
const u = getUsage4(await loadUsageIndex5(ctx.paths), fm.id);
|
|
840
898
|
return {
|
|
841
899
|
id: fm.id,
|
|
842
900
|
scope: fm.scope,
|
|
@@ -861,32 +919,32 @@ async function memGet(input, ctx) {
|
|
|
861
919
|
}
|
|
862
920
|
|
|
863
921
|
// src/tools/mem-delete.ts
|
|
864
|
-
import { existsSync as
|
|
922
|
+
import { existsSync as existsSync11 } from "fs";
|
|
865
923
|
import { unlink } from "fs/promises";
|
|
866
924
|
import {
|
|
867
|
-
loadMemoriesFromDir as
|
|
868
|
-
loadUsageIndex as
|
|
869
|
-
saveUsageIndex as
|
|
925
|
+
loadMemoriesFromDir as loadMemoriesFromDir9,
|
|
926
|
+
loadUsageIndex as loadUsageIndex6,
|
|
927
|
+
saveUsageIndex as saveUsageIndex3
|
|
870
928
|
} from "@hiveai/core";
|
|
871
|
-
import { z as
|
|
929
|
+
import { z as z11 } from "zod";
|
|
872
930
|
var MemDeleteInputSchema = {
|
|
873
|
-
id:
|
|
874
|
-
keep_usage:
|
|
931
|
+
id: z11.string().min(1).describe("Memory id to delete"),
|
|
932
|
+
keep_usage: z11.boolean().default(false).describe("Keep the usage.json entry instead of removing it alongside the file")
|
|
875
933
|
};
|
|
876
934
|
async function memDelete(input, ctx) {
|
|
877
|
-
if (!
|
|
935
|
+
if (!existsSync11(ctx.paths.memoriesDir)) {
|
|
878
936
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
879
937
|
}
|
|
880
|
-
const all = await
|
|
938
|
+
const all = await loadMemoriesFromDir9(ctx.paths.memoriesDir);
|
|
881
939
|
const found = all.find((m) => m.memory.frontmatter.id === input.id);
|
|
882
940
|
if (!found) throw new Error(`No memory with id "${input.id}".`);
|
|
883
941
|
await unlink(found.filePath);
|
|
884
942
|
let usageRemoved = false;
|
|
885
943
|
if (!input.keep_usage) {
|
|
886
|
-
const idx = await
|
|
944
|
+
const idx = await loadUsageIndex6(ctx.paths);
|
|
887
945
|
if (idx.by_id[input.id]) {
|
|
888
946
|
delete idx.by_id[input.id];
|
|
889
|
-
await
|
|
947
|
+
await saveUsageIndex3(ctx.paths, idx);
|
|
890
948
|
usageRemoved = true;
|
|
891
949
|
}
|
|
892
950
|
}
|
|
@@ -895,24 +953,24 @@ async function memDelete(input, ctx) {
|
|
|
895
953
|
|
|
896
954
|
// src/tools/mem-update.ts
|
|
897
955
|
import { writeFile as writeFile5 } from "fs/promises";
|
|
898
|
-
import { existsSync as
|
|
899
|
-
import { loadMemoriesFromDir as
|
|
900
|
-
import { z as
|
|
956
|
+
import { existsSync as existsSync12 } from "fs";
|
|
957
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir10, serializeMemory as serializeMemory4 } from "@hiveai/core";
|
|
958
|
+
import { z as z12 } from "zod";
|
|
901
959
|
var MemUpdateInputSchema = {
|
|
902
|
-
id:
|
|
903
|
-
body:
|
|
904
|
-
tags:
|
|
905
|
-
paths:
|
|
906
|
-
symbols:
|
|
907
|
-
commit:
|
|
908
|
-
domain:
|
|
909
|
-
author:
|
|
960
|
+
id: z12.string().min(1).describe("Id of the memory to update"),
|
|
961
|
+
body: z12.string().optional().describe("New Markdown body \u2014 replaces the existing body"),
|
|
962
|
+
tags: z12.array(z12.string()).optional().describe("New tags array \u2014 fully replaces existing tags"),
|
|
963
|
+
paths: z12.array(z12.string()).optional().describe("New anchor paths \u2014 fully replaces existing anchor.paths"),
|
|
964
|
+
symbols: z12.array(z12.string()).optional().describe("New anchor symbols \u2014 fully replaces existing anchor.symbols"),
|
|
965
|
+
commit: z12.string().optional().describe("New anchor commit SHA"),
|
|
966
|
+
domain: z12.string().optional().describe("New domain label"),
|
|
967
|
+
author: z12.string().optional().describe("New author handle or email")
|
|
910
968
|
};
|
|
911
969
|
async function memUpdate(input, ctx) {
|
|
912
|
-
if (!
|
|
970
|
+
if (!existsSync12(ctx.paths.memoriesDir)) {
|
|
913
971
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
914
972
|
}
|
|
915
|
-
const memories = await
|
|
973
|
+
const memories = await loadMemoriesFromDir10(ctx.paths.memoriesDir);
|
|
916
974
|
const loaded = memories.find((m) => m.memory.frontmatter.id === input.id);
|
|
917
975
|
if (!loaded) throw new Error(`No memory with id "${input.id}".`);
|
|
918
976
|
const { frontmatter, body } = loaded.memory;
|
|
@@ -954,20 +1012,20 @@ async function memUpdate(input, ctx) {
|
|
|
954
1012
|
}
|
|
955
1013
|
|
|
956
1014
|
// src/tools/mem-pending.ts
|
|
957
|
-
import { existsSync as
|
|
1015
|
+
import { existsSync as existsSync13 } from "fs";
|
|
958
1016
|
import {
|
|
959
|
-
getUsage as
|
|
960
|
-
loadMemoriesFromDir as
|
|
961
|
-
loadUsageIndex as
|
|
1017
|
+
getUsage as getUsage5,
|
|
1018
|
+
loadMemoriesFromDir as loadMemoriesFromDir11,
|
|
1019
|
+
loadUsageIndex as loadUsageIndex7
|
|
962
1020
|
} from "@hiveai/core";
|
|
963
|
-
import { z as
|
|
1021
|
+
import { z as z13 } from "zod";
|
|
964
1022
|
var MemPendingInputSchema = {
|
|
965
|
-
scope:
|
|
1023
|
+
scope: z13.enum(["personal", "team", "module"]).optional()
|
|
966
1024
|
};
|
|
967
1025
|
async function memPending(input, ctx) {
|
|
968
|
-
if (!
|
|
969
|
-
const all = await
|
|
970
|
-
const usage = await
|
|
1026
|
+
if (!existsSync13(ctx.paths.memoriesDir)) return { pending: [] };
|
|
1027
|
+
const all = await loadMemoriesFromDir11(ctx.paths.memoriesDir);
|
|
1028
|
+
const usage = await loadUsageIndex7(ctx.paths);
|
|
971
1029
|
const now = Date.now();
|
|
972
1030
|
const proposed = all.filter(({ memory }) => {
|
|
973
1031
|
if (memory.frontmatter.status !== "proposed") return false;
|
|
@@ -975,12 +1033,12 @@ async function memPending(input, ctx) {
|
|
|
975
1033
|
return true;
|
|
976
1034
|
});
|
|
977
1035
|
proposed.sort(
|
|
978
|
-
(a, b) =>
|
|
1036
|
+
(a, b) => getUsage5(usage, b.memory.frontmatter.id).read_count - getUsage5(usage, a.memory.frontmatter.id).read_count
|
|
979
1037
|
);
|
|
980
1038
|
return {
|
|
981
1039
|
pending: proposed.map(({ memory, filePath }) => {
|
|
982
1040
|
const fm = memory.frontmatter;
|
|
983
|
-
const u =
|
|
1041
|
+
const u = getUsage5(usage, fm.id);
|
|
984
1042
|
return {
|
|
985
1043
|
id: fm.id,
|
|
986
1044
|
scope: fm.scope,
|
|
@@ -998,20 +1056,20 @@ async function memPending(input, ctx) {
|
|
|
998
1056
|
|
|
999
1057
|
// src/tools/mem-approve.ts
|
|
1000
1058
|
import { writeFile as writeFile6 } from "fs/promises";
|
|
1001
|
-
import { existsSync as
|
|
1059
|
+
import { existsSync as existsSync14 } from "fs";
|
|
1002
1060
|
import {
|
|
1003
|
-
loadMemoriesFromDir as
|
|
1061
|
+
loadMemoriesFromDir as loadMemoriesFromDir12,
|
|
1004
1062
|
serializeMemory as serializeMemory5
|
|
1005
1063
|
} from "@hiveai/core";
|
|
1006
|
-
import { z as
|
|
1064
|
+
import { z as z14 } from "zod";
|
|
1007
1065
|
var MemApproveInputSchema = {
|
|
1008
|
-
id:
|
|
1066
|
+
id: z14.string().min(1).describe("Memory id to approve (sets status=validated immediately)")
|
|
1009
1067
|
};
|
|
1010
1068
|
async function memApprove(input, ctx) {
|
|
1011
|
-
if (!
|
|
1069
|
+
if (!existsSync14(ctx.paths.memoriesDir)) {
|
|
1012
1070
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
1013
1071
|
}
|
|
1014
|
-
const all = await
|
|
1072
|
+
const all = await loadMemoriesFromDir12(ctx.paths.memoriesDir);
|
|
1015
1073
|
const found = all.find((m) => m.memory.frontmatter.id === input.id);
|
|
1016
1074
|
if (!found) throw new Error(`No memory with id "${input.id}".`);
|
|
1017
1075
|
const previous = found.memory.frontmatter.status;
|
|
@@ -1030,7 +1088,7 @@ async function memApprove(input, ctx) {
|
|
|
1030
1088
|
|
|
1031
1089
|
// src/tools/mem-tried.ts
|
|
1032
1090
|
import { mkdir as mkdir3, writeFile as writeFile7 } from "fs/promises";
|
|
1033
|
-
import { existsSync as
|
|
1091
|
+
import { existsSync as existsSync15 } from "fs";
|
|
1034
1092
|
import path5 from "path";
|
|
1035
1093
|
import {
|
|
1036
1094
|
buildFrontmatter as buildFrontmatter2,
|
|
@@ -1038,19 +1096,19 @@ import {
|
|
|
1038
1096
|
serializeMemory as serializeMemory6,
|
|
1039
1097
|
suggestSensorFromMemory as suggestSensorFromMemory2
|
|
1040
1098
|
} from "@hiveai/core";
|
|
1041
|
-
import { z as
|
|
1099
|
+
import { z as z15 } from "zod";
|
|
1042
1100
|
var MemTriedInputSchema = {
|
|
1043
|
-
what:
|
|
1044
|
-
why_failed:
|
|
1045
|
-
instead:
|
|
1046
|
-
scope:
|
|
1047
|
-
module:
|
|
1048
|
-
tags:
|
|
1049
|
-
paths:
|
|
1050
|
-
author:
|
|
1101
|
+
what: z15.string().min(1).describe("Brief description of the approach that was tried"),
|
|
1102
|
+
why_failed: z15.string().min(1).describe("Why it failed or why it should NOT be used"),
|
|
1103
|
+
instead: z15.string().optional().describe("What to use or do instead (recommended alternative)"),
|
|
1104
|
+
scope: z15.enum(["personal", "team", "module"]).default("personal").describe("Visibility scope"),
|
|
1105
|
+
module: z15.string().optional().describe("Module name (required when scope=module)"),
|
|
1106
|
+
tags: z15.array(z15.string()).default([]).describe("Tags for filtering"),
|
|
1107
|
+
paths: z15.array(z15.string()).default([]).describe("Anchor file paths this applies to"),
|
|
1108
|
+
author: z15.string().optional().describe("Author handle or email")
|
|
1051
1109
|
};
|
|
1052
1110
|
async function memTried(input, ctx) {
|
|
1053
|
-
if (!
|
|
1111
|
+
if (!existsSync15(ctx.paths.haiveDir)) {
|
|
1054
1112
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
|
|
1055
1113
|
}
|
|
1056
1114
|
const slug = input.what.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 5).join("-");
|
|
@@ -1076,7 +1134,7 @@ async function memTried(input, ctx) {
|
|
|
1076
1134
|
}
|
|
1077
1135
|
const file = memoryFilePath2(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
1078
1136
|
await mkdir3(path5.dirname(file), { recursive: true });
|
|
1079
|
-
if (
|
|
1137
|
+
if (existsSync15(file)) {
|
|
1080
1138
|
throw new Error(`Memory already exists at ${file}`);
|
|
1081
1139
|
}
|
|
1082
1140
|
await writeFile7(file, serializeMemory6({ frontmatter, body }), "utf8");
|
|
@@ -1085,7 +1143,7 @@ async function memTried(input, ctx) {
|
|
|
1085
1143
|
|
|
1086
1144
|
// src/tools/mem-observe.ts
|
|
1087
1145
|
import { mkdir as mkdir4, writeFile as writeFile8 } from "fs/promises";
|
|
1088
|
-
import { existsSync as
|
|
1146
|
+
import { existsSync as existsSync16 } from "fs";
|
|
1089
1147
|
import path6 from "path";
|
|
1090
1148
|
import {
|
|
1091
1149
|
buildFrontmatter as buildFrontmatter3,
|
|
@@ -1093,22 +1151,22 @@ import {
|
|
|
1093
1151
|
memoryFilePath as memoryFilePath3,
|
|
1094
1152
|
serializeMemory as serializeMemory7
|
|
1095
1153
|
} from "@hiveai/core";
|
|
1096
|
-
import { z as
|
|
1154
|
+
import { z as z16 } from "zod";
|
|
1097
1155
|
var MemObserveInputSchema = {
|
|
1098
|
-
what:
|
|
1099
|
-
where:
|
|
1100
|
-
impact:
|
|
1101
|
-
fix:
|
|
1102
|
-
scope:
|
|
1103
|
-
module:
|
|
1104
|
-
tags:
|
|
1105
|
-
author:
|
|
1106
|
-
force:
|
|
1156
|
+
what: z16.string().min(1).describe("Short title: what did you observe? (e.g. 'MobilePaymentController has two @RequestBody on handleWebhook')"),
|
|
1157
|
+
where: z16.string().min(1).describe("File path(s) where the issue lives \u2014 be specific"),
|
|
1158
|
+
impact: z16.string().min(1).describe("What breaks or could break because of this (e.g. 'Spring MVC rejects the handler at startup')"),
|
|
1159
|
+
fix: z16.string().optional().describe("Suggested fix or workaround (optional \u2014 leave empty if unknown)"),
|
|
1160
|
+
scope: z16.enum(["personal", "team", "module"]).default("team").describe("Visibility scope \u2014 defaults to team since discoveries benefit everyone"),
|
|
1161
|
+
module: z16.string().optional().describe("Module name (required when scope=module)"),
|
|
1162
|
+
tags: z16.array(z16.string()).default([]).describe("Tags for filtering"),
|
|
1163
|
+
author: z16.string().optional().describe("Author handle or email"),
|
|
1164
|
+
force: z16.boolean().default(false).describe(
|
|
1107
1165
|
"Save even if the observation looks like generic, guessable knowledge. By default, low-specificity observations (things a capable model already knows) are SKIPPED to keep the corpus high-signal \u2014 only unguessable, team-specific discoveries are worth storing."
|
|
1108
1166
|
)
|
|
1109
1167
|
};
|
|
1110
1168
|
async function memObserve(input, ctx) {
|
|
1111
|
-
if (!
|
|
1169
|
+
if (!existsSync16(ctx.paths.haiveDir)) {
|
|
1112
1170
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
|
|
1113
1171
|
}
|
|
1114
1172
|
const signalText = [input.what, input.impact, input.fix ?? ""].join(" ");
|
|
@@ -1142,7 +1200,7 @@ async function memObserve(input, ctx) {
|
|
|
1142
1200
|
const body = lines.join("\n") + "\n";
|
|
1143
1201
|
const file = memoryFilePath3(ctx.paths, frontmatter.scope, frontmatter.id, frontmatter.module);
|
|
1144
1202
|
await mkdir4(path6.dirname(file), { recursive: true });
|
|
1145
|
-
if (
|
|
1203
|
+
if (existsSync16(file)) {
|
|
1146
1204
|
throw new Error(`Memory already exists at ${file}`);
|
|
1147
1205
|
}
|
|
1148
1206
|
await writeFile8(file, serializeMemory7({ frontmatter, body }), "utf8");
|
|
@@ -1151,15 +1209,15 @@ async function memObserve(input, ctx) {
|
|
|
1151
1209
|
|
|
1152
1210
|
// src/tools/mem-session-end.ts
|
|
1153
1211
|
import { writeFile as writeFile10, mkdir as mkdir6 } from "fs/promises";
|
|
1154
|
-
import { existsSync as
|
|
1212
|
+
import { existsSync as existsSync18 } from "fs";
|
|
1155
1213
|
import path8 from "path";
|
|
1156
1214
|
import {
|
|
1157
1215
|
buildFrontmatter as buildFrontmatter4,
|
|
1158
|
-
loadMemoriesFromDir as
|
|
1216
|
+
loadMemoriesFromDir as loadMemoriesFromDir13,
|
|
1159
1217
|
memoryFilePath as memoryFilePath4,
|
|
1160
1218
|
serializeMemory as serializeMemory8
|
|
1161
1219
|
} from "@hiveai/core";
|
|
1162
|
-
import { z as
|
|
1220
|
+
import { z as z17 } from "zod";
|
|
1163
1221
|
|
|
1164
1222
|
// src/session-tracker.ts
|
|
1165
1223
|
import {
|
|
@@ -1168,7 +1226,7 @@ import {
|
|
|
1168
1226
|
loadConfig as loadConfig2
|
|
1169
1227
|
} from "@hiveai/core";
|
|
1170
1228
|
import { mkdir as mkdir5, writeFile as writeFile9, rm } from "fs/promises";
|
|
1171
|
-
import { existsSync as
|
|
1229
|
+
import { existsSync as existsSync17 } from "fs";
|
|
1172
1230
|
import path7 from "path";
|
|
1173
1231
|
import { execSync } from "child_process";
|
|
1174
1232
|
function pendingDistillPath(ctx) {
|
|
@@ -1251,7 +1309,7 @@ var SessionTracker = class {
|
|
|
1251
1309
|
(e) => e.tool === "mem_session_end" && !e.summary?.startsWith("Auto-captured")
|
|
1252
1310
|
);
|
|
1253
1311
|
const isSubstantialSession = totalCalls >= 3 || writingTools.length > 0;
|
|
1254
|
-
if (!ranPostTask && isSubstantialSession &&
|
|
1312
|
+
if (!ranPostTask && isSubstantialSession && existsSync17(this.ctx.paths.haiveDir)) {
|
|
1255
1313
|
try {
|
|
1256
1314
|
const memoriesSaved = writingTools.map((e) => e.summary ?? "").filter(Boolean).slice(0, 20);
|
|
1257
1315
|
const payload = {
|
|
@@ -1285,7 +1343,7 @@ var SessionTracker = class {
|
|
|
1285
1343
|
};
|
|
1286
1344
|
async function clearPendingDistill(ctx) {
|
|
1287
1345
|
const p = pendingDistillPath(ctx);
|
|
1288
|
-
if (
|
|
1346
|
+
if (existsSync17(p)) {
|
|
1289
1347
|
try {
|
|
1290
1348
|
await rm(p);
|
|
1291
1349
|
} catch {
|
|
@@ -1302,15 +1360,15 @@ function summarizeTools(events) {
|
|
|
1302
1360
|
|
|
1303
1361
|
// src/tools/mem-session-end.ts
|
|
1304
1362
|
var MemSessionEndInputSchema = {
|
|
1305
|
-
goal:
|
|
1306
|
-
accomplished:
|
|
1307
|
-
discoveries:
|
|
1363
|
+
goal: z17.string().min(1).describe("What you were trying to accomplish this session (1\u20132 sentences)"),
|
|
1364
|
+
accomplished: z17.string().describe("What was actually done \u2014 bullet list recommended"),
|
|
1365
|
+
discoveries: z17.string().default("").describe(
|
|
1308
1366
|
"Any bugs, inconsistencies, surprises, or missing knowledge found during this session. Empty if nothing surprising was found."
|
|
1309
1367
|
),
|
|
1310
|
-
files_touched:
|
|
1311
|
-
next_steps:
|
|
1312
|
-
scope:
|
|
1313
|
-
module:
|
|
1368
|
+
files_touched: z17.array(z17.string()).default([]).describe("Key files that were read or modified \u2014 used as anchor paths"),
|
|
1369
|
+
next_steps: z17.string().default("").describe("What should happen next (for the next session or a teammate)"),
|
|
1370
|
+
scope: z17.enum(["personal", "team", "module"]).default("personal").describe("Visibility: personal = private to you, team = shared with the team"),
|
|
1371
|
+
module: z17.string().optional().describe("Module name (required when scope=module)")
|
|
1314
1372
|
};
|
|
1315
1373
|
function recapTopic(scope, module) {
|
|
1316
1374
|
return module ? `session-recap-${scope}-${module}` : `session-recap-${scope}`;
|
|
@@ -1340,7 +1398,7 @@ ${input.next_steps}`);
|
|
|
1340
1398
|
return lines.join("\n");
|
|
1341
1399
|
}
|
|
1342
1400
|
async function memSessionEnd(input, ctx) {
|
|
1343
|
-
if (!
|
|
1401
|
+
if (!existsSync18(ctx.paths.haiveDir)) {
|
|
1344
1402
|
throw new Error(`No .ai/ directory at ${ctx.paths.root}. Run 'haive init' first.`);
|
|
1345
1403
|
}
|
|
1346
1404
|
const body = buildBody(input);
|
|
@@ -1351,12 +1409,12 @@ async function memSessionEnd(input, ctx) {
|
|
|
1351
1409
|
return rel.startsWith("..") ? p : rel;
|
|
1352
1410
|
});
|
|
1353
1411
|
const invalidPaths = normalizedFiles.filter(
|
|
1354
|
-
(p) => !
|
|
1412
|
+
(p) => !existsSync18(path8.resolve(ctx.paths.root, p))
|
|
1355
1413
|
);
|
|
1356
1414
|
if (invalidPaths.length > 0) {
|
|
1357
1415
|
console.warn(`[haive] session end: anchor path(s) not found: ${invalidPaths.join(", ")}`);
|
|
1358
1416
|
}
|
|
1359
|
-
const existing =
|
|
1417
|
+
const existing = existsSync18(ctx.paths.memoriesDir) ? await loadMemoriesFromDir13(ctx.paths.memoriesDir) : [];
|
|
1360
1418
|
const topicMatch = existing.find(
|
|
1361
1419
|
({ memory }) => memory.frontmatter.topic === topic && memory.frontmatter.scope === input.scope && (!input.module || memory.frontmatter.module === input.module)
|
|
1362
1420
|
);
|
|
@@ -1416,14 +1474,16 @@ async function memSessionEnd(input, ctx) {
|
|
|
1416
1474
|
|
|
1417
1475
|
// src/tools/get-briefing.ts
|
|
1418
1476
|
import { readFile as readFile4, writeFile as writeFile11 } from "fs/promises";
|
|
1419
|
-
import { existsSync as
|
|
1477
|
+
import { existsSync as existsSync20 } from "fs";
|
|
1420
1478
|
import {
|
|
1421
1479
|
allocateBudget,
|
|
1480
|
+
computeImpact as computeImpact2,
|
|
1422
1481
|
DEFAULT_AUTO_PROMOTE_RULE,
|
|
1423
1482
|
deriveConfidence as deriveConfidence4,
|
|
1424
1483
|
estimateTokens,
|
|
1484
|
+
evaluateSkillActivation,
|
|
1425
1485
|
extractActionsBriefBody,
|
|
1426
|
-
getUsage as
|
|
1486
|
+
getUsage as getUsage6,
|
|
1427
1487
|
inferModulesFromPaths as inferModulesFromPaths2,
|
|
1428
1488
|
isAutoPromoteEligible,
|
|
1429
1489
|
isDecaying,
|
|
@@ -1432,8 +1492,8 @@ import {
|
|
|
1432
1492
|
literalMatchesAnyToken as literalMatchesAnyToken2,
|
|
1433
1493
|
loadCodeMap,
|
|
1434
1494
|
loadConfig as loadConfig3,
|
|
1435
|
-
loadMemoriesFromDir as
|
|
1436
|
-
loadUsageIndex as
|
|
1495
|
+
loadMemoriesFromDir as loadMemoriesFromDir14,
|
|
1496
|
+
loadUsageIndex as loadUsageIndex8,
|
|
1437
1497
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths2,
|
|
1438
1498
|
queryCodeMap,
|
|
1439
1499
|
resolveBriefingBudget,
|
|
@@ -1445,11 +1505,11 @@ import {
|
|
|
1445
1505
|
truncateToTokens,
|
|
1446
1506
|
writeBriefingMarker
|
|
1447
1507
|
} from "@hiveai/core";
|
|
1448
|
-
import { z as
|
|
1508
|
+
import { z as z18 } from "zod";
|
|
1449
1509
|
|
|
1450
1510
|
// src/tools/briefing-helpers.ts
|
|
1451
1511
|
import { readdir as readdir3, readFile as readFile3 } from "fs/promises";
|
|
1452
|
-
import { existsSync as
|
|
1512
|
+
import { existsSync as existsSync19 } from "fs";
|
|
1453
1513
|
import path9 from "path";
|
|
1454
1514
|
import { isGlobPath, isStackPackSeed, pathsOverlap } from "@hiveai/core";
|
|
1455
1515
|
function compactSummary(body) {
|
|
@@ -1576,7 +1636,7 @@ async function trySemanticHits(ctx, task, limit) {
|
|
|
1576
1636
|
}
|
|
1577
1637
|
async function loadModuleContexts2(ctx, modules) {
|
|
1578
1638
|
if (modules.length === 0) return [];
|
|
1579
|
-
if (!
|
|
1639
|
+
if (!existsSync19(ctx.paths.modulesContextDir)) return [];
|
|
1580
1640
|
const available = new Set(
|
|
1581
1641
|
(await readdir3(ctx.paths.modulesContextDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name)
|
|
1582
1642
|
);
|
|
@@ -1584,7 +1644,7 @@ async function loadModuleContexts2(ctx, modules) {
|
|
|
1584
1644
|
for (const m of modules) {
|
|
1585
1645
|
if (!available.has(m)) continue;
|
|
1586
1646
|
const file = path9.join(ctx.paths.modulesContextDir, m, "context.md");
|
|
1587
|
-
if (
|
|
1647
|
+
if (existsSync19(file)) {
|
|
1588
1648
|
out.push({ name: m, content: await readFile3(file, "utf8") });
|
|
1589
1649
|
}
|
|
1590
1650
|
}
|
|
@@ -1593,35 +1653,35 @@ async function loadModuleContexts2(ctx, modules) {
|
|
|
1593
1653
|
|
|
1594
1654
|
// src/tools/get-briefing.ts
|
|
1595
1655
|
var GetBriefingInputSchema = {
|
|
1596
|
-
task:
|
|
1656
|
+
task: z18.string().optional().describe(
|
|
1597
1657
|
"What you are about to do, in 1\u20132 sentences. Used to rank relevant memories semantically."
|
|
1598
1658
|
),
|
|
1599
|
-
files:
|
|
1600
|
-
max_tokens:
|
|
1659
|
+
files: z18.array(z18.string()).default([]).describe("Project-relative file paths the agent is currently looking at or about to edit"),
|
|
1660
|
+
max_tokens: z18.number().int().positive().default(8e3).describe(
|
|
1601
1661
|
"Approximate token budget for the entire briefing. Each section is allocated a share and truncated to fit."
|
|
1602
1662
|
),
|
|
1603
|
-
max_memories:
|
|
1604
|
-
include_project_context:
|
|
1605
|
-
include_module_contexts:
|
|
1606
|
-
semantic:
|
|
1663
|
+
max_memories: z18.number().int().positive().default(8).describe("Cap on memories surfaced regardless of token budget"),
|
|
1664
|
+
include_project_context: z18.boolean().default(true),
|
|
1665
|
+
include_module_contexts: z18.boolean().default(true),
|
|
1666
|
+
semantic: z18.boolean().default(true).describe(
|
|
1607
1667
|
"Use semantic ranking when a task is provided (requires `haive embeddings index`)."
|
|
1608
1668
|
),
|
|
1609
|
-
include_stale:
|
|
1610
|
-
track:
|
|
1611
|
-
format:
|
|
1669
|
+
include_stale: z18.boolean().default(false).describe("Include stale memories (excluded by default \u2014 they may be outdated)"),
|
|
1670
|
+
track: z18.boolean().default(true).describe("Increment read_count on returned memories"),
|
|
1671
|
+
format: z18.enum(["full", "compact", "actions"]).default("full").describe(
|
|
1612
1672
|
"Output format: 'full' returns memory bodies (honors token budget via truncation); 'compact' returns a 1-line summary per memory (call mem_get for detail); 'actions' squeezes bodies to actionable bullet lines \u2014 fewer tokens vs full."
|
|
1613
1673
|
),
|
|
1614
|
-
symbols:
|
|
1674
|
+
symbols: z18.array(z18.string()).default([]).describe(
|
|
1615
1675
|
"Symbol names to look up in the code-map (e.g. ['PaymentService', 'TenantFilter']). Returns the file(s) exporting each symbol so agents don't need to grep. Requires `haive index code` to have been run."
|
|
1616
1676
|
),
|
|
1617
|
-
min_semantic_score:
|
|
1677
|
+
min_semantic_score: z18.number().min(0).max(1).default(0).describe(
|
|
1618
1678
|
"Drop semantic-only memory hits whose cosine score is below this threshold. Useful to avoid weakly-related noise when the task is short or the corpus is broad. Has no effect on memories matched via anchor/module/literal \u2014 those are always kept. Try 0.25\u20130.4 for stricter matching."
|
|
1619
1679
|
),
|
|
1620
|
-
budget_preset:
|
|
1680
|
+
budget_preset: z18.enum(["quick", "balanced", "deep"]).optional().describe(
|
|
1621
1681
|
"Shortcut token budget: 'quick' minimizes tokens/skip module CONTEXT slices; 'balanced' mirrors historical defaults; 'deep' uses a larger briefing. When set, overrides max_tokens, max_memories, and include_module_contexts."
|
|
1622
1682
|
)
|
|
1623
1683
|
};
|
|
1624
|
-
var GetBriefingZod =
|
|
1684
|
+
var GetBriefingZod = z18.object(GetBriefingInputSchema);
|
|
1625
1685
|
async function getBriefing(input, ctx) {
|
|
1626
1686
|
const resolvedBudget = resolveBriefingBudget(input.budget_preset, {
|
|
1627
1687
|
max_tokens: input.max_tokens,
|
|
@@ -1637,8 +1697,8 @@ async function getBriefing(input, ctx) {
|
|
|
1637
1697
|
let usage = { version: 1, updated_at: "", by_id: {} };
|
|
1638
1698
|
let byId = /* @__PURE__ */ new Map();
|
|
1639
1699
|
let lastSession;
|
|
1640
|
-
if (
|
|
1641
|
-
const allLoaded = await
|
|
1700
|
+
if (existsSync20(ctx.paths.memoriesDir)) {
|
|
1701
|
+
const allLoaded = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
|
|
1642
1702
|
const recaps = allLoaded.filter(({ memory }) => memory.frontmatter.type === "session_recap").sort(
|
|
1643
1703
|
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
1644
1704
|
);
|
|
@@ -1660,7 +1720,7 @@ async function getBriefing(input, ctx) {
|
|
|
1660
1720
|
if (memory.frontmatter.type === "session_recap") return false;
|
|
1661
1721
|
return true;
|
|
1662
1722
|
});
|
|
1663
|
-
usage = await
|
|
1723
|
+
usage = await loadUsageIndex8(ctx.paths);
|
|
1664
1724
|
byId = new Map(allMemories.map((m) => [m.memory.frontmatter.id, m]));
|
|
1665
1725
|
const semanticHits = input.task && input.semantic ? await trySemanticHits(ctx, input.task, allMemories.length * 2) : null;
|
|
1666
1726
|
if (input.task && input.semantic) {
|
|
@@ -1682,7 +1742,8 @@ async function getBriefing(input, ctx) {
|
|
|
1682
1742
|
}
|
|
1683
1743
|
return;
|
|
1684
1744
|
}
|
|
1685
|
-
const u =
|
|
1745
|
+
const u = getUsage6(usage, fm.id);
|
|
1746
|
+
const imp = computeImpact2(fm, u);
|
|
1686
1747
|
seen.set(fm.id, {
|
|
1687
1748
|
id: fm.id,
|
|
1688
1749
|
scope: fm.scope,
|
|
@@ -1693,6 +1754,8 @@ async function getBriefing(input, ctx) {
|
|
|
1693
1754
|
confidence: deriveConfidence4(fm, u),
|
|
1694
1755
|
...fm.status === "draft" || fm.status === "proposed" ? { unverified: true } : {},
|
|
1695
1756
|
read_count: u.read_count,
|
|
1757
|
+
impact_score: imp.score,
|
|
1758
|
+
impact_tier: imp.tier,
|
|
1696
1759
|
reasons: [reason],
|
|
1697
1760
|
match_quality: matchQuality ?? "partial",
|
|
1698
1761
|
...score !== void 0 ? { semantic_score: score } : {},
|
|
@@ -1736,11 +1799,28 @@ async function getBriefing(input, ctx) {
|
|
|
1736
1799
|
}
|
|
1737
1800
|
}
|
|
1738
1801
|
}
|
|
1802
|
+
const activatedSkills = /* @__PURE__ */ new Set();
|
|
1803
|
+
for (const [id, m] of seen) {
|
|
1804
|
+
if (m.type !== "skill") continue;
|
|
1805
|
+
const loaded = byId.get(id);
|
|
1806
|
+
if (!loaded) continue;
|
|
1807
|
+
const act = evaluateSkillActivation(loaded.memory.frontmatter, {
|
|
1808
|
+
task: input.task,
|
|
1809
|
+
files: input.files
|
|
1810
|
+
});
|
|
1811
|
+
if (act.applicable && !act.activated) {
|
|
1812
|
+
seen.delete(id);
|
|
1813
|
+
continue;
|
|
1814
|
+
}
|
|
1815
|
+
if (act.applicable && act.activated) activatedSkills.add(id);
|
|
1816
|
+
}
|
|
1739
1817
|
const ranked = [...seen.values()].sort((a, b) => {
|
|
1740
1818
|
const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + (m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("symbol") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
|
|
1741
1819
|
const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
|
|
1742
|
-
const
|
|
1743
|
-
const
|
|
1820
|
+
const impactScore = (m) => (m.impact_score ?? 0) * 3;
|
|
1821
|
+
const activationBoost = (m) => activatedSkills.has(m.id) ? 5 : 0;
|
|
1822
|
+
const sa = priorityRank(classifyMemoryPriority(a, byId.get(a.id), input.files, input.symbols)) * 100 + reasonScore(a) + confidenceScore(a) + impactScore(a) + activationBoost(a) + (a.semantic_score ?? 0);
|
|
1823
|
+
const sb = priorityRank(classifyMemoryPriority(b, byId.get(b.id), input.files, input.symbols)) * 100 + reasonScore(b) + confidenceScore(b) + impactScore(b) + activationBoost(b) + (b.semantic_score ?? 0);
|
|
1744
1824
|
return sb - sa;
|
|
1745
1825
|
});
|
|
1746
1826
|
for (const mem of ranked.slice(0, briefingMaxMemories)) {
|
|
@@ -1756,7 +1836,7 @@ async function getBriefing(input, ctx) {
|
|
|
1756
1836
|
memories.push(...ranked.slice(0, briefingMaxMemories));
|
|
1757
1837
|
if (input.track && memories.length > 0) {
|
|
1758
1838
|
await trackReads3(ctx.paths, memories.map((m) => m.id));
|
|
1759
|
-
const freshUsage = await
|
|
1839
|
+
const freshUsage = await loadUsageIndex8(ctx.paths);
|
|
1760
1840
|
const cfg = await loadConfig3(ctx.paths);
|
|
1761
1841
|
const rule = {
|
|
1762
1842
|
minReads: cfg.autoPromoteMinReads ?? DEFAULT_AUTO_PROMOTE_RULE.minReads,
|
|
@@ -1765,7 +1845,7 @@ async function getBriefing(input, ctx) {
|
|
|
1765
1845
|
for (const m of memories) {
|
|
1766
1846
|
const loaded = byId.get(m.id);
|
|
1767
1847
|
if (!loaded) continue;
|
|
1768
|
-
const u =
|
|
1848
|
+
const u = getUsage6(freshUsage, m.id);
|
|
1769
1849
|
if (!isAutoPromoteEligible(loaded.memory.frontmatter, u, rule)) continue;
|
|
1770
1850
|
const newFm = { ...loaded.memory.frontmatter, status: "validated" };
|
|
1771
1851
|
try {
|
|
@@ -1777,12 +1857,12 @@ async function getBriefing(input, ctx) {
|
|
|
1777
1857
|
}
|
|
1778
1858
|
}
|
|
1779
1859
|
}
|
|
1780
|
-
const projectContextRaw = input.include_project_context &&
|
|
1860
|
+
const projectContextRaw = input.include_project_context && existsSync20(ctx.paths.projectContext) ? await readFile4(ctx.paths.projectContext, "utf8") : "";
|
|
1781
1861
|
const isTemplateContext = projectContextRaw.includes("TODO \u2014 high-level overview") || projectContextRaw.includes("Generated by `haive init`");
|
|
1782
1862
|
const setupWarnings = [];
|
|
1783
1863
|
let autoContextGenerated = false;
|
|
1784
1864
|
let projectContext = isTemplateContext ? "" : projectContextRaw;
|
|
1785
|
-
if ((isTemplateContext || !
|
|
1865
|
+
if ((isTemplateContext || !existsSync20(ctx.paths.projectContext)) && input.include_project_context) {
|
|
1786
1866
|
const haiveConfig = await loadConfig3(ctx.paths);
|
|
1787
1867
|
if (haiveConfig.autoContext) {
|
|
1788
1868
|
const codeMap = await loadCodeMap(ctx.paths);
|
|
@@ -1878,7 +1958,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1878
1958
|
const totalTokens = projectSlice.estimatedTokens + modulesSlice.estimatedTokens + memoriesSlice.estimatedTokens;
|
|
1879
1959
|
const decayWarnings = [];
|
|
1880
1960
|
for (const m of trimmedMemories) {
|
|
1881
|
-
const u =
|
|
1961
|
+
const u = getUsage6(usage, m.id);
|
|
1882
1962
|
const loaded = byId.get(m.id);
|
|
1883
1963
|
const createdAt = loaded?.memory.frontmatter.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1884
1964
|
if (isDecaying(u, createdAt)) decayWarnings.push(m.id);
|
|
@@ -1943,8 +2023,8 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1943
2023
|
actionRequired.push(extractActionItem(m.id, loaded.memory.body));
|
|
1944
2024
|
}
|
|
1945
2025
|
}
|
|
1946
|
-
if (
|
|
1947
|
-
const allMems = await
|
|
2026
|
+
if (existsSync20(ctx.paths.memoriesDir)) {
|
|
2027
|
+
const allMems = await loadMemoriesFromDir14(ctx.paths.memoriesDir);
|
|
1948
2028
|
for (const { memory } of allMems) {
|
|
1949
2029
|
const fm = memory.frontmatter;
|
|
1950
2030
|
if (!fm.requires_human_approval) continue;
|
|
@@ -1954,7 +2034,7 @@ ${m.content}`).join("\n\n---\n\n"),
|
|
|
1954
2034
|
}
|
|
1955
2035
|
}
|
|
1956
2036
|
const pendingDistillFile = pendingDistillPath(ctx);
|
|
1957
|
-
if (
|
|
2037
|
+
if (existsSync20(pendingDistillFile)) {
|
|
1958
2038
|
try {
|
|
1959
2039
|
const raw = await readFile4(pendingDistillFile, "utf8");
|
|
1960
2040
|
const pd = JSON.parse(raw);
|
|
@@ -1983,7 +2063,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
1983
2063
|
}
|
|
1984
2064
|
}
|
|
1985
2065
|
const memoriesEmpty = outputMemories.length === 0;
|
|
1986
|
-
const hasMemoriesDir =
|
|
2066
|
+
const hasMemoriesDir = existsSync20(ctx.paths.memoriesDir);
|
|
1987
2067
|
const isColdStart = isTemplateContext && memoriesEmpty && !lastSession && !autoContextGenerated;
|
|
1988
2068
|
const hasUnguessableSignal = outputMemories.some(
|
|
1989
2069
|
(m) => (m.priority === "must_read" || m.priority === "useful") && specificityScore(m.body) >= GUESSABLE_THRESHOLD
|
|
@@ -2030,7 +2110,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
2030
2110
|
"No team-specific policy matched these files/task \u2014 nothing here a capable model can't infer. The auto-generated project context was trimmed to keep this briefing near-zero-cost; proceed with normal Read/Grep."
|
|
2031
2111
|
);
|
|
2032
2112
|
}
|
|
2033
|
-
if (
|
|
2113
|
+
if (existsSync20(ctx.paths.haiveDir)) {
|
|
2034
2114
|
await writeBriefingMarker(ctx.paths, {
|
|
2035
2115
|
sessionId: process.env.HAIVE_SESSION_ID,
|
|
2036
2116
|
...input.task ? { task: input.task } : {},
|
|
@@ -2081,19 +2161,19 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
|
|
|
2081
2161
|
|
|
2082
2162
|
// src/tools/code-map.ts
|
|
2083
2163
|
import { estimateTokens as estimateTokens2, loadCodeMap as loadCodeMap2, queryCodeMap as queryCodeMap2 } from "@hiveai/core";
|
|
2084
|
-
import { z as
|
|
2164
|
+
import { z as z19 } from "zod";
|
|
2085
2165
|
var CodeMapInputSchema = {
|
|
2086
|
-
file:
|
|
2087
|
-
symbol:
|
|
2088
|
-
paths:
|
|
2166
|
+
file: z19.string().optional().describe("Filter to files whose path contains this substring"),
|
|
2167
|
+
symbol: z19.string().optional().describe("Filter to files exporting a symbol whose name contains this substring"),
|
|
2168
|
+
paths: z19.array(z19.string()).default([]).describe(
|
|
2089
2169
|
"Filter to files under any of these path prefixes (e.g. ['packages/mcp/src/tools/', 'src/auth/']). OR-joined with `file` substring; useful to get a focused view of one module."
|
|
2090
2170
|
),
|
|
2091
|
-
max_files:
|
|
2092
|
-
max_tokens:
|
|
2171
|
+
max_files: z19.number().int().positive().default(40).describe("Cap on returned files (hard limit, applied after token budget)"),
|
|
2172
|
+
max_tokens: z19.number().int().positive().optional().describe(
|
|
2093
2173
|
"Approximate token budget for the response. When the matching set exceeds it, files are ranked by export density (exports per LOC) and the highest-signal ones are kept first. Omit to disable budgeting (legacy behavior)."
|
|
2094
2174
|
)
|
|
2095
2175
|
};
|
|
2096
|
-
var CodeMapInputZod =
|
|
2176
|
+
var CodeMapInputZod = z19.object(CodeMapInputSchema);
|
|
2097
2177
|
async function codeMapTool(input, ctx) {
|
|
2098
2178
|
const map = await loadCodeMap2(ctx.paths);
|
|
2099
2179
|
if (!map) {
|
|
@@ -2162,18 +2242,18 @@ function estimateFileEntryTokens(f) {
|
|
|
2162
2242
|
}
|
|
2163
2243
|
|
|
2164
2244
|
// src/tools/mem-diff.ts
|
|
2165
|
-
import { existsSync as
|
|
2166
|
-
import { loadMemoriesFromDir as
|
|
2167
|
-
import { z as
|
|
2245
|
+
import { existsSync as existsSync21 } from "fs";
|
|
2246
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir15 } from "@hiveai/core";
|
|
2247
|
+
import { z as z20 } from "zod";
|
|
2168
2248
|
var MemDiffInputSchema = {
|
|
2169
|
-
id_a:
|
|
2170
|
-
id_b:
|
|
2249
|
+
id_a: z20.string().min(1).describe("First memory id"),
|
|
2250
|
+
id_b: z20.string().min(1).describe("Second memory id")
|
|
2171
2251
|
};
|
|
2172
2252
|
async function memDiff(input, ctx) {
|
|
2173
|
-
if (!
|
|
2253
|
+
if (!existsSync21(ctx.paths.memoriesDir)) {
|
|
2174
2254
|
throw new Error(`No .ai/memories at ${ctx.paths.root}.`);
|
|
2175
2255
|
}
|
|
2176
|
-
const all = await
|
|
2256
|
+
const all = await loadMemoriesFromDir15(ctx.paths.memoriesDir);
|
|
2177
2257
|
const foundA = all.find((m) => m.memory.frontmatter.id === input.id_a);
|
|
2178
2258
|
const foundB = all.find((m) => m.memory.frontmatter.id === input.id_b);
|
|
2179
2259
|
if (!foundA) throw new Error(`No memory with id "${input.id_a}".`);
|
|
@@ -2207,19 +2287,19 @@ async function memDiff(input, ctx) {
|
|
|
2207
2287
|
}
|
|
2208
2288
|
|
|
2209
2289
|
// src/tools/get-recap.ts
|
|
2210
|
-
import { existsSync as
|
|
2211
|
-
import { loadMemoriesFromDir as
|
|
2212
|
-
import { z as
|
|
2290
|
+
import { existsSync as existsSync22 } from "fs";
|
|
2291
|
+
import { loadMemoriesFromDir as loadMemoriesFromDir16 } from "@hiveai/core";
|
|
2292
|
+
import { z as z21 } from "zod";
|
|
2213
2293
|
var GetRecapInputSchema = {
|
|
2214
|
-
scope:
|
|
2294
|
+
scope: z21.enum(["personal", "team", "any"]).default("any").describe(
|
|
2215
2295
|
"Limit to a specific scope's recap. Default 'any' returns the most recent recap across both personal and team scopes."
|
|
2216
2296
|
)
|
|
2217
2297
|
};
|
|
2218
2298
|
async function getRecap(input, ctx) {
|
|
2219
|
-
if (!
|
|
2299
|
+
if (!existsSync22(ctx.paths.memoriesDir)) {
|
|
2220
2300
|
return { recap: null, notice: "No .ai/memories directory \u2014 haive not initialized here." };
|
|
2221
2301
|
}
|
|
2222
|
-
const all = await
|
|
2302
|
+
const all = await loadMemoriesFromDir16(ctx.paths.memoriesDir);
|
|
2223
2303
|
const recaps = all.filter(({ memory }) => memory.frontmatter.type === "session_recap").filter(({ memory }) => input.scope === "any" || memory.frontmatter.scope === input.scope).sort(
|
|
2224
2304
|
(a, b) => new Date(b.memory.frontmatter.created_at).getTime() - new Date(a.memory.frontmatter.created_at).getTime()
|
|
2225
2305
|
);
|
|
@@ -2243,13 +2323,13 @@ async function getRecap(input, ctx) {
|
|
|
2243
2323
|
}
|
|
2244
2324
|
|
|
2245
2325
|
// src/tools/mem-relevant-to.ts
|
|
2246
|
-
import { z as
|
|
2326
|
+
import { z as z22 } from "zod";
|
|
2247
2327
|
var MemRelevantToInputSchema = {
|
|
2248
|
-
task:
|
|
2249
|
-
files:
|
|
2250
|
-
limit:
|
|
2251
|
-
min_semantic_score:
|
|
2252
|
-
format:
|
|
2328
|
+
task: z22.string().min(1).describe("What you are about to do, in 1\u20132 sentences. Used to rank relevant memories."),
|
|
2329
|
+
files: z22.array(z22.string()).default([]).describe("Optional: files you are about to edit \u2014 surfaces anchored memories."),
|
|
2330
|
+
limit: z22.number().int().positive().max(30).default(8).describe("Cap on returned memories."),
|
|
2331
|
+
min_semantic_score: z22.number().min(0).max(1).default(0.25).describe("Drop weakly-related semantic hits below this cosine threshold."),
|
|
2332
|
+
format: z22.enum(["full", "compact", "actions"]).default("full").describe("'compact' = id + 1-line summary; 'full' = complete bodies; 'actions' = bullet-first excerpts.")
|
|
2253
2333
|
};
|
|
2254
2334
|
async function memRelevantTo(input, ctx) {
|
|
2255
2335
|
const briefingInput = {
|
|
@@ -2279,13 +2359,13 @@ async function memRelevantTo(input, ctx) {
|
|
|
2279
2359
|
}
|
|
2280
2360
|
|
|
2281
2361
|
// src/tools/code-search.ts
|
|
2282
|
-
import { z as
|
|
2362
|
+
import { z as z23 } from "zod";
|
|
2283
2363
|
var CodeSearchInputSchema = {
|
|
2284
|
-
query:
|
|
2364
|
+
query: z23.string().min(1).describe(
|
|
2285
2365
|
"Natural-language description of what you are looking for in the codebase (e.g. 'function that hashes passwords', 'JWT signing logic', 'route registration')."
|
|
2286
2366
|
),
|
|
2287
|
-
k:
|
|
2288
|
-
min_score:
|
|
2367
|
+
k: z23.number().int().positive().max(50).default(5).describe("Number of top hits to return."),
|
|
2368
|
+
min_score: z23.number().min(0).max(1).default(0.2).describe(
|
|
2289
2369
|
"Minimum cosine similarity. Hits below this threshold are dropped to avoid noise. Try 0.3+ for stricter matching."
|
|
2290
2370
|
)
|
|
2291
2371
|
};
|
|
@@ -2315,27 +2395,27 @@ async function codeSearch(input, ctx) {
|
|
|
2315
2395
|
}
|
|
2316
2396
|
|
|
2317
2397
|
// src/tools/why-this-file.ts
|
|
2318
|
-
import { existsSync as
|
|
2398
|
+
import { existsSync as existsSync23 } from "fs";
|
|
2319
2399
|
import { spawn } from "child_process";
|
|
2320
2400
|
import path10 from "path";
|
|
2321
2401
|
import {
|
|
2322
2402
|
deriveConfidence as deriveConfidence5,
|
|
2323
|
-
getUsage as
|
|
2403
|
+
getUsage as getUsage7,
|
|
2324
2404
|
loadCodeMap as loadCodeMap3,
|
|
2325
|
-
loadMemoriesFromDir as
|
|
2326
|
-
loadUsageIndex as
|
|
2405
|
+
loadMemoriesFromDir as loadMemoriesFromDir17,
|
|
2406
|
+
loadUsageIndex as loadUsageIndex9,
|
|
2327
2407
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths3
|
|
2328
2408
|
} from "@hiveai/core";
|
|
2329
|
-
import { z as
|
|
2409
|
+
import { z as z24 } from "zod";
|
|
2330
2410
|
var WhyThisFileInputSchema = {
|
|
2331
|
-
path:
|
|
2411
|
+
path: z24.string().min(1).describe(
|
|
2332
2412
|
"Project-relative path to the file you want context on (e.g. 'packages/mcp/src/tools/mem-save.ts')."
|
|
2333
2413
|
),
|
|
2334
|
-
git_log_limit:
|
|
2335
|
-
memory_limit:
|
|
2414
|
+
git_log_limit: z24.number().int().positive().max(20).default(5).describe("How many recent commits touching this file to include."),
|
|
2415
|
+
memory_limit: z24.number().int().positive().max(20).default(5).describe("Cap on memories anchored to this path.")
|
|
2336
2416
|
};
|
|
2337
2417
|
async function whyThisFile(input, ctx) {
|
|
2338
|
-
const fileExists =
|
|
2418
|
+
const fileExists = existsSync23(path10.join(ctx.paths.root, input.path));
|
|
2339
2419
|
const [commits, memories, codeMap] = await Promise.all([
|
|
2340
2420
|
runGitLog(ctx.paths.root, input.path, input.git_log_limit).catch(() => []),
|
|
2341
2421
|
collectAnchoredMemories(ctx, input.path, input.memory_limit),
|
|
@@ -2376,16 +2456,16 @@ async function whyThisFile(input, ctx) {
|
|
|
2376
2456
|
};
|
|
2377
2457
|
}
|
|
2378
2458
|
async function collectAnchoredMemories(ctx, filePath, limit) {
|
|
2379
|
-
if (!
|
|
2380
|
-
const all = await
|
|
2381
|
-
const usage = await
|
|
2459
|
+
if (!existsSync23(ctx.paths.memoriesDir)) return [];
|
|
2460
|
+
const all = await loadMemoriesFromDir17(ctx.paths.memoriesDir);
|
|
2461
|
+
const usage = await loadUsageIndex9(ctx.paths);
|
|
2382
2462
|
const out = [];
|
|
2383
2463
|
for (const { memory } of all) {
|
|
2384
2464
|
const fm = memory.frontmatter;
|
|
2385
2465
|
if (fm.status === "rejected" || fm.status === "deprecated") continue;
|
|
2386
2466
|
if (fm.type === "session_recap") continue;
|
|
2387
2467
|
if (!memoryMatchesAnchorPaths3(memory, [filePath])) continue;
|
|
2388
|
-
const u =
|
|
2468
|
+
const u = getUsage7(usage, fm.id);
|
|
2389
2469
|
out.push({
|
|
2390
2470
|
id: fm.id,
|
|
2391
2471
|
type: fm.type,
|
|
@@ -2431,33 +2511,33 @@ function runCommand(cmd, args, cwd) {
|
|
|
2431
2511
|
}
|
|
2432
2512
|
|
|
2433
2513
|
// src/tools/anti-patterns-check.ts
|
|
2434
|
-
import { existsSync as
|
|
2514
|
+
import { existsSync as existsSync24 } from "fs";
|
|
2435
2515
|
import {
|
|
2436
2516
|
addedLinesFromDiff,
|
|
2437
2517
|
deriveConfidence as deriveConfidence6,
|
|
2438
|
-
getUsage as
|
|
2518
|
+
getUsage as getUsage8,
|
|
2439
2519
|
isRetiredMemory as isRetiredMemory2,
|
|
2440
|
-
loadMemoriesFromDir as
|
|
2441
|
-
loadUsageIndex as
|
|
2520
|
+
loadMemoriesFromDir as loadMemoriesFromDir18,
|
|
2521
|
+
loadUsageIndex as loadUsageIndex10,
|
|
2442
2522
|
literalMatchesAnyToken as literalMatchesAnyToken3,
|
|
2443
2523
|
memoryMatchesAnchorPaths as memoryMatchesAnchorPaths4,
|
|
2444
2524
|
runSensors,
|
|
2445
2525
|
sensorTargetsFromDiff,
|
|
2446
2526
|
tokenizeQuery as tokenizeQuery3
|
|
2447
2527
|
} from "@hiveai/core";
|
|
2448
|
-
import { z as
|
|
2528
|
+
import { z as z25 } from "zod";
|
|
2449
2529
|
var AntiPatternsCheckInputSchema = {
|
|
2450
|
-
diff:
|
|
2530
|
+
diff: z25.string().optional().describe(
|
|
2451
2531
|
"Raw unified diff text (or any code/text snippet) to scan for previously documented anti-patterns. Tokens from the diff are used to match memory bodies and the embeddings index."
|
|
2452
2532
|
),
|
|
2453
|
-
paths:
|
|
2533
|
+
paths: z25.array(z25.string()).default([]).describe(
|
|
2454
2534
|
"File paths affected by the change. Memories anchored to any of these paths are surfaced regardless of the diff content."
|
|
2455
2535
|
),
|
|
2456
|
-
limit:
|
|
2457
|
-
semantic:
|
|
2536
|
+
limit: z25.number().int().positive().max(20).default(8).describe("Cap on returned warnings."),
|
|
2537
|
+
semantic: z25.boolean().default(true).describe(
|
|
2458
2538
|
"When true, also use semantic search (requires @hiveai/embeddings + memory index) to find related anti-patterns."
|
|
2459
2539
|
),
|
|
2460
|
-
min_semantic_score:
|
|
2540
|
+
min_semantic_score: z25.number().min(0).max(1).default(0.45).describe(
|
|
2461
2541
|
"Minimum cosine score for semantic-only anti-pattern hits. Anchor/literal matches still surface. Default 0.45 keeps broad, weakly-related memories out of review noise."
|
|
2462
2542
|
)
|
|
2463
2543
|
};
|
|
@@ -2525,10 +2605,10 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
2525
2605
|
notice: "Nothing to check \u2014 provide either `diff` text or `paths`."
|
|
2526
2606
|
};
|
|
2527
2607
|
}
|
|
2528
|
-
if (!
|
|
2608
|
+
if (!existsSync24(ctx.paths.memoriesDir)) {
|
|
2529
2609
|
return { scanned: 0, warnings: [], notice: "No .ai/memories directory \u2014 nothing to check against." };
|
|
2530
2610
|
}
|
|
2531
|
-
const all = await
|
|
2611
|
+
const all = await loadMemoriesFromDir18(ctx.paths.memoriesDir);
|
|
2532
2612
|
const minSemanticScore = input.min_semantic_score ?? 0.45;
|
|
2533
2613
|
const negative = all.filter(({ memory }) => {
|
|
2534
2614
|
const t = memory.frontmatter.type;
|
|
@@ -2539,7 +2619,7 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
2539
2619
|
if (negative.length === 0) {
|
|
2540
2620
|
return { scanned: 0, warnings: [], notice: "No attempt/gotcha memories found yet." };
|
|
2541
2621
|
}
|
|
2542
|
-
const usage = await
|
|
2622
|
+
const usage = await loadUsageIndex10(ctx.paths);
|
|
2543
2623
|
const seen = /* @__PURE__ */ new Map();
|
|
2544
2624
|
const upsert = (fm, body, reason, score) => {
|
|
2545
2625
|
const existing = seen.get(fm.id);
|
|
@@ -2550,7 +2630,7 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
2550
2630
|
}
|
|
2551
2631
|
return;
|
|
2552
2632
|
}
|
|
2553
|
-
const u =
|
|
2633
|
+
const u = getUsage8(usage, fm.id);
|
|
2554
2634
|
seen.set(fm.id, {
|
|
2555
2635
|
id: fm.id,
|
|
2556
2636
|
type: fm.type,
|
|
@@ -2629,19 +2709,19 @@ async function antiPatternsCheck(input, ctx) {
|
|
|
2629
2709
|
}
|
|
2630
2710
|
|
|
2631
2711
|
// src/tools/mem-distill.ts
|
|
2632
|
-
import { existsSync as
|
|
2712
|
+
import { existsSync as existsSync25 } from "fs";
|
|
2633
2713
|
import {
|
|
2634
|
-
loadMemoriesFromDir as
|
|
2714
|
+
loadMemoriesFromDir as loadMemoriesFromDir19,
|
|
2635
2715
|
tokenizeQuery as tokenizeQuery4
|
|
2636
2716
|
} from "@hiveai/core";
|
|
2637
|
-
import { z as
|
|
2717
|
+
import { z as z26 } from "zod";
|
|
2638
2718
|
var MemDistillInputSchema = {
|
|
2639
|
-
since_days:
|
|
2640
|
-
min_cluster:
|
|
2641
|
-
type_filter:
|
|
2719
|
+
since_days: z26.number().int().positive().default(30).describe("Only consider memories created in the last N days."),
|
|
2720
|
+
min_cluster: z26.number().int().min(2).default(3).describe("Minimum cluster size to surface."),
|
|
2721
|
+
type_filter: z26.enum(["gotcha", "attempt", "all"]).default("gotcha").describe(
|
|
2642
2722
|
"Memory type to scan. 'gotcha' targets observe-style discoveries that recur, 'attempt' surfaces failed approaches that repeat, 'all' considers both."
|
|
2643
2723
|
),
|
|
2644
|
-
scope:
|
|
2724
|
+
scope: z26.enum(["personal", "team", "module", "any"]).default("any").describe("Restrict to a specific scope.")
|
|
2645
2725
|
};
|
|
2646
2726
|
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
2647
2727
|
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
@@ -2681,11 +2761,11 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
2681
2761
|
"error"
|
|
2682
2762
|
]);
|
|
2683
2763
|
async function memDistill(input, ctx) {
|
|
2684
|
-
if (!
|
|
2764
|
+
if (!existsSync25(ctx.paths.memoriesDir)) {
|
|
2685
2765
|
return { scanned: 0, singletons: 0, clusters: [], notice: "No .ai/memories directory." };
|
|
2686
2766
|
}
|
|
2687
2767
|
const cutoff = Date.now() - input.since_days * MS_PER_DAY;
|
|
2688
|
-
const all = await
|
|
2768
|
+
const all = await loadMemoriesFromDir19(ctx.paths.memoriesDir);
|
|
2689
2769
|
const candidates = all.filter(({ memory }) => {
|
|
2690
2770
|
const fm = memory.frontmatter;
|
|
2691
2771
|
if (fm.status === "rejected" || fm.status === "deprecated" || fm.status === "stale") return false;
|
|
@@ -2789,22 +2869,22 @@ function firstHeading(body) {
|
|
|
2789
2869
|
}
|
|
2790
2870
|
|
|
2791
2871
|
// src/tools/why-this-decision.ts
|
|
2792
|
-
import { existsSync as
|
|
2872
|
+
import { existsSync as existsSync26 } from "fs";
|
|
2793
2873
|
import { spawn as spawn2 } from "child_process";
|
|
2794
2874
|
import {
|
|
2795
2875
|
deriveConfidence as deriveConfidence7,
|
|
2796
|
-
getUsage as
|
|
2797
|
-
loadMemoriesFromDir as
|
|
2798
|
-
loadUsageIndex as
|
|
2876
|
+
getUsage as getUsage9,
|
|
2877
|
+
loadMemoriesFromDir as loadMemoriesFromDir20,
|
|
2878
|
+
loadUsageIndex as loadUsageIndex11,
|
|
2799
2879
|
pathsOverlap as singlePathsOverlap
|
|
2800
2880
|
} from "@hiveai/core";
|
|
2801
|
-
import { z as
|
|
2881
|
+
import { z as z27 } from "zod";
|
|
2802
2882
|
var WhyThisDecisionInputSchema = {
|
|
2803
|
-
id:
|
|
2804
|
-
git_log_limit:
|
|
2883
|
+
id: z27.string().min(1).describe("Memory id to inspect (e.g. '2026-04-25-decision-esm-only')."),
|
|
2884
|
+
git_log_limit: z27.number().int().positive().max(20).default(5).describe("How many recent commits per anchor path to surface.")
|
|
2805
2885
|
};
|
|
2806
2886
|
async function whyThisDecision(input, ctx) {
|
|
2807
|
-
if (!
|
|
2887
|
+
if (!existsSync26(ctx.paths.memoriesDir)) {
|
|
2808
2888
|
return {
|
|
2809
2889
|
found: false,
|
|
2810
2890
|
related: [],
|
|
@@ -2813,8 +2893,8 @@ async function whyThisDecision(input, ctx) {
|
|
|
2813
2893
|
notice: "No .ai/memories directory."
|
|
2814
2894
|
};
|
|
2815
2895
|
}
|
|
2816
|
-
const all = await
|
|
2817
|
-
const usage = await
|
|
2896
|
+
const all = await loadMemoriesFromDir20(ctx.paths.memoriesDir);
|
|
2897
|
+
const usage = await loadUsageIndex11(ctx.paths);
|
|
2818
2898
|
const target = all.find(({ memory }) => memory.frontmatter.id === input.id);
|
|
2819
2899
|
if (!target) {
|
|
2820
2900
|
return {
|
|
@@ -2826,7 +2906,7 @@ async function whyThisDecision(input, ctx) {
|
|
|
2826
2906
|
};
|
|
2827
2907
|
}
|
|
2828
2908
|
const fm = target.memory.frontmatter;
|
|
2829
|
-
const targetUsage =
|
|
2909
|
+
const targetUsage = getUsage9(usage, fm.id);
|
|
2830
2910
|
const decision = {
|
|
2831
2911
|
id: fm.id,
|
|
2832
2912
|
type: fm.type,
|
|
@@ -2843,7 +2923,7 @@ async function whyThisDecision(input, ctx) {
|
|
|
2843
2923
|
const isExplicit = relatedSet.has(memory.frontmatter.id);
|
|
2844
2924
|
const isBackLink = (memory.frontmatter.related_ids ?? []).includes(fm.id);
|
|
2845
2925
|
if (!isExplicit && !isBackLink) continue;
|
|
2846
|
-
const u =
|
|
2926
|
+
const u = getUsage9(usage, memory.frontmatter.id);
|
|
2847
2927
|
related.push({
|
|
2848
2928
|
id: memory.frontmatter.id,
|
|
2849
2929
|
type: memory.frontmatter.type,
|
|
@@ -2863,7 +2943,7 @@ async function whyThisDecision(input, ctx) {
|
|
|
2863
2943
|
(p) => targetPaths.some((tp) => singlePathsOverlap(p, tp))
|
|
2864
2944
|
);
|
|
2865
2945
|
if (overlappingPaths.length === 0) continue;
|
|
2866
|
-
const u =
|
|
2946
|
+
const u = getUsage9(usage, memory.frontmatter.id);
|
|
2867
2947
|
path_neighbors.push({
|
|
2868
2948
|
id: memory.frontmatter.id,
|
|
2869
2949
|
type: memory.frontmatter.type,
|
|
@@ -2936,33 +3016,33 @@ function runCommand2(cmd, args, cwd) {
|
|
|
2936
3016
|
}
|
|
2937
3017
|
|
|
2938
3018
|
// src/tools/mem-conflicts.ts
|
|
2939
|
-
import { existsSync as
|
|
3019
|
+
import { existsSync as existsSync27 } from "fs";
|
|
2940
3020
|
import {
|
|
2941
3021
|
deriveConfidence as deriveConfidence8,
|
|
2942
|
-
getUsage as
|
|
2943
|
-
loadMemoriesFromDir as
|
|
2944
|
-
loadUsageIndex as
|
|
3022
|
+
getUsage as getUsage10,
|
|
3023
|
+
loadMemoriesFromDir as loadMemoriesFromDir21,
|
|
3024
|
+
loadUsageIndex as loadUsageIndex12,
|
|
2945
3025
|
pathsOverlap as pathsOverlap2,
|
|
2946
3026
|
tokenizeQuery as tokenizeQuery5
|
|
2947
3027
|
} from "@hiveai/core";
|
|
2948
|
-
import { z as
|
|
3028
|
+
import { z as z28 } from "zod";
|
|
2949
3029
|
var MemConflictsInputSchema = {
|
|
2950
|
-
id:
|
|
2951
|
-
min_score:
|
|
2952
|
-
semantic:
|
|
3030
|
+
id: z28.string().min(1).describe("Memory id to check for conflicts."),
|
|
3031
|
+
min_score: z28.number().min(0).max(1).default(0.5).describe("Minimum cosine similarity to consider a memory as a potential conflict (semantic mode)."),
|
|
3032
|
+
semantic: z28.boolean().default(true).describe("Use embeddings for similarity. Falls back to keyword overlap when embeddings are not installed.")
|
|
2953
3033
|
};
|
|
2954
3034
|
var POSITIVE_PATTERNS = /\b(use|prefer|always|should use|do this|recommended|ok to)\b/i;
|
|
2955
3035
|
var NEGATIVE_PATTERNS = /\b(do not use|don'?t use|never|avoid|forbidden|deprecated|stop using|do NOT|❌)\b/i;
|
|
2956
3036
|
async function memConflicts(input, ctx) {
|
|
2957
|
-
if (!
|
|
3037
|
+
if (!existsSync27(ctx.paths.memoriesDir)) {
|
|
2958
3038
|
return { found: false, scanned: 0, conflicts: [], notice: "No .ai/memories directory." };
|
|
2959
3039
|
}
|
|
2960
|
-
const all = await
|
|
3040
|
+
const all = await loadMemoriesFromDir21(ctx.paths.memoriesDir);
|
|
2961
3041
|
const target = all.find(({ memory }) => memory.frontmatter.id === input.id);
|
|
2962
3042
|
if (!target) {
|
|
2963
3043
|
return { found: false, scanned: 0, conflicts: [], notice: `Memory '${input.id}' not found.` };
|
|
2964
3044
|
}
|
|
2965
|
-
const usage = await
|
|
3045
|
+
const usage = await loadUsageIndex12(ctx.paths);
|
|
2966
3046
|
const others = all.filter(
|
|
2967
3047
|
({ memory }) => memory.frontmatter.id !== input.id && memory.frontmatter.type !== "session_recap"
|
|
2968
3048
|
);
|
|
@@ -3003,7 +3083,7 @@ async function memConflicts(input, ctx) {
|
|
|
3003
3083
|
}
|
|
3004
3084
|
}
|
|
3005
3085
|
if (reasons.length === 0) continue;
|
|
3006
|
-
const u =
|
|
3086
|
+
const u = getUsage10(usage, fm.id);
|
|
3007
3087
|
conflicts.push({
|
|
3008
3088
|
id: fm.id,
|
|
3009
3089
|
type: fm.type,
|
|
@@ -3069,17 +3149,17 @@ async function trySemanticSimilarities(ctx, target, others) {
|
|
|
3069
3149
|
}
|
|
3070
3150
|
|
|
3071
3151
|
// src/tools/precommit-check.ts
|
|
3072
|
-
import { z as
|
|
3152
|
+
import { z as z29 } from "zod";
|
|
3073
3153
|
var PreCommitCheckInputSchema = {
|
|
3074
|
-
diff:
|
|
3154
|
+
diff: z29.string().optional().describe(
|
|
3075
3155
|
"Raw unified diff text to scan. If omitted, only `paths` is used. When called from a pre-commit hook, pipe the output of `git diff --cached`."
|
|
3076
3156
|
),
|
|
3077
|
-
paths:
|
|
3078
|
-
block_on:
|
|
3157
|
+
paths: z29.array(z29.string()).default([]).describe("Project-relative paths affected by the change. At least one of `diff` or `paths` should be provided."),
|
|
3158
|
+
block_on: z29.enum(["any", "high-confidence", "never"]).default("high-confidence").describe(
|
|
3079
3159
|
"When to set should_block=true: 'any' = any warning blocks; 'high-confidence' = only warnings from authoritative/trusted memories block; 'never' = report only, never block."
|
|
3080
3160
|
),
|
|
3081
|
-
semantic:
|
|
3082
|
-
anchored_blocks:
|
|
3161
|
+
semantic: z29.boolean().default(true).describe("Enable semantic search in anti_patterns_check (requires embeddings index)."),
|
|
3162
|
+
anchored_blocks: z29.boolean().default(false).describe(
|
|
3083
3163
|
"When true, ALSO block a high-confidence anti-pattern (attempt/gotcha) that is anchored to a touched file AND corroborated by the diff (literal token overlap, or semantic >= 0.45) \u2014 not just very strong semantic matches. Powers the 'anchored' enforcement gate. Config/docs-only commits are still downgraded. Default false preserves the soft, semantic-only blocking behavior."
|
|
3084
3164
|
)
|
|
3085
3165
|
};
|
|
@@ -3361,7 +3441,7 @@ function repairTargetPathForWarning(warning, paths) {
|
|
|
3361
3441
|
|
|
3362
3442
|
// src/tools/pattern-detect.ts
|
|
3363
3443
|
import { mkdir as mkdir7, writeFile as writeFile12 } from "fs/promises";
|
|
3364
|
-
import { existsSync as
|
|
3444
|
+
import { existsSync as existsSync28 } from "fs";
|
|
3365
3445
|
import path11 from "path";
|
|
3366
3446
|
import { execSync as execSync2 } from "child_process";
|
|
3367
3447
|
import {
|
|
@@ -3370,7 +3450,7 @@ import {
|
|
|
3370
3450
|
readUsageEvents,
|
|
3371
3451
|
serializeMemory as serializeMemory10
|
|
3372
3452
|
} from "@hiveai/core";
|
|
3373
|
-
import { z as
|
|
3453
|
+
import { z as z30 } from "zod";
|
|
3374
3454
|
var CONFIG_PATTERNS = [
|
|
3375
3455
|
".eslintrc",
|
|
3376
3456
|
"eslint.config",
|
|
@@ -3393,12 +3473,12 @@ var CONFIG_PATTERNS = [
|
|
|
3393
3473
|
var MAX_DIFF_BYTES = 4096;
|
|
3394
3474
|
var HOT_FILE_MIN = 3;
|
|
3395
3475
|
var PatternDetectInputSchema = {
|
|
3396
|
-
since_days:
|
|
3397
|
-
dry_run:
|
|
3398
|
-
scope:
|
|
3476
|
+
since_days: z30.number().int().min(1).default(7).describe("Look-back window in days for both git history and usage log."),
|
|
3477
|
+
dry_run: z30.boolean().default(false).describe("When true, report matches without writing any memory files."),
|
|
3478
|
+
scope: z30.enum(["personal", "team"]).default("team").describe("Scope for proposed memories.")
|
|
3399
3479
|
};
|
|
3400
3480
|
async function patternDetect(input, ctx) {
|
|
3401
|
-
if (!
|
|
3481
|
+
if (!existsSync28(ctx.paths.haiveDir)) {
|
|
3402
3482
|
return {
|
|
3403
3483
|
scanned_events: 0,
|
|
3404
3484
|
matches: [],
|
|
@@ -3527,7 +3607,7 @@ async function patternDetect(input, ctx) {
|
|
|
3527
3607
|
fm.id,
|
|
3528
3608
|
void 0
|
|
3529
3609
|
);
|
|
3530
|
-
if (
|
|
3610
|
+
if (existsSync28(file)) continue;
|
|
3531
3611
|
await mkdir7(path11.dirname(file), { recursive: true });
|
|
3532
3612
|
await writeFile12(
|
|
3533
3613
|
file,
|
|
@@ -3573,25 +3653,25 @@ function gitFileDiff(root, file, sinceDays) {
|
|
|
3573
3653
|
}
|
|
3574
3654
|
|
|
3575
3655
|
// src/tools/mem-conflict-candidates.ts
|
|
3576
|
-
import { existsSync as
|
|
3656
|
+
import { existsSync as existsSync29 } from "fs";
|
|
3577
3657
|
import {
|
|
3578
3658
|
findLexicalConflictPairs,
|
|
3579
3659
|
findTopicStatusConflictPairs,
|
|
3580
|
-
loadMemoriesFromDir as
|
|
3660
|
+
loadMemoriesFromDir as loadMemoriesFromDir22
|
|
3581
3661
|
} from "@hiveai/core";
|
|
3582
|
-
import { z as
|
|
3662
|
+
import { z as z31 } from "zod";
|
|
3583
3663
|
var MemConflictCandidatesInputSchema = {
|
|
3584
|
-
since_days:
|
|
3585
|
-
types:
|
|
3586
|
-
min_jaccard:
|
|
3587
|
-
max_pairs:
|
|
3588
|
-
max_scan:
|
|
3589
|
-
max_topic_pairs:
|
|
3664
|
+
since_days: z31.number().int().positive().max(3650).default(365).describe("Only memories created since N days ago"),
|
|
3665
|
+
types: z31.array(z31.enum(["decision", "architecture", "convention", "gotcha"])).default(["decision", "architecture"]).describe("Memory types scanned for pairwise lexical overlap"),
|
|
3666
|
+
min_jaccard: z31.number().min(0).max(1).default(0.45).describe("Minimum Jaccard token similarity to surface as a candidate pair"),
|
|
3667
|
+
max_pairs: z31.number().int().positive().max(100).default(20).describe("Cap pairs returned"),
|
|
3668
|
+
max_scan: z31.number().int().positive().max(2e3).default(500).describe("Maximum memories sampled for O(n\xB2) scan \u2014 excess dropped after chronological sort."),
|
|
3669
|
+
max_topic_pairs: z31.number().int().positive().max(100).default(20).describe(
|
|
3590
3670
|
"Cap for extra signal: memories sharing the same topic with validated vs rejected status."
|
|
3591
3671
|
)
|
|
3592
3672
|
};
|
|
3593
3673
|
async function memConflictCandidates(input, ctx) {
|
|
3594
|
-
if (!
|
|
3674
|
+
if (!existsSync29(ctx.paths.memoriesDir)) {
|
|
3595
3675
|
return {
|
|
3596
3676
|
pairs: [],
|
|
3597
3677
|
topic_status_pairs: [],
|
|
@@ -3600,7 +3680,7 @@ async function memConflictCandidates(input, ctx) {
|
|
|
3600
3680
|
notice: "No .ai/memories directory."
|
|
3601
3681
|
};
|
|
3602
3682
|
}
|
|
3603
|
-
const all = await
|
|
3683
|
+
const all = await loadMemoriesFromDir22(ctx.paths.memoriesDir);
|
|
3604
3684
|
const { pairs, scanned, truncated } = findLexicalConflictPairs(all, {
|
|
3605
3685
|
sinceDays: input.since_days,
|
|
3606
3686
|
types: input.types,
|
|
@@ -3615,9 +3695,9 @@ async function memConflictCandidates(input, ctx) {
|
|
|
3615
3695
|
|
|
3616
3696
|
// src/tools/mem-resolve-project.ts
|
|
3617
3697
|
import { resolveProjectInfo } from "@hiveai/core";
|
|
3618
|
-
import { z as
|
|
3698
|
+
import { z as z32 } from "zod";
|
|
3619
3699
|
var MemResolveProjectInputSchema = {
|
|
3620
|
-
cwd:
|
|
3700
|
+
cwd: z32.string().optional().describe("Directory used for root discovery when HAIVE_PROJECT_ROOT is unset.")
|
|
3621
3701
|
};
|
|
3622
3702
|
async function memResolveProject(input, _ctx) {
|
|
3623
3703
|
void _ctx;
|
|
@@ -3631,10 +3711,10 @@ async function memResolveProject(input, _ctx) {
|
|
|
3631
3711
|
|
|
3632
3712
|
// src/tools/mem-suggest-topic.ts
|
|
3633
3713
|
import { MemoryTypeSchema, suggestTopicKey } from "@hiveai/core";
|
|
3634
|
-
import { z as
|
|
3714
|
+
import { z as z33 } from "zod";
|
|
3635
3715
|
var MemSuggestTopicInputSchema = {
|
|
3636
3716
|
type: MemoryTypeSchema.describe("Memory kind \u2014 drives the suggested topic family."),
|
|
3637
|
-
title:
|
|
3717
|
+
title: z33.string().min(1).describe("Short title or phrase (headers, headings) \u2014 turned into slug")
|
|
3638
3718
|
};
|
|
3639
3719
|
async function memSuggestTopic(input, _ctx) {
|
|
3640
3720
|
void _ctx;
|
|
@@ -3643,19 +3723,19 @@ async function memSuggestTopic(input, _ctx) {
|
|
|
3643
3723
|
}
|
|
3644
3724
|
|
|
3645
3725
|
// src/tools/mem-timeline.ts
|
|
3646
|
-
import { existsSync as
|
|
3647
|
-
import { collectTimelineEntries, loadMemoriesFromDir as
|
|
3648
|
-
import { z as
|
|
3726
|
+
import { existsSync as existsSync30 } from "fs";
|
|
3727
|
+
import { collectTimelineEntries, loadMemoriesFromDir as loadMemoriesFromDir23 } from "@hiveai/core";
|
|
3728
|
+
import { z as z34 } from "zod";
|
|
3649
3729
|
var MemTimelineInputSchema = {
|
|
3650
|
-
memory_id:
|
|
3651
|
-
topic:
|
|
3652
|
-
limit:
|
|
3730
|
+
memory_id: z34.string().optional().describe("Seed id \u2014 expands via related_ids, topic, anchors"),
|
|
3731
|
+
topic: z34.string().optional().describe("Frontmatter.topic value \u2014 chronological list when memory_id omitted"),
|
|
3732
|
+
limit: z34.number().int().positive().max(100).default(30).describe("Max timeline entries returned")
|
|
3653
3733
|
};
|
|
3654
3734
|
async function memTimeline(input, ctx) {
|
|
3655
|
-
if (!
|
|
3735
|
+
if (!existsSync30(ctx.paths.memoriesDir)) {
|
|
3656
3736
|
return { entries: [], total: 0, notice: "No .ai/memories directory." };
|
|
3657
3737
|
}
|
|
3658
|
-
const all = await
|
|
3738
|
+
const all = await loadMemoriesFromDir23(ctx.paths.memoriesDir);
|
|
3659
3739
|
const { entries, notice } = collectTimelineEntries(all, {
|
|
3660
3740
|
memoryId: input.memory_id,
|
|
3661
3741
|
topic: input.topic,
|
|
@@ -3666,11 +3746,11 @@ async function memTimeline(input, ctx) {
|
|
|
3666
3746
|
|
|
3667
3747
|
// src/tools/runtime-journal-append.ts
|
|
3668
3748
|
import { appendRuntimeJournalEntry as appendRuntimeJournalEntry2 } from "@hiveai/core";
|
|
3669
|
-
import { z as
|
|
3749
|
+
import { z as z35 } from "zod";
|
|
3670
3750
|
var RuntimeJournalAppendInputSchema = {
|
|
3671
|
-
message:
|
|
3672
|
-
kind:
|
|
3673
|
-
tool:
|
|
3751
|
+
message: z35.string().min(1).describe("Short line to append to the runtime session journal"),
|
|
3752
|
+
kind: z35.enum(["note", "session_end", "mcp"]).default("note"),
|
|
3753
|
+
tool: z35.string().optional().describe("When kind=mcp, which tool name (optional)")
|
|
3674
3754
|
};
|
|
3675
3755
|
async function runtimeJournalAppend(input, ctx) {
|
|
3676
3756
|
await appendRuntimeJournalEntry2(ctx.paths, {
|
|
@@ -3686,9 +3766,9 @@ async function runtimeJournalAppend(input, ctx) {
|
|
|
3686
3766
|
|
|
3687
3767
|
// src/tools/runtime-journal-tail.ts
|
|
3688
3768
|
import { readRuntimeJournalTail } from "@hiveai/core";
|
|
3689
|
-
import { z as
|
|
3769
|
+
import { z as z36 } from "zod";
|
|
3690
3770
|
var RuntimeJournalTailInputSchema = {
|
|
3691
|
-
limit:
|
|
3771
|
+
limit: z36.number().int().positive().max(500).default(30).describe("Last N journal entries to return")
|
|
3692
3772
|
};
|
|
3693
3773
|
async function runtimeJournalTail(input, ctx) {
|
|
3694
3774
|
const entries = await readRuntimeJournalTail(ctx.paths, input.limit);
|
|
@@ -3699,12 +3779,12 @@ async function runtimeJournalTail(input, ctx) {
|
|
|
3699
3779
|
}
|
|
3700
3780
|
|
|
3701
3781
|
// src/prompts/bootstrap-project.ts
|
|
3702
|
-
import { z as
|
|
3782
|
+
import { z as z37 } from "zod";
|
|
3703
3783
|
var BootstrapProjectArgsSchema = {
|
|
3704
|
-
module:
|
|
3784
|
+
module: z37.string().optional().describe(
|
|
3705
3785
|
"Optional module name to scope the analysis to (writes to .ai/modules/<module>/context.md)"
|
|
3706
3786
|
),
|
|
3707
|
-
focus:
|
|
3787
|
+
focus: z37.string().optional().describe("Optional area to emphasize (e.g. 'data layer', 'API surface')")
|
|
3708
3788
|
};
|
|
3709
3789
|
var ROOT_TEMPLATE = `# Project context
|
|
3710
3790
|
|
|
@@ -3786,10 +3866,10 @@ ${template}\`\`\`
|
|
|
3786
3866
|
}
|
|
3787
3867
|
|
|
3788
3868
|
// src/prompts/post-task.ts
|
|
3789
|
-
import { z as
|
|
3869
|
+
import { z as z38 } from "zod";
|
|
3790
3870
|
var PostTaskArgsSchema = {
|
|
3791
|
-
task_summary:
|
|
3792
|
-
files_touched:
|
|
3871
|
+
task_summary: z38.string().optional().describe("One sentence describing what you just did"),
|
|
3872
|
+
files_touched: z38.array(z38.string()).optional().describe("Files you created or modified during the task")
|
|
3793
3873
|
};
|
|
3794
3874
|
function postTaskPrompt(args, ctx) {
|
|
3795
3875
|
const taskLine = args.task_summary ? `
|
|
@@ -3885,12 +3965,12 @@ When done, respond with a brief summary: "Saved N memories: [list of IDs]. Sessi
|
|
|
3885
3965
|
}
|
|
3886
3966
|
|
|
3887
3967
|
// src/prompts/import-docs.ts
|
|
3888
|
-
import { z as
|
|
3968
|
+
import { z as z39 } from "zod";
|
|
3889
3969
|
var ImportDocsArgsSchema = {
|
|
3890
|
-
content:
|
|
3891
|
-
source:
|
|
3892
|
-
scope:
|
|
3893
|
-
dry_run:
|
|
3970
|
+
content: z39.string().describe("The documentation content to analyze and import as memories (Markdown, README, ADR, etc.)"),
|
|
3971
|
+
source: z39.string().optional().describe("Origin of the content (file path, URL, or document title) \u2014 used to anchor memories"),
|
|
3972
|
+
scope: z39.enum(["personal", "team"]).default("team").describe("Scope to assign to created memories"),
|
|
3973
|
+
dry_run: z39.boolean().default(false).describe("If true, describe what would be saved without actually calling mem_save")
|
|
3894
3974
|
};
|
|
3895
3975
|
function importDocsPrompt(args, ctx) {
|
|
3896
3976
|
const sourceLine = args.source ? `
|
|
@@ -3956,7 +4036,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
3956
4036
|
// src/server.ts
|
|
3957
4037
|
import { hasRecentBriefingMarker, loadConfigSync } from "@hiveai/core";
|
|
3958
4038
|
var SERVER_NAME = "haive";
|
|
3959
|
-
var SERVER_VERSION = "0.
|
|
4039
|
+
var SERVER_VERSION = "0.11.0";
|
|
3960
4040
|
function jsonResult(data) {
|
|
3961
4041
|
return {
|
|
3962
4042
|
content: [
|
|
@@ -3998,7 +4078,8 @@ var MAINTENANCE_PROFILE_TOOLS = [
|
|
|
3998
4078
|
"anti_patterns_check",
|
|
3999
4079
|
"mem_distill",
|
|
4000
4080
|
"mem_timeline",
|
|
4001
|
-
"mem_conflict_candidates"
|
|
4081
|
+
"mem_conflict_candidates",
|
|
4082
|
+
"mem_feedback"
|
|
4002
4083
|
];
|
|
4003
4084
|
var EXPERIMENTAL_PROFILE_TOOLS = [
|
|
4004
4085
|
...MAINTENANCE_PROFILE_TOOLS,
|
|
@@ -4030,6 +4111,7 @@ var MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
|
4030
4111
|
"mem_approve",
|
|
4031
4112
|
"mem_reject",
|
|
4032
4113
|
"mem_delete",
|
|
4114
|
+
"mem_feedback",
|
|
4033
4115
|
"runtime_journal_append",
|
|
4034
4116
|
"pattern_detect"
|
|
4035
4117
|
]);
|
|
@@ -4525,6 +4607,28 @@ function createHaiveServer(options = {}) {
|
|
|
4525
4607
|
MemRejectInputSchema,
|
|
4526
4608
|
async (input) => jsonResult(await memReject(input, context))
|
|
4527
4609
|
);
|
|
4610
|
+
registerTool(
|
|
4611
|
+
"mem_feedback",
|
|
4612
|
+
[
|
|
4613
|
+
"Record whether a memory actually HELPED \u2014 the closed-loop utility signal.",
|
|
4614
|
+
"",
|
|
4615
|
+
"USE right after a memory changed (or failed to change) what you did:",
|
|
4616
|
+
" - outcome='applied' \u2192 the memory steered your work (strong positive signal)",
|
|
4617
|
+
" - outcome='rejected' \u2192 it was wrong/outdated/unhelpful (negative signal)",
|
|
4618
|
+
"",
|
|
4619
|
+
"A read only means a memory was surfaced; 'applied' means it demonstrably helped.",
|
|
4620
|
+
"This powers `haive memory impact` (impact tiers + prune candidates) and future ranking.",
|
|
4621
|
+
"",
|
|
4622
|
+
"PARAMETERS:",
|
|
4623
|
+
" id \u2014 full memory id the feedback is about",
|
|
4624
|
+
" outcome \u2014 'applied' | 'rejected'",
|
|
4625
|
+
" reason \u2014 why it was rejected (optional, stored on the usage record)",
|
|
4626
|
+
"",
|
|
4627
|
+
"RETURNS: { ok, id, outcome, usage:{read_count,applied_count,rejected_count}, impact:{score,tier,signals} }"
|
|
4628
|
+
].join("\n"),
|
|
4629
|
+
MemFeedbackInputSchema,
|
|
4630
|
+
async (input) => jsonResult(await memFeedback(input, context))
|
|
4631
|
+
);
|
|
4528
4632
|
registerTool(
|
|
4529
4633
|
"mem_pending",
|
|
4530
4634
|
[
|