@polycode-projects/the-mechanical-code-talker 2.10.3 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +68 -12
  2. package/bin/tmct.mjs +5 -2
  3. package/corpus/sprites/src/sprite-facts.jsonl +18 -0
  4. package/corpus/worlds/manifest.json +5 -5
  5. package/corpus/worlds/shards/ashcombe-hall.jsonl.gz +0 -0
  6. package/corpus/worlds/src/ashcombe-hall.jsonl +27 -0
  7. package/data/sprites/book-icon.toml +12 -0
  8. package/data/sprites/cellar-icon.toml +12 -0
  9. package/data/sprites/drawing-room-icon.toml +13 -0
  10. package/data/sprites/garden-icon.toml +12 -0
  11. package/data/sprites/kitchen-icon.toml +13 -0
  12. package/data/sprites/library-icon.toml +12 -0
  13. package/data/sprites/pan-icon.toml +11 -0
  14. package/data/sprites/study-icon.toml +12 -0
  15. package/package.json +7 -2
  16. package/src/adapters/corpus/wikipedia-live.mjs +182 -26
  17. package/src/adapters/corpus/worlds-pack.mjs +8 -2
  18. package/src/adapters/memory/core.mjs +8 -1
  19. package/src/adapters/toml-config.mjs +6 -0
  20. package/src/domain/cli-verbs.mjs +2 -0
  21. package/src/domain/memory/trust.mjs +32 -2
  22. package/src/domain/sense-split.mjs +203 -0
  23. package/src/domain/worlds-pack.mjs +50 -0
  24. package/src/services/adventure-autoplay.mjs +5 -2
  25. package/src/services/adventure-viz.mjs +301 -33
  26. package/src/services/adventure.mjs +162 -14
  27. package/src/services/chat-page-viz.mjs +341 -197
  28. package/src/services/chat-session.mjs +24 -9
  29. package/src/services/chat.mjs +580 -47
  30. package/src/services/code-explorer-viz.mjs +198 -76
  31. package/src/services/extract-facts.mjs +384 -82
  32. package/src/services/fold.mjs +1 -1
  33. package/src/services/ingest-viz.mjs +637 -0
  34. package/src/services/ledger-viz.mjs +209 -0
  35. package/src/services/memory-panel-viz.mjs +159 -0
  36. package/src/services/research.mjs +266 -0
  37. package/src/services/sentences.mjs +19 -0
  38. package/src/services/session-log-format.mjs +64 -0
  39. package/src/services/sessions.mjs +56 -22
  40. package/src/services/spider-fly-turn.mjs +54 -1
  41. package/src/services/spider-fly-viz.mjs +41 -23
  42. package/src/surfaces/web/adventure-browser-entry.mjs +9 -5
  43. package/src/surfaces/web/chat-browser-entry.mjs +32 -11
  44. package/src/surfaces/web/code-explorer-browser-entry.mjs +27 -11
  45. package/src/surfaces/web/ingest-browser-entry.mjs +208 -0
  46. package/src/surfaces/web/ledger-browser-entry.mjs +24 -5
  47. package/src/surfaces/web/memory-ask-browser.bundle.js +134 -125
  48. package/src/surfaces/web/memory-stats.mjs +53 -0
  49. package/src/tools/definitions.mjs +14 -0
  50. package/src/tools/handlers/index.mjs +2 -0
  51. package/src/tools/handlers/tmct-ingest.mjs +43 -0
  52. package/src/tools/server.mjs +5 -2
