@polycode-projects/the-mechanical-code-talker 1.11.0 → 1.11.6

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.
@@ -507,7 +507,8 @@
507
507
  "recursive",
508
508
  "action-signature",
509
509
  "action-precond",
510
- "action-effect"
510
+ "action-effect",
511
+ "action-constraint"
511
512
  ]);
512
513
  RULE_SLOT_PROPS = {
513
514
  compose2: ["mgx:ruleBase1", "mgx:ruleBase2"],
@@ -524,6 +525,11 @@
524
525
  "mgx:ruleActionEffectPredicate",
525
526
  "mgx:ruleActionEffectSubject",
526
527
  "mgx:ruleActionEffectObject"
528
+ ],
529
+ "action-constraint": [
530
+ "mgx:ruleActionConstraintLeft",
531
+ "mgx:ruleActionConstraintRight",
532
+ "mgx:ruleActionConstraintGuard"
527
533
  ]
528
534
  };
529
535
  nonEmpty = (v) => typeof v === "string" && v.trim().length > 0;
@@ -688,19 +694,23 @@
688
694
  var core_exports = {};
689
695
  __export(core_exports, {
690
696
  CANONICALISED_FROM_PROP: () => CANONICALISED_FROM_PROP,
697
+ CAPABLE_OF_PREDICATE: () => CAPABLE_OF_PREDICATE,
691
698
  CONTRADICTION_TRUST_FLOOR: () => CONTRADICTION_TRUST_FLOOR,
692
699
  CREATED_AT_PROP: () => CREATED_AT_PROP,
693
700
  DEFAULT_RETENTION: () => DEFAULT_RETENTION,
694
701
  DERIVED_FROM_PROP: () => DERIVED_FROM_PROP,
695
702
  FACT_CLASS: () => FACT_CLASS,
703
+ HAS_A_PREDICATE: () => HAS_A_PREDICATE,
696
704
  IN_REPLY_TO_PROP: () => IN_REPLY_TO_PROP,
697
705
  MEMORY_DIR_REL: () => MEMORY_DIR_REL,
698
706
  MEMORY_GRAPH_REL: () => MEMORY_GRAPH_REL,
699
707
  MEMORY_MANIFEST_REL: () => MEMORY_MANIFEST_REL,
700
708
  MEMORY_SESSION_CLASS: () => MEMORY_SESSION_CLASS,
709
+ MULTI_VALUED_PREDICATES: () => MULTI_VALUED_PREDICATES,
701
710
  OPERATOR_SOURCE_ID: () => OPERATOR_SOURCE_ID,
702
711
  RULE_CLASS: () => RULE_CLASS,
703
712
  RULE_KINDS: () => RULE_KINDS2,
713
+ RULE_KIND_ACTION_CONSTRAINT: () => RULE_KIND_ACTION_CONSTRAINT,
704
714
  RULE_KIND_ACTION_EFFECT: () => RULE_KIND_ACTION_EFFECT,
705
715
  RULE_KIND_ACTION_PRECOND: () => RULE_KIND_ACTION_PRECOND,
706
716
  RULE_KIND_ACTION_SIGNATURE: () => RULE_KIND_ACTION_SIGNATURE,
@@ -1765,6 +1775,7 @@
1765
1775
  const rows = readFactRows(memory).filter((r) => r.trust >= floor);
1766
1776
  const byKey = /* @__PURE__ */ new Map();
1767
1777
  for (const r of rows) {
1778
+ if (MULTI_VALUED_PREDICATES.has(r.predicate)) continue;
1768
1779
  const key = `${r.subject} ${r.predicate}`;
1769
1780
  if (!byKey.has(key)) byKey.set(key, []);
1770
1781
  byKey.get(key).push(r);
@@ -1777,7 +1788,7 @@
1777
1788
  }
1778
1789
  return out.sort((a, b) => `${a[0].subject} ${a[0].predicate}`.localeCompare(`${b[0].subject} ${b[0].predicate}`));
1779
1790
  }
1780
- var MEMORY_DIR_REL, MEMORY_GRAPH_REL, UTTERANCE_CLASS, FACT_CLASS, MEMORY_SESSION_CLASS, SOURCE_CLASS, RULE_CLASS, SAID_IN_SESSION_PROP, IN_REPLY_TO_PROP, DERIVED_FROM_PROP, STATED_BY_PROP, CANONICALISED_FROM_PROP, CREATED_AT_PROP, UPDATED_AT_PROP, SOURCE_RELIABILITY_PROP, OPERATOR_SOURCE_ID, TEACH_SOURCE_ID, ROLES, LABEL_CAP, TEXT_CAP, MEMORY_VOCABULARY, memoryGraphFile, BACKEND_MEMORY, BACKEND_SQLITE, SQLITE_DDL, STD_EDGE_KEYS, cloneJson, MEMORY_MANIFEST_REL, DEFAULT_RETENTION, resolveManifestFile, MEMORY_INDEX, memoryIndexOf, normText, labelOf, nowIso, sourceLabel, isSessionScopedSourceId, factIdFor, RULE_KIND_COMPOSE2, RULE_KIND_FILTER, RULE_KIND_RECURSIVE, RULE_KIND_ACTION_SIGNATURE, RULE_KIND_ACTION_PRECOND, RULE_KIND_ACTION_EFFECT, RULE_KINDS2, RULE_NAME_PROP, RULE_KIND_PROP, RULE_SLOT_SPEC, ruleIdFor, CONTRADICTION_TRUST_FLOOR;
1791
+ var MEMORY_DIR_REL, MEMORY_GRAPH_REL, UTTERANCE_CLASS, FACT_CLASS, MEMORY_SESSION_CLASS, SOURCE_CLASS, RULE_CLASS, SAID_IN_SESSION_PROP, IN_REPLY_TO_PROP, DERIVED_FROM_PROP, STATED_BY_PROP, CANONICALISED_FROM_PROP, CREATED_AT_PROP, UPDATED_AT_PROP, SOURCE_RELIABILITY_PROP, OPERATOR_SOURCE_ID, TEACH_SOURCE_ID, ROLES, LABEL_CAP, TEXT_CAP, MEMORY_VOCABULARY, memoryGraphFile, BACKEND_MEMORY, BACKEND_SQLITE, SQLITE_DDL, STD_EDGE_KEYS, cloneJson, MEMORY_MANIFEST_REL, DEFAULT_RETENTION, resolveManifestFile, MEMORY_INDEX, memoryIndexOf, normText, labelOf, nowIso, sourceLabel, isSessionScopedSourceId, factIdFor, RULE_KIND_COMPOSE2, RULE_KIND_FILTER, RULE_KIND_RECURSIVE, RULE_KIND_ACTION_SIGNATURE, RULE_KIND_ACTION_PRECOND, RULE_KIND_ACTION_EFFECT, RULE_KIND_ACTION_CONSTRAINT, RULE_KINDS2, RULE_NAME_PROP, RULE_KIND_PROP, RULE_SLOT_SPEC, ruleIdFor, CONTRADICTION_TRUST_FLOOR, HAS_A_PREDICATE, CAPABLE_OF_PREDICATE, MULTI_VALUED_PREDICATES;
1781
1792
  var init_core = __esm({
1782
1793
  "src/memory/core.mjs"() {
1783
1794
  init_promises();
@@ -1870,13 +1881,15 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
1870
1881
  RULE_KIND_ACTION_SIGNATURE = "action-signature";
1871
1882
  RULE_KIND_ACTION_PRECOND = "action-precond";
1872
1883
  RULE_KIND_ACTION_EFFECT = "action-effect";
1884
+ RULE_KIND_ACTION_CONSTRAINT = "action-constraint";
1873
1885
  RULE_KINDS2 = Object.freeze([
1874
1886
  RULE_KIND_COMPOSE2,
1875
1887
  RULE_KIND_FILTER,
1876
1888
  RULE_KIND_RECURSIVE,
1877
1889
  RULE_KIND_ACTION_SIGNATURE,
1878
1890
  RULE_KIND_ACTION_PRECOND,
1879
- RULE_KIND_ACTION_EFFECT
1891
+ RULE_KIND_ACTION_EFFECT,
1892
+ RULE_KIND_ACTION_CONSTRAINT
1880
1893
  ]);
1881
1894
  RULE_NAME_PROP = "mgx:ruleName";
1882
1895
  RULE_KIND_PROP = "mgx:ruleKind";
@@ -1898,10 +1911,20 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
1898
1911
  ["predicate", "mgx:ruleActionEffectPredicate"],
1899
1912
  ["subjectRole", "mgx:ruleActionEffectSubject"],
1900
1913
  ["objectRole", "mgx:ruleActionEffectObject"]
1914
+ ],
1915
+ // "the <left> may not be with the <right> without the <guard>" — each slot
1916
+ // names a class whose sole member src/domain.mjs resolves at compile time.
1917
+ [RULE_KIND_ACTION_CONSTRAINT]: [
1918
+ ["left", "mgx:ruleActionConstraintLeft"],
1919
+ ["right", "mgx:ruleActionConstraintRight"],
1920
+ ["guard", "mgx:ruleActionConstraintGuard"]
1901
1921
  ]
1902
1922
  };
1903
1923
  ruleIdFor = (kind, name, slotValues) => `rule:${fnv1aHex([kind, name, ...slotValues].join("\0"))}`;
1904
1924
  CONTRADICTION_TRUST_FLOOR = 0.5;
1925
+ HAS_A_PREDICATE = "mgx:hasA";
1926
+ CAPABLE_OF_PREDICATE = "mgx:capableOf";
1927
+ MULTI_VALUED_PREDICATES = /* @__PURE__ */ new Set([HAS_A_PREDICATE, CAPABLE_OF_PREDICATE]);
1905
1928
  }
1906
1929
  });
1907
1930
 
@@ -2835,6 +2858,14 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
2835
2858
  if (m && INTERROGATIVE_LEAD_RE.test(m[1].trim())) q = m[1].trim();
2836
2859
  m = q.match(TELL_ME_WRAPPER_RE);
2837
2860
  if (m && INTERROGATIVE_LEAD_RE.test(m[1].trim())) q = m[1].trim();
2861
+ m = q.match(KNOW_WRAPPER_RE);
2862
+ if (m && INTERROGATIVE_LEAD_RE.test(m[1].trim())) q = m[1].trim();
2863
+ m = q.match(WANT_KNOW_WRAPPER_RE);
2864
+ if (m && INTERROGATIVE_LEAD_RE.test(m[1].trim())) q = m[1].trim();
2865
+ m = q.match(EMBEDDED_WHATIS_RE);
2866
+ if (m) q = `what ${m[2].toLowerCase()} ${m[1].trim()}`;
2867
+ m = q.match(EMBEDDED_MEANS_RE);
2868
+ if (m) q = `what does ${m[1].trim()} mean`;
2838
2869
  m = q.match(SHOW_GIVE_ME_RE);
2839
2870
  if (m) {
2840
2871
  const rest = m[1].trim();
@@ -2923,7 +2954,7 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
2923
2954
  if (!predicate) return null;
2924
2955
  return { entWord, predicate };
2925
2956
  }
