feat(agent): search_documents tool — the model can grep the indexed documents for an exact string
Build and Push Containers / build-and-push-db (push) Canceled after 0s
Build and Push Containers / build-and-push-app (push) Canceled after 1m11s

This commit is contained in:
2026-09-02 12:04:34 -04:00
parent 88293ed02f
commit 8cf3a827ee
26 changed files with 1780 additions and 85 deletions
+32 -2
View File
@@ -5,7 +5,8 @@ No new Python app logic exists for this task — the behavior lives in
suite (task 06). Like the other frontend-adjacent unit files, this module
pins the JS/CSS markers the story depends on, so a silent regression in
the tool branch, the persistence shape, or the tool-line styling is
caught without a browser.
catched without a browser. Phase 68 extends the pins with the
``search_documents`` status/line contract.
"""
from __future__ import annotations
@@ -39,7 +40,11 @@ def test_tool_branch_is_a_first_class_turn_branch() -> None:
"the turn handler must branch on tool frames"
)
branch = js[tool_idx:delta_idx]
assert "toolAcc.push" in branch, "every tool frame is recorded for persistence"
assert "toolAcc.push({ name, argument })" in branch, (
"every tool frame is recorded for persistence — the record stays"
" {name, argument}-generic, no per-tool shape (phase 68: the"
" search tool rides the same accumulator)"
)
assert "clearTurnTimeout()" in branch, "a tool frame proves the stream is alive"
assert 'addMessage("brain", "")' in branch, "first frame creates the brain wrap"
assert "uiState === UI_STATE.thinking" in branch, (
@@ -69,6 +74,16 @@ def test_calling_tool_label_strings() -> None:
assert "sendLabel" not in branch, "phase 48: the button keeps its Stop label"
assert "`${brand()} is listing documents`" in branch
assert "`${brand()} is reading ${argument}`" in branch
# Phase 68: the search status — locked name+argument gate, sitting
# BETWEEN the read branch and the listing fallback in the ternary.
assert "name === \"search_documents\" && argument" in branch, (
"the search status requires the name AND a string argument"
)
assert "`${brand()} is searching for ${argument}`" in branch
read = branch.find("is reading")
search = branch.find("is searching for")
listing = branch.find("is listing documents")
assert -1 < read < search < listing, "ternary order: read → search → listing"
assert "sendStatus.textContent = toolStatus" in branch, (
"the #send-status live region announces what Brain is doing"
)
@@ -106,6 +121,21 @@ def test_tool_lines_render_into_the_bubble_wrap() -> None:
"the path is data — textContent, never innerHTML"
)
assert "name === \"read_document\" && argument" in body
# Phase 68: the search branch mirrors the read branch — the same
# name+argument gate, a <code> element, and the pattern through
# textContent (never markup); the listing stays the final else.
assert "name === \"search_documents\" && argument" in body
assert 'line.textContent = "🔎 Searching for "' in body
search_part = body.split('name === "search_documents"', 1)[1]
assert 'document.createElement("code")' in search_part, (
"the pattern gets the same <code> treatment as the read path"
)
assert "code.textContent = argument" in search_part, (
"the pattern is data — textContent, never innerHTML"
)
assert 'line.textContent = "🔎 Listing documents"' in search_part, (
"the listing fallback remains the final else"
)
def test_tool_branch_is_append_only_and_interleaving_safe() -> None: