@polycode-projects/the-mechanical-code-talker 1.8.20 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -6
- package/ROADMAP.md +1 -1
- package/bin/tmct.mjs +167 -20
- package/corpus/generated/README.md +2 -2
- package/corpus/namenet/LICENSE-NOTICE +68 -0
- package/corpus/namenet/generate.mjs +309 -0
- package/corpus/namenet/manifest.json +20 -0
- package/corpus/namenet/namenet.jsonl +7260 -0
- package/corpus/wordnet/LICENSE-NOTICE +49 -0
- package/corpus/wordnet/generate.mjs +333 -0
- package/corpus/wordnet/manifest.json +34 -0
- package/corpus/wordnet/wordnet-full.jsonl +192498 -0
- package/corpus/wordnet/wordnet-xl.jsonl +23805 -0
- package/package.json +6 -2
- package/src/ask-browser-entry.mjs +13 -2
- package/src/ask-browser.bundle.js +272 -19
- package/src/chat.mjs +229 -82
- package/src/cli-args.mjs +20 -1
- package/src/codegraph.mjs +306 -1
- package/src/corpus/conceptnet-map.toml +17 -12
- package/src/corpus/conceptnet.mjs +23 -1
- package/src/extensions.mjs +44 -1
- package/src/init.mjs +99 -46
- package/src/interpret/normalize.mjs +1 -3
- package/src/memory/core.mjs +191 -14
- package/src/memory/trust.mjs +27 -6
- package/src/memory-ask-browser-entry.mjs +36 -0
- package/src/memory-ask-browser.bundle.js +5544 -0
- package/src/toml-config.mjs +11 -1
- package/src/viz.mjs +548 -73
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The Mechanical Code Talker (tmct) — a tolerant, offline, $0 chat surface that guides you toward precision queries about a software repository. ELIZA/PARRY-style but domain-obsessed with code. No model calls; no codebase index of its own.",
|
|
@@ -80,6 +80,7 @@
|
|
|
80
80
|
"wink-nlp": "^2.4.0"
|
|
81
81
|
},
|
|
82
82
|
"scripts": {
|
|
83
|
+
"help": "node bin/tmct.mjs --help",
|
|
83
84
|
"test": "node --test \"test/**/*.test.mjs\"",
|
|
84
85
|
"chat": "node bin/tmct.mjs",
|
|
85
86
|
"chat:repo": "node bin/tmct.mjs chat --repo",
|
|
@@ -87,6 +88,8 @@
|
|
|
87
88
|
"init:persona:human": "node bin/tmct.mjs init --with-persona human",
|
|
88
89
|
"init:persona:empty": "node bin/tmct.mjs init --with-persona empty",
|
|
89
90
|
"init:large": "node bin/tmct.mjs init && node bin/tmct.mjs import --corpus seon && node bin/tmct.mjs import --corpus conceptnet && node bin/tmct.mjs import --corpus aws && node bin/tmct.mjs import --corpus python && node bin/tmct.mjs import --corpus java",
|
|
91
|
+
"init:xl": "node bin/tmct.mjs init --persona-size large && node bin/tmct.mjs import --corpus seon && node bin/tmct.mjs import --corpus conceptnet && node bin/tmct.mjs import --corpus aws && node bin/tmct.mjs import --corpus python && node bin/tmct.mjs import --corpus java && node bin/tmct.mjs import --corpus wordnet-xl",
|
|
92
|
+
"init:xxl": "node bin/tmct.mjs init --persona-size large && node bin/tmct.mjs import --corpus seon && node bin/tmct.mjs import --corpus conceptnet && node bin/tmct.mjs import --corpus aws && node bin/tmct.mjs import --corpus python && node bin/tmct.mjs import --corpus java && node bin/tmct.mjs import --corpus wordnet-full && node bin/tmct.mjs import --corpus namenet",
|
|
90
93
|
"memory": "node bin/tmct.mjs memory",
|
|
91
94
|
"syllogise": "node bin/tmct.mjs syllogise",
|
|
92
95
|
"viz": "node bin/tmct.mjs viz",
|
|
@@ -100,7 +103,8 @@
|
|
|
100
103
|
"audit": "npm audit --audit-level=high",
|
|
101
104
|
"audit:fix": "npm audit fix",
|
|
102
105
|
"demo:build": "node scripts/build-demo-site.mjs",
|
|
103
|
-
"build:ask-bundle": "node scripts/build-ask-bundle.mjs"
|
|
106
|
+
"build:ask-bundle": "node scripts/build-ask-bundle.mjs",
|
|
107
|
+
"extract:facts": "node scripts/extract-facts-from-text.mjs"
|
|
104
108
|
},
|
|
105
109
|
"devDependencies": {
|
|
106
110
|
"esbuild": "^0.28.1",
|
|
@@ -17,10 +17,21 @@
|
|
|
17
17
|
import { ask, parseQuery } from "./ask.mjs";
|
|
18
18
|
import {
|
|
19
19
|
parseEntities, spiralExpand, mostRecentIndividual, derivedUpdatedAt, MEMORY_SPIRAL_EXPAND_KINDS,
|
|
20
|
-
buildVizNodesAndEdges,
|
|
20
|
+
MEMORY_FACT_LINK_KINDS, buildVizNodesAndEdges, deriveFactTermGraph, pickLegendDimension, legendValueFor,
|
|
21
|
+
edgeKindsFor, collapseToTopN,
|
|
21
22
|
} from "./codegraph.mjs";
|
|
22
23
|
|
|
24
|
+
// PLAN_VIZ_MEMORY.md Bug 2 fix + Controls port: deriveFactTermGraph/
|
|
25
|
+
// pickLegendDimension/legendValueFor/MEMORY_FACT_LINK_KINDS/edgeKindsFor/
|
|
26
|
+
// collapseToTopN are exported here (alongside the pre-existing traversal
|
|
27
|
+
// exports) so the viewer page's client-side recentre/edge-kind-toggle/
|
|
28
|
+
// dimension-switcher (a user double-clicking a node, changing which edge
|
|
29
|
+
// kinds the walk follows, or flipping the legend from "split by predicate"
|
|
30
|
+
// to "split by trust source") re-derives the SAME term graph, kind set, and
|
|
31
|
+
// legend the CLI's own generation-time computeVizGraph used — never a second
|
|
32
|
+
// hand-rolled copy that could drift.
|
|
23
33
|
globalThis.tmctViz = {
|
|
24
34
|
ask, parseQuery, parseEntities, spiralExpand, mostRecentIndividual, derivedUpdatedAt,
|
|
25
|
-
MEMORY_SPIRAL_EXPAND_KINDS, buildVizNodesAndEdges,
|
|
35
|
+
MEMORY_SPIRAL_EXPAND_KINDS, MEMORY_FACT_LINK_KINDS, buildVizNodesAndEdges,
|
|
36
|
+
deriveFactTermGraph, pickLegendDimension, legendValueFor, edgeKindsFor, collapseToTopN,
|
|
26
37
|
};
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
var writeFile = unavailable("writeFile");
|
|
48
48
|
var appendFile = unavailable("appendFile");
|
|
49
49
|
var mkdir = unavailable("mkdir");
|
|
50
|
+
var mkdtemp = unavailable("mkdtemp");
|
|
50
51
|
var rename = unavailable("rename");
|
|
51
52
|
var unlink = unavailable("unlink");
|
|
52
53
|
var rm = unavailable("rm");
|
|
@@ -54,9 +55,14 @@
|
|
|
54
55
|
var access = unavailable("access");
|
|
55
56
|
var copyFile = unavailable("copyFile");
|
|
56
57
|
var readdir = unavailable("readdir");
|
|
58
|
+
var createReadStream = unavailable("createReadStream");
|
|
59
|
+
var createWriteStream = unavailable("createWriteStream");
|
|
57
60
|
var randomBytes = unavailable("randomBytes");
|
|
58
61
|
var createHash = unavailable("createHash");
|
|
59
62
|
var createRequireFromPath = unavailable("createRequireFromPath");
|
|
63
|
+
var spawnSync = unavailable("spawnSync");
|
|
64
|
+
var createInterface = unavailable("createInterface");
|
|
65
|
+
var DatabaseSync = unavailable("DatabaseSync");
|
|
60
66
|
|
|
61
67
|
// node-stub:node:path
|
|
62
68
|
var unavailable2 = (name) => () => {
|
|
@@ -69,6 +75,7 @@
|
|
|
69
75
|
var writeFile2 = unavailable2("writeFile");
|
|
70
76
|
var appendFile2 = unavailable2("appendFile");
|
|
71
77
|
var mkdir2 = unavailable2("mkdir");
|
|
78
|
+
var mkdtemp2 = unavailable2("mkdtemp");
|
|
72
79
|
var rename2 = unavailable2("rename");
|
|
73
80
|
var unlink2 = unavailable2("unlink");
|
|
74
81
|
var rm2 = unavailable2("rm");
|
|
@@ -76,11 +83,16 @@
|
|
|
76
83
|
var access2 = unavailable2("access");
|
|
77
84
|
var copyFile2 = unavailable2("copyFile");
|
|
78
85
|
var readdir2 = unavailable2("readdir");
|
|
86
|
+
var createReadStream2 = unavailable2("createReadStream");
|
|
87
|
+
var createWriteStream2 = unavailable2("createWriteStream");
|
|
79
88
|
var join = (...a) => a.join("/");
|
|
80
89
|
var dirname = (p) => String(p).replace(/\/[^/]*$/, "");
|
|
81
90
|
var randomBytes2 = unavailable2("randomBytes");
|
|
82
91
|
var createHash2 = unavailable2("createHash");
|
|
83
92
|
var createRequireFromPath2 = unavailable2("createRequireFromPath");
|
|
93
|
+
var spawnSync2 = unavailable2("spawnSync");
|
|
94
|
+
var createInterface2 = unavailable2("createInterface");
|
|
95
|
+
var DatabaseSync2 = unavailable2("DatabaseSync");
|
|
84
96
|
|
|
85
97
|
// node-stub:node:url
|
|
86
98
|
var unavailable3 = (name) => () => {
|
|
@@ -93,6 +105,7 @@
|
|
|
93
105
|
var writeFile3 = unavailable3("writeFile");
|
|
94
106
|
var appendFile3 = unavailable3("appendFile");
|
|
95
107
|
var mkdir3 = unavailable3("mkdir");
|
|
108
|
+
var mkdtemp3 = unavailable3("mkdtemp");
|
|
96
109
|
var rename3 = unavailable3("rename");
|
|
97
110
|
var unlink3 = unavailable3("unlink");
|
|
98
111
|
var rm3 = unavailable3("rm");
|
|
@@ -100,10 +113,15 @@
|
|
|
100
113
|
var access3 = unavailable3("access");
|
|
101
114
|
var copyFile3 = unavailable3("copyFile");
|
|
102
115
|
var readdir3 = unavailable3("readdir");
|
|
116
|
+
var createReadStream3 = unavailable3("createReadStream");
|
|
117
|
+
var createWriteStream3 = unavailable3("createWriteStream");
|
|
103
118
|
var fileURLToPath = (u) => String(u);
|
|
104
119
|
var randomBytes3 = unavailable3("randomBytes");
|
|
105
120
|
var createHash3 = unavailable3("createHash");
|
|
106
121
|
var createRequireFromPath3 = unavailable3("createRequireFromPath");
|
|
122
|
+
var spawnSync3 = unavailable3("spawnSync");
|
|
123
|
+
var createInterface3 = unavailable3("createInterface");
|
|
124
|
+
var DatabaseSync3 = unavailable3("DatabaseSync");
|
|
107
125
|
|
|
108
126
|
// node-stub:node:fs/promises
|
|
109
127
|
var unavailable4 = (name) => () => {
|
|
@@ -116,6 +134,7 @@
|
|
|
116
134
|
var writeFile4 = unavailable4("writeFile");
|
|
117
135
|
var appendFile4 = unavailable4("appendFile");
|
|
118
136
|
var mkdir4 = unavailable4("mkdir");
|
|
137
|
+
var mkdtemp4 = unavailable4("mkdtemp");
|
|
119
138
|
var rename4 = unavailable4("rename");
|
|
120
139
|
var unlink4 = unavailable4("unlink");
|
|
121
140
|
var rm4 = unavailable4("rm");
|
|
@@ -123,9 +142,14 @@
|
|
|
123
142
|
var access4 = unavailable4("access");
|
|
124
143
|
var copyFile4 = unavailable4("copyFile");
|
|
125
144
|
var readdir4 = unavailable4("readdir");
|
|
145
|
+
var createReadStream4 = unavailable4("createReadStream");
|
|
146
|
+
var createWriteStream4 = unavailable4("createWriteStream");
|
|
126
147
|
var randomBytes4 = unavailable4("randomBytes");
|
|
127
148
|
var createHash4 = unavailable4("createHash");
|
|
128
149
|
var createRequireFromPath4 = unavailable4("createRequireFromPath");
|
|
150
|
+
var spawnSync4 = unavailable4("spawnSync");
|
|
151
|
+
var createInterface4 = unavailable4("createInterface");
|
|
152
|
+
var DatabaseSync4 = unavailable4("DatabaseSync");
|
|
129
153
|
|
|
130
154
|
// src/memory/trust.mjs
|
|
131
155
|
var SOURCE_PRIOR = Object.freeze({
|
|
@@ -133,7 +157,9 @@
|
|
|
133
157
|
teach: 0.95,
|
|
134
158
|
provider: 0.9,
|
|
135
159
|
corpus: 0.7,
|
|
160
|
+
corpusWeak: 0.55,
|
|
136
161
|
web: 0.4,
|
|
162
|
+
extracted: 0.45,
|
|
137
163
|
entailed: 0.3
|
|
138
164
|
});
|
|
139
165
|
var RECENCY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
@@ -144,6 +170,31 @@
|
|
|
144
170
|
var CREATED_AT_PROP = "mgx:createdAt";
|
|
145
171
|
var UPDATED_AT_PROP = "mgx:updatedAt";
|
|
146
172
|
var MEMORY_MANIFEST_REL = join(MEMORY_DIR_REL, "manifest.json");
|
|
173
|
+
function parseChatTagRest(rest) {
|
|
174
|
+
const at = rest.indexOf("@");
|
|
175
|
+
const beforeAt = at >= 0 ? rest.slice(0, at) : rest;
|
|
176
|
+
const createdAt = at >= 0 ? rest.slice(at + 1) : "";
|
|
177
|
+
const colon = beforeAt.indexOf(":");
|
|
178
|
+
const sessionId = colon >= 0 ? beforeAt.slice(colon + 1) : "";
|
|
179
|
+
return { createdAt, ...sessionId ? { sessionId } : {} };
|
|
180
|
+
}
|
|
181
|
+
function provenanceTagToSource(tag) {
|
|
182
|
+
const t = String(tag || "").trim();
|
|
183
|
+
if (!t) return null;
|
|
184
|
+
const head = t.split(/\s+/)[0];
|
|
185
|
+
if (head.startsWith("corpus-weak:")) return { kind: "corpusWeak", name: head.slice("corpus-weak:".length) || "unknown" };
|
|
186
|
+
if (head.startsWith("corpus:")) return { kind: "corpus", name: head.slice("corpus:".length) || "unknown" };
|
|
187
|
+
if (head.startsWith("ace:")) return { kind: "operator", ...parseChatTagRest(head.slice("ace:".length)) };
|
|
188
|
+
if (head.startsWith("teach:")) {
|
|
189
|
+
return { kind: "teach", ...parseChatTagRest(head.slice("teach:".length)) };
|
|
190
|
+
}
|
|
191
|
+
if (head.startsWith("web:")) return { kind: "web", url: head.slice("web:".length) };
|
|
192
|
+
if (head.startsWith("url:")) return { kind: "web", url: head.slice("url:".length) };
|
|
193
|
+
if (head.startsWith("extracted:")) return { kind: "extracted", name: head.slice("extracted:".length) || "unknown" };
|
|
194
|
+
if (head.startsWith("entailed:")) return { kind: "entailed", rule: head.slice("entailed:".length) };
|
|
195
|
+
if (head.startsWith("chat:") || head.startsWith("session:") || head.startsWith("operator")) return { kind: "operator" };
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
147
198
|
var RULE_KIND_COMPOSE2 = "compose2";
|
|
148
199
|
var RULE_KIND_FILTER = "filter";
|
|
149
200
|
var RULE_KIND_RECURSIVE = "recursive";
|
|
@@ -216,11 +267,21 @@
|
|
|
216
267
|
"mgx:saidinsession": "saidInSession",
|
|
217
268
|
"mgx:inreplyto": "inReplyTo",
|
|
218
269
|
"mgx:statedby": "statedBy",
|
|
219
|
-
"mgx:canonicalisedfrom": "canonicalisedFrom"
|
|
270
|
+
"mgx:canonicalisedfrom": "canonicalisedFrom",
|
|
271
|
+
// PLAN_VIZ_MEMORY.md Bug 2 fix: the two FIXED structural link kinds
|
|
272
|
+
// deriveFactTermGraph (below) synthesizes on every Fact — Fact -> its own
|
|
273
|
+
// subject/object Term individual. Without these a walk seeded on a Fact (the
|
|
274
|
+
// default mostRecentIndividual seed right after a teach turn) could never
|
|
275
|
+
// reach the term graph at all. Distinct from the per-predicate kinds below
|
|
276
|
+
// (an open-ended, DYNAMIC set — see relationKind's "factrel:" branch), these
|
|
277
|
+
// two are fixed and few, so a plain PROP_KIND row is the simplest fit.
|
|
278
|
+
"mgx:factsubjectterm": "factSubjectTerm",
|
|
279
|
+
"mgx:factobjectterm": "factObjectTerm"
|
|
220
280
|
};
|
|
221
281
|
function relationKind(group) {
|
|
222
282
|
const prop = String(group?.prop || "").toLowerCase();
|
|
223
283
|
if (PROP_KIND[prop]) return PROP_KIND[prop];
|
|
284
|
+
if (prop.startsWith("factrel:")) return group.predicate || null;
|
|
224
285
|
const pred = String(group?.predicate || "").toLowerCase();
|
|
225
286
|
if (/symbol/.test(pred)) {
|
|
226
287
|
if (/\b(call|invoke)/.test(pred)) return "callsSymbol";
|
|
@@ -338,7 +399,8 @@
|
|
|
338
399
|
kinds = SPIRAL_EXPAND_KINDS,
|
|
339
400
|
classPredicate = (ind) => (ind.class || "") === "Module",
|
|
340
401
|
idNormalizer = null,
|
|
341
|
-
seeds: seedsOpt = null
|
|
402
|
+
seeds: seedsOpt = null,
|
|
403
|
+
hubDegree = Infinity
|
|
342
404
|
} = {}) {
|
|
343
405
|
const byId = new Map(scored.map((s) => [s.ind.id, s]));
|
|
344
406
|
let maxSeed = 0;
|
|
@@ -412,6 +474,7 @@
|
|
|
412
474
|
emitted++;
|
|
413
475
|
}
|
|
414
476
|
if (node.hop >= depth) continue;
|
|
477
|
+
if (node.hop > 0 && degree(node.id) > hubDegree) continue;
|
|
415
478
|
const cands = [];
|
|
416
479
|
for (const nid of adj.get(node.id) || []) {
|
|
417
480
|
if (visited.has(nid)) continue;
|
|
@@ -499,6 +562,139 @@
|
|
|
499
562
|
}
|
|
500
563
|
return { nodes, edges };
|
|
501
564
|
}
|
|
565
|
+
var MEMORY_FACT_CLASS = "Fact";
|
|
566
|
+
var MEMORY_TERM_CLASS = "Term";
|
|
567
|
+
function deriveFactTermGraph(graph) {
|
|
568
|
+
const termById = /* @__PURE__ */ new Map();
|
|
569
|
+
const groupByPredicate = /* @__PURE__ */ new Map();
|
|
570
|
+
const subjectLinks = [];
|
|
571
|
+
const objectLinks = [];
|
|
572
|
+
const termId = (t) => `term:${t}`;
|
|
573
|
+
const ensureTerm = (t) => {
|
|
574
|
+
const id = termId(t);
|
|
575
|
+
if (!termById.has(id)) termById.set(id, { id, label: t, class: MEMORY_TERM_CLASS, attributes: [] });
|
|
576
|
+
return id;
|
|
577
|
+
};
|
|
578
|
+
for (const ind of graph?.individuals || []) {
|
|
579
|
+
if ((ind?.class || "") !== MEMORY_FACT_CLASS) continue;
|
|
580
|
+
const attrs = ind.attributes || [];
|
|
581
|
+
const s = attrs.find((a) => a?.prop === "rdf:subject")?.value;
|
|
582
|
+
const p = attrs.find((a) => a?.prop === "rdf:predicate")?.value;
|
|
583
|
+
const o = attrs.find((a) => a?.prop === "rdf:object")?.value;
|
|
584
|
+
if (!s || !p || !o) continue;
|
|
585
|
+
const subjectTermId = ensureTerm(s);
|
|
586
|
+
const objectTermId = ensureTerm(o);
|
|
587
|
+
let group = groupByPredicate.get(p);
|
|
588
|
+
if (!group) {
|
|
589
|
+
group = { predicate: p, prop: `factrel:${p}`, count: 0, edges: [] };
|
|
590
|
+
groupByPredicate.set(p, group);
|
|
591
|
+
}
|
|
592
|
+
group.edges.push({ subject: subjectTermId, object: objectTermId, subjectLabel: s, objectLabel: o });
|
|
593
|
+
group.count = group.edges.length;
|
|
594
|
+
subjectLinks.push({ subject: ind.id, object: subjectTermId, subjectLabel: ind.label, objectLabel: s });
|
|
595
|
+
objectLinks.push({ subject: ind.id, object: objectTermId, subjectLabel: ind.label, objectLabel: o });
|
|
596
|
+
}
|
|
597
|
+
if (!termById.size) return { graph, factRelationKinds: [] };
|
|
598
|
+
const individuals = [...graph.individuals || [], ...termById.values()];
|
|
599
|
+
const byId = new Map(graph.byId);
|
|
600
|
+
for (const term of termById.values()) byId.set(term.id, term);
|
|
601
|
+
const relations = [
|
|
602
|
+
...graph.relations || [],
|
|
603
|
+
...groupByPredicate.values(),
|
|
604
|
+
{ predicate: "factSubjectTerm", prop: "mgx:factSubjectTerm", count: subjectLinks.length, edges: subjectLinks },
|
|
605
|
+
{ predicate: "factObjectTerm", prop: "mgx:factObjectTerm", count: objectLinks.length, edges: objectLinks }
|
|
606
|
+
];
|
|
607
|
+
return {
|
|
608
|
+
graph: { ...graph, individuals, byId, relations },
|
|
609
|
+
factRelationKinds: [...groupByPredicate.keys()]
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
var MEMORY_FACT_LINK_KINDS = ["factSubjectTerm", "factObjectTerm"];
|
|
613
|
+
function edgeKindsFor(mode, factRelationKinds) {
|
|
614
|
+
const relationKinds = [...factRelationKinds, ...MEMORY_FACT_LINK_KINDS];
|
|
615
|
+
if (mode === "meta") return [...MEMORY_SPIRAL_EXPAND_KINDS];
|
|
616
|
+
if (mode === "relation") return relationKinds;
|
|
617
|
+
return [...MEMORY_SPIRAL_EXPAND_KINDS, ...relationKinds];
|
|
618
|
+
}
|
|
619
|
+
var LEGEND_MAX_BUCKETS = 20;
|
|
620
|
+
var LEGEND_MIN_BUCKETS = 2;
|
|
621
|
+
var LEGEND_COLLAPSE_TOP_N = 15;
|
|
622
|
+
function normalizedEntropy(buckets) {
|
|
623
|
+
const k = buckets.length;
|
|
624
|
+
if (k < 2) return 0;
|
|
625
|
+
const total = buckets.reduce((sum, b) => sum + b.count, 0);
|
|
626
|
+
if (!total) return 0;
|
|
627
|
+
let h = 0;
|
|
628
|
+
for (const b of buckets) {
|
|
629
|
+
if (!b.count) continue;
|
|
630
|
+
const p = b.count / total;
|
|
631
|
+
h -= p * Math.log2(p);
|
|
632
|
+
}
|
|
633
|
+
return h / Math.log2(k);
|
|
634
|
+
}
|
|
635
|
+
function collapseToTopN(buckets) {
|
|
636
|
+
if (buckets.length <= LEGEND_MAX_BUCKETS) return buckets;
|
|
637
|
+
const sorted = [...buckets].sort((a, b) => b.count - a.count || (a.value < b.value ? -1 : 1));
|
|
638
|
+
const kept = sorted.slice(0, LEGEND_COLLAPSE_TOP_N);
|
|
639
|
+
const restCount = sorted.slice(LEGEND_COLLAPSE_TOP_N).reduce((sum, b) => sum + b.count, 0);
|
|
640
|
+
return restCount ? [...kept, { value: "Other", count: restCount }] : kept;
|
|
641
|
+
}
|
|
642
|
+
function bucketCounts(values) {
|
|
643
|
+
const counts = /* @__PURE__ */ new Map();
|
|
644
|
+
for (const v of values) {
|
|
645
|
+
if (v == null || v === "") continue;
|
|
646
|
+
counts.set(v, (counts.get(v) || 0) + 1);
|
|
647
|
+
}
|
|
648
|
+
return [...counts.entries()].map(([value, count]) => ({ value, count }));
|
|
649
|
+
}
|
|
650
|
+
function provenanceBucketLabel(rawTag) {
|
|
651
|
+
const tag = String(rawTag || "").split(" | ")[0].trim();
|
|
652
|
+
if (!tag) return null;
|
|
653
|
+
const src = provenanceTagToSource(tag);
|
|
654
|
+
if (!src) return null;
|
|
655
|
+
if (src.kind === "corpus" || src.kind === "corpusWeak") {
|
|
656
|
+
return `${src.kind === "corpusWeak" ? "corpus-weak" : "corpus"}:${src.name || "unknown"}`;
|
|
657
|
+
}
|
|
658
|
+
if (src.kind === "extracted") return `extracted:${src.name || "unknown"}`;
|
|
659
|
+
if (src.kind === "entailed") return `entailed:${src.rule || "unknown"}`;
|
|
660
|
+
if (src.kind === "operator") return "ace:chat";
|
|
661
|
+
if (src.kind === "teach") return "teach:chat";
|
|
662
|
+
if (src.kind === "web") return "web";
|
|
663
|
+
return src.kind;
|
|
664
|
+
}
|
|
665
|
+
function legendValueFor(graph, node, dimension) {
|
|
666
|
+
if (dimension === "class") return node?.class || "(none)";
|
|
667
|
+
if (!node || node.class !== MEMORY_FACT_CLASS) return null;
|
|
668
|
+
const attrs = graph?.byId?.get?.(node.id)?.attributes || [];
|
|
669
|
+
if (dimension === "predicate") return attrs.find((a) => a?.prop === "rdf:predicate")?.value || null;
|
|
670
|
+
if (dimension === "provenance") return provenanceBucketLabel(attrs.find((a) => a?.prop === "mgx:factProvenance")?.value);
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
function pickLegendDimension(graph, nodes) {
|
|
674
|
+
const classBuckets = bucketCounts((nodes || []).map((n) => legendValueFor(graph, n, "class")));
|
|
675
|
+
const predicateBuckets = bucketCounts((nodes || []).map((n) => legendValueFor(graph, n, "predicate")));
|
|
676
|
+
const provenanceBuckets = bucketCounts((nodes || []).map((n) => legendValueFor(graph, n, "provenance")));
|
|
677
|
+
const score = (rawBuckets) => {
|
|
678
|
+
const buckets = collapseToTopN(rawBuckets);
|
|
679
|
+
const qualifies = buckets.length >= LEGEND_MIN_BUCKETS && buckets.length <= LEGEND_MAX_BUCKETS;
|
|
680
|
+
return { score: normalizedEntropy(buckets), qualifies, buckets };
|
|
681
|
+
};
|
|
682
|
+
const dimensions = {
|
|
683
|
+
class: score(classBuckets),
|
|
684
|
+
predicate: score(predicateBuckets),
|
|
685
|
+
provenance: score(provenanceBuckets)
|
|
686
|
+
};
|
|
687
|
+
let primary = "class";
|
|
688
|
+
let bestScore = -1;
|
|
689
|
+
for (const [name, d] of Object.entries(dimensions)) {
|
|
690
|
+
if (!d.qualifies) continue;
|
|
691
|
+
if (d.score > bestScore) {
|
|
692
|
+
bestScore = d.score;
|
|
693
|
+
primary = name;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return { primary, dimensions };
|
|
697
|
+
}
|
|
502
698
|
function moduleIdOfId(graph, id) {
|
|
503
699
|
const ind = graph.byId?.get?.(id);
|
|
504
700
|
if (ind) return moduleIdOf(graph, ind);
|
|
@@ -1141,7 +1337,16 @@
|
|
|
1141
1337
|
"sorta",
|
|
1142
1338
|
"btw",
|
|
1143
1339
|
"by the way",
|
|
1144
|
-
"you"
|
|
1340
|
+
"you",
|
|
1341
|
+
// "quick q" (BENCHMARK_CONVERSATION_1.8.14.md item 11): the casual abbreviated
|
|
1342
|
+
// sibling of GREETING_PREAMBLE_RE's own "quick question" clause (normalize.mjs)
|
|
1343
|
+
// — that frame requires a delimiter immediately after the greeting word
|
|
1344
|
+
// ("hey, quick question - …"), so it never matches "hey quick q, …" (no
|
|
1345
|
+
// delimiter between "hey" and "quick q"). Filler-stripping instead — this
|
|
1346
|
+
// list is matched word-boundary-anywhere, not anchored — closes the gap
|
|
1347
|
+
// without needing GREETING_PREAMBLE_RE's own stricter delimiter-position
|
|
1348
|
+
// shape.
|
|
1349
|
+
"quick q"
|
|
1145
1350
|
]);
|
|
1146
1351
|
var CONTEXT_PRONOUNS = Object.freeze(["this", "it", "that", "here", "this one", "that one"]);
|
|
1147
1352
|
var NEGATION_FRAMES = Object.freeze([
|
|
@@ -1437,6 +1642,15 @@
|
|
|
1437
1642
|
var FOR_DIGIT_EXAMPLE_RE = /\b4\s+(example|instance)\b(?!\s*[a-z])/gi;
|
|
1438
1643
|
var KIND_NOUN_ANAPHORA_RE = /\b(this|that)\s+(class|module|function|method|attribute|variable|file|commit)\b/gi;
|
|
1439
1644
|
var VERB_ALTERNATION = Object.keys(VERB_TO_KIND).sort((a, b) => b.length - a.length).map(escapeRegex).join("|");
|
|
1645
|
+
var FILLER_RE = FILLER_WORDS.length ? new RegExp(
|
|
1646
|
+
"\\b(" + [...FILLER_WORDS].sort((a, b) => b.length - a.length).map(escapeRegex).join("|") + ")\\b\\s*,?",
|
|
1647
|
+
"gi"
|
|
1648
|
+
) : null;
|
|
1649
|
+
function stripFillerWords(text) {
|
|
1650
|
+
let q = String(text || "");
|
|
1651
|
+
if (FILLER_RE) q = q.replace(FILLER_RE, " ");
|
|
1652
|
+
return q.replace(/\s+/g, " ").trim();
|
|
1653
|
+
}
|
|
1440
1654
|
var RELATION_VERB_RE = new RegExp(
|
|
1441
1655
|
"\\b(?:" + Object.keys(VERB_TO_KIND).sort((a, b) => b.length - a.length).map(escapeRegex).join("|") + ")\\b",
|
|
1442
1656
|
"i"
|
|
@@ -1591,13 +1805,7 @@
|
|
|
1591
1805
|
q = applySelfCorrectionFrames(q);
|
|
1592
1806
|
q = applySubordinationFrames(q);
|
|
1593
1807
|
q = applyConditionalFrames(q);
|
|
1594
|
-
|
|
1595
|
-
const fillerRe = new RegExp(
|
|
1596
|
-
"\\b(" + [...FILLER_WORDS].sort((a, b) => b.length - a.length).map(escapeRegex).join("|") + ")\\b",
|
|
1597
|
-
"gi"
|
|
1598
|
-
);
|
|
1599
|
-
q = q.replace(fillerRe, " ");
|
|
1600
|
-
}
|
|
1808
|
+
q = stripFillerWords(q);
|
|
1601
1809
|
q = q.replace(/\?{2,}\s*$/, "?");
|
|
1602
1810
|
return q.replace(/\s+/g, " ").trim();
|
|
1603
1811
|
}
|
|
@@ -2386,6 +2594,7 @@
|
|
|
2386
2594
|
var writeFile5 = unavailable5("writeFile");
|
|
2387
2595
|
var appendFile5 = unavailable5("appendFile");
|
|
2388
2596
|
var mkdir5 = unavailable5("mkdir");
|
|
2597
|
+
var mkdtemp5 = unavailable5("mkdtemp");
|
|
2389
2598
|
var rename5 = unavailable5("rename");
|
|
2390
2599
|
var unlink5 = unavailable5("unlink");
|
|
2391
2600
|
var rm5 = unavailable5("rm");
|
|
@@ -2393,9 +2602,14 @@
|
|
|
2393
2602
|
var access5 = unavailable5("access");
|
|
2394
2603
|
var copyFile5 = unavailable5("copyFile");
|
|
2395
2604
|
var readdir5 = unavailable5("readdir");
|
|
2605
|
+
var createReadStream5 = unavailable5("createReadStream");
|
|
2606
|
+
var createWriteStream5 = unavailable5("createWriteStream");
|
|
2396
2607
|
var randomBytes5 = unavailable5("randomBytes");
|
|
2397
2608
|
var createHash5 = unavailable5("createHash");
|
|
2398
2609
|
var createRequireFromPath5 = unavailable5("createRequireFromPath");
|
|
2610
|
+
var spawnSync5 = unavailable5("spawnSync");
|
|
2611
|
+
var createInterface5 = unavailable5("createInterface");
|
|
2612
|
+
var DatabaseSync5 = unavailable5("DatabaseSync");
|
|
2399
2613
|
|
|
2400
2614
|
// src/answer-variants.mjs
|
|
2401
2615
|
var import_meta = {};
|
|
@@ -2531,7 +2745,7 @@
|
|
|
2531
2745
|
function parseComposite(text, nlp) {
|
|
2532
2746
|
const w = splitWords(text);
|
|
2533
2747
|
const lc = w.map((x) => x.toLowerCase());
|
|
2534
|
-
return parseExistence(w, lc) || parseQualifierCheck(w, lc) || parseNegation(text, nlp, 0) || parseForwardNegation(w, lc, nlp) || parseTemporal(w, lc, nlp, 0) || parseCommitFilter(w, lc) || parseAnaphora(w, lc, nlp) || parseAggregate(w, lc, nlp) || parseSuperlative(w, lc, nlp) || parseFind(w, lc, nlp, 0) || parseList(w, lc, nlp, 0) || parseNested(w, lc, nlp, 0) || parseRelationalOrQualified(w, lc, nlp, 0);
|
|
2748
|
+
return parseExistence(w, lc) || parseQualifierCheck(w, lc) || parseNegation(text, nlp, 0) || parseForwardNegation(w, lc, nlp) || parseTemporal(w, lc, nlp, 0) || parseCommitFilter(w, lc) || parseAnaphora(w, lc, nlp) || parseAggregate(w, lc, nlp) || parseSuperlative(w, lc, nlp) || parseFind(w, lc, nlp, 0) || parseList(w, lc, nlp, 0) || parseNested(w, lc, nlp, 0) || parsePluralAnaphoraObject(w, lc, nlp) || parseRelationalOrQualified(w, lc, nlp, 0);
|
|
2535
2749
|
}
|
|
2536
2750
|
function complementAst(entityType, diffAtom) {
|
|
2537
2751
|
return {
|
|
@@ -2634,6 +2848,23 @@
|
|
|
2634
2848
|
}
|
|
2635
2849
|
return null;
|
|
2636
2850
|
}
|
|
2851
|
+
var PLURAL_ANAPHORA_OBJECT = /* @__PURE__ */ new Set(["those", "them"]);
|
|
2852
|
+
function parsePluralAnaphoraObject(w, lc, nlp) {
|
|
2853
|
+
for (let i = 0; i < lc.length; i += 1) {
|
|
2854
|
+
if (!PLURAL_ANAPHORA_OBJECT.has(lc[i])) continue;
|
|
2855
|
+
if (lc[i - 1] === "of") continue;
|
|
2856
|
+
const isTerminal = i === lc.length - 1;
|
|
2857
|
+
const leadsAVerb = i + 1 < lc.length && !!VERB_TO_KIND[lc[i + 1]];
|
|
2858
|
+
if (!isTerminal && !leadsAVerb) continue;
|
|
2859
|
+
const head = [...w.slice(0, i), NEST_SENTINEL, ...w.slice(i + 1)];
|
|
2860
|
+
const outer = parseSimpleClause(head.join(" "), nlp);
|
|
2861
|
+
if (!outer || outer.shape !== "reverse" && outer.shape !== "forward") continue;
|
|
2862
|
+
if (outer.object !== NEST_SENTINEL) continue;
|
|
2863
|
+
if (outer.modifier && outer.modifier !== "direct") continue;
|
|
2864
|
+
return { node: outer.shape === "reverse" ? "reverseSet" : "forwardSet", kind: outer.kind, entityType: outer.entityType, inner: { node: "prevSet" } };
|
|
2865
|
+
}
|
|
2866
|
+
return null;
|
|
2867
|
+
}
|
|
2637
2868
|
var TEMPORAL_AUX = /* @__PURE__ */ new Set(["did", "was", "were", "do", "does", "has", "have", "had"]);
|
|
2638
2869
|
var TEMPORAL_TAIL = /* @__PURE__ */ new Set([
|
|
2639
2870
|
"change",
|
|
@@ -3289,8 +3520,8 @@
|
|
|
3289
3520
|
const objs = uniqueById(MEMBERSHIP_KINDS.flatMap((k) => forwardOverSet(graph, k, /* @__PURE__ */ new Set([id]))));
|
|
3290
3521
|
return entityType ? objs.filter((o) => o.class === entityType) : objs;
|
|
3291
3522
|
}
|
|
3292
|
-
function resolveMembershipOwner(graph, term) {
|
|
3293
|
-
const r =
|
|
3523
|
+
function resolveMembershipOwner(graph, term, contextId = null) {
|
|
3524
|
+
const r = resolveTermOrContext(graph, term, contextId);
|
|
3294
3525
|
if (!(r.match && r.tier === 1)) {
|
|
3295
3526
|
const dirMods = directoryScopeModules(graph, term);
|
|
3296
3527
|
if (dirMods.length) return { kind: "dir", mods: dirMods };
|
|
@@ -3479,8 +3710,17 @@
|
|
|
3479
3710
|
const ids = new Set(evalSet(graph, ast.inner, opts).map((i) => i.id));
|
|
3480
3711
|
return forwardOverSet(graph, ast.kind, ids);
|
|
3481
3712
|
}
|
|
3713
|
+
// the previous list-shaped answer's own id set (parsePluralAnaphoraObject's
|
|
3714
|
+
// "those"/"them" leaf) — evalComposite's reverseSet/forwardSet dispatch already
|
|
3715
|
+
// intercepts the genuinely-empty (no `prev` at all) case as an honest "needs a
|
|
3716
|
+
// previous answer" miss, same as evalAnaphora's own no-prev branch; this is only
|
|
3717
|
+
// reached with a real, non-empty `prev` in hand.
|
|
3718
|
+
case "prevSet": {
|
|
3719
|
+
const prev = opts && opts.prev;
|
|
3720
|
+
return Array.isArray(prev) ? prev.map((id) => graph.byId.get(id)).filter(Boolean) : [];
|
|
3721
|
+
}
|
|
3482
3722
|
case "membership": {
|
|
3483
|
-
const owner = resolveMembershipOwner(graph, ast.term);
|
|
3723
|
+
const owner = resolveMembershipOwner(graph, ast.term, opts && opts.contextId);
|
|
3484
3724
|
if (owner.kind === "dir") {
|
|
3485
3725
|
if (!ast.entityType || ast.entityType === "Module") return owner.mods;
|
|
3486
3726
|
const ids = new Set(owner.mods.map((m) => m.id));
|
|
@@ -3640,7 +3880,7 @@
|
|
|
3640
3880
|
const memNode = qualNode ? qualNode.inner : ast;
|
|
3641
3881
|
const entityType = memNode.entityType;
|
|
3642
3882
|
const filterFn = qualNode ? (ind) => qualNode.filters.every((f) => qualHolds(graph, ind, QUALIFIERS[f])) : null;
|
|
3643
|
-
const owner = resolveMembershipOwner(graph, memNode.term);
|
|
3883
|
+
const owner = resolveMembershipOwner(graph, memNode.term, opts && opts.contextId);
|
|
3644
3884
|
if (owner.kind === "dir") {
|
|
3645
3885
|
let objs;
|
|
3646
3886
|
if (!entityType || entityType === "Module") objs = owner.mods;
|
|
@@ -3720,6 +3960,9 @@
|
|
|
3720
3960
|
broad: !narrow.length && broad.length > 0
|
|
3721
3961
|
};
|
|
3722
3962
|
}
|
|
3963
|
+
if ((ast.node === "reverseSet" || ast.node === "forwardSet") && ast.inner.node === "prevSet" && !(Array.isArray(opts.prev) && opts.prev.length)) {
|
|
3964
|
+
return { compositeMiss: true, reason: "no-prev", matches: [] };
|
|
3965
|
+
}
|
|
3723
3966
|
return { compositeKind: "set", matches: evalSet(graph, ast, opts), entityType: ast.entityType || null };
|
|
3724
3967
|
}
|
|
3725
3968
|
var compositeList = (matches) => listJoin(matches.slice(0, OVERFLOW_CAP).map((m) => ["Function", "Method"].includes(m.class) ? `${m.label}()` : m.label)) + (matches.length > OVERFLOW_CAP ? `, \u2026and ${matches.length - OVERFLOW_CAP} more` : "");
|
|
@@ -4204,7 +4447,7 @@
|
|
|
4204
4447
|
if (shape === "ask") {
|
|
4205
4448
|
const subj = resolveTermOrContext(graph, parsed.subject, contextId);
|
|
4206
4449
|
const obj = resolveTermOrContext(graph, parsed.object, contextId);
|
|
4207
|
-
if (!subj.match || !obj.match) {
|
|
4450
|
+
if (!subj.match || !obj.match || subj.ambiguous || obj.ambiguous) {
|
|
4208
4451
|
return {
|
|
4209
4452
|
matches: [],
|
|
4210
4453
|
objMatch: obj.match,
|
|
@@ -4398,7 +4641,11 @@
|
|
|
4398
4641
|
widenNote = `, widened to ${siblingClass} subjects (no ${entityType} recorded)`;
|
|
4399
4642
|
}
|
|
4400
4643
|
}
|
|
4401
|
-
|
|
4644
|
+
const upRefineEligible = (kind === "touches" || kind === "calls") && objMatch.class === "Class" && !edgesOfKind2(graph, "contains").some((e) => e.subject === objMatch.id);
|
|
4645
|
+
const upRefineModule = upRefineEligible ? graph.byId.get(moduleIdOf2(graph, objMatch) || "") : null;
|
|
4646
|
+
if (matches2.length || !(upRefineEligible && upRefineModule)) {
|
|
4647
|
+
return { matches: matches2, objMatch, candidates, traversal: `${symbolKind} edges where object = ${objMatch.label}${widenNote}`, ambiguous, matchedVia };
|
|
4648
|
+
}
|
|
4402
4649
|
}
|
|
4403
4650
|
let gObjMatch = objMatch;
|
|
4404
4651
|
let gCandidates = candidates;
|
|
@@ -4413,7 +4660,7 @@
|
|
|
4413
4660
|
gCandidates = retry.candidates;
|
|
4414
4661
|
gAmbiguous = retry.ambiguous;
|
|
4415
4662
|
gMatchedVia = retry.matchedVia;
|
|
4416
|
-
} else if (
|
|
4663
|
+
} else if (wantClass === "Module") {
|
|
4417
4664
|
const mid = moduleIdOf2(graph, gObjMatch);
|
|
4418
4665
|
const mod = mid && graph.byId.get(mid);
|
|
4419
4666
|
if (mod) {
|
|
@@ -5154,6 +5401,12 @@ ${lines.join("\n")}`;
|
|
|
5154
5401
|
mostRecentIndividual,
|
|
5155
5402
|
derivedUpdatedAt,
|
|
5156
5403
|
MEMORY_SPIRAL_EXPAND_KINDS,
|
|
5157
|
-
|
|
5404
|
+
MEMORY_FACT_LINK_KINDS,
|
|
5405
|
+
buildVizNodesAndEdges,
|
|
5406
|
+
deriveFactTermGraph,
|
|
5407
|
+
pickLegendDimension,
|
|
5408
|
+
legendValueFor,
|
|
5409
|
+
edgeKindsFor,
|
|
5410
|
+
collapseToTopN
|
|
5158
5411
|
};
|
|
5159
5412
|
})();
|