2926
- 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, 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, PHRASING_FRAMES, NEGATION_SET_RE, STOPWORDS2, splitWords, wordsOf;
2957
+ 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, 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, PHRASING_FRAMES, NEGATION_SET_RE, STOPWORDS2, splitWords, wordsOf;
2927
2958
  var init_normalize = __esm({
2928
2959
  "src/interpret/normalize.mjs"() {
2929
2960
  init_ask_vocab();
@@ -2983,6 +3014,10 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
2983
3014
  MODAL_WRAPPER_RE = /^(?:can|could|would|will)\s+you\s+(?:please\s+)?(.+?)(?:[,\s]+please)?\??$/i;
2984
3015
  EXPLAIN_WRAPPER_RE = /^explain\s+(?:to\s+me\s+|please\s+)*(.+?)\??$/i;
2985
3016
  TELL_ME_WRAPPER_RE = /^tell\s+me\s+(.+?)\??$/i;
3017
+ KNOW_WRAPPER_RE = /^do\s+you\s+know\s+(.+?)\??$/i;
3018
+ WANT_KNOW_WRAPPER_RE = /^i(?:'d|\s+would)?\s+(?:like|want|need)\s+to\s+know\s+(.+?)\??$/i;
3019
+ EMBEDDED_WHATIS_RE = /^what\s+((?:an?\s+|the\s+)?[\w'-]+(?:\s+[\w'-]+){0,2})\s+(is|are)\??$/i;
3020
+ EMBEDDED_MEANS_RE = /^what\s+((?:an?\s+|the\s+)?[\w'-]+(?:\s+[\w'-]+){0,2})\s+means\??$/i;
2986
3021
  SHOW_GIVE_ME_RE = /^(?:show|give)\s+me\s+(?:the\s+)?(.+?)\??$/i;
2987
3022
  LEADING_CONNECTIVE_RE = /^(?:and|also|so|then|now|but)\s+(.+)$/i;
2988
3023
  QUESTION_AUX_LEAD_RE = /^(?:does|do|did|is|are|was|were|has|have|had|can|could|will|would|should)\b/i;
@@ -3041,6 +3076,27 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
3041
3076
  // temporal query produces ("were" as an auxiliary never leads directly into
3042
3077
  // a bare "is").
3043
3078
  { re: /^were\s+is\s+(?:the\s+)?(.+?)\s+(defined|declared|located|implemented)\??$/i, to: (m) => `where is ${m[1]} ${m[2]}` },
3079
+ // DESCRIBE PARAPHRASES ("what is the purpose of X", "what does X do in
3080
+ // this codebase") → the meta/whatis shape ("what is a <term>"), which
3081
+ // already answers a unique code entity via metaFallbackEntityAnswer. The
3082
+ // term slot refuses an a/an article or a pronoun lead so the vocabulary
3083
+ // phrasings ("what is the purpose of a horse", "what does it do here")
3084
+ // pass through untouched to their own memory-facts and context readers,
3085
+ // which read the raw text and must keep their turn. A leading "the" is
3086
+ // entity-term noise (mirrors resolveObject's own article strip). The
3087
+ // sibling "what is X for" paraphrase is deliberately NOT a frame: chat's
3088
+ // module-overview lane owns that phrasing and gates on an ask() miss, so
3089
+ // it lives as ask()'s own miss-gated fallback (WHATIS_FOR_FALLBACK_RE)
3090
+ // instead, adopted only when the meta reading actually answers.
3091
+ { re: /^what\s+is\s+the\s+purpose\s+of\s+(?:the\s+)?(?!(?:an?|it|this|that|these|those)\s)(.+?)\??$/i, to: (m) => `what is a ${m[1]}` },
3092
+ // Scoped form only: bare "what does X do" stays unrewritten — the chat
3093
+ // surface's module-grain overview lane owns it and only gets its turn when
3094
+ // ask() misses, so claiming it here would swap that richer answer for the
3095
+ // one-line meta fallback.
3096
+ {
3097
+ re: new RegExp(`^what\\s+does\\s+(?:the\\s+)?(?!(?:an?|it|this|that|these|those)\\s)(.+?)\\s+do\\s+(?:${TRAILING_SCOPE_FILLER.map(escapeRegex).join("|")})\\??$`, "i"),
3098
+ to: (m) => `what is a ${m[1]}`
3099
+ },
3044
3100
  // PREDICATIVE QUALIFIER ("which modules are untested") → the ATTRIBUTIVE form
3045
3101
  // ("untested modules") the grammar already answers. The QUALIFIER must sit
3046
3102
  // immediately after are/is, so "…are NOT tested" keeps its own set-complement handler.
@@ -3383,10 +3439,14 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
3383
3439
  verbHit = findPhrase(lemmaWords, VERB_TO_KIND);
3384
3440
  if (verbHit) canonWords = lemmaWords;
3385
3441
  }
3442
+ let fuzzyVerb = false;
3386
3443
  if (!verbHit) {
3387
3444
  const fuzzyWords = lcWords.map((w) => w.length >= 4 && eligibleForCanon(w) ? fuzzyVocabWord(w) || w : w);
3388
3445
  verbHit = findPhrase(fuzzyWords, VERB_TO_KIND);
3389
- if (verbHit) canonWords = fuzzyWords;
3446
+ if (verbHit) {
3447
+ canonWords = fuzzyWords;
3448
+ fuzzyVerb = true;
3449
+ }
3390
3450
  }
3391
3451
  if (!verbHit && lcWords.includes("by")) {
3392
3452
  for (let i = 0; i < lcWords.length; i += 1) {
@@ -3398,6 +3458,7 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
3398
3458
  }
3399
3459
  }
3400
3460
  if (!verbHit) return null;
3461
+ const stamp = (ast) => fuzzyVerb ? { ...ast, fuzzyVerb: true } : ast;
3401
3462
  if (nlp && verbHit.end - verbHit.start === 1) {
3402
3463
  const i = verbHit.start;
3403
3464
  const det = lcWords[i - 1];
@@ -3405,7 +3466,7 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
3405
3466
  const tags = nlp.posTags(words);
3406
3467
  if (tags[i] === "NOUN") {
3407
3468
  const objText = words.slice(i + 2).filter((w, j) => !STOPWORDS2.has(lcWords[i + 2 + j])).join(" ").trim();
3408
- if (objText) return { shape: "forward", entityType: null, modifier: "direct", kind: verbHit.kind, object: objText };
3469
+ if (objText) return stamp({ shape: "forward", entityType: null, modifier: "direct", kind: verbHit.kind, object: objText });
3409
3470
  }
3410
3471
  }
3411
3472
  }
@@ -3435,11 +3496,11 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
3435
3496
  const modifier = modifierHit ? MODIFIER_TO_KIND[canonWords.slice(modifierHit.start, modifierHit.end).join(" ")] : "direct";
3436
3497
  if (kind === "touches" && lcWords.includes("when")) {
3437
3498
  const objText = beforeText || afterText;
3438
- if (objText) return { shape: "when", entityType: null, modifier: "direct", kind: "touches", object: objText };
3499
+ if (objText) return stamp({ shape: "when", entityType: null, modifier: "direct", kind: "touches", object: objText });
3439
3500
  }
3440
3501
  if (kind === "touches" && lcWords.includes("who") && lcWords.includes("last")) {
3441
3502
  const objText = beforeText || afterText;
3442
- if (objText) return { shape: "whoLast", entityType: null, modifier: "direct", kind: "touches", object: objText };
3503
+ if (objText) return stamp({ shape: "whoLast", entityType: null, modifier: "direct", kind: "touches", object: objText });
3443
3504
  }
3444
3505
  const byIdx = lcWords.indexOf("by");
3445
3506
  const hasPassiveAux = lcWords.slice(0, verbHit.start).some((w) => PASSIVE_AUX.has(w));
@@ -3459,7 +3520,7 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
3459
3520
  break;
3460
3521
  }
3461
3522
  const agentNamed = nextAfterBy != null && !WH_WORDS.has(nextAfterBy) && !ENTITY_TO_TYPE[nextAfterBy];
3462
- return { shape: agentNamed ? "forward" : "reverse", entityType, modifier, kind, object };
3523
+ return stamp({ shape: agentNamed ? "forward" : "reverse", entityType, modifier, kind, object });
3463
3524
  }
3464
3525
  }
3465
3526
  if (beforeText && afterText) {
@@ -3467,14 +3528,14 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
3467
3528
  let subject = beforeText;
3468
3529
  let object = afterText;
3469
3530
  if (INHERITS_REVERSE_VERBS.includes(verbPhrase)) [subject, object] = [object, subject];
3470
- return { shape: "ask", entityType: null, modifier: "direct", kind, subject, object };
3531
+ return stamp({ shape: "ask", entityType: null, modifier: "direct", kind, subject, object });
3471
3532
  }
3472
- if (afterText) return { shape: "reverse", entityType, modifier, kind, object: afterText };
3533
+ if (afterText) return stamp({ shape: "reverse", entityType, modifier, kind, object: afterText });
3473
3534
  if (kind === "inherits" && !beforeText && entityHit && entityHit.start === verbHit.end) {
3474
3535
  const entityText = canonWords.slice(entityHit.start, entityHit.end).join(" ");
3475
- if (entityText) return { shape: "reverse", entityType: null, modifier, kind, object: entityText };
3536
+ if (entityText) return stamp({ shape: "reverse", entityType: null, modifier, kind, object: entityText });
3476
3537
  }
3477
- if (beforeText) return { shape: "forward", entityType, modifier: "direct", kind, object: beforeText };
3538
+ if (beforeText) return stamp({ shape: "forward", entityType, modifier: "direct", kind, object: beforeText });
3478
3539
  return null;
3479
3540
  }
3480
3541
  var PASSIVE_AUX, WH_WORDS, PLACEHOLDER_SET, keywordSpotStrategy;
@@ -3791,6 +3852,7 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
3791
3852
  __export(ask_exports, {
3792
3853
  applyNegationFrames: () => applyNegationFrames,
3793
3854
  ask: () => ask,
3855
+ classDisplayName: () => classDisplayName,
3794
3856
  compositionalHint: () => compositionalHint,
3795
3857
  degreeMetric: () => degreeMetric,
3796
3858
  evalComposite: () => evalComposite,
@@ -3825,6 +3887,14 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
3825
3887
  const [s, p] = PLURAL_FORMS[entityType] || ["result", "results"];
3826
3888
  return n === 1 ? s : p;
3827
3889
  }
3890
+ function classDisplayName(cls) {
3891
+ const s = String(cls || "");
3892
+ if (typeof splitIdentifierWords === "function") {
3893
+ const words = splitIdentifierWords(s).join(" ");
3894
+ if (words) return words;
3895
+ }
3896
+ return s.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase();
3897
+ }
3828
3898
  function verbFor(kind) {
3829
3899
  return REVERSE_MISS_VERB[kind] || kind;
3830
3900
  }
@@ -5434,6 +5504,15 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
5434
5504
  ...extra
5435
5505
  };
5436
5506
  }
5507
+ function rankEntryPointModules(graph, term) {
5508
+ const queryWords = new Set(String(term || "").toLowerCase().split(/[\s-]+/).filter(Boolean));
5509
+ return (graph.individuals || []).filter((i) => i.class === "Module" && ENTRY_POINT_BASENAMES.has(moduleStemOf(i.label))).map((ind) => ({
5510
+ ind,
5511
+ named: queryWords.has(moduleStemOf(ind.label)) ? 1 : 0,
5512
+ depth: String(ind.label).split("/").length,
5513
+ fixture: isTestFixturePath(ind.label) ? 1 : 0
5514
+ })).sort((a, b) => b.named - a.named || a.depth - b.depth || a.fixture - b.fixture || String(a.ind.label).localeCompare(String(b.ind.label))).map((x) => x.ind);
5515
+ }
5437
5516
  function modifierIsWired(shape, kind, entityType) {
5438
5517
  return shape === "reverse" && (kind === "imports" || kind === "calls") && (!entityType || entityType === "Module");
5439
5518
  }
@@ -5493,6 +5572,17 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
5493
5572
  traversal: `proseIndex word lookup for "${term}"`
5494
5573
  };
5495
5574
  }
