@polycode-projects/the-mechanical-code-talker 1.9.2 → 1.10.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.
- package/README.md +441 -202
- package/bin/tmct.mjs +126 -1
- package/package.json +4 -2
- package/src/answer-variants.mjs +8 -36
- package/src/ask-browser-entry.mjs +5 -23
- package/src/ask-browser.bundle.js +1 -2
- package/src/ask-nlp.mjs +9 -23
- package/src/ask-vocab.mjs +139 -589
- package/src/ask.mjs +627 -1729
- package/src/chat.mjs +1684 -2872
- package/src/cli-args.mjs +14 -28
- package/src/codegraph.mjs +236 -644
- package/src/completions/complete.mjs +18 -62
- package/src/completions/graph-adapter.mjs +14 -60
- package/src/completions/group.mjs +12 -68
- package/src/completions/infer.mjs +38 -126
- package/src/completions/prune.mjs +17 -70
- package/src/completions/rank.mjs +16 -69
- package/src/completions/search.mjs +8 -31
- package/src/concept.mjs +32 -88
- package/src/conformance.mjs +11 -15
- package/src/corpus/conceptnet.mjs +31 -89
- package/src/corpus/templates.mjs +19 -45
- package/src/corpus/unknown-ingest.mjs +31 -92
- package/src/embed.mjs +10 -22
- package/src/extensions.mjs +50 -154
- package/src/finish.mjs +35 -91
- package/src/grammar/ace.mjs +16 -40
- package/src/grammar/assert.mjs +1 -1
- package/src/grammar/lexicon-core.json +1 -1
- package/src/grammar/lexicon.mjs +9 -27
- package/src/graph-merge.mjs +2 -3
- package/src/hash.mjs +6 -14
- package/src/index.mjs +6 -10
- package/src/init.mjs +38 -125
- package/src/interpret/fuzzy.mjs +10 -29
- package/src/interpret/merge.mjs +9 -27
- package/src/interpret/normalize.mjs +137 -585
- package/src/interpret/pipeline.mjs +23 -71
- package/src/interpret/strategies/ace.mjs +7 -31
- package/src/interpret/strategies/constructions.mjs +14 -41
- package/src/interpret/strategies/grammar.mjs +21 -60
- package/src/interpret/strategies/keywords.mjs +42 -131
- package/src/interpret/strategies/noise-strip.mjs +18 -89
- package/src/memory/bias.mjs +11 -54
- package/src/memory/blocks.mjs +18 -69
- package/src/memory/core.mjs +171 -591
- package/src/memory/fold.mjs +0 -0
- package/src/memory/inspect.mjs +7 -25
- package/src/memory/shacl.mjs +10 -39
- package/src/memory/trust.mjs +26 -127
- package/src/memory-ask-browser-entry.mjs +7 -30
- package/src/memory-ask-browser.bundle.js +1 -1
- package/src/paraphrase.mjs +20 -53
- package/src/planning.mjs +15 -157
- package/src/prose-nlp.mjs +4 -17
- package/src/prose.mjs +19 -67
- package/src/providers/bootstrap.mjs +1 -2
- package/src/providers/fixture.mjs +1 -2
- package/src/providers/graph-service.mjs +28 -59
- package/src/repository-interface.mjs +6 -8
- package/src/router/drive.mjs +183 -0
- package/src/router/goal-reasoner.mjs +66 -231
- package/src/router/guardrail.mjs +20 -58
- package/src/router/planner.mjs +15 -46
- package/src/router/registry.mjs +13 -43
- package/src/router/resolver.mjs +46 -131
- package/src/router/results.mjs +231 -0
- package/src/schema-docs.mjs +10 -27
- package/src/server-http.mjs +10 -19
- package/src/server.mjs +22 -28
- package/src/sessions.mjs +15 -30
- package/src/source-slice.mjs +5 -7
- package/src/source.mjs +10 -20
- package/src/syllogise.mjs +187 -575
- package/src/telemetry.mjs +3 -3
- package/src/toml-config.mjs +4 -4
- package/src/tui/app.mjs +9 -19
- package/src/viz.mjs +66 -123
- package/src/wink-model.mjs +10 -24
package/src/telemetry.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// telemetry.mjs — OPT-IN, fire-and-forget query telemetry
|
|
1
|
+
// telemetry.mjs — OPT-IN, fire-and-forget query telemetry.
|
|
2
2
|
//
|
|
3
3
|
// The measurement contract is absolute: telemetry is OFF by default and the OFF path
|
|
4
4
|
// must be BYTE-IDENTICAL — no file, no stdout/stderr change, ~zero cost. createTelemetry
|
|
@@ -16,8 +16,8 @@ import { uuidv7 } from "./uuid.mjs";
|
|
|
16
16
|
|
|
17
17
|
/** Field names whose VALUES are (or embed) raw source and must never be logged. `body` is
|
|
18
18
|
* the Repository Interface's own field name for real source text (snippet()/context()'s
|
|
19
|
-
* source-capable body sections
|
|
20
|
-
*
|
|
19
|
+
* source-capable body sections) — without it, raw source read via a source-capable
|
|
20
|
+
* createGraphService could leak straight into a telemetry log. */
|
|
21
21
|
const DROP_KEYS = new Set(["text", "content", "snippet", "body"]);
|
|
22
22
|
/** String fields longer than this are truncated — except query.raw (the user's own
|
|
23
23
|
* question, which is the correlation key and is not file content). */
|
package/src/toml-config.mjs
CHANGED
|
@@ -88,14 +88,14 @@ export async function normalizeConfig(raw, { configDir } = {}) {
|
|
|
88
88
|
cfg.outRoot = resolve(dir, String(src.out_root));
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
// `tmct init` onboarding keys
|
|
91
|
+
// `tmct init` onboarding keys. Sparse like the rest: only a
|
|
92
92
|
// key actually present appears, so "unset" stays distinguishable from "set to
|
|
93
93
|
// the default". `graph_file` is resolved against configDir to match outRoot.
|
|
94
94
|
if (src.graph_file !== undefined) {
|
|
95
95
|
cfg.graphFile = resolve(dir, String(src.graph_file));
|
|
96
96
|
}
|
|
97
|
-
// `graph_files` (array, multi-graph
|
|
98
|
-
//
|
|
97
|
+
// `graph_files` (array, multi-graph): sits ALONGSIDE `graph_file`, never
|
|
98
|
+
// replaces it. A single-element array is
|
|
99
99
|
// legal but `graph_file` stays the byte-identical single-graph path most
|
|
100
100
|
// repos use; `graph_files` only matters once it names more than one file.
|
|
101
101
|
if (src.graph_files !== undefined) {
|
|
@@ -161,7 +161,7 @@ export async function normalizeConfig(raw, { configDir } = {}) {
|
|
|
161
161
|
// DEFAULT_RETENTION) is applied where the value is actually CONSUMED
|
|
162
162
|
// (snapshotMemory), not injected here.
|
|
163
163
|
//
|
|
164
|
-
// [memory] backend — the storage-backend seam (
|
|
164
|
+
// [memory] backend — the storage-backend seam (createSession's
|
|
165
165
|
// `memoryBackend` option / TMCT_MEMORY_BACKEND env): "default" (the flat
|
|
166
166
|
// OWL-labelled JSON file under .tmct/memory/), "memory" (in-process only,
|
|
167
167
|
// nothing on disk), or "sqlite" (a local SQLite file). Written by `tmct init
|
package/src/tui/app.mjs
CHANGED
|
@@ -1,26 +1,16 @@
|
|
|
1
|
-
// tui/app.mjs — the Ink full-screen chat shell (the default on a TTY)
|
|
1
|
+
// tui/app.mjs — the Ink full-screen chat shell (the default on a TTY): an
|
|
2
|
+
// alternate-screen layout with a scrolling transcript pane, a bottom input line, and a
|
|
3
|
+
// status bar (repo, module count, session id).
|
|
2
4
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// `tmct(label)> ` focus prompt, and a thin status bar (repo · module count ·
|
|
7
|
-
// session id · an honest "no graph — starting empty" when bootstrapping).
|
|
5
|
+
// Every turn goes through the SAME createSession sink (src/chat.mjs) the plain readline
|
|
6
|
+
// shell uses — transcript log, sidecar, graph upsert, and memory side-write are
|
|
7
|
+
// byte-identical to `--plain`; only the screen drawing differs.
|
|
8
8
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// upsert and memory side-write are byte-identical to `--plain`; only the
|
|
12
|
-
// screen drawing differs. Slash-commands work unchanged (they're session.turn's
|
|
13
|
-
// job); `/exit` and a conversational "bye" end the session; Ctrl+C exits
|
|
14
|
-
// cleanly through the same close() (Ink's exitOnCtrlC → waitUntilExit → close).
|
|
15
|
-
//
|
|
16
|
-
// Library decision (ROADMAP Phase 1 shell work): Ink 7 + React 19 — plain Node
|
|
17
|
-
// ESM, no JSX/build step (React.createElement throughout). OpenTUI was ruled
|
|
18
|
-
// out for now: @opentui/core depends on Bun FFI (bun-ffi-structs / a native Zig
|
|
19
|
-
// renderer), so it doesn't run under plain Node; revisit when it does.
|
|
9
|
+
// Library decision: Ink 7 + React 19, no JSX/build step. OpenTUI was ruled out:
|
|
10
|
+
// @opentui/core depends on Bun FFI, so it doesn't run under plain Node.
|
|
20
11
|
//
|
|
21
12
|
// The view-model is PURE and exported (statusText, appendTurn, transcriptLines,
|
|
22
|
-
// wrapLines) so node:test exercises it without a terminal
|
|
23
|
-
// is thin glue over it.
|
|
13
|
+
// wrapLines) so node:test exercises it without a terminal.
|
|
24
14
|
|
|
25
15
|
import React, { useEffect, useState } from "react";
|
|
26
16
|
import { render, Box, Text, useApp, useInput, useStdout } from "ink";
|
package/src/viz.mjs
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
// viz.mjs — `tmct viz`: a real, navigable, self-contained HTML graph view over
|
|
2
|
-
// the memory graph
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// site/viz.mjs + scripts/build-ask-bundle.mjs, adapted here to bundle tmct's
|
|
6
|
-
// ask.mjs directly rather than an external package import).
|
|
2
|
+
// the memory graph, with a real "Ask the graph" chat panel running tmct's OWN
|
|
3
|
+
// engine client-side (adapted to bundle tmct's ask.mjs directly rather than
|
|
4
|
+
// an external package import).
|
|
7
5
|
//
|
|
8
6
|
// Three pure/impure-separated pieces, mirroring src/syllogise.mjs's shape:
|
|
9
7
|
// - computeVizGraph(repoDir, {focus}) — I/O (loadMemory) + graph traversal,
|
|
10
8
|
// reusing spiralExpand/mostRecentIndividual/MEMORY_SPIRAL_EXPAND_KINDS/
|
|
11
|
-
// buildVizNodesAndEdges
|
|
12
|
-
// already generalized them for this. No new traversal logic here.
|
|
9
|
+
// buildVizNodesAndEdges. No new traversal logic here.
|
|
13
10
|
// - renderVizHtml({nodes, edges, focus, payload, askBundle}) — a pure
|
|
14
11
|
// string-builder: one complete <!doctype html> document, graph data
|
|
15
12
|
// JSON-embedded inline, the real ask-engine bundle inlined verbatim, no
|
|
@@ -27,66 +24,35 @@ import { readFile } from "node:fs/promises";
|
|
|
27
24
|
import { fileURLToPath } from "node:url";
|
|
28
25
|
import { dirname, join } from "node:path";
|
|
29
26
|
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
// `init:large`-seeded repo this session (see HANDOVER.md for the measured numbers).
|
|
27
|
+
// The page-size strategy: a three-cap default (200 base, tuned up to 300 here),
|
|
28
|
+
// raised from the code-graph-only SPIRAL_NODE_LIMIT_DEFAULT (12 — a MID-tier
|
|
29
|
+
// digest breadth, not a graph-viewer breadth) now that real concept-relation
|
|
30
|
+
// edges are walkable.
|
|
35
31
|
export const VIZ_NODE_LIMIT_DEFAULT = 300;
|
|
36
|
-
export const VIZ_HUB_DEGREE_DEFAULT = 40;
|
|
37
|
-
export const VIZ_DEPTH_DEFAULT = 3; // mirrors spiralExpand's own SPIRAL_DEPTH_DEFAULT
|
|
38
|
-
|
|
39
|
-
// edgeKindsFor
|
|
40
|
-
//
|
|
41
|
-
// can't import viz.mjs itself, a real-fs-I/O module) can share the SAME
|
|
42
|
-
// kind-combination logic instead of a second hand-rolled copy. See
|
|
43
|
-
// codegraph.mjs's own doc comment on edgeKindsFor for the full reasoning.
|
|
32
|
+
export const VIZ_HUB_DEGREE_DEFAULT = 40;
|
|
33
|
+
export const VIZ_DEPTH_DEFAULT = 3; // mirrors spiralExpand's own SPIRAL_DEPTH_DEFAULT
|
|
34
|
+
|
|
35
|
+
// edgeKindsFor lives in codegraph.mjs; re-exported here so the browser bundle's
|
|
36
|
+
// client-side re-walk (which can't import this fs-touching module) can share it.
|
|
44
37
|
export { edgeKindsFor };
|
|
45
38
|
|
|
46
|
-
/** Load the memory graph under `repoDir`, walk it from a seed, and enrich each
|
|
47
|
-
*
|
|
48
|
-
* Seed precedence:
|
|
49
|
-
*
|
|
50
|
-
* `
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* `
|
|
54
|
-
*
|
|
55
|
-
* `
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* (`MEMORY_SPIRAL_EXPAND_KINDS`) AND the real concept-relation kinds
|
|
59
|
-
* (`deriveFactTermGraph`'s per-predicate + link kinds) together by default
|
|
60
|
-
* (`edgeKindMode: "both"`) — a click on "dog" now reaches "animal"/"tail"/
|
|
61
|
-
* "bark" the same way asking about it in chat would surface those same
|
|
62
|
-
* facts, not just its provenance chain. `edgeKindMode: "meta"` reproduces
|
|
63
|
-
* today's exact byte-identical provenance-only walk (never deleted, just no
|
|
64
|
-
* longer the only option); `"relation"` isolates the concept view alone.
|
|
65
|
-
*
|
|
66
|
-
* `hubDegree` (seonix's third cap, PLAN_VIZ_MEMORY.md): stop expanding
|
|
67
|
-
* THROUGH a node with more than this many connections (still shows the hub
|
|
68
|
-
* itself) — without it a common hypernym could swallow the whole node
|
|
69
|
-
* budget in one hop. `VIZ_HUB_DEGREE_DEFAULT` applies when omitted.
|
|
70
|
-
*
|
|
71
|
-
* Returns `{nodes, edges, focus, payload, legend}` — `focus` is the seed id
|
|
72
|
-
* actually used (null when the graph is empty and no seed could be picked);
|
|
73
|
-
* `payload` is the FULL raw graph (every individual, not just the walked
|
|
74
|
-
* subset) — the embedded "Ask the graph" panel queries the whole graph and
|
|
75
|
-
* can re-walk from a new focus, never just the initially rendered subgraph,
|
|
76
|
-
* mirroring seonix's own "never the depth-limited display sub-graph"
|
|
77
|
-
* precedent; `legend` is `pickLegendDimension`'s precomputed output over the
|
|
78
|
-
* walked node set. Never throws on a missing/empty memory dir: loadMemory's
|
|
79
|
-
* own ENOENT fallback (emptyMemory()) already degrades to zero individuals,
|
|
80
|
-
* which this function turns into `{nodes: [], edges: [], focus: null,
|
|
81
|
-
* payload, legend: null}`. */
|
|
39
|
+
/** Load the memory graph under `repoDir`, walk it from a seed, and enrich each walked node
|
|
40
|
+
* with the real label/class/timestamp data a renderer needs.
|
|
41
|
+
* Seed precedence: `--focus <id>`, then `--term <word>` (via deriveFactTermGraph's
|
|
42
|
+
* synthetic `term:<word>` node, reaching the term's whole concept neighbourhood), then
|
|
43
|
+
* `mostRecentIndividual`.
|
|
44
|
+
* `edgeKindMode`: "both" (default) walks meta/provenance kinds AND real concept-relation
|
|
45
|
+
* kinds together; "meta" is provenance-only; "relation" isolates the concept view.
|
|
46
|
+
* `hubDegree`: stop expanding THROUGH a node with more connections than this (still shows
|
|
47
|
+
* the hub itself), so a common hypernym can't swallow the whole node budget in one hop.
|
|
48
|
+
* Returns `{nodes, edges, focus, payload, legend}` — `payload` is the FULL raw graph (the
|
|
49
|
+
* "Ask the graph" panel can re-walk from a new focus over it). Never throws on a
|
|
50
|
+
* missing/empty memory dir. */
|
|
82
51
|
export async function computeVizGraph(repoDir, { focus, term, depth, nodeLimit, hubDegree, edgeKindMode = "both" } = {}) {
|
|
83
52
|
const payload = await loadMemory(repoDir);
|
|
84
53
|
const graph = parseEntities(payload);
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
// for the client-side re-walk (recentre/edge-kind toggle) to stay consistent
|
|
88
|
-
// with whatever this page was generated with, rather than silently falling
|
|
89
|
-
// back to spiralExpand's own much-smaller code-graph defaults (nodeLimit 12).
|
|
54
|
+
// Always resolved (defaults filled in), so renderVizHtml can embed it for a
|
|
55
|
+
// client-side re-walk to stay consistent with how this page was generated.
|
|
90
56
|
const walkOpts = {
|
|
91
57
|
depth: depth != null ? depth : VIZ_DEPTH_DEFAULT,
|
|
92
58
|
nodeLimit: nodeLimit != null ? nodeLimit : VIZ_NODE_LIMIT_DEFAULT,
|
|
@@ -122,12 +88,9 @@ export async function computeVizGraph(repoDir, { focus, term, depth, nodeLimit,
|
|
|
122
88
|
return { nodes, edges, focus: seedId, payload, legend, walkOpts };
|
|
123
89
|
}
|
|
124
90
|
|
|
125
|
-
/** Read the checked-in browser ask-engine bundle (
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* throws) if the bundle hasn't been built yet — `renderVizHtml` renders a
|
|
129
|
-
* graph-only page with an honest "chat unavailable" note in that case,
|
|
130
|
-
* rather than a broken page. */
|
|
91
|
+
/** Read the checked-in browser ask-engine bundle (`src/ask-browser.bundle.js`). Returns
|
|
92
|
+
* `""`, never throws, if the bundle hasn't been built — renderVizHtml then renders a
|
|
93
|
+
* graph-only page with an honest "chat unavailable" note. */
|
|
131
94
|
export async function readAskBundle() {
|
|
132
95
|
try {
|
|
133
96
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -137,13 +100,9 @@ export async function readAskBundle() {
|
|
|
137
100
|
}
|
|
138
101
|
}
|
|
139
102
|
|
|
140
|
-
/** Read the checked-in browser
|
|
141
|
-
* (
|
|
142
|
-
*
|
|
143
|
-
* engine (chat.mjs's factAnswer), alongside the pre-existing code-graph
|
|
144
|
-
* ask.mjs bundle readAskBundle() already reads. Same never-throws contract:
|
|
145
|
-
* `""` when the bundle hasn't been built yet, so renderVizHtml degrades to
|
|
146
|
-
* whichever engine (if any) IS present rather than a broken page. */
|
|
103
|
+
/** Read the checked-in browser memory-ask-engine bundle (`src/memory-ask-browser.bundle.js`)
|
|
104
|
+
* — the real memory-graph answer engine (chat.mjs's factAnswer). Same never-throws
|
|
105
|
+
* contract as readAskBundle(). */
|
|
147
106
|
export async function readMemoryAskBundle() {
|
|
148
107
|
try {
|
|
149
108
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -169,22 +128,12 @@ function embedJson(value) {
|
|
|
169
128
|
}
|
|
170
129
|
|
|
171
130
|
/** Render one complete, self-contained `<!doctype html>` document for
|
|
172
|
-
* `{nodes, edges, focus, payload, askBundle}` (computeVizGraph's return
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* implementing a concentric ring layout keyed on hop (PLAN_VIZ.md §4 —
|
|
179
|
-
* seed/newest at the centre, each ring one hop further out), paint-order-by-
|
|
180
|
-
* hop with a lightness/opacity falloff for the depth read, pan (drag) + zoom
|
|
181
|
-
* (wheel), click-a-node for details, a depth stepper + per-class visibility
|
|
182
|
-
* filters (operator directive, seonix precedent), and a real "Ask the graph"
|
|
183
|
-
* chat panel — a query resolves via the SAME ask() the CLI ships, re-walks
|
|
184
|
-
* the graph from the resolved entity (focus-follows-answer), and a node's
|
|
185
|
-
* class/label in the detail panel are click-to-query affordances. Pure
|
|
186
|
-
* string building — no fs/network, no external <script src>, no CDN, no
|
|
187
|
-
* fonts. */
|
|
131
|
+
* `{nodes, edges, focus, payload, askBundle}` (computeVizGraph's return shape, plus the
|
|
132
|
+
* ask-engine bundle text from readAskBundle()): graph data JSON-embedded inline, the real
|
|
133
|
+
* ask.mjs engine inlined verbatim, inline <style>, inline vanilla-JS implementing a
|
|
134
|
+
* concentric ring layout keyed on hop, pan/zoom, click-a-node details, visibility filters,
|
|
135
|
+
* and a real "Ask the graph" chat panel (focus-follows-answer). Pure string building — no
|
|
136
|
+
* fs/network, no external <script src>, no CDN, no fonts. */
|
|
188
137
|
export function renderVizHtml({ nodes, edges, focus, payload, askBundle, memoryAskBundle, legend, walkOpts }) {
|
|
189
138
|
const graphJson = embedJson({ nodes, edges, focus });
|
|
190
139
|
const payloadJson = embedJson(payload || { individuals: [], objectProperties: [] });
|
|
@@ -316,7 +265,7 @@ ${hasMemChat ? `<script>\n${memoryAskBundle}\n</script>` : ""}
|
|
|
316
265
|
var hasEngine = typeof tmctViz !== "undefined";
|
|
317
266
|
var hasMemEngine = typeof tmctMemoryAsk !== "undefined";
|
|
318
267
|
var FULL_GRAPH = hasEngine ? tmctViz.parseEntities(PAYLOAD) : null;
|
|
319
|
-
// The term-relation view
|
|
268
|
+
// The term-relation view over the FULL graph, computed once —
|
|
320
269
|
// recentre()/edge-kind-toggle re-walks reuse it rather than re-deriving it
|
|
321
270
|
// per click. hasEngine-gated: the walk/legend exports only exist in the
|
|
322
271
|
// ask-browser bundle, not the memory-ask one.
|
|
@@ -369,8 +318,7 @@ ${hasMemChat ? `<script>\n${memoryAskBundle}\n</script>` : ""}
|
|
|
369
318
|
}
|
|
370
319
|
renderTypeFilters();
|
|
371
320
|
|
|
372
|
-
// ---- legend-as-filter
|
|
373
|
-
// dimension"): LEGEND.primary names the server's auto-picked dimension
|
|
321
|
+
// ---- legend-as-filter: LEGEND.primary names the server's auto-picked dimension
|
|
374
322
|
// (class/predicate/provenance, scored by normalized Shannon entropy over
|
|
375
323
|
// the INITIAL walk); the dropdown lets a user switch dimension without
|
|
376
324
|
// regenerating the page. Bucket COUNTS are always recomputed live over the
|
|
@@ -466,8 +414,8 @@ ${hasMemChat ? `<script>\n${memoryAskBundle}\n</script>` : ""}
|
|
|
466
414
|
|
|
467
415
|
// degree over the CURRENTLY displayed edge set (hub-hide/beam-prune are
|
|
468
416
|
// display-time filters, distinct from the generation-time hubDegree cap
|
|
469
|
-
// which only stops the WALK expanding through a hub — both useful
|
|
470
|
-
//
|
|
417
|
+
// which only stops the WALK expanding through a hub — both useful).
|
|
418
|
+
// Memoized: draw() runs on every
|
|
471
419
|
// pan/zoom/hover mousemove, and both draw() and visibleNodeIds() (which
|
|
472
420
|
// draw() itself calls) each need it — recomputing an O(edges) map twice per
|
|
473
421
|
// frame during a drag is real, avoidable per-frame cost. Invalidated by
|
|
@@ -602,12 +550,11 @@ ${hasMemChat ? `<script>\n${memoryAskBundle}\n</script>` : ""}
|
|
|
602
550
|
return { x: (sx * dpr - cx) / (view.scale * dpr), y: (sy * dpr - cy) / (view.scale * dpr) };
|
|
603
551
|
}
|
|
604
552
|
|
|
605
|
-
// Label-density modes
|
|
606
|
-
//
|
|
607
|
-
//
|
|
608
|
-
//
|
|
609
|
-
//
|
|
610
|
-
// variant has no seonix equivalent). "none" draws no labels at all.
|
|
553
|
+
// Label-density modes: "smart" (the default) draws a label only for the
|
|
554
|
+
// focus/selection/direct-neighbours/top-20-by-degree; everything else
|
|
555
|
+
// labels on hover only. "all"/"name-source" always draw (name-source
|
|
556
|
+
// appends the Fact's own provenance prefix — trust tier is a first-class
|
|
557
|
+
// concept here). "none" draws no labels at all.
|
|
611
558
|
var hoverId = null;
|
|
612
559
|
canvas.addEventListener("mousemove", function (ev) {
|
|
613
560
|
if (labelMode !== "smart" || dragging) return;
|
|
@@ -747,7 +694,7 @@ ${hasMemChat ? `<script>\n${memoryAskBundle}\n</script>` : ""}
|
|
|
747
694
|
}
|
|
748
695
|
document.getElementById("resetview").addEventListener("click", function () { fitToVisible(); draw(); });
|
|
749
696
|
|
|
750
|
-
// ---- recentre: RE-WALK the FULL graph (via TERM_GRAPH —
|
|
697
|
+
// ---- recentre: RE-WALK the FULL graph (via TERM_GRAPH — the augmented
|
|
751
698
|
// view, so a recentre reaches real concept-relation edges the same way
|
|
752
699
|
// generation-time computeVizGraph does) from a new seed via the real,
|
|
753
700
|
// bundled spiralExpand (byte-identical to the CLI's own walk — never a
|
|
@@ -832,14 +779,13 @@ ${hasMemChat ? `<script>\n${memoryAskBundle}\n</script>` : ""}
|
|
|
832
779
|
document.getElementById("panelClose").addEventListener("click", function () {
|
|
833
780
|
panel.classList.remove("show"); selectedId = null; draw();
|
|
834
781
|
});
|
|
835
|
-
// Class badge: a click-to-query affordance
|
|
836
|
-
//
|
|
837
|
-
//
|
|
838
|
-
//
|
|
839
|
-
//
|
|
840
|
-
//
|
|
841
|
-
//
|
|
842
|
-
// may miss, never faked.
|
|
782
|
+
// Class badge: a click-to-query affordance — isolate this class in the
|
|
783
|
+
// type-filter row (a REAL "show all of that kind currently in view"
|
|
784
|
+
// action; ask.mjs has no generic "list all X of class Y" shape for
|
|
785
|
+
// memory-graph classes — that richer machinery lives in chat.mjs's
|
|
786
|
+
// factAnswer cascade, out of this bundle's ask.mjs-only scope) AND fire a
|
|
787
|
+
// real "where is X mentioned" query on the class name — an honest
|
|
788
|
+
// attempt, may miss, never faked.
|
|
843
789
|
var classLink = document.getElementById("classLink");
|
|
844
790
|
if (classLink) classLink.addEventListener("click", function () {
|
|
845
791
|
typeFiltersEl.querySelectorAll("label.typechk").forEach(function (lbl) {
|
|
@@ -889,20 +835,19 @@ ${hasMemChat ? `<script>\n${memoryAskBundle}\n</script>` : ""}
|
|
|
889
835
|
});
|
|
890
836
|
|
|
891
837
|
// ---- Ask the graph: TWO real engines over the FULL graph, never just the
|
|
892
|
-
// currently-displayed subgraph
|
|
893
|
-
// order per query:
|
|
838
|
+
// currently-displayed subgraph — tried in order per query:
|
|
894
839
|
// 1. tmctMemoryAsk.factAnswer (src/chat.mjs's REAL memory-graph answer
|
|
895
840
|
// engine, the same one 'npm run chat' uses) — a Fact/definition-shaped
|
|
896
841
|
// question ("what is a dog", "what is a horse used for") answers HERE,
|
|
897
|
-
// which the code-graph engine below
|
|
898
|
-
//
|
|
899
|
-
//
|
|
900
|
-
//
|
|
901
|
-
//
|
|
902
|
-
//
|
|
842
|
+
// which the code-graph engine below cannot. Given an in-memory
|
|
843
|
+
// Backend-B handle carrying the page's own embedded PAYLOAD — ZERO fs
|
|
844
|
+
// I/O (see memory-ask-browser-entry.mjs's own doc comment) — and
|
|
845
|
+
// envelope:null/miss:true, the exact documented "no parse pipeline
|
|
846
|
+
// available" bootstrap path that arms factAnswer's own bare-question
|
|
847
|
+
// regex fallbacks.
|
|
903
848
|
// 2. tmctViz.ask (tmct's code-graph query engine) — generic "where is X
|
|
904
|
-
// mentioned" navigation and code-graph queries
|
|
905
|
-
//
|
|
849
|
+
// mentioned" navigation and code-graph queries. Only reached when (1)
|
|
850
|
+
// is unavailable or didn't hit.
|
|
906
851
|
// A resolved answer's own target re-centres the view — focus follows the
|
|
907
852
|
// answer — for EITHER engine. --------------------------------------------
|
|
908
853
|
var askInput = document.getElementById("askq");
|
|
@@ -988,9 +933,7 @@ ${hasMemChat ? `<script>\n${memoryAskBundle}\n</script>` : ""}
|
|
|
988
933
|
askOut.innerHTML = '<div class="q">"' + esc(query) + '"</div>' + esc(t.content) + canon;
|
|
989
934
|
// Focus-follows-answer: frame EVERY real match this answer resolved to
|
|
990
935
|
// (envelope.matches is already the full candidate list ask.mjs itself
|
|
991
|
-
// ranked
|
|
992
|
-
// rest of a multi-match answer's own result set), never a guess beyond
|
|
993
|
-
// what the engine itself actually returned.
|
|
936
|
+
// ranked), never a guess beyond what the engine itself actually returned.
|
|
994
937
|
var targetIds = (envelope.matches || []).map(function (m) { return m.id; }).filter(Boolean);
|
|
995
938
|
frameQueryResult(targetIds);
|
|
996
939
|
draw();
|
package/src/wink-model.mjs
CHANGED
|
@@ -1,27 +1,13 @@
|
|
|
1
|
-
// wink-model.mjs — the ONE place tmct loads the wink-nlp engine + model
|
|
1
|
+
// wink-model.mjs — the ONE place tmct loads the wink-nlp engine + model, shared by
|
|
2
|
+
// ask-nlp.mjs and prose-nlp.mjs.
|
|
2
3
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// twice, and both Node-only. That duplication is single-sourced here, and the
|
|
7
|
-
// Node-only limitation is lifted with a browser seam, WITHOUT eagerly bundling the
|
|
8
|
-
// ~1 MB model into anything.
|
|
4
|
+
// A static `import "wink-nlp"` would drag the ~1 MB model into the base/viewer bundle.
|
|
5
|
+
// Instead, `registerWinkModel` lets a browser/bundler entry hand in an already-imported
|
|
6
|
+
// `{ winkNLP, model }` pair once; Node instead resolves lazily via `createRequire`.
|
|
9
7
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// build, so the model can run in the page — what was missing is a load path a
|
|
14
|
-
// bundler can satisfy without a Node `require`. That path is `registerWinkModel`:
|
|
15
|
-
// a browser/bundler entry imports wink with its own `import` and hands the pair
|
|
16
|
-
// in ONCE, before any lemma/POS use. Node needs nothing — it falls back to
|
|
17
|
-
// `createRequire`. This is the Phase-8 browser-mode unblocker the dependency
|
|
18
|
-
// audit called for (a wiring fix; the model was always browser-capable).
|
|
19
|
-
//
|
|
20
|
-
// The loader stays SYNCHRONOUS (the adapters and their callers are sync): the
|
|
21
|
-
// browser host registers up front; Node resolves lazily via createRequire. Failure
|
|
22
|
-
// is cached as null — a checkout without the optional deps, or a page that never
|
|
23
|
-
// registered a model, simply runs adapter-less (lemma/POS tiers honestly off), it
|
|
24
|
-
// never throws.
|
|
8
|
+
// The loader stays synchronous. Failure is cached as null — a checkout without the
|
|
9
|
+
// optional deps, or a page that never registered a model, runs adapter-less rather
|
|
10
|
+
// than throwing.
|
|
25
11
|
|
|
26
12
|
import { createRequire } from "node:module";
|
|
27
13
|
|
|
@@ -50,8 +36,8 @@ export function loadWinkModel() {
|
|
|
50
36
|
return cached;
|
|
51
37
|
}
|
|
52
38
|
|
|
53
|
-
/** Node fallback: resolve wink through the module system (never a guessed path)
|
|
54
|
-
*
|
|
39
|
+
/** Node fallback: resolve wink through the module system (never a guessed path).
|
|
40
|
+
* CJS deps, so `createRequire`. */
|
|
55
41
|
function nodeRequireWink() {
|
|
56
42
|
const require = createRequire(import.meta.url);
|
|
57
43
|
return {
|