@polycode-projects/the-mechanical-code-talker 0.2.0 → 0.4.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 (53) hide show
  1. package/README.md +77 -3
  2. package/ROADMAP.md +416 -3
  3. package/bin/tmct.mjs +308 -12
  4. package/corpus/README.md +52 -0
  5. package/corpus/conceptnet/LICENSE-NOTICE +37 -0
  6. package/corpus/conceptnet/README.md +103 -0
  7. package/corpus/conceptnet/fetch-slice.mjs +136 -0
  8. package/corpus/conceptnet/filter-dump.mjs +89 -0
  9. package/corpus/conceptnet/slice.jsonl +14258 -0
  10. package/data/phrasebook/software-phrases.txt +231 -0
  11. package/data/templates/grammar-rules.toml +89 -0
  12. package/data/templates/responses.jsonl +68 -0
  13. package/package.json +40 -3
  14. package/src/ask-nlp.mjs +22 -10
  15. package/src/ask-vocab.mjs +35 -1
  16. package/src/ask.mjs +171 -494
  17. package/src/chat.mjs +709 -81
  18. package/src/corpus/conceptnet-map.toml +251 -0
  19. package/src/corpus/conceptnet.mjs +167 -0
  20. package/src/corpus/templates.mjs +188 -0
  21. package/src/finish.mjs +443 -0
  22. package/src/grammar/ace.mjs +341 -0
  23. package/src/grammar/assert.mjs +40 -0
  24. package/src/grammar/lexicon-core.json +287 -0
  25. package/src/grammar/lexicon.mjs +202 -0
  26. package/src/hash.mjs +32 -0
  27. package/src/index.mjs +21 -5
  28. package/src/init.mjs +264 -0
  29. package/src/interpret/fuzzy.mjs +89 -0
  30. package/src/interpret/merge.mjs +148 -0
  31. package/src/interpret/normalize.mjs +151 -0
  32. package/src/interpret/pipeline.mjs +112 -0
  33. package/src/interpret/strategies/grammar.mjs +137 -0
  34. package/src/interpret/strategies/keywords.mjs +241 -0
  35. package/src/interpret/strategies/noise-strip.mjs +114 -0
  36. package/src/memory/blocks.mjs +221 -0
  37. package/src/memory/core.mjs +533 -0
  38. package/src/memory/fold.mjs +0 -0
  39. package/src/memory/inspect.mjs +141 -0
  40. package/src/memory/trust.mjs +113 -0
  41. package/src/prose-nlp.mjs +14 -16
  42. package/src/providers/bootstrap.mjs +24 -0
  43. package/src/providers/fixture.mjs +118 -0
  44. package/src/providers/graph-service.mjs +312 -0
  45. package/src/repository-interface.mjs +318 -0
  46. package/src/server.mjs +44 -28
  47. package/src/sessions.mjs +137 -4
  48. package/src/source.mjs +44 -5
  49. package/src/syllogise.mjs +0 -0
  50. package/src/toml-config.mjs +14 -0
  51. package/src/tui/app.mjs +173 -0
  52. package/src/wink-model.mjs +74 -0
  53. package/bin/cli.mjs +0 -226
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env node
2
+ // fetch-slice.mjs — regenerate corpus/conceptnet/slice.jsonl from the public
3
+ // ConceptNet API (https://api.conceptnet.io). NOT part of the product path —
4
+ // a maintainer tool, run by hand, results committed.
5
+ //
6
+ // node corpus/conceptnet/fetch-slice.mjs [outFile]
7
+ //
8
+ // Polite client: strictly sequential, ~1.1s between requests (the public API
9
+ // asks for roughly 1 req/s sustained), exponential backoff on 429/5xx. A full
10
+ // run over the ~80 seed terms takes a few minutes.
11
+ //
12
+ // Filter rules (also documented in README.md here):
13
+ // - /query?node=/c/en/<term>&other=/c/en — both endpoints English;
14
+ // - keep only edges whose rel is one of the 34 canonical relations
15
+ // (src/corpus/conceptnet-map.toml is the same closed set);
16
+ // - drop en→en edges whose start/end still carry a sense suffix mismatch
17
+ // (we keep the bare /c/en/<term> and /c/en/<term>/<pos> forms, normalized
18
+ // to the bare term URI);
19
+ // - dedupe by (start, rel, end), keeping the higher weight;
20
+ // - one JSON object per line: {start, rel, end, surfaceText?, weight},
21
+ // sorted by rel then start then end (deterministic diffs).
22
+ //
23
+ // Output is CC-BY-SA 4.0 (ConceptNet-derived) — see LICENSE-NOTICE.
24
+
25
+ import { writeFile } from "node:fs/promises";
26
+ import { fileURLToPath } from "node:url";
27
+ import { dirname, join } from "node:path";
28
+
29
+ export const SEED_TERMS = [
30
+ // core artifacts
31
+ "software", "computer", "program", "code", "source_code", "module",
32
+ "function", "subroutine", "algorithm", "data_structure", "database",
33
+ "server", "network", "internet", "file", "directory", "memory",
34
+ "application", "library", "framework", "api", "operating_system",
35
+ "compiler", "interpreter", "script", "programming_language",
36
+ // code constructs
37
+ "variable", "array", "string", "integer", "boolean", "loop", "class",
38
+ "object", "method", "parameter", "pointer", "stack", "queue", "cache",
39
+ "thread", "process", "byte", "bit", "binary", "syntax", "logic",
40
+ // the work
41
+ "bug", "error", "test", "debug", "crash", "software_bug", "programmer",
42
+ "computation", "data", "information", "password", "encryption",
43
+ // version control
44
+ "repository", "commit", "branch", "merge", "version",
45
+ // hardware & devices
46
+ "keyboard", "mouse", "screen", "monitor", "hardware", "cpu", "processor",
47
+ "disk", "laptop", "smartphone", "robot", "circuit", "chip",
48
+ // the wider net
49
+ "email", "website", "browser", "cloud", "protocol", "http", "terminal",
50
+ "shell", "command", "linux", "unix", "artificial_intelligence",
51
+ "machine_learning", "virtual_machine",
52
+ ];
53
+
54
+ export const CANONICAL_RELS = new Set([
55
+ "/r/RelatedTo", "/r/FormOf", "/r/IsA", "/r/PartOf", "/r/HasA", "/r/UsedFor",
56
+ "/r/CapableOf", "/r/AtLocation", "/r/Causes", "/r/HasSubevent",
57
+ "/r/HasFirstSubevent", "/r/HasLastSubevent", "/r/HasPrerequisite",
58
+ "/r/HasProperty", "/r/MotivatedByGoal", "/r/ObstructedBy", "/r/Desires",
59
+ "/r/CreatedBy", "/r/Synonym", "/r/Antonym", "/r/DistinctFrom",
60
+ "/r/DerivedFrom", "/r/SymbolOf", "/r/DefinedAs", "/r/MannerOf",
61
+ "/r/LocatedNear", "/r/HasContext", "/r/SimilarTo",
62
+ "/r/EtymologicallyRelatedTo", "/r/EtymologicallyDerivedFrom",
63
+ "/r/CausesDesire", "/r/MadeOf", "/r/ReceivesAction", "/r/ExternalURL",
64
+ ]);
65
+
66
+ // Relations excluded from the committed slice by policy (see README.md):
67
+ // pure-lexical/etymology noise and link-outs — they'd spend the size budget
68
+ // on rows toFacts() can never emit.
69
+ export const FILTERED_RELS = new Set([
70
+ "/r/EtymologicallyRelatedTo", "/r/EtymologicallyDerivedFrom", "/r/ExternalURL",
71
+ ]);
72
+
73
+ const API = "https://api.conceptnet.io";
74
+ const LIMIT = 100; // edges fetched per seed term
75
+ const PAUSE_MS = 1100; // polite: ~1 req/s sustained
76
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
77
+
78
+ /** Normalize a concept URI to its bare English term: /c/en/dog/n → /c/en/dog.
79
+ * Returns null for anything that is not an English concept. */
80
+ export const bareEnTerm = (uri) => {
81
+ const m = /^\/c\/en\/([^/]+)/.exec(String(uri || ""));
82
+ return m ? `/c/en/${m[1]}` : null;
83
+ };
84
+
85
+ /** Filter one raw API edge → a slice row, or null when it fails the rules. */
86
+ export function toRow(edge) {
87
+ const rel = edge?.rel?.["@id"];
88
+ if (!rel || !CANONICAL_RELS.has(rel) || FILTERED_RELS.has(rel)) return null;
89
+ const start = bareEnTerm(edge?.start?.["@id"]);
90
+ const end = bareEnTerm(edge?.end?.["@id"]);
91
+ if (!start || !end || start === end) return null;
92
+ const row = { start, rel, end, weight: Number(edge?.weight) || 1 };
93
+ if (edge?.surfaceText) row.surfaceText = String(edge.surfaceText);
94
+ return row;
95
+ }
96
+
97
+ async function fetchTerm(term, { fetchImpl = fetch, log = console.error } = {}) {
98
+ const url = `${API}/query?node=/c/en/${term}&other=/c/en&limit=${LIMIT}`;
99
+ for (let attempt = 1, wait = 5000; attempt <= 5; attempt += 1, wait *= 2) {
100
+ const res = await fetchImpl(url, { headers: { accept: "application/json" } });
101
+ if (res.ok) return (await res.json()).edges || [];
102
+ log(` ${term}: HTTP ${res.status}${attempt < 5 ? `, backing off ${wait / 1000}s` : " — giving up"}`);
103
+ if (res.status !== 429 && res.status < 500) return [];
104
+ if (attempt < 5) await sleep(wait);
105
+ }
106
+ throw new Error(`ConceptNet API unreachable while fetching "${term}"`);
107
+ }
108
+
109
+ export async function fetchSlice({ terms = SEED_TERMS, fetchImpl = fetch, log = console.error, pauseMs = PAUSE_MS } = {}) {
110
+ const byKey = new Map(); // "start rel end" -> row (higher weight wins)
111
+ for (const term of terms) {
112
+ const edges = await fetchTerm(term, { fetchImpl, log });
113
+ let kept = 0;
114
+ for (const edge of edges) {
115
+ const row = toRow(edge);
116
+ if (!row) continue;
117
+ const key = `${row.start} ${row.rel} ${row.end}`;
118
+ const prev = byKey.get(key);
119
+ if (!prev || row.weight > prev.weight) byKey.set(key, row);
120
+ kept += 1;
121
+ }
122
+ log(` ${term}: ${edges.length} edges, ${kept} kept`);
123
+ await sleep(pauseMs);
124
+ }
125
+ return [...byKey.values()].sort((a, b) =>
126
+ a.rel.localeCompare(b.rel) || a.start.localeCompare(b.start) || a.end.localeCompare(b.end));
127
+ }
128
+
129
+ const isMain = process.argv[1] && import.meta.url === new URL(`file://${process.argv[1]}`).href;
130
+ if (isMain) {
131
+ const out = process.argv[2] || join(dirname(fileURLToPath(import.meta.url)), "slice.jsonl");
132
+ const rows = await fetchSlice({});
133
+ const text = rows.map((r) => JSON.stringify(r)).join("\n") + "\n";
134
+ await writeFile(out, text);
135
+ console.error(`wrote ${rows.length} assertions (${text.length} bytes) to ${out}`);
136
+ }
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+ // filter-dump.mjs — regenerate corpus/conceptnet/slice.jsonl from a ConceptNet
3
+ // ASSERTIONS DUMP instead of the API (the route actually used for the
4
+ // committed slice: api.conceptnet.io was hard-down, 502, on 2026-07-04).
5
+ // NOT part of the product path — a maintainer tool.
6
+ //
7
+ // curl -s https://s3.amazonaws.com/conceptnet/downloads/2019/edges/conceptnet-assertions-5.7.0.csv.gz \
8
+ // | gunzip -c \
9
+ // | node corpus/conceptnet/filter-dump.mjs > corpus/conceptnet/slice.jsonl
10
+ //
11
+ // Input: the tab-separated 5.7.0 dump on stdin —
12
+ // assertionURI \t rel \t start \t end \t {json: weight, surfaceText, …}
13
+ //
14
+ // Filter rules (shared with fetch-slice.mjs; also in README.md):
15
+ // - start AND end are English concepts (/c/en/…), sense tags stripped
16
+ // (/c/en/dog/n → /c/en/dog);
17
+ // - rel is one of the 34 canonical relations, minus the policy-filtered
18
+ // etymology/ExternalURL ones;
19
+ // - at least ONE endpoint's bare term is in the ~90-term tech seed list
20
+ // (fetch-slice.mjs SEED_TERMS);
21
+ // - dedupe by (start, rel, end), keeping the higher weight;
22
+ // - budget (~1.4 MB of JSONL), TWO-TIER: assertions whose relation MAPS to
23
+ // an ACE-OWL pattern (conceptnet-map.toml, ace != "none") are kept first
24
+ // (weight-descending); ace="none" relations (RelatedTo, Synonym, …) fill
25
+ // whatever budget remains — they never crowd out seedable facts;
26
+ // - final order (rel, start, end) for deterministic diffs.
27
+ // Stats land on stderr; the JSONL lands on stdout.
28
+
29
+ import { createInterface } from "node:readline";
30
+ import { SEED_TERMS, CANONICAL_RELS, FILTERED_RELS, bareEnTerm } from "./fetch-slice.mjs";
31
+ import { loadMap } from "../../src/corpus/conceptnet.mjs";
32
+
33
+ const MAX_BYTES = 1_400_000; // committed-slice budget (hard cap 1.5 MB)
34
+ const SEEDS = new Set(SEED_TERMS);
35
+ const termOf = (uri) => uri.slice("/c/en/".length);
36
+
37
+ const byKey = new Map(); // "start rel end" -> row
38
+ let scanned = 0;
39
+ const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
40
+ for await (const line of rl) {
41
+ scanned += 1;
42
+ const cols = line.split("\t");
43
+ if (cols.length < 5) continue;
44
+ const rel = cols[1];
45
+ if (!CANONICAL_RELS.has(rel) || FILTERED_RELS.has(rel)) continue;
46
+ const start = bareEnTerm(cols[2]);
47
+ const end = bareEnTerm(cols[3]);
48
+ if (!start || !end || start === end) continue;
49
+ if (!SEEDS.has(termOf(start)) && !SEEDS.has(termOf(end))) continue;
50
+ let info = {};
51
+ try {
52
+ info = JSON.parse(cols[4]);
53
+ } catch {
54
+ /* a malformed info column loses us weight/surfaceText, not the edge */
55
+ }
56
+ const row = { start, rel, end, weight: Number(info.weight) || 1 };
57
+ if (info.surfaceText) row.surfaceText = String(info.surfaceText);
58
+ const key = `${start} ${rel} ${end}`;
59
+ const prev = byKey.get(key);
60
+ if (!prev || row.weight > prev.weight) byKey.set(key, row);
61
+ }
62
+
63
+ // budget-trim: mappable relations first, then none-rows — each tier
64
+ // weight-descending, so the strongest seedable facts always survive
65
+ const map = await loadMap();
66
+ const byWeight = (a, b) =>
67
+ b.weight - a.weight || a.rel.localeCompare(b.rel) || a.start.localeCompare(b.start) || a.end.localeCompare(b.end);
68
+ const all = [...byKey.values()];
69
+ const mappable = all.filter((r) => map.get(r.rel)?.ace !== "none").sort(byWeight);
70
+ const unmappable = all.filter((r) => map.get(r.rel)?.ace === "none").sort(byWeight);
71
+ const kept = [];
72
+ let bytes = 0;
73
+ for (const row of [...mappable, ...unmappable]) {
74
+ const line = JSON.stringify(row) + "\n";
75
+ if (bytes + line.length > MAX_BYTES) continue;
76
+ bytes += line.length;
77
+ kept.push(row);
78
+ }
79
+ kept.sort((a, b) => a.rel.localeCompare(b.rel) || a.start.localeCompare(b.start) || a.end.localeCompare(b.end));
80
+
81
+ for (const row of kept) process.stdout.write(JSON.stringify(row) + "\n");
82
+
83
+ const perRel = new Map();
84
+ for (const r of kept) perRel.set(r.rel, (perRel.get(r.rel) || 0) + 1);
85
+ const keptMappable = kept.filter((r) => map.get(r.rel)?.ace !== "none").length;
86
+ console.error(`scanned ${scanned} dump lines; matched ${all.length} unique en→en seed assertions `
87
+ + `(${mappable.length} mappable + ${unmappable.length} ace=none); `
88
+ + `kept ${kept.length} (${keptMappable} mappable + ${kept.length - keptMappable} none) in ${bytes} bytes`);
89
+ for (const [rel, n] of [...perRel.entries()].sort((a, b) => b[1] - a[1])) console.error(` ${rel}: ${n}`);