5575
+ if (shape === "where" && ENTRY_POINT_QUERY_RE.test(String(parsed.object || "").trim())) {
5576
+ const ranked = rankEntryPointModules(graph, parsed.object);
5577
+ return {
5578
+ matches: ranked,
5579
+ objMatch: ranked[0] || null,
5580
+ candidates: ranked.slice(1, 5),
5581
+ ambiguous: false,
5582
+ entryPointShape: true,
5583
+ traversal: `Module individuals with an entry-point basename (${[...ENTRY_POINT_BASENAMES].join("/")}), ranked query-named basename first, then shallower path, then non-test path`
5584
+ };
5585
+ }
5496
5586
  if (parsed.modifier && parsed.modifier !== "direct" && !modifierIsWired(shape, kind, entityType)) {
5497
5587
  return {
5498
5588
  matches: [],
@@ -5651,7 +5741,7 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
5651
5741
  matchedVia,
5652
5742
  forwardGrainMiss: true,
5653
5743
  wantClasses: [...wantClasses],
5654
- traversal: `${fwdKinds.join("+")} edges where subject = ${objMatch.label} (grain mismatch: this "${kind}" relation never targets a ${entityType})`
5744
+ traversal: `${fwdKinds.join("+")} edges where subject = ${objMatch.label} (grain mismatch: this "${kind}" relation never targets a ${classDisplayName(entityType)})`
5655
5745
  };
5656
5746
  }
5657
5747
  }
@@ -5734,7 +5824,7 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
5734
5824
  matchedVia: gMatchedVia,
5735
5825
  wrongGrainMiss: true,
5736
5826
  wantClass,
5737
- traversal: `"${parsed.object}" resolved to ${gObjMatch.class} ${gObjMatch.label} (grain mismatch: this "${kind}" question needs a ${wantClass}, and no containing module could be found to refine to)`
5827
+ traversal: `"${parsed.object}" resolved to ${classDisplayName(gObjMatch.class)} ${gObjMatch.label} (grain mismatch: this "${kind}" question needs a ${classDisplayName(wantClass)}, and no containing module could be found to refine to)`
5738
5828
  };
5739
5829
  }
5740
5830
  } else {
@@ -5746,7 +5836,7 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
5746
5836
  matchedVia: gMatchedVia,
5747
5837
  wrongGrainMiss: true,
5748
5838
  wantClass,
5749
- traversal: `"${parsed.object}" resolved to ${gObjMatch.class} ${gObjMatch.label} (grain mismatch: this "${kind}" question needs a ${wantClass})`
5839
+ traversal: `"${parsed.object}" resolved to ${classDisplayName(gObjMatch.class)} ${gObjMatch.label} (grain mismatch: this "${kind}" question needs a ${classDisplayName(wantClass)})`
5750
5840
  };
5751
5841
  }
5752
5842
  }
@@ -5781,7 +5871,7 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
5781
5871
  } else if (entityType !== "Module" && subjects.some((s) => s.class === "Module")) {
5782
5872
  const moduleIds = new Set(subjects.filter((s) => s.class === "Module").map((s) => s.id));
5783
5873
  matches = refineToEntities(graph, moduleIds, entityType);
5784
- grainNote = `, then ${entityType} defined in the matched module(s)`;
5874
+ grainNote = `, then ${classDisplayName(entityType)} defined in the matched module(s)`;
5785
5875
  } else {
5786
5876
  matches = [];
5787
5877
  }
@@ -5874,7 +5964,8 @@ ${options2}
5874
5964
  (ask one of these directly, or try rephrasing more specifically, to get just that reading)`,
5875
5965
  miss: false,
5876
5966
  ambiguous: true,
5877
- candidates: parsed.candidates.map(describeParse)
5967
+ candidates: parsed.candidates.map(describeParse),
5968
+ candidateParses: parsed.candidates
5878
5969
  };
5879
5970
  }
5880
5971
  const options = parsed.candidates.map((p, i) => `${i + 1}) ${describeParse(p)}`).join(" or ");
@@ -5882,7 +5973,8 @@ ${options2}
5882
5973
  content: `this could mean more than one thing: ${options} \u2014 try rephrasing more specifically.`,
5883
5974
  miss: false,
5884
5975
  ambiguous: true,
5885
- candidates: parsed.candidates.map(describeParse)
5976
+ candidates: parsed.candidates.map(describeParse),
5977
+ candidateParses: parsed.candidates
5886
5978
  };
5887
5979
  }
5888
5980
  if (result.unresolvedPronoun) {
@@ -5948,6 +6040,26 @@ ${options2}
5948
6040
  matches: result.matches
5949
6041
  };
5950
6042
  }
6043
+ if (result.entryPointShape) {
6044
+ if (!result.matches.length) {
6045
+ return {
6046
+ content: `no entry-point module found in the index \u2014 no module basename matches ${listJoin([...ENTRY_POINT_BASENAMES])}.`,
6047
+ miss: true,
6048
+ ambiguous: false,
6049
+ candidates: []
6050
+ };
6051
+ }
6052
+ const [top, ...rest] = result.matches;
6053
+ const shownRest = rest.slice(0, OVERFLOW_CAP).map((i) => i.label);
6054
+ const extra2 = rest.length > OVERFLOW_CAP ? `, \u2026and ${rest.length - OVERFLOW_CAP} more` : "";
6055
+ const also = rest.length ? ` \u2014 also matched: ${listJoin(shownRest)}${extra2}` : "";
6056
+ return {
6057
+ content: `ranked ${result.matches.length} entry-point match${result.matches.length === 1 ? "" : "es"}; top: ${top.label}${also}.`,
6058
+ miss: false,
6059
+ ambiguous: false,
6060
+ matches: result.matches
6061
+ };
6062
+ }
5951
6063
  if (!result.objMatch && (!result.candidates || result.candidates.length === 0) && parsed.shape !== "ask") {
5952
6064
  const objText = String(parsed.object || "").trim();
5953
6065
  const fallback = parsed.entityType && PLURAL_FORMS[parsed.entityType] ? nounFor(parsed.entityType, 1) : "module";
@@ -6364,6 +6476,21 @@ ${result.branches.map((b, i) => `${i + 1}) ${b.candidate.label}: ${b.rendered.co
6364
6476
  }
6365
6477
  }
6366
6478
  }
6479
+ if (parsed === null && rendered.miss && !rendered.ambiguous) {
6480
+ const forM = normalizeQuery(String(query || "")).match(WHATIS_FOR_FALLBACK_RE);
6481
+ const forTerm = forM?.[1]?.trim();
6482
+ if (forTerm) {
6483
+ const forParsed = parseQuery(`what is a ${forTerm}`, { nlp });
6484
+ if (forParsed?.shape === "meta") {
6485
+ const forResult = traverse(graph, forParsed, { contextId, prev });
6486
+ const forRendered = render(forParsed, forResult);
6487
+ if (!forRendered.miss && !forRendered.ambiguous) {
6488
+ result = forResult;
6489
+ rendered = forRendered;
6490
+ }
6491
+ }
6492
+ }
6493
+ }
6367
6494
  let content = relaxed && !rendered.miss && relaxed.to !== relaxed.from ? `read as "${relaxed.to}" \u2014 ${rendered.content}` : rendered.content;
6368
6495
  if (!relaxed && !rendered.miss && !rendered.ambiguous && directFull.alternates.length) {
6369
6496
  const answered = directFull.alternates.map((a) => {
@@ -6401,11 +6528,11 @@ ${lines.join("\n")}`;
6401
6528
  // edit-distance, announced in the content as "assuming you meant …");
6402
6529
  // null for every literal-identifier tier.
6403
6530
  matchedVia: result.matchedVia || null,
6404
- ...rendered.ambiguous ? { candidates: rendered.candidates } : {}
6531
+ ...rendered.ambiguous ? { candidates: rendered.candidates, candidateParses: rendered.candidateParses } : {}
6405
6532
  }
6406
6533
  };
6407
6534
  }
6408
- var askEdgesOfKindCache, SYMBOL_GRAIN_SIBLING, FINE_ENTITY_TYPES, FINE_CLASS_SIBLING, KIND_UNIONS, kindsFor, OVERFLOW_CAP, PLURAL_FORMS, REVERSE_MISS_VERB, LEADING_RELATION_VERB_RE, FROZEN_META_AMBIGUOUS_TERMS, MAX_COMPOSE_DEPTH, NEST_SENTINEL, PRED_LEAD_SKIP, FRAME_WORDS, COPULA_WORDS, entityNoun, isGerundVerb, FWD_NEG_FRAME, PLURAL_ANAPHORA_OBJECT, TEMPORAL_AUX, TEMPORAL_TAIL, TEMPORAL_TRAIL_FILLER, TEMPORAL_DET, COMMIT_FILTER_OPS, ANAPHORA_NAME_TOKEN_RE, AGG_TAIL_FILLER, LIST_SKIP, LIST_TRIGGERS_SORTED, LISTABLE_KINDS, SCOPE_PREPOSITIONS, FIND_LINKERS, RECENT_COMMIT_LEAD, qualCache, META_FALLBACK_CLASSES, inheritsApplicableCache, FIND_TIER, DEGREE_KINDS, COMMIT_FILTER_DATE_RE, compositeList, LEADING_ARTICLE_RE, TRAILING_GRAIN_WORD_RE, TRANSITIVE_MAX_DEPTH, CONTENT_VOCAB, STRUCTURAL_WORDS, CASCADE_NOISE_SET, NOISE_OR_SCAFFOLD, TRIGGER_FUZZY_WORDS, CASCADE_FUZZY_TARGETS, LAST_COMMIT_PHRASE_RE, BARE_WHEN_COMMIT_RE, DYNAMIC_LIST_TRIGGER_RE, DYNAMIC_COUNT_TRIGGER_RE, DYNAMIC_TAIL_OK_RE, BARE_META_WHATIS_RE;
6535
+ var askEdgesOfKindCache, SYMBOL_GRAIN_SIBLING, FINE_ENTITY_TYPES, FINE_CLASS_SIBLING, KIND_UNIONS, kindsFor, OVERFLOW_CAP, PLURAL_FORMS, REVERSE_MISS_VERB, LEADING_RELATION_VERB_RE, FROZEN_META_AMBIGUOUS_TERMS, MAX_COMPOSE_DEPTH, NEST_SENTINEL, PRED_LEAD_SKIP, FRAME_WORDS, COPULA_WORDS, entityNoun, isGerundVerb, FWD_NEG_FRAME, PLURAL_ANAPHORA_OBJECT, TEMPORAL_AUX, TEMPORAL_TAIL, TEMPORAL_TRAIL_FILLER, TEMPORAL_DET, COMMIT_FILTER_OPS, ANAPHORA_NAME_TOKEN_RE, AGG_TAIL_FILLER, LIST_SKIP, LIST_TRIGGERS_SORTED, LISTABLE_KINDS, SCOPE_PREPOSITIONS, FIND_LINKERS, RECENT_COMMIT_LEAD, qualCache, META_FALLBACK_CLASSES, inheritsApplicableCache, FIND_TIER, DEGREE_KINDS, COMMIT_FILTER_DATE_RE, compositeList, LEADING_ARTICLE_RE, TRAILING_GRAIN_WORD_RE, ENTRY_POINT_QUERY_RE, ENTRY_POINT_BASENAMES, TEST_FIXTURE_PATH_SEGMENTS, moduleStemOf, isTestFixturePath, TRANSITIVE_MAX_DEPTH, CONTENT_VOCAB, STRUCTURAL_WORDS, CASCADE_NOISE_SET, NOISE_OR_SCAFFOLD, TRIGGER_FUZZY_WORDS, CASCADE_FUZZY_TARGETS, LAST_COMMIT_PHRASE_RE, BARE_WHEN_COMMIT_RE, DYNAMIC_LIST_TRIGGER_RE, DYNAMIC_COUNT_TRIGGER_RE, DYNAMIC_TAIL_OK_RE, BARE_META_WHATIS_RE, WHATIS_FOR_FALLBACK_RE;
6409
6536
  var init_ask = __esm({
6410
6537
  "src/ask.mjs"() {
6411
6538
  init_codegraph();
@@ -6513,6 +6640,11 @@ ${lines.join("\n")}`;
6513
6640
  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` : "");
6514
6641
  LEADING_ARTICLE_RE = /^(?:the|a|an)\s+/i;
6515
6642
  TRAILING_GRAIN_WORD_RE = new RegExp(`\\s+(${Object.keys(ENTITY_TO_TYPE).join("|")})$`, "i");
6643
+ ENTRY_POINT_QUERY_RE = /^(?:the\s+)?(?:main\s+|primary\s+)?entry[\s-]?points?(?:\s+(?:of|to|for)\s+(?:this|the)\s+(?:codebase|code|repo|repository|project|app))?$/i;
6644
+ ENTRY_POINT_BASENAMES = /* @__PURE__ */ new Set(["index", "main", "app", "server", "cli", "__main__"]);
6645
+ TEST_FIXTURE_PATH_SEGMENTS = /* @__PURE__ */ new Set(["test", "tests", "__tests__", "fixture", "fixtures", "spec", "specs", "testdata"]);
6646
+ moduleStemOf = (label) => String(label).toLowerCase().split("/").pop().replace(/\.[a-z0-9]+$/, "");
6647
+ isTestFixturePath = (label) => String(label).toLowerCase().split("/").slice(0, -1).some((seg) => TEST_FIXTURE_PATH_SEGMENTS.has(seg));
6516
6648
  TRANSITIVE_MAX_DEPTH = 8;
6517
6649
  CONTENT_VOCAB = /* @__PURE__ */ new Set([
6518
6650
  ...wordsOf(Object.keys(VERB_TO_KIND)),
@@ -6560,6 +6692,7 @@ ${lines.join("\n")}`;
6560
6692
  DYNAMIC_COUNT_TRIGGER_RE = /^(?:how\s+many|number\s+of|count(?:\s+the)?)\s+([a-z][a-z'-]*)\s*(.*)$/i;
6561
6693
  DYNAMIC_TAIL_OK_RE = /^(?:are there(?:\s+in\s+total)?|is there|do you know(?:\s+about)?|do you have|exist(?:s)?|are known|in (?:the |a )?(?:graph|memory)|you know(?:\s+about)?)?[?.!\s]*$/i;
6562
6694
  BARE_META_WHATIS_RE = /^what\s+(?:is|are)\s+(?:an?\s+)?(.+?)[?.!\s]*$/i;
6695
+ WHATIS_FOR_FALLBACK_RE = /^what\s+is\s+(?:the\s+)?(?!(?:an?|it|this|that|these|those)\s)(.+?)\s+(?:used\s+)?for[?.!\s]*$/i;
6563
6696
  }
6564
6697
  });
6565
6698
 
@@ -7226,15 +7359,20 @@ ${lines.join("\n")}`;
7226
7359
  };
7227
7360
  }
7228
7361
  function winkInstance() {
7362
+ if (instance !== void 0) return instance;
7229
7363
  const loaded = loadWinkModel();
7230
- if (!loaded) return null;
7364
+ if (!loaded) {
7365
+ instance = null;
7366
+ return null;
7367
+ }
7231
7368
  try {
7232
- return loaded.winkNLP(loaded.model);
7369
+ instance = loaded.winkNLP(loaded.model);
7233
7370
  } catch {
7234
- return null;
7371
+ instance = null;
7235
7372
  }
7373
+ return instance;
7236
7374
  }
7237
- var import_meta6, injected, cached;
7375
+ var import_meta6, injected, cached, instance;
7238
7376
  var init_wink_model = __esm({
7239
7377
  "src/wink-model.mjs"() {
7240
7378
  init_node_module();
@@ -7242,6 +7380,13 @@ ${lines.join("\n")}`;
7242
7380
  }
7243
7381
  });
