Commit Graph
8 Commits
Author SHA1 Message Date
amosblomqvist d85d8a58a0 fix(tools): wire the searchAvailable gate + research/pdf in the probe phase
Fix wave (2026-09-20), two owner-approved fixes:

Fix 1 — the searchAvailable gate was dead code on the live wiring path:
createTeacherTools RECEIVED ctx.searchAvailable but dropped it when
building the inner JobToolContext, so jobTools' research gate always took
its ?? true default (jobTools-direct tests passed; the production path
never saw the signal). Threaded through — the gate now reads the live
server signal on the teacher registry too.

Fix 2 — the prober registry gains the research + pdf dispatch tools
(owner ruling, 2026-09-20): these two are phase-omnipresent — the prober
can verify facts or read learner PDFs while assessing. demo/visual stay
teach-phase-only (they need the lesson's plan context). Same gating as
the teacher side: toggle-filtered, research subject to the search gate
(honestly absent when no engine is live — the prober's dispatch closes
over the SAME jobTools builder, one gate, one semantics). probe_done
stays always-on-by-construction; the learner-fs set untouched.

- ProberToolContext (extends ProbeDoneContext with the job wiring);
  createProberAgent takes it; chat-handler injects jobStore + spawn +
  searchAvailable into the prober ctx.
- TeacherToolContext.spawn's stale input union fixed (PdfInput was
  missing from the TYPE — runtime already passed it).
- Tests: the threaded-gate describe in web-search.test.ts pins the LIVE
  factory path both ways (teacher + prober); question-tools' registry
  key-sets gain research/pdf; the prober toggle test pins the
  research:false path.

Gates: typecheck, 807/807 tests, lint, build — all green.
2026-09-22 16:22:57 +03:00
amosblomqvist 6c42393e42 feat(tools): the P2 toggle bridge — toolToggles first-class, definitive vocabulary
Reconciliation of the diverged tool-toggles seam (2026-09-19, master's
P2): Platform injected the resolved delta as an ad-hoc intersection on
AgentBuildOptions while the factories' enabled? param sat unread — the
seam was inert on both sides.

- teacher-agent: AgentBuildOptions gains toolToggles?: EnabledTools;
  both constructors thread it to the factories (createTeacherTools /
  createProberTools). An explicitly injected opts.tools registry is
  never filtered — the caller owns it.
- Vocabulary ADJUDICATED with the user (2026-09-19): TOGGLEABLE_TOOLS is
  the definitive list — the job tools only (research/demo/visual/pdf).
  quiz/ask_user_question stay always-on forever (the prober's flow is
  graded-quiz brackets -> probe_done; quiz-off collapses assessment and
  teaching doctrine is built on graded verification — the wedge danger);
  the learner-fs set, plan tools, and probe_done also always-on.
- settings-body (write side): unknown tool keys reject with 400 + the
  supported list, mirroring the prompt-switch validation; the read-side
  sanitizer stays lenient for forward-compat.
- chat-handler: the intersection is gone (field now first-class) and the
  seam comment states the adjudicated vocabulary.

Gates: typecheck, 28 new/updated tests green (tool-toggles-bridge,
question-tools, settings-body), chat-flow + settings suites green.
2026-09-19 18:16:17 +03:00
amosblomqvist 7a3e19e95f feat(sources v1): the agent locates files itself — ls/find/grep + tool-call cards
User pivot (2026-09-19): no attachments UI at all. Pasting paths is native
for technical users and alien for everyone else - so the agent gets the
tools to find what the learner merely NAMES. The learner communicates in
messages; the agent locates and reads. Zero cross-stream seams: paths
already live in tool parts, so the sources "list" is the transcript.

- ls/find/grep: pi-verbatim schemas, descriptions, and output formats
  (fd's glob semantics, rg's path:line: match format, pi's truncation
  notices), ported to pure Node - no binaries, no shell-out, read-only by
  construction. Deltas from pi: home-anchored resolution (the harness has
  no cwd), .gitignore replaced by hard noise exclusions
  (node_modules/.git/Library/.Trash), 10s walk timeout with partial results.
- read: description swapped to pi-verbatim (doctrine moved to the prompt);
  relative paths now resolve against the learner's home like the rest.