@@ -0,0 +1,53 @@
1
+ // memory-stats.mjs — the "what does this session's memory hold" reader,
2
+ // shared by chat-browser-entry.mjs and ingest-browser-entry.mjs so both
3
+ // bundles' stats panels read the exact same breakdown for the exact same
4
+ // store shape.
5
+ import { loadMemory, readFactRows } from "../../adapters/memory/core.mjs";
6
+ import { provenanceTagToSource } from "../../domain/memory/trust.mjs";
7
+
8
+ /**
9
+ * The memory a running session holds, broken down by where each fact came
10
+ * from: the seed corpus bands it booted with (keyed by the SAME band name
11
+ * build-chat-seed.mjs/extensions.mjs seed under — "human", "seon",
12
+ * "conceptnet" today, whichever bands a future seed adds tomorrow) plus
13
+ * whatever has been taught THIS session. Reuses memory/trust.mjs's own
14
+ * `provenanceTagToSource` against each fact's already-stored provenance
15
+ * tag(s) (readFactRows' `provenance`, the ' | '-joined compat string) rather
16
+ * than inventing a second provenance parse — the same tag chat.mjs's own
17
+ * "(source: ...)" citation already carries, so this panel and a turn's
18
+ * citation always agree on where a fact came from.
19
+ *
20
+ * Returns { total, bandCounts, taught }: `bandCounts` maps a band label to
21
+ * its fact count ("taught this session" for a teach/operator-sourced fact
22
+ * with no corpus band, "other" for anything provenance can't place);
23
+ * `taught` lists every session-taught fact (subject/predicate/object + its
24
+ * own provenance tag), most-recently-taught last — a stats panel's
25
+ * provenance column reads straight off this, no further lookup needed.
26
+ */
27
+ export async function memoryStats(memoryDir) {
28
+ const memory = await loadMemory(memoryDir);
29
+ const rows = readFactRows(memory);
30
+ const bandCounts = {};
31
+ const taught = [];
32
+ for (const row of rows) {
33
+ const tags = String(row.provenance || "").split(" | ").filter(Boolean);
34
+ let band = null;
35
+ let isTaught = false;
36
+ let taughtTag = "";
37
+ for (const tag of tags) {
38
+ const src = provenanceTagToSource(tag);
39
+ if (!src) continue;
40
+ // corpusWeak (a /r/RelatedTo-strength triple, e.g. ConceptNet's or
41
+ // SEON's own weaker associations) names the SAME band as corpus (a
42
+ // /r/IsA-strength one) — both carry `src.name`, and a band count that
43
+ // dropped the weak tier would undercount a corpus by exactly its
44
+ // weak-relation facts (SEON: 19 of them, all `corpus-weak:seon`).
45
+ if ((src.kind === "corpus" || src.kind === "corpusWeak") && src.name) band = src.name;
46
+ if (src.kind === "teach" || src.kind === "operator") { isTaught = true; taughtTag = tag; }
47
+ }
48
+ const label = band || (isTaught ? "taught this session" : "other");
49
+ bandCounts[label] = (bandCounts[label] || 0) + 1;
50
+ if (isTaught) taught.push({ subject: row.subject, predicate: row.predicate, object: row.object, tag: taughtTag });
51
+ }
52
+ return { total: rows.length, bandCounts, taught };
53
+ }
@@ -297,6 +297,20 @@ export const TOOL_DEFINITIONS = Object.freeze([
297
297
  inputSchema: { type: "object", required: [], properties: {} },
298
298
  example: {},
299
299
  },
300
+ {
301
+ name: "tmct_ingest",
302
+ tier: "cold",
303
+ summary: "Ground plain text into memory facts with the same deterministic recognizer the chat teach lane uses — the strict tier, plus an optional lower-trust fuzzy tier — and report the canonical triples.",
304
+ inputSchema: {
305
+ type: "object",
306
+ required: ["text"],
307
+ properties: {
308
+ text: { type: "string", description: "The plain text to ground into facts." },
309
+ optimistic: { type: "boolean", description: "Also run the lower-trust fuzzy tier over sentences the strict recognizer skips." },
310
+ },
311
+ },
312
+ example: { text: "A kettle is a container. An otter is a mammal.", optimistic: true },
313
+ },
300
314
  ]);
301
315
 
302
316
  export const HOT_TOOLS = TOOL_DEFINITIONS.filter((t) => t.tier === "hot");
@@ -29,6 +29,7 @@ import { tmct_callees } from "./tmct-callees.mjs";
29
29
  import { tmct_calls } from "./tmct-calls.mjs";
30
30
  import { tmct_cochanges } from "./tmct-cochanges.mjs";
31
31
  import { tmct_export } from "./tmct-export.mjs";
32
+ import { tmct_ingest } from "./tmct-ingest.mjs";
32
33
 