7244
7382
 
7383
+ // src/sentences.mjs
7384
+ var init_sentences = __esm({
7385
+ "src/sentences.mjs"() {
7386
+ init_wink_model();
7387
+ }
7388
+ });
7389
+
7245
7390
  // src/prose-nlp.mjs
7246
7391
  var prose_nlp_exports = {};
7247
7392
  __export(prose_nlp_exports, {
@@ -7727,6 +7872,7 @@ ${lines.join("\n")}`;
7727
7872
  const trustByTriple = /* @__PURE__ */ new Map();
7728
7873
  for (const r of rows) trustByTriple.set(`${r.subject}${SEP}${r.predicate}${SEP}${r.object}`, r.trust);
7729
7874
  const premiseTrust = (s, p, o) => trustByTriple.get(`${s}${SEP}${p}${SEP}${o}`);
7875
+ const hasTriple = (s, p, o) => trustByTriple.has(`${s}${SEP}${p}${SEP}${o}`);
7730
7876
  const numericOnly = (arr) => arr.filter((t) => typeof t === "number");
7731
7877
  const scmDerived = deriveSubClassClosure(subClassEdges, { depth, budget, focus: normalizedFocus });
7732
7878
  const enlargedSubClassEdges = subClassEdges.concat(scmDerived.map((d) => [d.subject, d.object]));
@@ -7745,11 +7891,12 @@ ${lines.join("\n")}`;
7745
7891
  predicate: SUBCLASS_PREDICATE,
7746
7892
  object: d.object,
7747
7893
  provenance: ENTAILED_PROVENANCE,
7748
- // Persisted justification, scm-sco only: the two premise fact ids this
7749
- // conclusion rode (a⊑b, b⊑c) — content-addressed ids work even when a
7750
- // premise is itself an entailment this same pass just derived. Read
7751
- // back by retractSubClassOf (below) to find every entailment a
7752
- // retracted premise could have supported.
7894
+ // Persisted justification: the premise fact ids this conclusion rode
7895
+ // (a⊑b, b⊑c) — content-addressed ids work even when a premise is
7896
+ // itself an entailment this same pass just derived. Read back by
7897
+ // retractSubClassOf (below) to find every entailment a retracted
7898
+ // premise could have supported. All five rules persist one, each
7899
+ // citing its own premise shape.
7753
7900
  justification: [
7754
7901
  factIdForTriple(d.subject, SUBCLASS_PREDICATE, d.via),
7755
7902
  factIdForTriple(d.via, SUBCLASS_PREDICATE, d.object)
@@ -7759,13 +7906,22 @@ ${lines.join("\n")}`;
7759
7906
  subject: d.subject,
7760
7907
  predicate: TYPE_PREDICATE,
7761
7908
  object: d.object,
7762
- provenance: ENTAILED_TYPE_PROVENANCE
7909
+ provenance: ENTAILED_TYPE_PROVENANCE,
7910
+ // The ⊑ premise is cited as the DIRECT via⊑object edge even when the
7911
+ // taught chain is multi-hop: scm-sco materializes that edge (this same
7912
+ // pass or an earlier one), and retraction re-VERIFIES every candidate
7913
+ // anyway, so a citation left dangling by budget truncation is inert.
7914
+ justification: [
7915
+ factIdForTriple(d.subject, TYPE_PREDICATE, d.via),
7916
+ factIdForTriple(d.via, SUBCLASS_PREDICATE, d.object)
7917
+ ]
7763
7918
  })),
7764
7919
  ...dwDerived.map((d) => {
7765
- const dwTrust = premiseTrust(d.viaClass, DISJOINT_PREDICATE, d.object) ?? premiseTrust(d.object, DISJOINT_PREDICATE, d.viaClass);
7920
+ const dwStoredForward = hasTriple(d.viaClass, DISJOINT_PREDICATE, d.object);
7921
+ const [dwS, dwO] = dwStoredForward ? [d.viaClass, d.object] : [d.object, d.viaClass];
7766
7922
  const premiseTrusts = numericOnly([
7767
7923
  premiseTrust(d.subject, TYPE_PREDICATE, d.viaType),
7768
- dwTrust,
7924
+ premiseTrust(dwS, DISJOINT_PREDICATE, dwO),
7769
7925
  // the ⊑-lift premise only exists when this IS a lift (viaClass !==
7770
7926
  // viaType) — a direct hit has no extra subClassOf premise to price in.
7771
7927
  ...d.viaClass !== d.viaType ? [premiseTrust(d.viaType, SUBCLASS_PREDICATE, d.viaClass)] : []
@@ -7775,6 +7931,11 @@ ${lines.join("\n")}`;
7775
7931
  predicate: DISJOINT_PREDICATE,
7776
7932
  object: d.object,
7777
7933
  provenance: ENTAILED_DISJOINT_PROVENANCE,
7934
+ justification: [
7935
+ factIdForTriple(d.subject, TYPE_PREDICATE, d.viaType),
7936
+ factIdForTriple(dwS, DISJOINT_PREDICATE, dwO),
7937
+ ...d.viaClass !== d.viaType ? [factIdForTriple(d.viaType, SUBCLASS_PREDICATE, d.viaClass)] : []
7938
+ ],
7778
7939
  ...premiseTrusts.length ? { premiseTrusts, ruleConfidence: CAX_DW_RULE_CONFIDENCE } : {}
7779
7940
  };
7780
7941
  }),
@@ -7793,6 +7954,13 @@ ${lines.join("\n")}`;
7793
7954
  predicate: TYPE_PREDICATE,
7794
7955
  object: d.object,
7795
7956
  provenance: ENTAILED_SVF1_PROVENANCE,
7957
+ justification: [
7958
+ factIdForTriple(d.subject, d.viaProperty, d.viaValue),
7959
+ factIdForTriple(d.viaValue, TYPE_PREDICATE, d.viaType),
7960
+ factIdForTriple(d.object, ON_PROPERTY_PREDICATE, d.viaPropertyKey),
7961
+ factIdForTriple(d.object, SOME_VALUES_FROM_PREDICATE, d.viaTarget),
7962
+ ...d.viaType !== d.viaTarget ? [factIdForTriple(d.viaType, SUBCLASS_PREDICATE, d.viaTarget)] : []
7963
+ ],
7796
7964
  // same sub-1 discount as cax-dw, same reason (see CAX_DW_RULE_CONFIDENCE).
7797
7965
  ...premiseTrusts.length ? { premiseTrusts, ruleConfidence: CLS_SVF1_RULE_CONFIDENCE } : {}
7798
7966
  };
@@ -7812,6 +7980,13 @@ ${lines.join("\n")}`;
7812
7980
  predicate: SUBCLASS_PREDICATE,
7813
7981
  object: d.object,
7814
7982
  provenance: ENTAILED_SCM_SVF_PROVENANCE,
7983
+ justification: [
7984
+ ...r1 ? [factIdForTriple(d.subject, ON_PROPERTY_PREDICATE, r1.property)] : [],
7985
+ factIdForTriple(d.subject, SOME_VALUES_FROM_PREDICATE, d.viaY1),
7986
+ ...r2 ? [factIdForTriple(d.object, ON_PROPERTY_PREDICATE, r2.property)] : [],
7987
+ factIdForTriple(d.object, SOME_VALUES_FROM_PREDICATE, d.viaY2),
7988
+ factIdForTriple(d.viaY1, SUBCLASS_PREDICATE, d.viaY2)
7989
+ ],
7815
7990
  // same sub-1 discount as cax-dw/cls-svf1, same reason (see CAX_DW_RULE_CONFIDENCE).
7816
7991
  ...premiseTrusts.length ? { premiseTrusts, ruleConfidence: SCM_SVF_RULE_CONFIDENCE } : {}
7817
7992
  };
