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