@polycode-projects/the-mechanical-code-talker 2.5.4 → 2.6.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 +2 -2
- package/data/templates/responses.jsonl +1 -1
- package/package.json +1 -1
- package/src/domain/ask.mjs +66 -10
- package/src/domain/codegraph.mjs +6 -2
- package/src/domain/interpret/normalize.mjs +1 -1
- package/src/domain/router/registry.mjs +17 -4
- package/src/domain/router/resolver.mjs +4 -2
- package/src/services/chat.mjs +626 -43
- package/src/services/sessions.mjs +10 -2
- package/src/surfaces/web/memory-ask-browser.bundle.js +88 -19
|
@@ -103,7 +103,9 @@ export function upsertSession(entities, record) {
|
|
|
103
103
|
|
|
104
104
|
const started = record.started || "";
|
|
105
105
|
const ended = record.ended || turns.at(-1)?.ts || started;
|
|
106
|
-
|
|
106
|
+
// The session's recorded questions quote the user VERBATIM (`input`) when
|
|
107
|
+
// the turn carried a rewrite; `query` is the fallback for older sidecars.
|
|
108
|
+
const queries = turns.map((t) => String(t?.input || t?.query || "")).filter(Boolean);
|
|
107
109
|
const label = sessionLabel(record.id);
|
|
108
110
|
entities.individuals.push({
|
|
109
111
|
id: sid, label, class: SESSION_CLASS,
|
|
@@ -206,6 +208,9 @@ async function recordSessionMemory(graphFile, record, repoDirOverride = null) {
|
|
|
206
208
|
const utterances = [];
|
|
207
209
|
for (const t of record.turns || []) {
|
|
208
210
|
const query = String(t?.query || "");
|
|
211
|
+
// The visitor utterance quotes the user VERBATIM; `query` (possibly a
|
|
212
|
+
// rewrite) stays the transcript-matching key below, unchanged.
|
|
213
|
+
const spoken = String(t?.input || t?.query || "");
|
|
209
214
|
const ts = String(t?.ts || "");
|
|
210
215
|
if (!query || !ts) continue;
|
|
211
216
|
// the structured parse the turn produced — stored on the visitor utterance
|
|
@@ -216,7 +221,7 @@ async function recordSessionMemory(graphFile, record, repoDirOverride = null) {
|
|
|
216
221
|
if (t.miss) parsed.miss = true;
|
|
217
222
|
if (t.via) parsed.via = t.via; // answer provenance (W1) — carried into memory
|
|
218
223
|
utterances.push({
|
|
219
|
-
role: "visitor", text:
|
|
224
|
+
role: "visitor", text: spoken, ts, sessionId: record.id, sessionStarted: record.started || "",
|
|
220
225
|
...(Object.keys(parsed).length ? { parsed } : {}),
|
|
221
226
|
});
|
|
222
227
|
const answer = answers.get(turnKey(ts, query));
|
|
@@ -258,6 +263,9 @@ export function parseSessionJsonl(text) {
|
|
|
258
263
|
else if (rec?.type === "turn") {
|
|
259
264
|
turns.push({
|
|
260
265
|
ts: String(rec.ts || ""), query: String(rec.query || ""),
|
|
266
|
+
// the VERBATIM user line, recorded beside any rewritten query — the
|
|
267
|
+
// recall surfaces quote this form, never the rewrite.
|
|
268
|
+
...(rec.input ? { input: String(rec.input) } : {}),
|
|
261
269
|
resolvedIds: arr(rec.resolvedIds), answeredIds: arr(rec.answeredIds), miss: !!rec.miss,
|
|
262
270
|
// preserved for the memory fold (memory/fold.mjs): slash-command turns and
|
|
263
271
|
// conversational filler are recorded but never folded into the corpus.
|
|
@@ -1388,7 +1388,8 @@
|
|
|
1388
1388
|
lines.push(i === 0 ? `depth 1 (${level.length} direct dependents):` : `depth ${i + 1} (${level.length}):`);
|
|
1389
1389
|
for (const dep of level.slice(0, IMPACT_PER_DEPTH)) {
|
|
1390
1390
|
const tests = dep.tests.length ? `tests: ${capJoin(dep.tests, IMPACT_TESTS_PER_DEP)}` : "tests: none recorded";
|
|
1391
|
-
|
|
1391
|
+
const receipt = i === 0 ? `${dep.via} it` : "reaches it through an intermediary";
|
|
1392
|
+
lines.push(` - ${dep.label} (${receipt}) \u2014 ${tests}`);
|
|
1392
1393
|
}
|
|
1393
1394
|
if (level.length > IMPACT_PER_DEPTH) lines.push(` \u2026+${level.length - IMPACT_PER_DEPTH} more at depth ${i + 1}`);
|
|
1394
1395
|
});
|
|
@@ -1398,7 +1399,7 @@
|
|
|
1398
1399
|
});
|
|
1399
1400
|
if (truncatedStructural.length) {
|
|
1400
1401
|
lines.push(
|
|
1401
|
-
"warning: partial edge lists (" + truncatedStructural.map((t) => `${t.predicate}: ${t.shown}/${t.count}`).join(", ") + ") \u2014 this closure may be missing edges. Cross-check critical results with
|
|
1402
|
+
"warning: partial edge lists (" + truncatedStructural.map((t) => `${t.predicate}: ${t.shown}/${t.count}`).join(", ") + ") \u2014 this closure may be missing edges. Cross-check critical results with a lexical search (/find <term>)."
|
|
1402
1403
|
);
|
|
1403
1404
|
}
|
|
1404
1405
|
return lines.join("\n");
|
|
@@ -2876,7 +2877,7 @@ ${shown.join("\n")}${tail}`;
|
|
|
2876
2877
|
GREETING_PREAMBLE_RE = /^(?:hi|hiya|hello|hey|yo|howdy|g'?day|yeah\s+nah|good\s+(?:morning|afternoon|evening|day)|greetings|salutations)(?:\s+(?:there|pardner|folks|friend|mate))?\s*[,.—–-]\s*(?:(?:just\s+a\s+)?quick\s+question\s*[,:—–-]?\s*)?(.+)$/i;
|
|
2877
2878
|
THANKS_PREAMBLE_RE = /^(?:thanks|thank\s+you|many\s+thanks|thx|ty|cheers)(?:\s+(?:so\s+much|a\s+lot|very\s+much|a\s+bunch))?\s*[,—–-]\s*(?:(?:just\s+a\s+)?quick\s+question\s*[,:—–-]?\s*)?(.+)$/i;
|
|
2878
2879
|
ACK_PREAMBLE_RE = /^(?:(?:ok(?:ay)?|aight|cool|alright|sure|right|fine|great|nice|got it|gotcha|sounds good|no worries|no problem)[\s,]+)+(.+)$/i;
|
|
2879
|
-
BROWSING_PREAMBLE_RE = /^(?:just\s+(?:poking\s+around|looking\s+around|browsing|exploring|checking\s+(?:this|it)\s+out)|first\s+time\s+(?:trying\s+this\s+out|using\s+this|here))\s*[,.—–-]\s*(.+)$/i;
|
|
2880
|
+
BROWSING_PREAMBLE_RE = /^(?:just\s+(?:poking\s+around|looking\s+around|browsing|exploring|checking\s+(?:this|it)\s+out)|first\s+time\s+(?:trying\s+this\s+out|using\s+this|here)|i'?m\s+new\s+(?:here|around\s+here|to\s+(?:this|all\s+this)(?:\s+(?:repo|codebase|project|app|tool|thing))?))\s*[,.—–-]\s*(.+)$/i;
|
|
2880
2881
|
HEDGE_ADVERB_PREAMBLE_RE = /^(?:(?:maybe|possibly|perhaps)\s+)+(.+)$/i;
|
|
2881
2882
|
TROUBLE_ASIDE_RE = /,?\s*if\s+(?:it'?s|it\s+is|that'?s|that\s+is)\s+not\s+too\s+much\s+(?:trouble|bother|hassle)\s*,?\s*/i;
|
|
2882
2883
|
MODAL_WRAPPER_RE = /^(?:can|could|would|will)\s+you\s+(?:please\s+)?(.+?)(?:[,\s]+please)?\??$/i;
|
|
@@ -3782,7 +3783,7 @@ ${shown.join("\n")}${tail}`;
|
|
|
3782
3783
|
function parseComposite(text, nlp) {
|
|
3783
3784
|
const w = splitWords(text);
|
|
3784
3785
|
const lc = w.map((x) => x.toLowerCase());
|
|
3785
|
-
return parseExistence(w, lc) || parseQualifierCheck(w, lc) || parseUniversal(w, lc, nlp) || parseNegation(text, nlp, 0) || parseNegatedAsk(w, lc) || 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);
|
|
3786
|
+
return parseExistence(w, lc) || parseQualifierCheck(w, lc) || parseUniversal(w, lc, nlp) || parseNegation(text, nlp, 0) || parseNegatedAsk(w, lc, nlp) || 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);
|
|
3786
3787
|
}
|
|
3787
3788
|
function complementAst(entityType, diffAtom) {
|
|
3788
3789
|
return {
|
|
@@ -4109,11 +4110,11 @@ ${shown.join("\n")}${tail}`;
|
|
|
4109
4110
|
if (!term) return { node: "miss", reason: `"is/are <qualifier>" needs a named thing to check first` };
|
|
4110
4111
|
return { node: "qualCheck", term, qualifier: lc[qualIdx], negated, scope: tail?.scope || null };
|
|
4111
4112
|
}
|
|
4112
|
-
function parseNegatedAsk(w, lc) {
|
|
4113
|
-
if (
|
|
4113
|
+
function parseNegatedAsk(w, lc, nlp) {
|
|
4114
|
+
if (!["do", "does", "did", "is", "are", "was", "were"].includes(lc[0])) return null;
|
|
4114
4115
|
const notIdx = lc.indexOf("not", 1);
|
|
4115
4116
|
if (notIdx < 0) return null;
|
|
4116
|
-
const positive =
|
|
4117
|
+
const positive = parseSimpleClause(w.filter((_, i) => i !== notIdx).join(" "), nlp);
|
|
4117
4118
|
if (!positive || positive.shape !== "ask") return null;
|
|
4118
4119
|
return { ...positive, negated: true };
|
|
4119
4120
|
}
|
|
@@ -4916,7 +4917,11 @@ ${shown.join("\n")}${tail}`;
|
|
|
4916
4917
|
return { compositeKind: "temporal", matches: commits, entityType: ast.entityType, innerCount: inner.length };
|
|
4917
4918
|
}
|
|
4918
4919
|
function evalSuperlative(graph, ast) {
|
|
4919
|
-
|
|
4920
|
+
let pool = graph.individuals.filter((i) => i.class === ast.entityType);
|
|
4921
|
+
if (ast.metric?.kind === "tests" && ast.entityType === "Module") {
|
|
4922
|
+
const testSubjects = new Set(edgesOfKind2(graph, "tests").map((e) => e.subject));
|
|
4923
|
+
pool = pool.filter((i) => !testSubjects.has(i.id) && !isTestPath(String(i.label).toLowerCase()));
|
|
4924
|
+
}
|
|
4920
4925
|
const scored = pool.map((ind) => ({ ind, score: degreeMetric(graph, ind, ast.metric) })).sort((a, z) => ast.extreme === "most" ? z.score - a.score : a.score - z.score);
|
|
4921
4926
|
if (!scored.length) return { compositeKind: "superlative", entityType: ast.entityType, matches: [] };
|
|
4922
4927
|
const best = scored[0].score;
|
|
@@ -5497,7 +5502,7 @@ ${shown.join("\n")}${tail}`;
|
|
|
5497
5502
|
}
|
|
5498
5503
|
return declineOnUnplacedWords(resolveObjectCore(graph, term, opts), term);
|
|
5499
5504
|
}
|
|
5500
|
-
return resolveObjectCore(graph, term, opts);
|
|
5505
|
+
return declineOnUnplacedWords(resolveObjectCore(graph, term, opts), term);
|
|
5501
5506
|
}
|
|
5502
5507
|
function resolveTermOrContext(graph, term, contextId) {
|
|
5503
5508
|
if (CONTEXT_PRONOUNS.includes(String(term || "").trim().toLowerCase())) {
|
|
@@ -5770,7 +5775,7 @@ ${shown.join("\n")}${tail}`;
|
|
|
5770
5775
|
if (entityType && entityType !== "Change") {
|
|
5771
5776
|
const wantClasses = classesForKinds(graph, fwdKinds);
|
|
5772
5777
|
const siblingClass = FINE_CLASS_SIBLING[entityType];
|
|
5773
|
-
if (!wantClasses.has(entityType) && !(siblingClass && wantClasses.has(siblingClass))) {
|
|
5778
|
+
if (wantClasses.size && !wantClasses.has(entityType) && !(siblingClass && wantClasses.has(siblingClass))) {
|
|
5774
5779
|
return {
|
|
5775
5780
|
matches: [],
|
|
5776
5781
|
objMatch,
|
|
@@ -6143,13 +6148,44 @@ ${options2}
|
|
|
6143
6148
|
};
|
|
6144
6149
|
}
|
|
6145
6150
|
if (result.ambiguous) {
|
|
6146
|
-
|
|
6151
|
+
let pool = [result.objMatch, ...result.candidates || []].filter(Boolean);
|
|
6152
|
+
let branches = result.branches;
|
|
6153
|
+
if (parsed.kind && parsed.kind !== "touches" && !pool.every((i) => i.class === "Commit")) {
|
|
6154
|
+
const grainOk = (i) => !["Commit", "Session", "Source", "Utterance"].includes(i?.class);
|
|
6155
|
+
const kept = pool.filter(grainOk);
|
|
6156
|
+
if (kept.length) {
|
|
6157
|
+
pool = kept;
|
|
6158
|
+
if (branches?.length) branches = branches.filter((b) => grainOk(b.candidate));
|
|
6159
|
+
}
|
|
6160
|
+
}
|
|
6161
|
+
if (graph && !/[/.]/.test(String(parsed.object || ""))) {
|
|
6162
|
+
const tLc = String(parsed.object || "").toLowerCase();
|
|
6163
|
+
const termWords = new Set(splitIdentifierWords(String(parsed.object || "")).filter((w) => w.length >= 3));
|
|
6164
|
+
const already = new Set(pool.map((i) => i.id));
|
|
6165
|
+
let nearest = null;
|
|
6166
|
+
let bestShared = 0;
|
|
6167
|
+
let bestD = Infinity;
|
|
6168
|
+
if (termWords.size) {
|
|
6169
|
+
for (const i of graph.individuals) {
|
|
6170
|
+
if (!["Function", "Method", "Class", "GlobalVariable", "Attribute"].includes(i.class) || already.has(i.id)) continue;
|
|
6171
|
+
const shared = splitIdentifierWords(String(i.label)).filter((w) => termWords.has(w)).length;
|
|
6172
|
+
if (!shared || shared < bestShared) continue;
|
|
6173
|
+
const d = editDistance(String(i.label).toLowerCase(), tLc, 8);
|
|
6174
|
+
if (shared > bestShared || d < bestD) {
|
|
6175
|
+
nearest = i;
|
|
6176
|
+
bestShared = shared;
|
|
6177
|
+
bestD = d;
|
|
6178
|
+
}
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
if (nearest) pool = [...pool, nearest];
|
|
6182
|
+
}
|
|
6147
6183
|
const noun = pool.length && pool.every((i) => i.class === "Commit") ? "commit" : "module";
|
|
6148
6184
|
const shown = pool.slice(0, OVERFLOW_CAP).map((i) => i.label);
|
|
6149
6185
|
const extra2 = pool.length > OVERFLOW_CAP ? `, \u2026and ${pool.length - OVERFLOW_CAP} more` : "";
|
|
6150
6186
|
const lead = `"${parsed.object}" matches more than one ${noun} ambiguously \u2014 did you mean ${listJoin(shown)}${extra2}? Try one of those. If you're not sure, narrow it to one name.`;
|
|
6151
|
-
const content =
|
|
6152
|
-
${
|
|
6187
|
+
const content = branches && branches.length ? `${lead}
|
|
6188
|
+
${branches.map((b, i) => `${i + 1}) ${b.candidate.label}: ${b.rendered.content}`).join("\n")}` : lead;
|
|
6153
6189
|
return {
|
|
6154
6190
|
content,
|
|
6155
6191
|
miss: false,
|
|
@@ -23093,7 +23129,7 @@ ${JSON.stringify(envelope, null, 2)}`;
|
|
|
23093
23129
|
signature: { tool: "tmct_signature", arg: "symbol", help: "a symbol's signature only" },
|
|
23094
23130
|
members: { tool: "tmct_members", arg: "class", help: "the methods/attributes of a class" },
|
|
23095
23131
|
subclasses: { tool: "tmct_subclasses", arg: "class", help: "the subclasses of a class" },
|
|
23096
|
-
impact: { tool: "tmct_impact", arg: "module", help: "what a change to this module reaches (impact closure)" },
|
|
23132
|
+
impact: { tool: "tmct_impact", arg: "module", help: "what a change to this module or symbol reaches (impact closure)" },
|
|
23097
23133
|
callers: { tool: "tmct_callers", arg: "symbol", help: "functions that call this symbol" },
|
|
23098
23134
|
callees: { tool: "tmct_callees", arg: "symbol", help: "functions this symbol calls" },
|
|
23099
23135
|
tests: { tool: "tmct_tests_for", arg: "symbol", help: "the tests covering this symbol" },
|
|
@@ -23390,8 +23426,25 @@ ${JSON.stringify(envelope, null, 2)}`;
|
|
|
23390
23426
|
}
|
|
23391
23427
|
return { predicate, object: String(objectRaw || "").trim() };
|
|
23392
23428
|
}
|
|
23393
|
-
var TEACH_PRONOUNS = Object.freeze([
|
|
23429
|
+
var TEACH_PRONOUNS = Object.freeze([
|
|
23430
|
+
"you're",
|
|
23431
|
+
"i'm",
|
|
23432
|
+
"it's",
|
|
23433
|
+
"they're",
|
|
23434
|
+
"he's",
|
|
23435
|
+
"she's",
|
|
23436
|
+
"we're",
|
|
23437
|
+
"you",
|
|
23438
|
+
"i",
|
|
23439
|
+
"it",
|
|
23440
|
+
"they",
|
|
23441
|
+
"he",
|
|
23442
|
+
"she",
|
|
23443
|
+
"we"
|
|
23444
|
+
]);
|
|
23394
23445
|
var TEACH_PRONOUN_RE = new RegExp(`^(?:every\\s+|each\\s+|all\\s+|some\\s+|a few\\s+|a\\s+|an\\s+)?(${TEACH_PRONOUNS.join("|")})\\s+\\S+`, "i");
|
|
23446
|
+
var BOARD_TEACH_LOCATIVE_RE = new RegExp(`^([\\w-]+)\\s+([a-z]+)s\\s+(${PREP_SRC})\\s+([\\w-]+)$`, "i");
|
|
23447
|
+
var RETRACT_FORGET_LOCATIVE_RE = new RegExp(`^forget\\s+(?:that\\s+)?([\\w-]+)\\s+([a-z]+)s\\s+(${PREP_SRC})\\s+([\\w-]+)$`, "i");
|
|
23395
23448
|
var TRAILING_ADVERB_RE = "(?:\\s+(?:exactly|really|actually|anyway))?";
|
|
23396
23449
|
var MODULE_ORIENT_RE = new RegExp(`^what\\s+does\\s+(.+?)\\s+do${TRAILING_ADVERB_RE}\\??$`, "i");
|
|
23397
23450
|
var MODULE_ORIENT_SVO_RE = new RegExp(`^what\\s+(.+?)\\s+does${TRAILING_ADVERB_RE}\\??$`, "i");
|
|
@@ -23519,6 +23572,7 @@ ${JSON.stringify(envelope, null, 2)}`;
|
|
|
23519
23572
|
if (f.provenance.includes("ace:chat") || f.provenance.includes("teach:chat")) return `you told me: ${factPhrase(f)}${cite}`;
|
|
23520
23573
|
if (f.provenance.includes("corpus-weak:")) return `possibly: ${factPhrase(f)}${cite}`;
|
|
23521
23574
|
if (f.provenance.includes("corpus:")) return `${factPhrase(f)}${cite}`;
|
|
23575
|
+
if (f.provenance.includes("reference:")) return `${factPhrase(f)}${cite}`;
|
|
23522
23576
|
return `i learned: ${factPhrase(f)}${cite}`;
|
|
23523
23577
|
}
|
|
23524
23578
|
function indefiniteArticleFor(term) {
|
|
@@ -24124,12 +24178,19 @@ ${shown.map(renderFactLine).join("\n")}`,
|
|
|
24124
24178
|
);
|
|
24125
24179
|
const hit2 = hasHit(subj);
|
|
24126
24180
|
if (hit2) return { text: `yes \u2014 ${renderFactLine(hit2)}`, replace: true };
|
|
24127
|
-
|
|
24128
|
-
|
|
24129
|
-
|
|
24181
|
+
let liftFrontier = subj;
|
|
24182
|
+
const liftChain = [];
|
|
24183
|
+
const liftSeen = /* @__PURE__ */ new Set();
|
|
24184
|
+
for (let hop = 0; hop < 4; hop += 1) {
|
|
24185
|
+
const step = facts.find((f) => ISA_PREDICATES2.has(f.predicate) && liftFrontier.has(f.subject) && !liftSeen.has(f.object));
|
|
24186
|
+
if (!step) break;
|
|
24187
|
+
liftSeen.add(step.object);
|
|
24188
|
+
liftChain.push(step);
|
|
24189
|
+
const lifted = hasHit(factTermVariants(normFactTerm2, step.object));
|
|
24130
24190
|
if (lifted) {
|
|
24131
|
-
return { text: `yes \u2014 ${renderFactLine
|
|
24191
|
+
return { text: `yes \u2014 ${[...liftChain.map(renderFactLine), renderFactLine(lifted)].join("; ")}`, replace: true };
|
|
24132
24192
|
}
|
|
24193
|
+
liftFrontier = factTermVariants(normFactTerm2, step.object);
|
|
24133
24194
|
}
|
|
24134
24195
|
return null;
|
|
24135
24196
|
}
|
|
@@ -24389,6 +24450,14 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
|
|
|
24389
24450
|
const directObjVariants = factTermVariants(normFactTerm2, stripTrailingDiscourseTag(directIsaAsk[2]));
|
|
24390
24451
|
const directSup = inheritsChain(graph, ent.id).find((sup) => [...factTermVariants(normFactTerm2, sup.label)].some((v) => directObjVariants.has(v)));
|
|
24391
24452
|
if (directSup) return { text: `yes \u2014 the code graph says ${ent.label} inherits ${directSup.label}.`, replace: true };
|
|
24453
|
+
const objEnt = await resolveEntity(graph, stripTrailingDiscourseTag(directIsaAsk[2]));
|
|
24454
|
+
if (objEnt && inheritsChain(graph, objEnt.id).some((sup) => sup.id === ent.id)) {
|
|
24455
|
+
return {
|
|
24456
|
+
text: `I can't confirm that \u2014 the code graph's stored direction runs the other way: ${objEnt.label} inherits ${ent.label}. An inheritance doesn't reverse.`,
|
|
24457
|
+
replace: true,
|
|
24458
|
+
miss: true
|
|
24459
|
+
};
|
|
24460
|
+
}
|
|
24392
24461
|
}
|
|
24393
24462
|
}
|
|
24394
24463
|
}
|