@@ -7846,6 +8021,71 @@ ${lines.join("\n")}`;
7846
8021
  const tags = String(provenance || "").split(" | ").filter(Boolean);
7847
8022
  return tags.length > 0 && tags.every((t) => t.startsWith("entailed:"));
7848
8023
  }
8024
+ function buildSurvivorDerivabilityCheck(rows) {
8025
+ const subClassEdges = [];
8026
+ const typesOf = /* @__PURE__ */ new Map();
8027
+ const disjointOf = /* @__PURE__ */ new Map();
8028
+ const onPropertyOf = /* @__PURE__ */ new Map();
8029
+ const someValuesFromOf = /* @__PURE__ */ new Map();
8030
+ const propertyEdgesOf = /* @__PURE__ */ new Map();
8031
+ for (const r of rows) {
8032
+ const pLower = String(r.predicate || "").trim().toLowerCase();
8033
+ if (isSubClassOf(r.predicate)) subClassEdges.push([r.subject, r.object]);
8034
+ else if (isType(r.predicate)) {
8035
+ if (!typesOf.has(r.subject)) typesOf.set(r.subject, /* @__PURE__ */ new Set());
8036
+ typesOf.get(r.subject).add(r.object);
8037
+ } else if (isDisjoint(r.predicate)) {
8038
+ if (!disjointOf.has(r.subject)) disjointOf.set(r.subject, /* @__PURE__ */ new Set());
8039
+ disjointOf.get(r.subject).add(r.object);
8040
+ if (!disjointOf.has(r.object)) disjointOf.set(r.object, /* @__PURE__ */ new Set());
8041
+ disjointOf.get(r.object).add(r.subject);
8042
+ } else if (isOnProperty(r.predicate)) onPropertyOf.set(r.subject, r.object);
8043
+ else if (isSomeValuesFrom(r.predicate)) someValuesFromOf.set(r.subject, r.object);
8044
+ else if (!RESERVED_PREDICATES.has(pLower)) {
8045
+ if (!propertyEdgesOf.has(r.subject)) propertyEdgesOf.set(r.subject, []);
8046
+ propertyEdgesOf.get(r.subject).push([normFactTerm(r.predicate), r.object]);
8047
+ }
8048
+ }
8049
+ const ancestorsOf2 = buildAncestorCloser(subClassEdges);
8050
+ const reaches = (a, b) => a !== b && ancestorsOf2(a).has(b);
8051
+ const restrictionOf = (node) => {
8052
+ const property = onPropertyOf.get(node);
8053
+ const target = someValuesFromOf.get(node);
8054
+ return property && target ? { property: normFactTerm(property), target } : null;
8055
+ };
8056
+ return (row) => {
8057
+ if (isSubClassOf(row.predicate)) {
8058
+ if (reaches(row.subject, row.object)) return true;
8059
+ const r1 = restrictionOf(row.subject);
8060
+ const r2 = restrictionOf(row.object);
8061
+ return Boolean(r1 && r2 && r1.property === r2.property && reaches(r1.target, r2.target));
8062
+ }
8063
+ if (isType(row.predicate)) {
8064
+ for (const c of typesOf.get(row.subject) || []) {
8065
+ if (reaches(c, row.object)) return true;
8066
+ }
8067
+ const rec = restrictionOf(row.object);
8068
+ if (rec) {
8069
+ for (const [pKey, y] of propertyEdgesOf.get(row.subject) || []) {
8070
+ if (pKey !== rec.property) continue;
8071
+ for (const c of typesOf.get(y) || []) {
8072
+ if (c === rec.target || reaches(c, rec.target)) return true;
8073
+ }
8074
+ }
8075
+ }
8076
+ return false;
8077
+ }
8078
+ if (isDisjoint(row.predicate)) {
8079
+ for (const c of typesOf.get(row.subject) || []) {
8080
+ for (const d of [c, ...ancestorsOf2(c)]) {
8081
+ if (disjointOf.get(d)?.has(row.object)) return true;
8082
+ }
8083
+ }
8084
+ return false;
8085
+ }
8086
+ return true;
8087
+ };
8088
+ }
7849
8089
  async function retractSubClassOf(repoDir, subject, object, { budget = 50, depth = 32 } = {}) {
7850
8090
  const s = normFactTerm(subject);
7851
8091
  const o = normFactTerm(object);
@@ -7854,19 +8094,18 @@ ${lines.join("\n")}`;
7854
8094
  const rows = readFactRows(memory);
7855
8095
  const byId = new Map(rows.map((r) => [r.id, r]));
7856
8096
  if (!byId.has(targetId)) return { retracted: [], count: 0, budget, depth, truncated: false, found: false };
7857
- const scRows = rows.filter((r) => isSubClassOf(r.predicate));
7858
- const edgeOf = new Map(scRows.map((r) => [r.id, [r.subject, r.object]]));
7859
- const entailedScRows = scRows.filter((r) => r.justification.length && isPurelyEntailed(r.provenance));
8097
+ const entailedRows = rows.filter((r) => r.justification.length && isPurelyEntailed(r.provenance));
7860
8098
  const removed = /* @__PURE__ */ new Set([targetId]);
7861
8099
  const order = [targetId];
7862
8100
  let truncated = false;
7863
8101
  let round2 = 0;
7864
8102
  for (; round2 < depth; round2 += 1) {
7865
- const candidates = entailedScRows.filter((r) => !removed.has(r.id) && r.justification.some((j) => removed.has(j))).sort((a, b) => a.subject.localeCompare(b.subject) || a.object.localeCompare(b.object));
8103
+ const candidates = entailedRows.filter((r) => !removed.has(r.id) && r.justification.some((j) => removed.has(j))).sort((a, b) => a.subject.localeCompare(b.subject) || a.predicate.localeCompare(b.predicate) || a.object.localeCompare(b.object));
7866
8104
  if (!candidates.length) break;
7867
8105
  const candidateIds = new Set(candidates.map((c) => c.id));
7868
- const survivingEdges = [...edgeOf.entries()].filter(([id]) => !removed.has(id) && !candidateIds.has(id)).map(([, e]) => e);
7869
- const ancestorsOf2 = buildAncestorCloser(survivingEdges);
8106
+ const stillDerivable = buildSurvivorDerivabilityCheck(
8107
+ rows.filter((r) => !removed.has(r.id) && !candidateIds.has(r.id))
8108
+ );
7870
8109
  let progressed = false;
7871
8110
  let hitBudget = false;
7872
8111
  for (const c of candidates) {
@@ -7874,7 +8113,7 @@ ${lines.join("\n")}`;
7874
8113
  hitBudget = true;
7875
8114
  break;
7876
8115
  }
7877
- if (ancestorsOf2(c.subject).has(c.object)) continue;
8116
+ if (stillDerivable(c)) continue;
7878
8117
  removed.add(c.id);
7879
8118
  order.push(c.id);
7880
8119
  progressed = true;
@@ -7886,7 +8125,7 @@ ${lines.join("\n")}`;
7886
8125
  if (!progressed) break;
7887
8126
  }
7888
8127
  if (!truncated && round2 >= depth) {
7889
- truncated = entailedScRows.some((r) => !removed.has(r.id) && r.justification.some((j) => removed.has(j)));
8128
+ truncated = entailedRows.some((r) => !removed.has(r.id) && r.justification.some((j) => removed.has(j)));
7890
8129
  }
7891
8130
  const { removed: actuallyRemoved } = await removeFacts(repoDir, order);
7892
8131
  return { retracted: actuallyRemoved, count: actuallyRemoved.length, budget, depth, truncated, found: true };
@@ -7971,6 +8210,30 @@ ${lines.join("\n")}`;
7971
8210
  });
7972
8211
 
7973
8212
  // src/chat.mjs
8213
+ function deduceGoalFromParsed(parsed) {
8214
+ if (!parsed) return null;
8215
+ const { node, shape, kind } = parsed;
8216
+ if (node === "find") return `locate a specific named entity ("${parsed.term}")`;
8217
+ if (node === "count") return `get a count of ${goalNoun(parsed.entityType)}`;
8218
+ if (node === "list") return `list/enumerate ${goalNoun(parsed.entityType)} matching a condition`;
8219
+ if (node === "superlative") return `rank/compare ${goalNoun(parsed.entityType)} by ${parsed.metricNoun || parsed.metric || "a metric"}`;
8220
+ if (node === "anaphora") return "follow up on the previous answer's result set (discourse anaphora)";
8221
+ if (node === "membership") return `understand "${parsed.term || "an entity"}"'s membership/relationship`;
8222
+ if (node === "clause") return deduceGoalFromParsed(parsed.clause);
8223
+ if (node === "miss") return null;
8224
+ if (node === "boolean" || node === "qualifier" || node === "reverseSet" || node === "forwardSet" || node === "allOfClass" || node === "temporal") {
8225
+ const k = kind || parsed.inner?.kind;
8226
+ return k && GOAL_BY_KIND[k] ? GOAL_BY_KIND[k] : `filter/traverse ${goalNoun(parsed.entityType)} by a relationship`;
8227
+ }
8228
+ if (shape === "meta") return `understand a vocabulary/definition term ("${parsed.object}")`;
8229
+ if (shape === "where") return `locate where something is defined ("${parsed.object}")`;
8230
+ if (shape === "when") return "understand when something last changed (history)";
8231
+ if (shape === "whoLast") return "find who most recently touched something (history)";
8232
+ if (shape === "mentions") return `find where something is mentioned in prose ("${parsed.object}")`;
8233
+ if (shape === "ask") return kind && GOAL_BY_KIND[kind] || "check a specific subject/object relationship";
8234
+ if ((shape === "reverse" || shape === "forward") && kind) return GOAL_BY_KIND[kind] || `understand a "${kind}" relationship`;
8235
+ return "understand a graph relationship";
8236
+ }
7974
8237
  function collapsedIndex(set) {
7975
8238
  const idx = /* @__PURE__ */ new Map();
7976
8239
  for (const phrase of set) if (!idx.has(collapseRuns(phrase))) idx.set(collapseRuns(phrase), phrase);
@@ -8117,6 +8380,10 @@ ${lines.join("\n")}`;
8117
8380
  }
8118
8381
  return null;
8119
8382
  }
8383
+ function matchGenitiveWhoAsk(q) {
8384
+ const g = String(q).match(GENITIVE_WHO_ASK_RE);
8385
+ return g ? [g[0], g[2], g[1]] : null;
8386
+ }
8120
8387
  function matchWhyIsa(q) {
8121
8388
  const stripped = String(q || "").replace(WHY_ISA_LEAD_RE, "");
8122
8389
  if (stripped !== q) {
@@ -8127,6 +8394,14 @@ ${lines.join("\n")}`;
8127
8394
  if (ehyk) return `is ${ehyk[1].trim()} a ${ehyk[2].trim()}`.match(ISA_ASK_RE);
8128
8395
  return null;
8129
8396
  }
8397
+ function comparativeOfSuperlative(superlative) {
8398
+ const s = String(superlative || "").toLowerCase().trim().replace(/\s+/g, " ");
8399
+ if (s === "best") return "better";
8400
+ if (s === "worst") return "worse";
8401
+ const graded = s.match(/^(most|least)\s+([a-z][\w-]*)$/);
8402
+ if (graded) return `${graded[1] === "most" ? "more" : "less"} ${graded[2]}`;
8403
+ return /[a-z]est$/.test(s) && s.length > 4 ? `${s.slice(0, -3)}er` : null;
8404
+ }
8130
8405
  function uniqueFacts(rows) {
8131
8406
  const seen = /* @__PURE__ */ new Set();
8132
8407
  const out = [];
@@ -8139,6 +8414,26 @@ ${lines.join("\n")}`;
8139
8414
  return out;
8140
8415
  }
8141
8416
  async function factAnswer(memoryDir, query, envelope, miss2, biasByBundle = {}, cache = null) {
8417
+ return withDeducedGoal(await factAnswerReaders(memoryDir, query, envelope, miss2, biasByBundle, cache), envelope, query);
8418
+ }
8419
+ function withDeducedGoal(res, envelope, query) {
8420
+ if (!res || res.goal !== void 0) return res;
8421
+ const q = String(query || "").trim();
8422
+ let goal = deduceGoalFromParsed(envelope?.parsed);
8423
+ if (!goal && res.generalVerbQuery) goal = TAUGHT_FACT_LOOKUP_GOAL;
8424
+ if (!goal) {
8425
+ const yesNo = q.match(RELATION_FACT_YESNO_RE);
8426
+ const whoAsk = yesNo ? null : q.match(RELATION_WHO_ASK_RE) || matchGenitiveWhoAsk(q);
8427
+ const role = yesNo ? yesNo[2] : whoAsk ? whoAsk[1] : null;
8428
+ if (role && !ISA_IDIOM_ROLE_WORDS.has(role.toLowerCase())) goal = TAUGHT_FACT_LOOKUP_GOAL;
8429
+ }
8430
+ if (!goal) {
8431
+ const whatIs = q.match(BARE_WHATIS_RE);
8432
+ if (whatIs) goal = deduceGoalFromParsed({ shape: "meta", object: whatIs[1] });
8433
+ }
8434
+ return goal ? { ...res, goal } : res;
8435
+ }
8436
+ async function factAnswerReaders(memoryDir, query, envelope, miss2, biasByBundle = {}, cache = null) {
8142
8437
  let normFactTerm2;
8143
8438
  try {
8144
8439
  ({ normFactTerm: normFactTerm2 } = await Promise.resolve().then(() => (init_core(), core_exports)));
@@ -8174,11 +8469,82 @@ ${lines.join("\n")}`;
8174
8469
  \u2026and ${rest.length} more \u2014 say 'more' to see them.` : "";
8175
8470
  return { text: shown.join("\n") + extra, replace: true, ...rest.length ? { pending: { items: rest, noun: "facts" } } : {} };
8176
8471
  }
8472
+ const whichSup = q.match(WHICH_KIND_SUPERLATIVE_RE);
8473
+ const whatSup = whichSup ? null : q.match(WHAT_IS_SUPERLATIVE_KIND_RE);
8474
+ const supKindRaw = whichSup ? whichSup[1] : whatSup?.[2];
8475
+ const supWord = whichSup ? whichSup[2] : whatSup?.[1];
8476
+ const supCompBase = supKindRaw && supWord ? comparativeOfSuperlative(supWord) : null;
8477
+ if (supCompBase) {
8478
+ const supPredicate = `mgx:${supCompBase.replace(/\s+/g, "-")}-than`;
8479
+ const rows = await factRows(memoryDir, cache);
8480
+ const kindVariants = factTermVariants(normFactTerm2, supKindRaw);
8481
+ const kindSingular = [...kindVariants].sort((a, b) => a.length - b.length)[0];
8482
+ const memberOfKind = (node) => kindVariants.has(node) || node.startsWith(`${kindSingular}-`) || node.startsWith(`${kindSingular} `) || rows.some((g) => ISA_PREDICATES.has(g.predicate) && g.subject === node && kindVariants.has(g.object));
8483
+ const pairs = uniqueFacts(rows.filter((f) => f.predicate === supPredicate && isOperatorTaught(f))).filter((f) => f.subject !== f.object && memberOfKind(f.subject) && memberOfKind(f.object));
8484
+ if (pairs.length) {
8485
+ const nodes = /* @__PURE__ */ new Set();
8486
+ const inDeg = /* @__PURE__ */ new Map();
8487
+ for (const f of pairs) {
8488
+ nodes.add(f.subject);
8489
+ nodes.add(f.object);
8490
+ inDeg.set(f.object, (inDeg.get(f.object) || 0) + 1);
8491
+ if (!inDeg.has(f.subject)) inDeg.set(f.subject, inDeg.get(f.subject) || 0);
8492
+ }
8493
+ const remaining = new Map(inDeg);
8494
+ const order = [];
8495
+ let declined = null;
8496
+ while (remaining.size) {
8497
+ const sources = [...remaining.keys()].filter((n) => remaining.get(n) === 0);
8498
+ if (sources.length !== 1) {
8499
+ declined = sources.length === 0 ? {
8500
+ text: `I can't order the ${kindSingular}s \u2014 the "${supCompBase} than" facts I have loop back on themselves, so no ${supWord} exists. /memory to inspect them.`,
8501
+ replace: true,
8502
+ miss: true
8503
+ } : {
8504
+ text: `I can't pick the ${supWord} ${kindSingular} from what I know \u2014 nothing compares ${sources[0]} and ${sources[1]}. Teach me, e.g. "${sources[0]} is ${supCompBase} than ${sources[1]}".`,
8505
+ replace: true,
8506
+ miss: true
8507
+ };
8508
+ break;
8509
+ }
8510
+ const head = sources[0];
8511
+ order.push(head);
8512
+ remaining.delete(head);
8513
+ for (const f of pairs) {
8514
+ if (f.subject === head && remaining.has(f.object)) remaining.set(f.object, remaining.get(f.object) - 1);
8515
+ }
8516
+ }
8517
+ if (declined) return declined;
8518
+ const steps = order.slice(0, -1).map((n, i) => pairs.find((f) => f.subject === n && f.object === order[i + 1]));
8519
+ if (steps.every(Boolean)) {
8520
+ const cite = steps.map((g) => `${factPhrase(g)}${g.provenance ? ` (source: ${g.provenance})` : ""}`).join("; ");
8521
+ return { text: `${order[0]} \u2014 ${cite}; so ${order[0]} is the ${supWord} ${kindSingular}`, replace: true };
8522
+ }
8523
+ }
8524
+ }
8525
+ const whereQ = miss2 ? q.match(WHERE_IS_FACT_RE) : null;
8526
+ if (whereQ) {
8527
+ const variants = factTermVariants(normFactTerm2, whereQ[1]);
8528
+ const hits = (await factRows(memoryDir, cache)).filter((f) => LOCATIVE_FACT_PREDICATE_RE.test(f.predicate) && variants.has(f.subject));
8529
+ if (hits.length) {
8530
+ const ranked = rankByBiasThenTrust(uniqueFacts(hits), biasByBundle);
8531
+ const lines = ranked.map(renderFactLine);
8532
+ const shown = lines.slice(0, FACT_ANSWER_CAP);
8533
+ const rest = lines.slice(FACT_ANSWER_CAP);
8534
+ const extra = rest.length ? `
8535
+ \u2026and ${rest.length} more \u2014 say 'more' to see them.` : "";
8536
+ return { text: shown.join("\n") + extra, replace: true, ...rest.length ? { pending: { items: rest, noun: "facts" } } : {} };
8537
+ }
8538
+ }
8177
8539
  let metaTerm = envelope?.parsed?.shape === "meta" ? envelope.parsed.object : null;
8178
8540
  if (!metaTerm && miss2 && !envelope?.parsed && !WHAT_INHERITS_RE.test(q)) {
8179
8541
  const m = q.match(BARE_WHATIS_RE) || q.match(/^what\s+(?:does|do)\s+(.+?)\s+means?[?.!\s]*$/i);
8180
8542
  if (m) metaTerm = stripTrailingScopeFiller(m[1]);
8181
8543
  }
8544
+ if (!metaTerm && envelope?.ambiguous && Array.isArray(envelope.candidateParses)) {
8545
+ const metaCand = envelope.candidateParses.find((c) => c?.shape === "meta" && c.object);
8546
+ if (metaCand) metaTerm = stripTrailingScopeFiller(String(metaCand.object));
8547
+ }
8182
8548
  if (metaTerm) {
8183
8549
  const { subject, predicate } = splitMetaPredicate(metaTerm);
8184
8550
  const variants = factTermVariants(normFactTerm2, subject);
@@ -8268,7 +8634,7 @@ ${lines.join("\n")}`;
8268
8634
  if (knownCan.length) {
8269
8635
  const shown = knownCan.slice(0, 3).map(renderFactLine).join("; ");
8270
8636
  return {
8271
- text: `I can't confirm that \u2014 nothing I remember says ${can[1]} can ${can[2]}. I do know: ${shown}. If it's true, teach me: "a ${can[1]} can ${can[2]}".`,
8637
+ text: `I can't confirm that \u2014 nothing I remember says ${can[1]} can ${can[2]}. I do know: ${shown}. If it's true, teach me: "a ${knownCan[0].subject} can ${can[2]}".`,
8272
8638
  replace: true,
8273
8639
  miss: true
8274
8640
  };
@@ -8285,6 +8651,34 @@ ${lines.join("\n")}`;
8285
8651
  if (hit2) return { text: `yes \u2014 ${renderFactLine(hit2)}`, replace: true };
8286
8652
  return null;
8287
8653
  }
8654
+ const doAsk = q.match(DO_VERB_ASK_RE);
8655
+ if (doAsk) {
8656
+ const facts = await memoryFacts(memoryDir);
8657
+ const universal = !!doAsk[1];
8658
+ const subj = factTermVariants(normFactTerm2, doAsk[2]);
8659
+ const obj = factTermVariants(normFactTerm2, doAsk[3]);
8660
+ const hit2 = facts.find(
8661
+ (f) => f.predicate === "mgx:capableOf" && subj.has(f.subject) && obj.has(f.object)
8662
+ );
8663
+ if (hit2 && universal) {
8664
+ return {
8665
+ text: `I can't speak for all ${doAsk[2]} \u2014 what I remember is generic, not universal. I do know: ${renderFactLine(hit2)}.`,
8666
+ replace: true
8667
+ };
8668
+ }
8669
+ if (hit2) return { text: `yes \u2014 ${renderFactLine(hit2)}`, replace: true };
8670
+ if (miss2) {
8671
+ const knownCan = facts.filter((f) => f.predicate === "mgx:capableOf" && subj.has(f.subject));
8672
+ if (knownCan.length) {
8673
+ const shown = knownCan.slice(0, 3).map(renderFactLine).join("; ");
8674
+ return {
8675
+ text: `I can't confirm that \u2014 nothing I remember says ${doAsk[2]} can ${doAsk[3]}. I do know: ${shown}. If it's true, teach me: "a ${knownCan[0].subject} can ${doAsk[3]}".`,
8676
+ replace: true,
8677
+ miss: true
8678
+ };
8679
+ }
8680
+ }
8681
+ }
8288
8682
  const canDo = q.match(WHAT_CAN_DO_RE);
8289
8683
  if (canDo) {
8290
8684
  const variants = factTermVariants(normFactTerm2, canDo[1]);
@@ -8298,6 +8692,59 @@ ${lines.join("\n")}`;
8298
8692
  \u2026and ${rest.length} more \u2014 say 'more' to see them.` : "";
8299
8693
  return { text: shown.join("\n") + extra, replace: true, ...rest.length ? { pending: { items: rest, noun: "facts" } } : {} };
8300
8694
  }
