wave 7: the prober reads its map — graph-context injection dies whole
The owner's zero-configuration ruling (2026-09-23): nothing configures the graph beyond chat models. The prior-knowledge injection — its budget dial, its runner, its marker part, its dock/dispatch entries — is removed across all bands; the prober now reads the learner's persistent knowledge map on demand through two gated tools instead: - list_sessions: every birth session, names + kinds + counts + dates (graphSessionIndex, Platform's core seam) - list_propositions(session): what was born there + the session's own latest understanding level + that evaluation's date (graphSessionPropositions) — names/levels/dating ONLY, no statement text (owner ruling: the map's organization is the prober's business, its prose is not) Registered via createProberTools when chat-handler passes graphStore (the graphToolsEnabled gate decided server-side, per-turn fresh); the prober doctrine gains the map-review nudge at session start. Also: runner's JobKind special-casing collapses (KIND_ROLE/KIND_RUNNERS are plain records again), semantic-clusters.ts dies with its test, the embedder dep's doc tells the wave-7 stub story, and ports.ts's readClusterLabels/readNamingRuns surface stays untouched for Platform's ledger-port interlude. 848/848 tests, typecheck/lint/build green.
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { z } from "zod";
|
||||
import { lhTool, type LhTool } from "../lh-tool.ts";
|
||||
import { graphSessionIndex, graphSessionPropositions } from "../../core/graph.ts";
|
||||
import type { ChatStore, GraphStore } from "../../core/ports.ts";
|
||||
|
||||
/**
|
||||
* THE PROBER'S GRAPH TOOLS (wave 7, owner-locked 2026-09-23): context
|
||||
* injection is dead — zero configuration beyond chat models. The prober
|
||||
* reads the learner's persistent knowledge map through two tools instead,
|
||||
* replacing the old injected block with on-demand eyes.
|
||||
*
|
||||
* Names + levels + dating ONLY — no statement text, no session contents
|
||||
* (owner ruling: the map's ORGANIZATION is the prober's business, its
|
||||
* prose is not). The data comes from Platform's pure core seams
|
||||
* (graphSessionIndex / graphSessionPropositions) over the same ports the
|
||||
* snapshot reads, so the prober and the learner's graph view can never
|
||||
* disagree.
|
||||
*
|
||||
* Registered on the prober ONLY when the settings gate opens
|
||||
* (graphToolsEnabled — the graph master switch AND the agent.graphTools
|
||||
* toggle); the composition root decides, the factory just receives the
|
||||
* store (absent = the gate is closed, no map tools — tests drive this).
|
||||
*/
|
||||
|
||||
/** What the graph tools close over: the stores the session views read. */
|
||||
export interface GraphToolContext {
|
||||
chatStore: ChatStore;
|
||||
graphStore: GraphStore;
|
||||
}
|
||||
|
||||
export function proberGraphTools(ctx: GraphToolContext): Record<string, LhTool<any, any>> {
|
||||
const deps = { chatStore: ctx.chatStore, graphStore: ctx.graphStore };
|
||||
return {
|
||||
list_sessions: lhTool({
|
||||
name: "list_sessions",
|
||||
description:
|
||||
"List the sessions of the learner's persistent knowledge graph — every chat or imported-notes file that ever contributed a proposition, most recent first. The map is organized into birth-session clusters (propositions grouped by the session they were first learned in), and session names are those clusters' labels. " +
|
||||
"Each line: the session's name, kind (chat/file), how many propositions were born in it, its dates, and its id — use that id with list_propositions. " +
|
||||
"Reading this first shows what he has already covered and when, before you probe anything.",
|
||||
schema: z.object({}),
|
||||
execute: async (): Promise<string> => {
|
||||
const sessions = graphSessionIndex(deps);
|
||||
if (sessions.length === 0) return "The knowledge graph is empty — no session has contributed propositions yet.";
|
||||
const lines = sessions.map((s, i) =>
|
||||
`${i + 1}. ${s.name} (${s.kind}, ${s.propositions} proposition${s.propositions === 1 ? "" : "s"}, ` +
|
||||
`born ${s.createdAt ?? "unknown date"}${s.updatedAt ? `, last active ${s.updatedAt}` : ""}) — id: ${s.id}`,
|
||||
);
|
||||
return `${sessions.length} session(s) in the knowledge graph, most recent first:\n${lines.join("\n")}`;
|
||||
},
|
||||
}),
|
||||
list_propositions: lhTool({
|
||||
name: "list_propositions",
|
||||
description:
|
||||
"List the propositions BORN in one knowledge-graph session — what was first learned there — each with his latest understanding level on the scale solid / shaky / accepted / resisted / not yet engaged, and the date that level was set. " +
|
||||
"Names, levels, and dating only — no statement text, no session contents. " +
|
||||
"The level is that session's own latest evaluation; other sessions may have moved it since, so cross-session levels can differ.",
|
||||
schema: z.object({
|
||||
session: z
|
||||
.string()
|
||||
.min(1)
|
||||
.describe("The session's id exactly as list_sessions reported it (a chat id or a notes source id)"),
|
||||
}),
|
||||
execute: async ({ session }): Promise<string> => {
|
||||
const propositions = graphSessionPropositions(deps, session);
|
||||
if (propositions.length === 0) {
|
||||
return `No propositions born in session "${session}". Call list_sessions for the current ids.`;
|
||||
}
|
||||
const lines = propositions.map(
|
||||
(p) => `- ${p.name} — ${p.understanding ?? "never evaluated"}${p.evaluatedAt ? ` (as of ${p.evaluatedAt})` : ""}`,
|
||||
);
|
||||
return `${propositions.length} proposition(s) born in this session:\n${lines.join("\n")}`;
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user