@polycode-projects/the-mechanical-code-talker 2.7.0 → 2.7.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 +46 -1
- package/package.json +1 -1
- package/src/domain/ask-vocab.mjs +10 -2
- package/src/domain/ask.mjs +6 -0
- package/src/domain/interpret/normalize.mjs +32 -8
- package/src/domain/memory/trust.mjs +9 -0
- package/src/services/adventure.mjs +50 -1
- package/src/services/chat.mjs +112 -32
- package/src/surfaces/web/memory-ask-browser.bundle.js +49 -22
- package/src/tools/definitions.mjs +2 -2
package/README.md
CHANGED
|
@@ -369,6 +369,51 @@ asserts the plan is exactly 2^n − 1 moves every time, and a second game
|
|
|
369
369
|
conjunction) solves with zero interpreter changes. `--render blocks` writes
|
|
370
370
|
the plan as a self-contained animated page (see "Two more surfaces" above).
|
|
371
371
|
|
|
372
|
+
## Play a game with it
|
|
373
|
+
|
|
374
|
+
Two games run inside an ordinary chat session, no setup.
|
|
375
|
+
|
|
376
|
+
**Guess the number.** Say `I'm thinking of a number between 1 and 100` and
|
|
377
|
+
tmct guesses by narrowing an interval — answer `higher`, `lower`, or
|
|
378
|
+
`correct`. It finds any number in at most 7 guesses, and if your answers
|
|
379
|
+
contradict each other it names the contradicting pair and stops rather than
|
|
380
|
+
guessing on. Say `think of a number` to swap seats: tmct commits to a secret
|
|
381
|
+
and answers your guesses honestly, reveals on request, and corrects you from
|
|
382
|
+
its own record if you claim it already said `correct`. The behaviour is
|
|
383
|
+
pinned by `test/corpus/games/guess-number.jsonl`, and the home page's demo
|
|
384
|
+
rail plays the guesser side live in your browser.
|
|
385
|
+
|
|
386
|
+
**A text adventure.** Say `start the adventure` (or `play ashcombe hall`)
|
|
387
|
+
and tmct loads a small country-house mystery from a lazily-fetched worlds
|
|
388
|
+
pack (`corpus/worlds/`) into the session's ordinary memory graph — rooms,
|
|
389
|
+
objects and people become graph facts, and the verbs (`go`, `take`, `open`,
|
|
390
|
+
`unlock`, `look`…) are taught action rules, not hard-wired code. Every move
|
|
391
|
+
writes per-turn snapshot facts, `look` is an extractive digest of the graph,
|
|
392
|
+
a blocked action declines by name, and one of the household moves on its own
|
|
393
|
+
schedule whether you are there to see it or not. The full worked mystery is
|
|
394
|
+
pinned step by step in `test/corpus/games/adventure.jsonl`.
|
|
395
|
+
|
|
396
|
+
## Learning on a miss
|
|
397
|
+
|
|
398
|
+
A question tmct cannot ground is still an honest miss — but on the cleanest
|
|
399
|
+
kind of miss (a recognised word, a clean parse, simply no facts anywhere) it
|
|
400
|
+
now consults two shipped, lazily-loaded packs before giving up:
|
|
401
|
+
|
|
402
|
+
- `corpus/child/` — 93k everyday-world triples filtered from ConceptNet by a
|
|
403
|
+
child-concept seed. Asked `what is a kettle` cold, tmct loads the term's
|
|
404
|
+
triples into memory (provenance `child:conceptnet:kettle`, ranked below
|
|
405
|
+
anything you teach) and answers from them; the next ask answers from
|
|
406
|
+
memory directly.
|
|
407
|
+
- `corpus/reference/` — 3,887 Simple English Wikipedia summaries. When the
|
|
408
|
+
triples cannot answer, a matching article answers as a cited read-out
|
|
409
|
+
(`source: reference article "Otter"…, CC BY-SA 4.0`).
|
|
410
|
+
|
|
411
|
+
Facts first, prose second; if neither pack carries the term, the turn is the
|
|
412
|
+
same honest miss it always was, byte for byte. An unknown word, a parse
|
|
413
|
+
failure, or an ambiguous reading never consults a pack at all. The gate and
|
|
414
|
+
both fallbacks are pinned by `test/corpus/reference.jsonl` and the
|
|
415
|
+
chat-lane tests beside it, and the home page demos the article path live.
|
|
416
|
+
|
|
372
417
|
## How it remembers
|
|
373
418
|
|
|
374
419
|
tmct's memory has two layers, both fed by every parsed request and response and
|
|
@@ -922,7 +967,7 @@ The remaining tools are **cold**: still served, but not billed to an agent every
|
|
|
922
967
|
| --- | --- | --- |
|
|
923
968
|
| `tmct_describe` | Locate one symbol and list its typed edges (both directions) with provenance. | `symbol` (required) |
|
|
924
969
|
| `tmct_signature` | One symbol's API surface (params, returns, raises/catches, flags, decorators, doc) without the body. | `symbol` (required) |
|
|
925
|
-
| `tmct_impact` | Transitive reverse closure over imports/calls — what breaks if a module changes, by depth, with tests. | `module` (required) |
|
|
970
|
+
| `tmct_impact` | Transitive reverse closure over imports/calls — what breaks if a module or symbol changes, by depth, with tests. | `module` (required) |
|
|
926
971
|
| `tmct_search` | Free-text/ranked lookup over the code-map to find the right module or symbol. | `query`, `kind`, `decorator`, `name` |
|
|
927
972
|
| `tmct_members` | A class's methods + attributes (file:line, decorators) in one slice. | `class` (required) |
|
|
928
973
|
| `tmct_subclasses` | A class's base classes plus the transitive set of classes that extend it. | `class` (required) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.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.",
|
package/src/domain/ask-vocab.mjs
CHANGED
|
@@ -466,12 +466,20 @@ export const PLACEHOLDER_NOUNS = Object.freeze([
|
|
|
466
466
|
|
|
467
467
|
/** Boolean connectives over same-subject clauses -> a set operation on result ids.
|
|
468
468
|
* "and" = intersection, "or" = union, "but not"/"and not"/"without"/"except" =
|
|
469
|
-
* difference.
|
|
470
|
-
*
|
|
469
|
+
* difference. The do-support negations ("but do not import Y", the expanded
|
|
470
|
+
* form every "don't" reaches after the contraction pass) are difference too —
|
|
471
|
+
* the auxiliary is part of the connective, never of the branch. A bare "but"
|
|
472
|
+
* is contrastive coordination, which still intersects ("inheriting from X but
|
|
473
|
+
* untested" = both at once). Multi-word keys are matched longest-first by
|
|
474
|
+
* ask.mjs so "but do not" wins over "but not" wins over a bare "but".
|
|
475
|
+
* Left-associative in ask.mjs's fold. */
|
|
471
476
|
export const BOOLEAN_CONNECTIVES = Object.freeze({
|
|
477
|
+
"but do not": "difference", "but does not": "difference",
|
|
478
|
+
"and do not": "difference", "and does not": "difference",
|
|
472
479
|
"but not": "difference", "and not": "difference", "except": "difference",
|
|
473
480
|
"without": "difference",
|
|
474
481
|
"and": "intersection", "plus": "intersection",
|
|
482
|
+
"but": "intersection",
|
|
475
483
|
"or": "union",
|
|
476
484
|
});
|
|
477
485
|
|
package/src/domain/ask.mjs
CHANGED
|
@@ -437,6 +437,12 @@ function parseNested(w, lc, nlp, depth) {
|
|
|
437
437
|
if (!noun) continue; // marker not preceded by a noun
|
|
438
438
|
const head = w.slice(0, r - 1); // outer clause words, minus the placeholder noun
|
|
439
439
|
if (!head.length) continue; // noun is the leading subject → subject-relative, not this shape
|
|
440
|
+
// A head made ENTIRELY of qualifier adjectives ("tested modules importing
|
|
441
|
+
// X") is an adjective stack over the subject, not an outer clause —
|
|
442
|
+
// "tested" doubles as a relation verb, so parseSimpleClause would read it
|
|
443
|
+
// as reverse(tests) over the inner set and answer the test module itself.
|
|
444
|
+
// parseRelationalOrQualified owns the adjective reading.
|
|
445
|
+
if (head.every((x) => QUALIFIERS[x.toLowerCase()])) continue;
|
|
440
446
|
const outer = parseSimpleClause([...head, NEST_SENTINEL].join(" "), nlp);
|
|
441
447
|
if (!outer || (outer.shape !== "reverse" && outer.shape !== "forward")) continue;
|
|
442
448
|
if (outer.modifier && outer.modifier !== "direct") continue; // no transitive-over-set closure primitive
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import {
|
|
9
9
|
CONTRACTIONS, MISSPELLINGS, WRONG_WORDS, G_DROP, FILLER_WORDS,
|
|
10
10
|
NEGATION_FRAMES, COMMIT_CONTENT_FRAMES, VERB_TO_KIND, ENTITY_TO_TYPE,
|
|
11
|
-
TRAILING_SCOPE_FILLER, TRAILING_TEMPORAL_ADVERBS,
|
|
11
|
+
TRAILING_SCOPE_FILLER, TRAILING_TEMPORAL_ADVERBS, stripTrailingDiscourseTag,
|
|
12
12
|
} from "../ask-vocab.mjs";
|
|
13
13
|
|
|
14
14
|
export function escapeRegex(s) {
|
|
@@ -251,18 +251,29 @@ export function applyPreambleFrames(text) {
|
|
|
251
251
|
return q;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
/** Strippable leading framing clause: "since/although/
|
|
255
|
-
* whereas/given that/now that <clause>, <Q>" -> "<Q>".
|
|
256
|
-
* non-empty-remainder-required, same discipline as
|
|
254
|
+
/** Strippable leading framing clause: "since/[even] though/although/while/
|
|
255
|
+
* because/[even] if/whereas/given that/now that <clause>, <Q>" -> "<Q>".
|
|
256
|
+
* Comma-anchored and non-empty-remainder-required, same discipline as
|
|
257
|
+
* GREETING_PREAMBLE_RE. */
|
|
257
258
|
const SUBORDINATION_FRAMES_RE =
|
|
258
|
-
/^(?:since|although|though|while|because|whereas|given\s+that|now\s+that)\s+.+?,\s*(.+)$/i;
|
|
259
|
+
/^(?:since|although|(?:even\s+)?though|while|because|whereas|given\s+that|now\s+that)\s+.+?,\s*(.+)$/i;
|
|
260
|
+
|
|
261
|
+
/** The same framing clause TRAILING the question ("<Q>, because the sprint
|
|
262
|
+
* just kicked off") — comma-anchored on the same subordinator set, so the
|
|
263
|
+
* clause never reads as part of the object term. A bare "while"/"since"
|
|
264
|
+
* mid-question without the comma is untouched (those can be content). */
|
|
265
|
+
const TRAILING_SUBORDINATION_RE =
|
|
266
|
+
/^(.+?),\s*(?:since|although|(?:even\s+)?though|while|because|whereas|given\s+that|now\s+that)\s+.+$/i;
|
|
259
267
|
|
|
260
268
|
export function applySubordinationFrames(text) {
|
|
261
269
|
let q = String(text || "");
|
|
262
270
|
for (let pass = 0; pass < 3; pass++) {
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
q = m[1].trim();
|
|
271
|
+
const before = q;
|
|
272
|
+
let m = q.match(SUBORDINATION_FRAMES_RE);
|
|
273
|
+
if (m) q = m[1].trim();
|
|
274
|
+
m = q.match(TRAILING_SUBORDINATION_RE);
|
|
275
|
+
if (m) q = m[1].trim();
|
|
276
|
+
if (q === before) break;
|
|
266
277
|
}
|
|
267
278
|
return q;
|
|
268
279
|
}
|
|
@@ -390,6 +401,14 @@ export function normalizeQuery(text) {
|
|
|
390
401
|
q = applySubordinationFrames(q);
|
|
391
402
|
q = applyConditionalFrames(q);
|
|
392
403
|
q = stripFillerWords(q);
|
|
404
|
+
// A trailing bare discourse tag ("which modules import a.mjs THEN") is
|
|
405
|
+
// conversational glue, not part of the object term — the same curated call
|
|
406
|
+
// ask-vocab's stripTrailingDiscourseTag already makes for the meta-whatis
|
|
407
|
+
// object. Stripped here, in the shared pre-pass and AFTER the filler strip
|
|
408
|
+
// (the noise wrappers that leave the tag behind sit at the other end), so
|
|
409
|
+
// both parse strategies see one string and the residue guard never has to
|
|
410
|
+
// refuse over a word that carried no content.
|
|
411
|
+
q = stripTrailingDiscourseTag(q);
|
|
393
412
|
// emphatic trailing punctuation (item 10): a run of terminal "?" collapses to
|
|
394
413
|
// one — the anchored templates consume exactly one optional trailing "?", so
|
|
395
414
|
// "…walk.mjs??" otherwise leaks a stray "?" into the captured object term (the
|
|
@@ -485,6 +504,11 @@ const PHRASING_FRAMES = Object.freeze([
|
|
|
485
504
|
{ re: /^what\s+does\s+(.+?)\s+changes?\s+together\s+with\??$/i, to: (m) => `what co-changes with ${m[1]}` },
|
|
486
505
|
{ re: /^what\s+changes?\s+together\s+with\s+(.+?)\??$/i, to: (m) => `what co-changes with ${m[1]}` },
|
|
487
506
|
|
|
507
|
+
// COMMIT-COUNT PASSIVE ("how many commits are recorded for X") → the touch
|
|
508
|
+
// phrasing the count restrictor already compiles. "recorded" is no relation
|
|
509
|
+
// verb, so the passive otherwise dies in the restrictor parse.
|
|
510
|
+
{ re: /^how\s+many\s+commits\s+(?:are|were)\s+(?:recorded|logged)\s+(?:for|against)\s+(?:the\s+)?(.+?)\??$/i, to: (m) => `how many commits touched ${m[1]}` },
|
|
511
|
+
|
|
488
512
|
// AUTHORSHIP → "who touched X" (tmct's touch edge IS the authorship signal).
|
|
489
513
|
// A commit sha object is excluded — that dumps the commit's touch-set, not its author.
|
|
490
514
|
{ re: /^who\s+(?:wrote|authored)\s+(?:the\s+)?(?!(?:commit\s+)?[0-9a-f]{7,40}\??$)(.+?)\??$/i, to: (m) => `who touched ${m[1]}` },
|
|
@@ -35,6 +35,11 @@ function parseChatTagRest(rest) {
|
|
|
35
35
|
* same 0.7 prior, same shared Source as the bulk conceptnet import; the
|
|
36
36
|
* <term> segment records which miss pulled the fact in and is not part of
|
|
37
37
|
* the Source identity)
|
|
38
|
+
* world:<name>[:turnN] -> { kind:"corpus", name:<name> }
|
|
39
|
+
* (a loaded world's facts are first-party authored shipped content — the
|
|
40
|
+
* same tier the hand-written tier2 corpus already scores at; the :turnN
|
|
41
|
+
* segment a snapshot write carries records when, not who, and is not
|
|
42
|
+
* part of the Source identity)
|
|
38
43
|
* ace:chat:<session>@<ts> -> { kind:"operator", createdAt:<ts>, sessionId:<session> }
|
|
39
44
|
* teach:chat:<session>@<ts> -> { kind:"teach", createdAt:<ts>, sessionId:<session> }
|
|
40
45
|
* web:<url> | url:<url> -> { kind:"web", url:<url> }
|
|
@@ -62,6 +67,10 @@ export function provenanceTagToSource(tag) {
|
|
|
62
67
|
// child:<pack>:<term> — the lazy child triples pack, scored at the corpus tier
|
|
63
68
|
// under the pack's shared Source; the per-term tail is dropped from the id.
|
|
64
69
|
if (head.startsWith("child:")) return { kind: "corpus", name: head.slice("child:".length).split(":")[0] || "unknown" };
|
|
70
|
+
// world:<name>[:turnN] — a loaded world's facts and snapshots, first-party
|
|
71
|
+
// authored shipped content scored at the corpus tier; the per-turn tail is
|
|
72
|
+
// dropped from the id so every write of one world corroborates one Source.
|
|
73
|
+
if (head.startsWith("world:")) return { kind: "corpus", name: head.slice("world:".length).split(":")[0] || "unknown" };
|
|
65
74
|
if (head.startsWith("ace:")) return { kind: "operator", ...parseChatTagRest(head.slice("ace:".length)) };
|
|
66
75
|
if (head.startsWith("teach:")) {
|
|
67
76
|
// the chat teach lane's natural frames — chat.mjs's teachProvenanceTag
|
|
@@ -13,7 +13,7 @@ import { parseImperative } from "../domain/grammar/ace.mjs";
|
|
|
13
13
|
import { createCompletionsGraphAdapter } from "../domain/completions/graph-adapter.mjs";
|
|
14
14
|
import { actionFamilies } from "../domain/router/taught.mjs";
|
|
15
15
|
import { getWorldsPackProvider } from "../adapters/corpus/worlds-pack.mjs";
|
|
16
|
-
import { appendFacts, appendRule, loadMemory, readFactRows, readRuleRows } from "../adapters/memory/core.mjs";
|
|
16
|
+
import { appendFacts, appendRule, loadMemory, normFactTerm, readFactRows, readRuleRows } from "../adapters/memory/core.mjs";
|
|
17
17
|
import { COMPLETIONS_STORE, generateCompletion } from "./completions.mjs";
|
|
18
18
|
|
|
19
19
|
// ---- recognizers: the closed opening/stop set --------------------------------
|
|
@@ -579,6 +579,53 @@ async function runWorldCommand(cmd, { world, memoryDir, env, graph, cache }) {
|
|
|
579
579
|
);
|
|
580
580
|
}
|
|
581
581
|
|
|
582
|
+
// A mid-game "where is X" aside. Optional trailing "now": the question means
|
|
583
|
+
// the same with or without it, and the fold IS the now.
|
|
584
|
+
const WORLD_WHERE_RE = /^where(?:'s|\s+is|\s+are)\s+(?:the\s+|a\s+|an\s+)?(.+?)(?:\s+now)?[?.!\s]*$/i;
|
|
585
|
+
|
|
586
|
+
/** A locative aside about a placed world thing, answered from the SAME @turnN
|
|
587
|
+
* fold every other world reader uses — never the raw base rows, whose
|
|
588
|
+
* superseded placements would answer where things stood at load time. Null
|
|
589
|
+
* when the asked thing has no placement in the world, so an ordinary
|
|
590
|
+
* locative question (a code symbol, a taught board piece) keeps its lane. A
|
|
591
|
+
* hidden thing is declined without naming its hiding place. */
|
|
592
|
+
async function worldWhereAnswer(line, { memoryDir }) {
|
|
593
|
+
const m = String(line).match(WORLD_WHERE_RE);
|
|
594
|
+
if (!m) return null;
|
|
595
|
+
const thing = normFactTerm(m[1]);
|
|
596
|
+
let rows;
|
|
597
|
+
try { rows = readFactRows(await loadMemory(memoryDir)); } catch { return null; }
|
|
598
|
+
const state = foldWorldState(rows);
|
|
599
|
+
const place = state.placements.get(thing);
|
|
600
|
+
if (!place) return null;
|
|
601
|
+
if (place.predicate === "mgx:hidden-in") {
|
|
602
|
+
return answer(
|
|
603
|
+
`nothing you've seen says where the ${thing} is.`,
|
|
604
|
+
`ADVENTURE — where-aside: ${thing} is hidden; declined without naming the hiding place`,
|
|
605
|
+
{ miss: true, goal: `locate the ${thing}` },
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
if (thing === "player") {
|
|
609
|
+
return answer(
|
|
610
|
+
`you are in the ${place.object}.`,
|
|
611
|
+
"ADVENTURE — where-aside: the player's own room, from the current world fold",
|
|
612
|
+
{ goal: "check where you are" },
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
if (place.object === "player") {
|
|
616
|
+
return answer(
|
|
617
|
+
`you are carrying the ${thing}.`,
|
|
618
|
+
`ADVENTURE — where-aside: ${thing} is carried, from the current world fold`,
|
|
619
|
+
{ goal: `locate the ${thing}` },
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
return answer(
|
|
623
|
+
`the ${thing} is in the ${place.object}.`,
|
|
624
|
+
`ADVENTURE — where-aside: ${thing}'s current placement from the world fold (as of turn ${place.turn})`,
|
|
625
|
+
{ goal: `locate the ${thing}` },
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
|
|
582
629
|
async function inventoryAnswer({ memoryDir, graph }) {
|
|
583
630
|
const memory = await loadMemory(memoryDir);
|
|
584
631
|
const rows = readFactRows(memory);
|
|
@@ -665,5 +712,7 @@ export async function adventureTurn(line, { planHolder, memoryDir, sessionId = "
|
|
|
665
712
|
if (INVENTORY_RE.test(line)) return inventoryAnswer({ memoryDir, graph });
|
|
666
713
|
const cmd = parseImperative(line, lexicon ?? undefined);
|
|
667
714
|
if (cmd) return runWorldCommand(cmd, { world: adventure.world, memoryDir, env, graph, cache });
|
|
715
|
+
const whereAside = await worldWhereAnswer(line, { memoryDir });
|
|
716
|
+
if (whereAside) return whereAside;
|
|
668
717
|
return null; // a mid-game aside — the ordinary lanes answer, world untouched
|
|
669
718
|
}
|
package/src/services/chat.mjs
CHANGED
|
@@ -48,6 +48,8 @@ import { loadLexicon, lookupNoun } from "../domain/grammar/lexicon.mjs";
|
|
|
48
48
|
import { pickPhrase } from "../domain/answer-variants.mjs";
|
|
49
49
|
import { REFERENCE_PACK_NAME, cleanMissReferenceTerm, renderReferenceAnswer, referenceProvenanceTag } from "../domain/reference-pack.mjs";
|
|
50
50
|
import { getReferencePackProvider } from "../adapters/corpus/reference-pack.mjs";
|
|
51
|
+
import { CHILD_PACK_NAME, childProvenanceTag } from "../domain/child-pack.mjs";
|
|
52
|
+
import { getChildPackProvider } from "../adapters/corpus/child-pack.mjs";
|
|
51
53
|
import { dialogueActForLane } from "../domain/dialogue-acts.mjs";
|
|
52
54
|
import { relatedForTerm } from "../domain/skos-view.mjs";
|
|
53
55
|
import { adventureTurn } from "./adventure.mjs";
|
|
@@ -2794,6 +2796,16 @@ const GENERAL_VERB_DETERMINER_TEACH_RE = new RegExp(
|
|
|
2794
2796
|
* stored, and cited, as "len"). */
|
|
2795
2797
|
const QUANTIFIED_HAS_TEACH_RE = /^(every|each|all)\s+([\w'-]+)\s+(?:has|have)\s+(.+?)[.!?]*$/i;
|
|
2796
2798
|
const quantifiedHasSubject = (m) => (/^all$/i.test(m[1]) ? singularizeSurface(m[2]) : m[2]);
|
|
2799
|
+
/** The OBJECT side of the same fold: "all dogs have tails" states one tail
|
|
2800
|
+
* per dog, so the plural sentence form's object stores as its singular
|
|
2801
|
+
* ("tail" — the spelling the have-questions and the seeded corpus's own hasA
|
|
2802
|
+
* facts read back). Only the "all" form folds, the same gate the subject
|
|
2803
|
+
* uses: "every"/"each" take singular grammar, so their object's number is
|
|
2804
|
+
* the speaker's own ("every dog has fur"). The last word folds so a
|
|
2805
|
+
* modified object ("all dogs have long tails") keeps its modifier. */
|
|
2806
|
+
const quantifiedHasObject = (m) => (/^all$/i.test(m[1])
|
|
2807
|
+
? m[3].replace(/[\w'-]+$/, (w) => singularizeSurface(w))
|
|
2808
|
+
: m[3]);
|
|
2797
2809
|
/** Verbs owned by an earlier, more specific recognizer in this lane — is/are
|
|
2798
2810
|
* (class-membership/property, above) and owns/maintains (ownership, above).
|
|
2799
2811
|
* generalVerbTeach declines outright on these so it can never race a more
|
|
@@ -2942,7 +2954,7 @@ async function generalVerbTeach(payload) {
|
|
|
2942
2954
|
if (quantHas) {
|
|
2943
2955
|
subjectRaw = quantifiedHasSubject(quantHas);
|
|
2944
2956
|
verbRaw = "has";
|
|
2945
|
-
objectRaw = quantHas
|
|
2957
|
+
objectRaw = quantifiedHasObject(quantHas);
|
|
2946
2958
|
} else {
|
|
2947
2959
|
const det = p.match(GENERAL_VERB_DETERMINER_TEACH_RE);
|
|
2948
2960
|
if (!det) return null; // not a bare-name subject, and no preposition to pin the verb
|
|
@@ -9045,14 +9057,15 @@ async function curatedDefinitionAnswer(query, envelope, { memoryDir, lexicon })
|
|
|
9045
9057
|
return { text: `${def} (source: corpus/seon)`, term };
|
|
9046
9058
|
}
|
|
9047
9059
|
|
|
9048
|
-
// ---- learn-on-miss: the shipped reference
|
|
9060
|
+
// ---- learn-on-miss: the shipped child + reference packs behind the cleanest miss ----
|
|
9049
9061
|
|
|
9050
|
-
/** The learn-on-miss gate, shared by the
|
|
9051
|
-
* fallback so the
|
|
9052
|
-
*
|
|
9053
|
-
* no remembered fact — then, and
|
|
9054
|
-
*
|
|
9055
|
-
|
|
9062
|
+
/** The learn-on-miss gate, shared by the child-pack hook, the articled
|
|
9063
|
+
* reference hook and the bare-form fallback so the three can never disagree.
|
|
9064
|
+
* Passes only on the CLEANEST miss: a definition-shaped term the lexicon
|
|
9065
|
+
* knows, resolving to no graph entity and no remembered fact — then, and
|
|
9066
|
+
* only then, may a pack provider be consulted. Null means the turn proceeds
|
|
9067
|
+
* byte-identically to a pack-less run. */
|
|
9068
|
+
async function cleanMissPackKey(term, { graph, memoryDir, lexicon, cache }) {
|
|
9056
9069
|
if (!term || !memoryDir) return null;
|
|
9057
9070
|
let key = null;
|
|
9058
9071
|
try { key = cleanMissReferenceTerm(term, lexicon ?? undefined); } catch { key = null; }
|
|
@@ -9064,12 +9077,43 @@ async function referencePackMissAnswer(term, { graph, memoryDir, lexicon, env, c
|
|
|
9064
9077
|
variants.add(key);
|
|
9065
9078
|
const rows = await factRows(memoryDir, cache);
|
|
9066
9079
|
if (rows.some((f) => variants.has(f.subject) || variants.has(f.object))) return null;
|
|
9080
|
+
return key;
|
|
9081
|
+
}
|
|
9082
|
+
|
|
9083
|
+
/** The reference-pack lookup for an already-gated key: the article, or null
|
|
9084
|
+
* (absent pack, missing term, any read failure — all byte-identical). */
|
|
9085
|
+
async function referencePackAnswerForKey(key, env) {
|
|
9067
9086
|
let article = null;
|
|
9068
9087
|
try { article = await getReferencePackProvider(env).lookup(key); } catch { article = null; }
|
|
9069
9088
|
if (!article) return null;
|
|
9070
9089
|
return { key, article, text: renderReferenceAnswer(key, article) };
|
|
9071
9090
|
}
|
|
9072
9091
|
|
|
9092
|
+
/** The gate + the reference lookup in one call, for a caller that has a term
|
|
9093
|
+
* rather than a gated key. */
|
|
9094
|
+
async function referencePackMissAnswer(term, { graph, memoryDir, lexicon, env, cache }) {
|
|
9095
|
+
const key = await cleanMissPackKey(term, { graph, memoryDir, lexicon, cache });
|
|
9096
|
+
return key ? referencePackAnswerForKey(key, env) : null;
|
|
9097
|
+
}
|
|
9098
|
+
|
|
9099
|
+
/** The child-pack half of learn-on-miss, for an already-gated key: look the
|
|
9100
|
+
* key up in the shipped child triples pack and append every fact under child
|
|
9101
|
+
* provenance, so the SAME question can be re-asked from the store. Null on a
|
|
9102
|
+
* pack miss or any failure — the turn then proceeds byte-identically. */
|
|
9103
|
+
async function childPackFactsForKey(key, { memoryDir, env, cache }) {
|
|
9104
|
+
let row = null;
|
|
9105
|
+
try { row = await getChildPackProvider(env).lookup(key); } catch { row = null; }
|
|
9106
|
+
if (!row?.facts?.length) return null;
|
|
9107
|
+
try {
|
|
9108
|
+
const { appendFacts } = await import("../adapters/memory/core.mjs");
|
|
9109
|
+
await appendFacts(memoryDir, row.facts.map(({ subject, predicate, object }) => ({
|
|
9110
|
+
subject, predicate, object, provenance: childProvenanceTag(key),
|
|
9111
|
+
})));
|
|
9112
|
+
} catch { return null; }
|
|
9113
|
+
if (cache) cache.rows = null;
|
|
9114
|
+
return { key, count: row.facts.length };
|
|
9115
|
+
}
|
|
9116
|
+
|
|
9073
9117
|
/** Store the article's first-sentence isa as a subClassOf fact carrying
|
|
9074
9118
|
* reference provenance — AFTER the cited answer composed, and failure-
|
|
9075
9119
|
* tolerated: the answer stands whether or not the fact lands. */
|
|
@@ -10492,9 +10536,12 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
10492
10536
|
// actually stores it, the ORIGINAL ask-engine answer is restored unchanged
|
|
10493
10537
|
// (see "COLLISION RESTORE" below).
|
|
10494
10538
|
const trimmedQuery = String(query).trim();
|
|
10495
|
-
|
|
10539
|
+
// Boolean() and not a bare && chain: with no memoryDir the chain would
|
|
10540
|
+
// short-circuit to null, and `miss` below (askMiss || THIS) would record a
|
|
10541
|
+
// null miss flag on every relaxation-rescued turn of a memory-less session.
|
|
10542
|
+
const relaxedTeachCollision = Boolean(!!(envelope?.relaxed?.dropped?.length) && !askMiss && memoryDir
|
|
10496
10543
|
&& !QUESTION_LEAD_RE.test(trimmedQuery) && !/\?\s*$/.test(trimmedQuery)
|
|
10497
|
-
&& DECLARATIVE_KIND_OF_RE.test(trimmedQuery);
|
|
10544
|
+
&& DECLARATIVE_KIND_OF_RE.test(trimmedQuery));
|
|
10498
10545
|
const preCollisionAnswer = relaxedTeachCollision ? answer : null;
|
|
10499
10546
|
const miss = askMiss || relaxedTeachCollision;
|
|
10500
10547
|
// Answer provenance (W1): "composed" is the ask engine's productive band; the
|
|
@@ -10789,13 +10836,23 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
10789
10836
|
const def = await curatedDefinitionAnswer(factQuery, envelope, { memoryDir, lexicon });
|
|
10790
10837
|
if (def) bareMetaHit = { text: def.text, replace: true };
|
|
10791
10838
|
}
|
|
10792
|
-
// The
|
|
10793
|
-
// under the IDENTICAL clean-miss gate the articled hook (4h)
|
|
10794
|
-
// "what is otter" reaches the
|
|
10839
|
+
// The learn-on-miss packs' bare-form fallback, beside the curated one
|
|
10840
|
+
// and under the IDENTICAL clean-miss gate the articled hook (4h)
|
|
10841
|
+
// applies — "what is otter" reaches the packs exactly as "what is an
|
|
10842
|
+
// otter" does, child triples first, article prose second.
|
|
10795
10843
|
if (!bareMetaHit) {
|
|
10796
10844
|
const refTerm = metaTermOf(factQuery, envelope);
|
|
10797
|
-
const
|
|
10798
|
-
|
|
10845
|
+
const key = refTerm ? await cleanMissPackKey(refTerm, { graph, memoryDir, lexicon, cache }) : null;
|
|
10846
|
+
const learned = key ? await childPackFactsForKey(key, { memoryDir, env, cache }) : null;
|
|
10847
|
+
if (learned) {
|
|
10848
|
+
const fact = (await factAnswer(memoryDir, factQuery, envelope, miss, biasByBundle, cache, newFocus?.label))
|
|
10849
|
+
?? (await factReadBack(memoryDir, factQuery, envelope, miss, graph, newFocus?.label, biasByBundle, cache));
|
|
10850
|
+
if (fact && !fact.miss) bareMetaHit = { text: fact.text, replace: true, child: learned };
|
|
10851
|
+
}
|
|
10852
|
+
if (!bareMetaHit && key) {
|
|
10853
|
+
const ref = await referencePackAnswerForKey(key, env);
|
|
10854
|
+
if (ref) bareMetaHit = { text: ref.text, replace: true, reference: ref };
|
|
10855
|
+
}
|
|
10799
10856
|
}
|
|
10800
10857
|
}
|
|
10801
10858
|
// A bare "what is X" naming a REAL code-graph entity (not a taught fact,
|
|
@@ -10851,8 +10908,13 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
10851
10908
|
if (!bareMetaHit.miss) { via = "fact"; recordMiss = false; }
|
|
10852
10909
|
handled = true;
|
|
10853
10910
|
if (bareMetaHit.pending) factPending = bareMetaHit.pending;
|
|
10854
|
-
|
|
10855
|
-
|
|
10911
|
+
if (bareMetaHit.child) {
|
|
10912
|
+
note(trace, "lane: (2b) CHILD PACK — a bare \"what is X\" clean miss pulled the term's triples from the shipped child pack into memory, and the question was re-answered from the store");
|
|
10913
|
+
note(trace, `source: child pack ${CHILD_PACK_NAME} — ${bareMetaHit.child.count} fact(s) appended as ${childProvenanceTag(bareMetaHit.child.key)}, answer served from .tmct/memory Facts`);
|
|
10914
|
+
} else {
|
|
10915
|
+
note(trace, "lane: (2b) BARE META FACT — \"what is X\" (no article) / \"is X <adjective>\" resolved to a remembered fact before the conversational catch-all could claim it");
|
|
10916
|
+
note(trace, "source: .tmct/memory Facts (see /memory for provenance per line)");
|
|
10917
|
+
}
|
|
10856
10918
|
} else if (isConversationalCandidate && habitualGroundingHint) {
|
|
10857
10919
|
// A bare habitual teach ("penguins swim") naming a subject grounded
|
|
10858
10920
|
// nowhere: an honest, actionable grounding hint beats the orientation
|
|
@@ -11212,22 +11274,40 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
11212
11274
|
note(trace, `lane: (4g) FUZZY-VERB DECLINE — "${from}" only became a verb through the edit-distance repair tier ("${to}"), and the two words are different verbs, so the repaired sentence's graph answer is dropped rather than shown as an answer to what was typed`);
|
|
11213
11275
|
}
|
|
11214
11276
|
}
|
|
11215
|
-
// (4h)
|
|
11216
|
-
//
|
|
11217
|
-
// remembered fact.
|
|
11218
|
-
// a
|
|
11219
|
-
//
|
|
11220
|
-
//
|
|
11277
|
+
// (4h) LEARN-ON-MISS PACKS — the cleanest miss consults the shipped packs:
|
|
11278
|
+
// a definition-shaped term the lexicon knows, no graph entity, no
|
|
11279
|
+
// remembered fact. The CHILD triples pack goes first (facts before prose):
|
|
11280
|
+
// a hit appends the term's triples under child provenance and the SAME
|
|
11281
|
+
// question is re-asked from the store, so the answer is an ordinary cited
|
|
11282
|
+
// fact answer. Only when the store still cannot answer does the reference
|
|
11283
|
+
// pack's article speak, cited as before. Both packs missing leaves the
|
|
11284
|
+
// honest miss byte-identical.
|
|
11221
11285
|
if (miss && recordMiss && via === "composed" && memoryDir) {
|
|
11222
11286
|
const refTerm = metaTermOf(query, envelope);
|
|
11223
|
-
const
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11228
|
-
|
|
11229
|
-
|
|
11230
|
-
|
|
11287
|
+
const key = refTerm ? await cleanMissPackKey(refTerm, { graph, memoryDir, lexicon, cache }) : null;
|
|
11288
|
+
const learned = key ? await childPackFactsForKey(key, { memoryDir, env, cache }) : null;
|
|
11289
|
+
if (learned) {
|
|
11290
|
+
const fact = (await factAnswer(memoryDir, query, envelope, miss, biasByBundle, cache, newFocus?.label))
|
|
11291
|
+
?? (await factReadBack(memoryDir, query, envelope, miss, graph, newFocus?.label, biasByBundle, cache));
|
|
11292
|
+
if (fact && !fact.miss) {
|
|
11293
|
+
answer = fact.replace ? fact.text : `${answer}\n${fact.text}`;
|
|
11294
|
+
via = "fact";
|
|
11295
|
+
recordMiss = false;
|
|
11296
|
+
if (fact.pending) factPending = fact.pending;
|
|
11297
|
+
note(trace, "lane: (4h) CHILD PACK — a clean miss on a lexicon term pulled the term's triples from the shipped child pack into memory, and the question was re-answered from the store");
|
|
11298
|
+
note(trace, `source: child pack ${CHILD_PACK_NAME} — ${learned.count} fact(s) appended as ${childProvenanceTag(key)}, answer served from .tmct/memory Facts`);
|
|
11299
|
+
}
|
|
11300
|
+
}
|
|
11301
|
+
if (miss && recordMiss && via === "composed" && key) {
|
|
11302
|
+
const ref = await referencePackAnswerForKey(key, env);
|
|
11303
|
+
if (ref) {
|
|
11304
|
+
answer = ref.text;
|
|
11305
|
+
via = "reference";
|
|
11306
|
+
recordMiss = false;
|
|
11307
|
+
note(trace, "lane: (4h) REFERENCE PACK — a clean miss on a lexicon term answered from the shipped reference pack, cited");
|
|
11308
|
+
note(trace, `source: reference pack ${REFERENCE_PACK_NAME} — article "${ref.article.title}" (revid ${ref.article.revid})`);
|
|
11309
|
+
await appendReferenceIsaFact(memoryDir, ref.key, ref.article, cache);
|
|
11310
|
+
}
|
|
11231
11311
|
}
|
|
11232
11312
|
}
|
|
11233
11313
|
// (5) #1 SHORT TAILORED MISS — replace ONLY the engine's full grammar cheat-sheet
|
|
@@ -12532,7 +12612,7 @@ export async function runTurn(input, { config, source = defaultSource, graph = n
|
|
|
12532
12612
|
if (advTurn) {
|
|
12533
12613
|
note(trace, `lane: ${advTurn.note}`);
|
|
12534
12614
|
if (advTurn.goal) note(trace, `goal: ${advTurn.goal}`);
|
|
12535
|
-
const result = plainTurn(workingLine, advTurn.text, { via: "game", focus });
|
|
12615
|
+
const result = plainTurn(workingLine, advTurn.text, { via: "game", miss: !!advTurn.miss, focus });
|
|
12536
12616
|
if (advTurn.goal) result.goal = advTurn.goal;
|
|
12537
12617
|
result.lane = advTurn.lane;
|
|
12538
12618
|
const rec = withLast(result, advTurn.goal ?? "play the adventure");
|
|
@@ -673,12 +673,17 @@
|
|
|
673
673
|
"symbols"
|
|
674
674
|
]);
|
|
675
675
|
BOOLEAN_CONNECTIVES = Object.freeze({
|
|
676
|
+
"but do not": "difference",
|
|
677
|
+
"but does not": "difference",
|
|
678
|
+
"and do not": "difference",
|
|
679
|
+
"and does not": "difference",
|
|
676
680
|
"but not": "difference",
|
|
677
681
|
"and not": "difference",
|
|
678
682
|
"except": "difference",
|
|
679
683
|
"without": "difference",
|
|
680
684
|
"and": "intersection",
|
|
681
685
|
"plus": "intersection",
|
|
686
|
+
"but": "intersection",
|
|
682
687
|
"or": "union"
|
|
683
688
|
});
|
|
684
689
|
QUALIFIERS = Object.freeze({
|
|
@@ -985,6 +990,7 @@
|
|
|
985
990
|
if (head.startsWith("corpus-weak:")) return { kind: "corpusWeak", name: head.slice("corpus-weak:".length) || "unknown" };
|
|
986
991
|
if (head.startsWith("corpus:")) return { kind: "corpus", name: head.slice("corpus:".length) || "unknown" };
|
|
987
992
|
if (head.startsWith("child:")) return { kind: "corpus", name: head.slice("child:".length).split(":")[0] || "unknown" };
|
|
993
|
+
if (head.startsWith("world:")) return { kind: "corpus", name: head.slice("world:".length).split(":")[0] || "unknown" };
|
|
988
994
|
if (head.startsWith("ace:")) return { kind: "operator", ...parseChatTagRest(head.slice("ace:".length)) };
|
|
989
995
|
if (head.startsWith("teach:")) {
|
|
990
996
|
return { kind: "teach", ...parseChatTagRest(head.slice("teach:".length)) };
|
|
@@ -2752,9 +2758,12 @@ ${shown.join("\n")}${tail}`;
|
|
|
2752
2758
|
function applySubordinationFrames(text) {
|
|
2753
2759
|
let q = String(text || "");
|
|
2754
2760
|
for (let pass = 0; pass < 3; pass++) {
|
|
2755
|
-
const
|
|
2756
|
-
|
|
2757
|
-
q = m[1].trim();
|
|
2761
|
+
const before = q;
|
|
2762
|
+
let m = q.match(SUBORDINATION_FRAMES_RE);
|
|
2763
|
+
if (m) q = m[1].trim();
|
|
2764
|
+
m = q.match(TRAILING_SUBORDINATION_RE);
|
|
2765
|
+
if (m) q = m[1].trim();
|
|
2766
|
+
if (q === before) break;
|
|
2758
2767
|
}
|
|
2759
2768
|
return q;
|
|
2760
2769
|
}
|
|
@@ -2799,6 +2808,7 @@ ${shown.join("\n")}${tail}`;
|
|
|
2799
2808
|
q = applySubordinationFrames(q);
|
|
2800
2809
|
q = applyConditionalFrames(q);
|
|
2801
2810
|
q = stripFillerWords(q);
|
|
2811
|
+
q = stripTrailingDiscourseTag(q);
|
|
2802
2812
|
q = q.replace(/\?{2,}\s*$/, "?");
|
|
2803
2813
|
return q.replace(/\s+/g, " ").trim();
|
|
2804
2814
|
}
|
|
@@ -2824,7 +2834,7 @@ ${shown.join("\n")}${tail}`;
|
|
|
2824
2834
|
if (!predicate) return null;
|
|
2825
2835
|
return { entWord, predicate };
|
|
2826
2836
|
}
|
|
2827
|
-
var tableRe, CONTRACTION_RE, correctionRe, MISSPELLING_RE, WRONG_WORD_RE, W_SLASH_RE, FOR_DIGIT_THANKS_RE, FOR_DIGIT_EXAMPLE_RE, KIND_NOUN_ANAPHORA_RE, VERB_ALTERNATION, FILLER_RE, RELATION_VERB_RE, INTERROGATIVE_LEAD_RE, LISTING_TAIL_KINDS, BARE_KIND_RE, isListingRemainder, GREETING_PREAMBLE_RE, THANKS_PREAMBLE_RE, ACK_PREAMBLE_RE, BROWSING_PREAMBLE_RE, HEDGE_ADVERB_PREAMBLE_RE, TROUBLE_ASIDE_RE, MODAL_WRAPPER_RE, EXPLAIN_WRAPPER_RE, TELL_ME_WRAPPER_RE, KNOW_WRAPPER_RE, WANT_KNOW_WRAPPER_RE, WONDERING_WRAPPER_RE, EMBEDDED_WHATIS_RE, EMBEDDED_MEANS_RE, SHOW_GIVE_ME_RE, LEADING_CONNECTIVE_RE, QUESTION_AUX_LEAD_RE, TOPIC_SWITCH_PREAMBLE_RE, SUBORDINATION_FRAMES_RE, SELF_CORRECTION_RE, CONDITIONAL_VERB_GERUND, CONDITIONAL_KIND_PLURAL, CONDITIONAL_QUALIFIER_SRC, CONDITIONAL_QUALIFIER_RE, COUNTERFACTUAL_RE, WHERE_TRAILING_TEMPORAL_RE, PHRASING_FRAMES, NEGATION_SET_RE, STOPWORDS2, splitWords, wordsOf;
|
|
2837
|
+
var tableRe, CONTRACTION_RE, correctionRe, MISSPELLING_RE, WRONG_WORD_RE, W_SLASH_RE, FOR_DIGIT_THANKS_RE, FOR_DIGIT_EXAMPLE_RE, KIND_NOUN_ANAPHORA_RE, VERB_ALTERNATION, FILLER_RE, RELATION_VERB_RE, INTERROGATIVE_LEAD_RE, LISTING_TAIL_KINDS, BARE_KIND_RE, isListingRemainder, GREETING_PREAMBLE_RE, THANKS_PREAMBLE_RE, ACK_PREAMBLE_RE, BROWSING_PREAMBLE_RE, HEDGE_ADVERB_PREAMBLE_RE, TROUBLE_ASIDE_RE, MODAL_WRAPPER_RE, EXPLAIN_WRAPPER_RE, TELL_ME_WRAPPER_RE, KNOW_WRAPPER_RE, WANT_KNOW_WRAPPER_RE, WONDERING_WRAPPER_RE, EMBEDDED_WHATIS_RE, EMBEDDED_MEANS_RE, SHOW_GIVE_ME_RE, LEADING_CONNECTIVE_RE, QUESTION_AUX_LEAD_RE, TOPIC_SWITCH_PREAMBLE_RE, SUBORDINATION_FRAMES_RE, TRAILING_SUBORDINATION_RE, SELF_CORRECTION_RE, CONDITIONAL_VERB_GERUND, CONDITIONAL_KIND_PLURAL, CONDITIONAL_QUALIFIER_SRC, CONDITIONAL_QUALIFIER_RE, COUNTERFACTUAL_RE, WHERE_TRAILING_TEMPORAL_RE, PHRASING_FRAMES, NEGATION_SET_RE, STOPWORDS2, splitWords, wordsOf;
|
|
2828
2838
|
var init_normalize = __esm({
|
|
2829
2839
|
"src/domain/interpret/normalize.mjs"() {
|
|
2830
2840
|
init_ask_vocab();
|
|
@@ -2893,7 +2903,8 @@ ${shown.join("\n")}${tail}`;
|
|
|
2893
2903
|
LEADING_CONNECTIVE_RE = /^(?:and|also|so|then|now|but)\s+(.+)$/i;
|
|
2894
2904
|
QUESTION_AUX_LEAD_RE = /^(?:does|do|did|is|are|was|were|has|have|had|can|could|will|would|should)\b/i;
|
|
2895
2905
|
TOPIC_SWITCH_PREAMBLE_RE = /^(?:(?:actually|no\s+wait|wait|hold\s+on|never\s+mind|scratch\s+that|on\s+second\s+thought|i\s+mean(?:t)?)[\s,.]+)+(.+)$/i;
|
|
2896
|
-
SUBORDINATION_FRAMES_RE = /^(?:since|although|though|while|because|whereas|given\s+that|now\s+that)\s+.+?,\s*(.+)$/i;
|
|
2906
|
+
SUBORDINATION_FRAMES_RE = /^(?:since|although|(?:even\s+)?though|while|because|whereas|given\s+that|now\s+that)\s+.+?,\s*(.+)$/i;
|
|
2907
|
+
TRAILING_SUBORDINATION_RE = /^(.+?),\s*(?:since|although|(?:even\s+)?though|while|because|whereas|given\s+that|now\s+that)\s+.+$/i;
|
|
2897
2908
|
SELF_CORRECTION_RE = /^.+?(?:\s*(?:--|—|-)\s*)?\b(?:sorry|i\s+mean)\b\s*(?:--|—|-|,|:)\s*(.+)$/i;
|
|
2898
2909
|
CONDITIONAL_VERB_GERUND = Object.freeze({
|
|
2899
2910
|
imports: "importing",
|
|
@@ -2992,6 +3003,10 @@ ${shown.join("\n")}${tail}`;
|
|
|
2992
3003
|
// CO-CHANGE → "what co-changes with X" (the plainest phrasing a developer types).
|
|
2993
3004
|
{ re: /^what\s+does\s+(.+?)\s+changes?\s+together\s+with\??$/i, to: (m) => `what co-changes with ${m[1]}` },
|
|
2994
3005
|
{ re: /^what\s+changes?\s+together\s+with\s+(.+?)\??$/i, to: (m) => `what co-changes with ${m[1]}` },
|
|
3006
|
+
// COMMIT-COUNT PASSIVE ("how many commits are recorded for X") → the touch
|
|
3007
|
+
// phrasing the count restrictor already compiles. "recorded" is no relation
|
|
3008
|
+
// verb, so the passive otherwise dies in the restrictor parse.
|
|
3009
|
+
{ re: /^how\s+many\s+commits\s+(?:are|were)\s+(?:recorded|logged)\s+(?:for|against)\s+(?:the\s+)?(.+?)\??$/i, to: (m) => `how many commits touched ${m[1]}` },
|
|
2995
3010
|
// AUTHORSHIP → "who touched X" (tmct's touch edge IS the authorship signal).
|
|
2996
3011
|
// A commit sha object is excluded — that dumps the commit's touch-set, not its author.
|
|
2997
3012
|
{ re: /^who\s+(?:wrote|authored)\s+(?:the\s+)?(?!(?:commit\s+)?[0-9a-f]{7,40}\??$)(.+?)\??$/i, to: (m) => `who touched ${m[1]}` },
|
|
@@ -3908,6 +3923,7 @@ ${shown.join("\n")}${tail}`;
|
|
|
3908
3923
|
if (!noun) continue;
|
|
3909
3924
|
const head = w.slice(0, r - 1);
|
|
3910
3925
|
if (!head.length) continue;
|
|
3926
|
+
if (head.every((x) => QUALIFIERS[x.toLowerCase()])) continue;
|
|
3911
3927
|
const outer = parseSimpleClause([...head, NEST_SENTINEL].join(" "), nlp);
|
|
3912
3928
|
if (!outer || outer.shape !== "reverse" && outer.shape !== "forward") continue;
|
|
3913
3929
|
if (outer.modifier && outer.modifier !== "direct") continue;
|
|
@@ -22013,7 +22029,7 @@ ${codeblock}`, options);
|
|
|
22013
22029
|
});
|
|
22014
22030
|
|
|
22015
22031
|
// src/adapters/corpus/conceptnet.mjs
|
|
22016
|
-
var
|
|
22032
|
+
var import_meta7, PKG_ROOT5, SLICE_FILE, MAP_FILE, SEON_CONCEPTS_FILE, SEON_DEFINITIONS_FILE, TIER2_DIR, TIER2_MANIFEST_FILE, WORDNET_DIR, WORDNET_MANIFEST_FILE;
|
|
22017
22033
|
var init_conceptnet = __esm({
|
|
22018
22034
|
"src/adapters/corpus/conceptnet.mjs"() {
|
|
22019
22035
|
init_node_fs();
|
|
@@ -22023,15 +22039,15 @@ ${codeblock}`, options);
|
|
|
22023
22039
|
init_node_path();
|
|
22024
22040
|
init_dist();
|
|
22025
22041
|
init_core();
|
|
22026
|
-
|
|
22027
|
-
|
|
22028
|
-
SLICE_FILE = join(
|
|
22029
|
-
MAP_FILE = join(
|
|
22030
|
-
SEON_CONCEPTS_FILE = join(
|
|
22031
|
-
SEON_DEFINITIONS_FILE = join(
|
|
22032
|
-
TIER2_DIR = join(
|
|
22042
|
+
import_meta7 = {};
|
|
22043
|
+
PKG_ROOT5 = join(dirname(fileURLToPath2(import_meta7.url)), "..", "..", "..");
|
|
22044
|
+
SLICE_FILE = join(PKG_ROOT5, "corpus", "conceptnet", "slice.jsonl");
|
|
22045
|
+
MAP_FILE = join(PKG_ROOT5, "src", "adapters", "corpus", "conceptnet-map.toml");
|
|
22046
|
+
SEON_CONCEPTS_FILE = join(PKG_ROOT5, "corpus", "seon", "concepts.jsonl");
|
|
22047
|
+
SEON_DEFINITIONS_FILE = join(PKG_ROOT5, "corpus", "seon", "definitions.jsonl");
|
|
22048
|
+
TIER2_DIR = join(PKG_ROOT5, "corpus", "tier2");
|
|
22033
22049
|
TIER2_MANIFEST_FILE = join(TIER2_DIR, "manifest.json");
|
|
22034
|
-
WORDNET_DIR = join(
|
|
22050
|
+
WORDNET_DIR = join(PKG_ROOT5, "corpus", "wordnet");
|
|
22035
22051
|
WORDNET_MANIFEST_FILE = join(WORDNET_DIR, "manifest.json");
|
|
22036
22052
|
}
|
|
22037
22053
|
});
|
|
@@ -22125,7 +22141,7 @@ ${codeblock}`, options);
|
|
|
22125
22141
|
}
|
|
22126
22142
|
};
|
|
22127
22143
|
}
|
|
22128
|
-
var
|
|
22144
|
+
var import_meta8, NAMENET_DIR, EXTENSION_KINDS, CONCEPTNET_PREFER, BUILTIN_EXTENSIONS;
|
|
22129
22145
|
var init_extensions = __esm({
|
|
22130
22146
|
"src/services/extensions.mjs"() {
|
|
22131
22147
|
init_node_path();
|
|
@@ -22133,8 +22149,8 @@ ${codeblock}`, options);
|
|
|
22133
22149
|
init_node_url();
|
|
22134
22150
|
init_toml_config();
|
|
22135
22151
|
init_conceptnet();
|
|
22136
|
-
|
|
22137
|
-
NAMENET_DIR = join(dirname(fileURLToPath2(
|
|
22152
|
+
import_meta8 = {};
|
|
22153
|
+
NAMENET_DIR = join(dirname(fileURLToPath2(import_meta8.url)), "..", "..", "corpus", "namenet");
|
|
22138
22154
|
EXTENSION_KINDS = Object.freeze(["corpus", "lexicon", "templates", "pack", "ontology"]);
|
|
22139
22155
|
CONCEPTNET_PREFER = ["rdfs:subClassOf", "rdf:type", "mgx:usedFor", "mgx:partOf", "mgx:capableOf"];
|
|
22140
22156
|
BUILTIN_EXTENSIONS = Object.freeze(builtinExtensions());
|
|
@@ -22300,8 +22316,8 @@ ${codeblock}`, options);
|
|
|
22300
22316
|
{
|
|
22301
22317
|
name: "tmct_impact",
|
|
22302
22318
|
tier: "cold",
|
|
22303
|
-
summary: "Transitive reverse closure over imports/calls \u2014 what breaks if a module changes, by depth, with tests.",
|
|
22304
|
-
inputSchema: moduleArg("The module whose dependents you want."),
|
|
22319
|
+
summary: "Transitive reverse closure over imports/calls \u2014 what breaks if a module or symbol changes, by depth, with tests.",
|
|
22320
|
+
inputSchema: moduleArg("The module (or a symbol it defines) whose dependents you want."),
|
|
22305
22321
|
example: { module: "django/utils/text.py" }
|
|
22306
22322
|
},
|
|
22307
22323
|
{
|
|
@@ -23233,6 +23249,17 @@ ${JSON.stringify(envelope, null, 2)}`;
|
|
|
23233
23249
|
var import_meta4 = {};
|
|
23234
23250
|
var PKG_ROOT2 = join(dirname(fileURLToPath2(import_meta4.url)), "..", "..", "..");
|
|
23235
23251
|
|
|
23252
|
+
// src/domain/child-pack.mjs
|
|
23253
|
+
init_hash();
|
|
23254
|
+
|
|
23255
|
+
// src/adapters/corpus/child-pack.mjs
|
|
23256
|
+
init_node_fs();
|
|
23257
|
+
init_node_url();
|
|
23258
|
+
init_node_path();
|
|
23259
|
+
init_hash();
|
|
23260
|
+
var import_meta5 = {};
|
|
23261
|
+
var PKG_ROOT3 = join(dirname(fileURLToPath2(import_meta5.url)), "..", "..", "..");
|
|
23262
|
+
|
|
23236
23263
|
// src/domain/dialogue-acts.mjs
|
|
23237
23264
|
var DIALOGUE_ACT_DIMENSIONS = Object.freeze([
|
|
23238
23265
|
"task",
|
|
@@ -23392,10 +23419,10 @@ ${JSON.stringify(envelope, null, 2)}`;
|
|
|
23392
23419
|
init_node_fs();
|
|
23393
23420
|
init_node_url();
|
|
23394
23421
|
init_node_path();
|
|
23395
|
-
var
|
|
23396
|
-
var
|
|
23422
|
+
var import_meta6 = {};
|
|
23423
|
+
var PKG_ROOT4 = join(dirname(fileURLToPath2(import_meta6.url)), "..", "..", "..");
|
|
23397
23424
|
function worldsPackDir(env = process.env) {
|
|
23398
|
-
return env?.TMCT_WORLDS_PACK_DIR || join(
|
|
23425
|
+
return env?.TMCT_WORLDS_PACK_DIR || join(PKG_ROOT4, "corpus", "worlds");
|
|
23399
23426
|
}
|
|
23400
23427
|
var indexCacheByDir = /* @__PURE__ */ new Map();
|
|
23401
23428
|
var worldCacheByKey = /* @__PURE__ */ new Map();
|
|
@@ -146,8 +146,8 @@ export const TOOL_DEFINITIONS = Object.freeze([
|
|
|
146
146
|
{
|
|
147
147
|
name: "tmct_impact",
|
|
148
148
|
tier: "cold",
|
|
149
|
-
summary: "Transitive reverse closure over imports/calls — what breaks if a module changes, by depth, with tests.",
|
|
150
|
-
inputSchema: moduleArg("The module whose dependents you want."),
|
|
149
|
+
summary: "Transitive reverse closure over imports/calls — what breaks if a module or symbol changes, by depth, with tests.",
|
|
150
|
+
inputSchema: moduleArg("The module (or a symbol it defines) whose dependents you want."),
|
|
151
151
|
example: { module: "django/utils/text.py" },
|
|
152
152
|
},
|
|
153
153
|
{
|