8695
+ const whichCan = q.match(WHICH_KIND_CAN_RE);
8696
+ if (whichCan) {
8697
+ const kindVariants = factTermVariants(normFactTerm2, whichCan[1]);
8698
+ const verbVariants = factTermVariants(normFactTerm2, whichCan[2]);
8699
+ const facts = await factRows(memoryDir, cache);
8700
+ const capable = uniqueFacts(facts.filter((f) => f.predicate === "mgx:capableOf" && verbVariants.has(f.object)));
8701
+ if (capable.length) {
8702
+ const { findIsaChain: findIsaChain2, SUBCLASS_PREDICATE: SC_PRED, TYPE_PREDICATE: TYPE_PRED } = await Promise.resolve().then(() => (init_syllogise(), syllogise_exports));
8703
+ const subClassRows = facts.filter((f) => f.predicate === SC_PRED);
8704
+ const typeRows = facts.filter((f) => f.predicate === TYPE_PRED);
8705
+ const subClassEdges = subClassRows.map((f) => [f.subject, f.object]);
8706
+ const typeEdges = typeRows.map((f) => [f.subject, f.object]);
8707
+ const rowForStep = (step) => (step.predicate === SC_PRED ? subClassRows : typeRows).find((g) => g.subject === step.subject && g.object === step.object);
8708
+ const chainBySubject = /* @__PURE__ */ new Map();
8709
+ const inKind = capable.filter((f) => {
8710
+ if (kindVariants.has(f.subject)) return true;
8711
+ if (!chainBySubject.has(f.subject)) {
8712
+ chainBySubject.set(f.subject, findIsaChain2(f.subject, kindVariants, typeEdges, subClassEdges, { maxHops: 3 }));
8713
+ }
8714
+ return !!chainBySubject.get(f.subject);
8715
+ });
8716
+ const ranked = rankByBiasThenTrust(inKind.length ? inKind : capable, biasByBundle);
8717
+ const lines = ranked.map((f) => {
8718
+ const chain = inKind.length ? chainBySubject.get(f.subject) : null;
8719
+ if (!chain || chain.length < 2) return renderFactLine(f);
8720
+ const steps = chain.map(rowForStep);
8721
+ if (!steps.every(Boolean)) return renderFactLine(f);
8722
+ const cite = steps.map((g) => `${factPhrase(g)}${g.provenance ? ` (source: ${g.provenance})` : ""}`).join("; ");
8723
+ return `${renderFactLine(f)} \u2014 via: ${cite}`;
8724
+ });
8725
+ const shown = lines.slice(0, FACT_ANSWER_CAP);
8726
+ const rest = lines.slice(FACT_ANSWER_CAP);
8727
+ const extra = rest.length ? `
8728
+ \u2026and ${rest.length} more \u2014 say 'more' to see them.` : "";
8729
+ const preamble = inKind.length ? "" : `nothing I remember ties these to "${whichCan[1]}", but:
8730
+ `;
8731
+ return { text: preamble + shown.join("\n") + extra, replace: true, ...rest.length ? { pending: { items: rest, noun: "facts" } } : {} };
8732
+ }
8733
+ }
8734
+ const canVerb = q.match(WHAT_CAN_VERB_RE);
8735
+ if (canVerb && canVerb[1].trim().split(/\s+/).at(-1)?.toLowerCase() !== "do") {
8736
+ const verbVariants = factTermVariants(normFactTerm2, canVerb[1]);
8737
+ const hits = (await factRows(memoryDir, cache)).filter((f) => f.predicate === "mgx:capableOf" && verbVariants.has(f.object));
8738
+ if (hits.length) {
8739
+ const ranked = rankByBiasThenTrust(uniqueFacts(hits), biasByBundle);
8740
+ const lines = ranked.map(renderFactLine);
8741
+ const shown = lines.slice(0, FACT_ANSWER_CAP);
8742
+ const rest = lines.slice(FACT_ANSWER_CAP);
8743
+ const extra = rest.length ? `
8744
+ \u2026and ${rest.length} more \u2014 say 'more' to see them.` : "";
8745
+ return { text: shown.join("\n") + extra, replace: true, ...rest.length ? { pending: { items: rest, noun: "facts" } } : {} };
8746
+ }
8747
+ }
8301
8748
  const hasQ = q.match(WHAT_HAS_RE);
8302
8749
  if (hasQ && !HAS_TEMPORAL_TAIL.has(hasQ[1].trim().split(/\s+/)[0]?.toLowerCase())) {
8303
8750
  const variants = factTermVariants(normFactTerm2, hasQ[1]);
@@ -8413,6 +8860,9 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
8413
8860
  return out;
8414
8861
  }
8415
8862
  async function factReadBack(memoryDir, query, envelope, miss2, graph = null, focusLabel = null, biasByBundle = {}, cache = null) {
8863
+ return withDeducedGoal(await factReadBackReaders(memoryDir, query, envelope, miss2, graph, focusLabel, biasByBundle, cache), envelope, query);
8864
+ }
8865
+ async function factReadBackReaders(memoryDir, query, envelope, miss2, graph = null, focusLabel = null, biasByBundle = {}, cache = null) {
8416
8866
  if (!miss2) return null;
8417
8867
  let normFactTerm2;
8418
8868
  try {
@@ -8505,7 +8955,7 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
8505
8955
  };
8506
8956
  }
8507
8957
  }
8508
- const whoAsk = qHedge.match(RELATION_WHO_ASK_RE);
8958
+ const whoAsk = qHedge.match(RELATION_WHO_ASK_RE) || matchGenitiveWhoAsk(qHedge);
8509
8959
  if (whoAsk) {
8510
8960
  const relationName = whoAsk[1].trim().toLowerCase();
8511
8961
  const rawObject = whoAsk[2].trim();
@@ -9050,7 +9500,7 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
9050
9500
  if (!hits.length) return null;
9051
9501
  return renderMany(hits);
9052
9502
  }