- prompts/shared/05-learner-files.md: one minimal doctrine fragment
  composed into BOTH agents (composeInstructions grew a shared/ dir,
  before the agent's own fragments). teacher/30-jobs.md keeps only the
  pdf-dispatch addendum.
- ToolCallCard: the generic quiet tool-call card the user specced -
  collapsed one-liner (tool name + headline argument), expandable to full
  input/output, errors red, image payloads never render. Registered for
  read/ls/find/grep.
- ui-message.ts: TeacherUITools entries for the four tools (mechanical).
- tests: fs-tools suite (glob semantics, pi formats, exclusions, limits),
  registry/card renders, shared-fragment composition pins, tool-registry
  key updates, read re-anchor.
2026-09-19 16:50:20 +03:00
amosblomqvist 9a90dd51d9 feat(T2b): sources v0 — read tool + pdf-reader sub-agent
Learner source files, on the teacher's own terms (nothing auto-injects):

- read tool (src/agent/tools/read.ts): pi-read port — path/offset/limit,
  2000-line/50KB truncation with use-offset continuation notices, image
  files (jpg/png/gif/webp/bmp) returned as native image content parts
  (new LhToolImageResult escape hatch in lh-tool.ts). Always-on in both
  the prober and teacher registries (user-decided 2026-09).
- pdf-reader sub-agent (JobKind 'pdf', toggleable, ack-and-steer): triage
  via pdf_info (pdfjs text layer: per-page text/image/math-density),
  targeted pdf_extract, regex pdf_search (hits count as consultation),
  and pdf_render — pages painted by pdfjs onto a canvas shim in headless
  Chrome (the pdfium viewer doesn't paint headless), screenshot fed back
  image-in-the-loop (visual-maker idiom, providerSafePng applied). The
  unread-finish gate bounces a zero-consultation brief once. Teacher gets
  a text brief with page citations only; renders never leave the loop.
- MODELS.pdfReader = qwen/qwen3.8-flash (user override 2026-09-19: the
  look-at-renders workload wants the vision-proven model; catalog listed
  glm-5.3-flash as image-capable but qwen is the known-good daily driver).
- UI: PdfCompleteBadge + PdfBriefCard (steer-card, job-card), dispatch
  card + activity-dock kind entries (type-forced, flagged for UI review).
- Prompts: 'Learner source files' section in teacher/30-jobs.md; read
  line in prober/10-probe-doctrine.md. Doctrine rides tool descriptions
  per the self-contained-tools principle.
2026-09-19 15:24:26 +03:00
amosblomqvist cab1632c74 feat(T2a): tool-toggle seam + phase-based question principles
Part 1 — tool availability (the settings seam, machinery generic):
- core/pending.ts: TOGGLEABLE_TOOLS (today: research, demo, visual —
  the model-facing tool names), EnabledTools = the JSON-native seam
  shape Partial<Record<string, boolean>> (absent key = enabled, so old
  records behave exactly as today), filterEnabledTools shared helper.
  quiz/ask_user_question are always-on FOR NOW (user-decided); the
  filtering is generic so future toggleables (question-tool variants,
  the read tool, …) need no factory change.
- createProberTools/createTeacherTools take enabled?: EnabledTools; the
  composition root (chat-handler, Platform's lane this wave) passes the
  resolved settings through. probe_done is re-added after the filter —
  always-on by construction (it ends the phase; a prober that can never
  conclude is a lockout).
- Prompts stay STATIC under toggles this wave (user-decided); standing
  principle recorded: tools ship self-contained usage guidance in their
  descriptions; system prompts reference only always-on tools.

Part 2 — phase-based question-writing principles (user-locked wording,
drafted with the user over three iterations):
- NEW teacher/21-question-principles.md (composes after 20-teaching):
  answerable-by-reasoning-from-the-session (the fairness rule that
  filters unanswerable questions without forbidding visible answers),
  never-just-reciting-your-words-back, PREFER problems that apply the
  knowledge (preference, not hard rule — honest recall is the fallback,
  not the habit), catch-confusion-early-and-nameable (wrong options as
  real misconceptions), answer-is-signal-not-verdict.
- prober/10-probe-doctrine.md: 'no rush' retired; quick-measurement
  directive in — every question moves an edge (bracket, narrow, or
  characterize); a bracketed strand is done.

Tests: TOGGLEABLE_TOOLS pin, prober/teacher filtering (absent = full,
false drops exactly that tool, probe_done survives), prompt pins (the
principles compose verbatim into the teacher prompt, the no-rush
framing is gone from the prober). 547/547, typecheck/lint/build green.
2026-09-19 14:06:07 +03:00
amosblomqvist 2d717fd932 Swap the server engine to pi-ai/pi-agent-core (Band B+C of docs/pi-engine-swap.md)
The Vercel AI SDK survives only as Band D (useChat client + UIMessage
record types); @openrouter/ai-sdk-provider is uninstalled.

- src/agent/models.ts: PiEngine {model, streamFn} via createModels +
  openrouterProvider subpath; thinkingLevelFor maps config effort none->off
- src/agent/lh-tool.ts: neutral LhTool (zod authoring, optional execute,
  clientQuestion flag); toPiTool converts via z.toJSONSchema and re-runs
  zod inside execute so refinements ride the error-result retry path
- src/server/pi/record-adapter.ts: UIMessage records -> pi Messages
  (dangling tool calls get synthesized interrupted results)
- src/server/pi/ui-stream.ts: SSE bridge emitting AI-SDK-shaped chunks so
  the useChat client parses unchanged (continuation id rule, question
  tools never emit output, tool-input-error for bad authoring)
- teacher-agent.ts: stateless per-turn pi Agents; shouldStopAfterTurn
  stops only when every question-tool call resolved cleanly (errors retry)
- chat-handler.ts: buildAgent(outgoing) factory seam (stateless doctrine);
  assertValidUiMessages owns the retired SDK validation gate
- sub-agents via shared runSubagent (pi-run.ts); extraction stages stop
  after a clean submit; visual-maker keeps its hand-rolled loop over
  direct streamSimple calls; aborted model calls bill no usage
- tests: all model seams ported to pi-ai faux provider (scripted
  FauxResponseSteps, agent factories); handleChatRequest second param
  is now an agent factory

Gates: tsc, lint, build, 305/305. Live GLM smoke (disposable driver,
deleted): prober streaming + persistence, question seam, turn-2
continuation same message id, per-turn real token usage - ALL PASS.
Data wiped at the pivot (chats/graph/jobs, .gitkeeps kept).
2026-09-12 02:34:18 +03:00
amosblomqvist 5653c77a29 feat: probe/teach agent split — explicit phase markers, position-only plan map
Diagnosis (2026-09-11 user testing, minimal-effort GLM): sessions stuck on
'plan' forever. Two causes: (1) the three-phase view read the stored tree's
currentNodeId, which nothing ever writes post author-once (2026-09-03) — a
structural dead seam; (2) the teacher stochastically left quiz.nodeId
untagged, and done-by-quiz-result semantics would have falsely completed
nodes anyway.

The redesign (docs/probe-teach-split.md) abolishes derived occupancy in
favor of explicit events:

- Two agents over one transcript: createProberAgent (quiz without a
  nodeId field, ask, probe_done) and createTeacherAgent (full toolset,
  plan_init onward). The chat-handler construction branch keys on a
  probe_done part in the transcript. MODELS.prober added.
- probe_done: server-side seam tool — carries the learner assessment
  (strands + free-form edge read), refuses while a question is open or
  the probe is already concluded, and queues a handoff steer through the
  generalized wake pipeline (steers now dedupe by id, not jobId; job
  finalization only for job steers). The teacher auto-wakes and authors
  the plan; the learner sees the assessment via the probe-done card.
- phases: probe ends at a concluded probe_done, plan ends at a
  successful plan_init — strictly sequential, no skipping, no legacy
  compatibility (data wiped at the pivot per doctrine).
- Plan map = position, not completion: derivePlanStatuses reads only
  tagged quiz CALLS (state/outcome feed nothing); current = latest
  tagged node with consecutive duplicates collapsed, visited = earlier
  distinct stays (a revisit stays in both — current wins at render).
  PlanTree is structure-only; currentNodeId/doneNodeIds and the dead
  mutation helpers are gone. No fallback current.
- Prompts split per agent (prompts/prober/, prompts/teacher/ over the
  trimmed shared base); graph-context injection removed entirely
  (shelved, docs/probe-teach-split.md §5); knowledge-graph context no
  longer reaches either agent.
- Plan blocks are full-only snapshots at plan_init (neighborhood
  machinery retired; the persistent DAG window is the future UI wave);
  usage capture unchanged.

Gates: tsc clean, 294/294 tests (phases/plan-tree/plan-blocks/question-
tools rewritten to the new semantics; probe-done + handoff-wake tests
added), lint, build. Live GLM smoke PASS: prober concluded with an honest
assessment, teacher auto-woke, authored a 10-node plan with the block
attached, then tagged checks on slope-instinct → secant derived current/
visited exactly as designed; grading honest both ways (driver off-by-one
proved a wrong answer grades false).
2026-09-11 06:44:20 +03:00
amosblomqvist 67fbc0575f Author-once plan protocol: single mermaid plan_init + derived statuses + live quiz nodeId validation
- plan_init is now the ONLY plan tool: a mermaid 'graph TD' block parsed by a
  constrained-subset parser (mermaid-plan.ts) with line-precise errors; the
  seven mutation/tracking tools are gone by design (2026-09-03 design pass).
- Re-call plan_init = sanctioned wholesale rewrite; slugs are permanent.
- derivePlanStatuses (contracts/plan.ts): done = passed check tagged with a
  quiz nodeId; current = open check's node else first topo-ready node — the
  tracking the teacher no longer declares by hand.
- questionToolsFor(ctx) (tools.ts): the quiz schema chains a dynamic plan-
  nodeId validation against the LIVE record — an invalid nodeId is rejected
  at the call boundary and the teacher retries mid-turn, before any UI.
  Static questionTools stay validator-less for the default path.
- Prompts rewritten for mermaid authoring + derived position.
- Out-of-lane surgical touches (flagged for master review):
  src/core/question-core.ts (quizInputObject export + nodeId description),
  src/core/contracts/plan.ts (derivePlanStatuses + doc).
2026-09-09 03:04:16 +03:00