33
34
  export const HANDLERS = Object.freeze({
34
35
  tmct_context,
@@ -55,4 +56,5 @@ export const HANDLERS = Object.freeze({
55
56
  tmct_calls,
56
57
  tmct_cochanges,
57
58
  tmct_export,
59
+ tmct_ingest,
58
60
  });
@@ -0,0 +1,43 @@
1
+ // tmct_ingest — ground plain text into the conversational-memory store with the
2
+ // same deterministic recognizer the chat teach lane uses. The strict tier keeps
3
+ // only sentences the recognizer turns into stored facts; with optimistic:true the
4
+ // lower-trust fuzzy tier also reaches the sentences it skips. Answers with the
5
+ // canonical triples it grounded and an honest recognized/skipped summary — no
6
+ // LLM, no guessing.
7
+ //
8
+ // The recognizer (ingestText, src/services/extract-facts.mjs) lives in the SERVICE
9
+ // layer, above the tool layer, so this handler never imports it: a service-layer
10
+ // caller injects it as `ingest` through dispatchTool's ctx (bin/tmct.mjs's `cli`
11
+ // route and the chat surface both hold it). Called without that seam wired, the
12
+ // tool says so honestly rather than guessing.
13
+ //
14
+ // Reads the conversational-memory store (not the code-map graph), so it owns its
15
+ // own load and writes with or without a code graph present.
16
+
17
+ import { dirname } from "node:path";
18
+ import { openConfiguredMemoryBackend } from "../../adapters/memory/core.mjs";
19
+
20
+ export async function tmct_ingest(args, { config, ingest }) {
21
+ const text = String(args?.text ?? "");
22
+ if (!text.trim()) return "(nothing to ingest — pass some text)";
23
+ if (typeof ingest !== "function") {
24
+ return "(ingest isn't wired into this context — invoke tmct_ingest through the CLI `cli` route or the chat surface)";
25
+ }
26
+ const optimistic = args?.optimistic === true;
27
+ const { dir, close } = await openConfiguredMemoryBackend(dirname(dirname(config.graphFile)));
28
+ try {
29
+ const result = await ingest(text, { memoryDir: dir, config, sourceTag: "tool", optimistic, canonical: true });
30
+ const grounded = result.extracted.length + result.optimistic.length;
31
+ const header = `${result.sentences} sentence(s), ${result.recognized} recognized`
32
+ + (optimistic ? `, ${result.optimistic.length} optimistic candidate(s)` : "")
33
+ + `, ${result.skipped} skipped — ${grounded} fact(s) grounded.`;
34
+ return grounded ? `${header}\n${result.canonical.join("\n")}` : header;
35
+ } finally {
36
+ await close();
37
+ }
38
+ }
39
+
40
+ // The memory store is this tool's only source — it writes with or without a
41
+ // code-map graph, so the shared code-graph load (which refuses an empty graph)
42
+ // must not gate it.
43
+ tmct_ingest.ownsGraphLoad = true;
@@ -44,13 +44,16 @@ export const TOOLS = HOT_TOOLS.map(({ name, agentDescription, inputSchema }) =>
44
44
  inputSchema,
45
45
  }));
46
46
 
47
- export async function dispatchTool(name, args, { config, source = defaultSource, tel = null } = {}) {
47
+ export async function dispatchTool(name, args, { config, source = defaultSource, tel = null, ingest = null } = {}) {
48
48
  // Reject an unknown tool BEFORE touching the graph — an unknown name never
49
49
  // triggers a load. hasOwn, so an inherited name ("constructor", "toString")
50
50
  // is unknown rather than a callable found on the prototype chain.
51
51
  if (!Object.hasOwn(HANDLERS, name)) throw new ToolError(`unknown tool: ${name}`);
52
52
  const handle = HANDLERS[name];
53
- if (handle.ownsGraphLoad) return handle(args, { config, source, tel });
53
+ // `ingest` is the recognizer seam a caller in the service layer injects (the
54
+ // tool layer sits UNDER services and must not import one, so a tool that needs
55
+ // the chat recognizer receives it here rather than importing it).
56
+ if (handle.ownsGraphLoad) return handle(args, { config, source, tel, ingest });
54
57
  const graph = await loadGraph(config, source);
55
58
  // repo root = the dir containing .tmct/ (graphFile = <repo>/.tmct/graph.json). Passed to
56
59
  // createGraphService so svc.snippet()/svc.context() are usable directly, and on to the