9053
- var CONTEXT_WORDS, isPronoun, GOAL_BY_KIND, COMMANDS, COMMAND_WORDS, CLASS_LABELS, AMBIGUOUS_HAVE_VERBS, RESTRICTOR_VERB_RE, GREET, THANKS, BYE, OK_ACK, collapseRuns, GREET_COLLAPSED, THANKS_COLLAPSED, BYE_COLLAPSED, ACK_LEAD_RE, CONVERSATIONAL_PHRASES, COMPARATIVE_SRC, COMPARATIVE_TEACH_RE, COMPARATIVE_ASK_RE, PREP_SRC, OWNED_BY_PREDICATE, HAS_PROPERTY_PREDICATE, SUBCLASS_PREDICATE2, HAS_A_PREDICATE, TAUGHT_SOURCE_TYPES, isOperatorTaught, ACTION_SIGNATURE_TEACH_RE, ACTION_PRECOND_NOTHING_RE, ACTION_PRECOND_COMPARATIVE_RE, ACTION_EFFECT_TEACH_RE, TEACH_ADVERB_SKIP_SRC, GENERAL_VERB_TEACH_RE, GENERAL_VERB_EXCLUDE_RE, GENERAL_VERB_ANYWHERE_EXCLUDE_RE, GENERAL_VERB_NOT_A_VERB_RE, GENERAL_VERB_YESNO_RE, GENERAL_VERB_OPEN_RE, GENERAL_VERB_QUERY_EXCLUDE_RE, GENERAL_VERB_PREP_RE, TRAILING_ADVERB_RE, MODULE_ORIENT_RE, MODULE_ORIENT_SVO_RE, AUTHOR_NAME_SRC, AUTHOR_WHO_IS_RE, AUTHOR_TOUCHED_RE, OPINION_ADJ_SRC, OPINION_NUDGE_RE, PERSONAL_ASSISTANT_NUDGE_RE, PREDICATE_WORDS, FACT_PREDICATE_PHRASES, factPhrase, TRAILING_PREDICATE_MARKERS, GENERIC_ENTITY_WORDS, SYNONYM_DENYLIST, RELATION_FACT_YESNO_RE, RELATION_WHO_ASK_RE, RECURSIVE_LIST_ASK_RE, ISA_ASK_RE, ISA_PREDICATES, WHY_ISA_LEAD_RE, EXPLAIN_HOW_YOU_KNOW_RE, ISA_IDIOM_ROLE_WORDS, CONFIRM_TAG_RE, KNOW_ABOUT_RE, FACT_ANSWER_CAP, CAN_ASK_RE, DOES_HAVE_ASK_RE, WHAT_CAN_DO_RE, WHAT_HAS_RE, WHAT_USED_FOR_RE, REVERSE_PREDICATE_EXCLUDE, REVERSE_PREDICATE_MARKERS, FORWARD_YESNO_EXCLUDE, FORWARD_YESNO_MARKERS, WHAT_INHERITS_RE, HAS_TEMPORAL_TAIL, TOLD_ABOUT_RE, KIND_OF_RE, CARD_AT_LEAST_ASK_RE, CARD_EXISTENCE_ASK_RE, CARDINALITY_ROW_PREDICATES, WHO_OWNS_RE, OWNS_YESNO_RE, OWNS_PASSIVE_YESNO_RE, HAS_METHOD_YESNO_RE, HAS_METHOD_OPEN_RE, IS_ADJECTIVE_YESNO_RE, IS_ADJECTIVE_PRONOUN_RE, IS_ADJECTIVE_YESNO_PRONOUN_SUBJECT_RE, unknownAdjectiveOffer, WHOLE_RECALL_RE, INHERITS_GROUP_RE, INHERITS_MAX_HOPS, PRONOUN_IN_QUERY_RE, BARE_WHATIS_RE, DESCRIBE_GRAIN_WORD_RE, GOAL_BY_COMMAND, BASE_QUALIFIER_SRC, USES_AS_BASE_WH_ASK_RE, USES_AS_BASE_WHAT_FRONT_RE, USES_AS_BASE_YESNO_RE, USES_AS_BASE_TEACH_RE, SEED_MARKER_REL;
9503
+ var CONTEXT_WORDS, isPronoun, GOAL_BY_KIND, goalNoun, TAUGHT_FACT_LOOKUP_GOAL, COMMANDS, COMMAND_WORDS, CLASS_LABELS, AMBIGUOUS_HAVE_VERBS, RESTRICTOR_VERB_RE, GREET, THANKS, BYE, OK_ACK, collapseRuns, GREET_COLLAPSED, THANKS_COLLAPSED, BYE_COLLAPSED, ACK_LEAD_RE, CONVERSATIONAL_PHRASES, COMPARATIVE_SRC, COMPARATIVE_TEACH_RE, COMPARATIVE_ASK_RE, PREP_SRC, OWNED_BY_PREDICATE, HAS_PROPERTY_PREDICATE, SUBCLASS_PREDICATE2, TAUGHT_SOURCE_TYPES, isOperatorTaught, ACTION_SIGNATURE_TEACH_RE, ACTION_SIGNATURE_PASSIVE_RE, ACTION_PRECOND_NOTHING_RE, ACTION_PRECOND_COMPARATIVE_RE, ACTION_EFFECT_TEACH_RE, ACTION_CONSTRAINT_TEACH_RE, GOAL_TEACH_RE, GOAL_TEACH_INFINITIVE_RE, TEACH_ADVERB_SKIP_SRC, GENERAL_VERB_TEACH_RE, GENERAL_VERB_EXCLUDE_RE, GENERAL_VERB_ANYWHERE_EXCLUDE_RE, GENERAL_VERB_NOT_A_VERB_RE, GENERAL_VERB_YESNO_RE, GENERAL_VERB_OPEN_RE, GENERAL_VERB_QUERY_EXCLUDE_RE, GENERAL_VERB_PREP_RE, TRAILING_ADVERB_RE, MODULE_ORIENT_RE, MODULE_ORIENT_SVO_RE, AUTHOR_NAME_SRC, AUTHOR_WHO_IS_RE, AUTHOR_TOUCHED_RE, OPINION_ADJ_SRC, OPINION_NUDGE_RE, PERSONAL_ASSISTANT_NUDGE_RE, PREDICATE_WORDS, FACT_PREDICATE_PHRASES, factPhrase, TRAILING_PREDICATE_MARKERS, GENERIC_ENTITY_WORDS, SYNONYM_DENYLIST, RELATION_FACT_YESNO_RE, RELATION_WHO_ASK_RE, GENITIVE_WHO_ASK_RE, RECURSIVE_LIST_ASK_RE, ISA_ASK_RE, ISA_PREDICATES, WHY_ISA_LEAD_RE, EXPLAIN_HOW_YOU_KNOW_RE, ISA_IDIOM_ROLE_WORDS, CONFIRM_TAG_RE, KNOW_ABOUT_RE, FACT_ANSWER_CAP, CAN_ASK_RE, DOES_HAVE_ASK_RE, WHAT_CAN_DO_RE, WHAT_HAS_RE, WHAT_USED_FOR_RE, WHERE_IS_FACT_RE, LOCATIVE_FACT_PREDICATE_RE, DO_VERB_ASK_RE, WHAT_CAN_VERB_RE, WHICH_KIND_CAN_RE, SUPERLATIVE_WORD_SRC, WHICH_KIND_SUPERLATIVE_RE, WHAT_IS_SUPERLATIVE_KIND_RE, REVERSE_PREDICATE_EXCLUDE, REVERSE_PREDICATE_MARKERS, FORWARD_YESNO_EXCLUDE, FORWARD_YESNO_MARKERS, WHAT_INHERITS_RE, HAS_TEMPORAL_TAIL, TOLD_ABOUT_RE, KIND_OF_RE, CARD_AT_LEAST_ASK_RE, CARD_EXISTENCE_ASK_RE, CARDINALITY_ROW_PREDICATES, WHO_OWNS_RE, OWNS_YESNO_RE, OWNS_PASSIVE_YESNO_RE, HAS_METHOD_YESNO_RE, HAS_METHOD_OPEN_RE, IS_ADJECTIVE_YESNO_RE, IS_ADJECTIVE_PRONOUN_RE, IS_ADJECTIVE_YESNO_PRONOUN_SUBJECT_RE, unknownAdjectiveOffer, WHOLE_RECALL_RE, INHERITS_GROUP_RE, INHERITS_MAX_HOPS, PRONOUN_IN_QUERY_RE, BARE_WHATIS_RE, DESCRIBE_GRAIN_WORD_RE, GOAL_BY_COMMAND, BASE_QUALIFIER_SRC, USES_AS_BASE_WH_ASK_RE, USES_AS_BASE_WHAT_FRONT_RE, USES_AS_BASE_YESNO_RE, USES_AS_BASE_TEACH_RE, SEED_MARKER_REL;
9054
9504
  var init_chat = __esm({
9055
9505
  "src/chat.mjs"() {
9056
9506
  init_node_path();
@@ -9063,6 +9513,7 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
9063
9513
  init_config();
9064
9514
  init_cli_args();
9065
9515
  init_codegraph();
9516
+ init_ask();
9066
9517
  init_sessions();
9067
9518
  init_uuid();
9068
9519
  init_telemetry();
@@ -9070,7 +9521,9 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
9070
9521
  init_templates();
9071
9522
  init_extensions();
9072
9523
  init_bias();
9524
+ init_core();
9073
9525
  init_finish();
9526
+ init_sentences();
9074
9527
  init_ask_vocab();
9075
9528
  init_normalize();
9076
9529
  init_fuzzy();
@@ -9091,6 +9544,8 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
9091
9544
  cochange: "understand change-coupling between modules",
9092
9545
  reexports: "understand a module's public exports/API surface"
9093
9546
  };
9547
+ goalNoun = (entityType) => entityType ? `${String(entityType).toLowerCase()}(s)` : "entities";
9548
+ TAUGHT_FACT_LOOKUP_GOAL = "look up a taught fact about a subject/verb/object";
9094
9549
  COMMANDS = {
9095
9550
  find: { tool: "tmct_search", arg: "query", help: "lexical search across the graph" },
9096
9551
  search: { tool: "tmct_search", arg: "query", help: "alias of /find" },
@@ -9263,11 +9718,14 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
9263
9718
  OWNED_BY_PREDICATE = "mgx:ownedBy";
9264
9719
  HAS_PROPERTY_PREDICATE = "mgx:hasProperty";
9265
9720
  SUBCLASS_PREDICATE2 = "rdfs:subClassOf";
9266
- HAS_A_PREDICATE = "mgx:hasA";
9267
9721
  TAUGHT_SOURCE_TYPES = /* @__PURE__ */ new Set(["operator", "teach", "entailed"]);
9268
9722
  isOperatorTaught = (f) => !!f.sourceTypes?.some((t) => TAUGHT_SOURCE_TYPES.has(t));
9269
9723
  ACTION_SIGNATURE_TEACH_RE = new RegExp(
9270
- `^you\\s+can\\s+([a-z]+)\\s+an?\\s+([a-z][\\w-]*)\\s+(${PREP_SRC})\\s+an?\\s+([a-z][\\w-]*)[.!?]*$`,
9724
+ `^you\\s+(?:can|may)\\s+([a-z]+)\\s+an?\\s+([a-z][\\w-]*)\\s+(${PREP_SRC})\\s+an?\\s+([a-z][\\w-]*)[.!?]*$`,
9725
+ "i"
9726
+ );
9727
+ ACTION_SIGNATURE_PASSIVE_RE = new RegExp(
9728
+ `^an?\\s+([a-z][\\w-]*)\\s+(?:can|may)\\s+be\\s+([a-z]+)\\s+(${PREP_SRC})\\s+an?\\s+([a-z][\\w-]*)[.!?]*$`,
9271
9729
  "i"
9272
9730
  );
9273
9731
  ACTION_PRECOND_NOTHING_RE = new RegExp(
@@ -9279,7 +9737,19 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
9279
9737
  "i"
9280
9738
  );
9281
9739
  ACTION_EFFECT_TEACH_RE = new RegExp(
9282
- `^([a-z]+ing)\\s+an?\\s+([a-z][\\w-]*)\\s+(${PREP_SRC})\\s+an?\\s+([a-z][\\w-]*)\\s+makes\\s+the\\s+([a-z][\\w-]*)\\s+([a-z]+)\\s+(${PREP_SRC})\\s+the\\s+([a-z][\\w-]*)[.!?]*$`,
9740
+ `^([a-z]+ing)\\s+an?\\s+([a-z][\\w-]*)\\s+(${PREP_SRC})\\s+an?\\s+([a-z][\\w-]*)\\s+makes\\s+(?:it|the\\s+([a-z][\\w-]*))\\s+([a-z]+)\\s+(${PREP_SRC})\\s+the\\s+([a-z][\\w-]*)[.!?]*$`,
9741
+ "i"
9742
+ );
9743
+ ACTION_CONSTRAINT_TEACH_RE = new RegExp(
9744
+ `^to\\s+([a-z]+)\\s+an?\\s+([a-z][\\w-]*)\\s+(${PREP_SRC})\\s+an?\\s+([a-z][\\w-]*)\\s*,?\\s*the\\s+([a-z][\\w-]*)\\s+may\\s+not\\s+be\\s+with\\s+the\\s+([a-z][\\w-]*)\\s+without\\s+the\\s+([a-z][\\w-]*)[.!?]*$`,
9745
+ "i"
9746
+ );
9747
+ GOAL_TEACH_RE = new RegExp(
9748
+ `^the\\s+goal\\s+is\\s+that\\s+(?:(every|each|all)\\s+)?([\\w-]+)\\s+([a-z]+s)\\s+(${PREP_SRC})\\s+([\\w-]+)[.!?]*$`,
9749
+ "i"
9750
+ );
9751
+ GOAL_TEACH_INFINITIVE_RE = new RegExp(
9752
+ `^(?:the\\s+goal\\s+is\\s+for|i\\s+want)\\s+(?:(every|each|all)\\s+)?([\\w-]+)\\s+to\\s+([a-z]+)\\s+(${PREP_SRC})\\s+([\\w-]+)[.!?]*$`,
9283
9753
  "i"
9284
9754
  );
9285
9755
  TEACH_ADVERB_SKIP_SRC = "(?:(?:usually|often|sometimes|rarely|never|always|typically|generally|occasionally|frequently|normally|regularly|commonly|mostly|currently|still|also|really|actually)\\s+)?";
@@ -9407,6 +9877,7 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
9407
9877
  ].map(([a, b]) => [a, b].sort().join("|")));
9408
9878
  RELATION_FACT_YESNO_RE = /^(?:is|are|was|were)\s+([\w'-]+(?:\s+[A-Z][\w'-]*)?)\s+(?:the|an?)\s+([a-z][\w-]*)\s+of\s+([\w'-]+(?:\s+[A-Z][\w'-]*)?)[?.!\s]*$/i;
9409
9879
  RELATION_WHO_ASK_RE = /^(?:who|what)\s+(?:is|are)\s+(?:the|an?)\s+([a-z][\w-]*)\s+of\s+([\w'-]+(?:\s+[A-Z][\w'-]*)?)[?.!\s]*$/i;
9880
+ GENITIVE_WHO_ASK_RE = /^(?:who|what)\s+(?:is|are|was|were)\s+([\w-]+(?:\s+[A-Z][\w-]*)?)'s\s+([a-z][\w-]*)[?.!\s]*$/i;
9410
9881
  RECURSIVE_LIST_ASK_RE = /^list\s+(?:the\s+|all\s+)?([a-z][\w-]*)\s+of\s+([\w'-]+(?:\s+[A-Z][\w'-]*)?)[?.!\s]*$/i;
9411
9882
  ISA_ASK_RE = /^(?:is|are)\s+(?:an?\s+)?(.+?)\s+(?:a\s+kind\s+of|a\s+type\s+of|an?)\s+(.+?)[?.!\s]*$/i;
9412
9883
  ISA_PREDICATES = /* @__PURE__ */ new Set(["rdfs:subClassOf", "rdf:type"]);
@@ -9421,6 +9892,14 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
9421
9892
  WHAT_CAN_DO_RE = /^what\s+can\s+(?:an?\s+)?(.+?)\s+do[?.!\s]*$/i;
9422
9893
  WHAT_HAS_RE = /^what\s+has\s+(?:an?\s+)?(.+?)[?.!\s]*$/i;
9423
9894
  WHAT_USED_FOR_RE = /^what\s+(?:(?:can\s+be|is)\s+used\s+for|is\s+for)\s+(.+?)[?.!\s]*$/i;
9895
+ WHERE_IS_FACT_RE = /^where(?:'s|\s+is|\s+are)\s+(.+?)(?:\s+now)?\s*[?.!]*$/i;
9896
+ LOCATIVE_FACT_PREDICATE_RE = /^mgx:[a-z]+-(?:on|in|at|inside|under|below|above|near|beside|behind|by)$/;
9897
+ DO_VERB_ASK_RE = /^(?:do|does)\s+(all\s+|every\s+)?(?:an?\s+|the\s+)?([\w'-]+(?:\s+[\w'-]+)*?)\s+([a-z-]+)[?.!\s]*$/i;
9898
+ WHAT_CAN_VERB_RE = /^what\s+can\s+(?!be\s)(.+?)[?.!\s]*$/i;
9899
+ WHICH_KIND_CAN_RE = /^(?:which|what)\s+([\w'-]+(?:\s+[\w'-]+)*?)\s+can\s+(.+?)[?.!\s]*$/i;
9900
+ SUPERLATIVE_WORD_SRC = "(?:most|least)\\s+[a-z][\\w-]*|[a-z][\\w-]*est|best|worst";
9901
+ WHICH_KIND_SUPERLATIVE_RE = new RegExp(`^which\\s+([\\w'-]+)\\s+(?:is|are)\\s+(?:the\\s+)?(${SUPERLATIVE_WORD_SRC})[?.!\\s]*$`, "i");
9902
+ WHAT_IS_SUPERLATIVE_KIND_RE = new RegExp(`^what(?:'s|s|\\s+is)\\s+the\\s+(${SUPERLATIVE_WORD_SRC})\\s+([\\w'-]+)[?.!\\s]*$`, "i");
9424
9903
  REVERSE_PREDICATE_EXCLUDE = /* @__PURE__ */ new Set([
9425
9904
  "rdfs:subClassOf",
9426
9905
  "rdf:type",