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

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.
@@ -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