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.
This commit is contained in:
amosblomqvist
2026-09-19 16:50:20 +03:00
parent dfe1a21989
commit 7a3e19e95f
18 changed files with 977 additions and 41 deletions
+15
View File
@@ -1089,6 +1089,21 @@ describe("prompt composition", () => {
assert.ok(prober.includes("the `read` tool can inspect it directly"), "the prober knows it can read learner files");
});
it("the sources-v1 shared fragment composes into BOTH agents (find/ls/grep doctrine)", () => {
for (const kind of ["teacher", "prober"] as const) {
const instructions = composeInstructions(kind);
assert.ok(instructions.includes("## Learner files"), `${kind} carries the shared learner-files fragment`);
assert.ok(instructions.includes("find it yourself with `find`"), `${kind} knows to locate files itself`);
assert.ok(instructions.includes("home directory"), `${kind} knows the home-anchored path rule`);
}
// shared composes BEFORE the agent's own fragments (the seam's order rule)
const teacher = composeInstructions("teacher");
assert.ok(
teacher.indexOf("## Learner files") < teacher.indexOf("## Learner source files"),
"shared fragment precedes the teacher's own 30-jobs section",
);
});
it("guards prompt size — no fragment can silently balloon (the README-leak class)", () => {
// The wave-1 leak: README.md (815 bytes / ~200 tokens) rode into every
// teacher turn until the numeric-prefix filter stopped it. This guard