A time in no particular zone, and a preview missing what it previews
The first audit pass: everything from 0.8.1 to 0.9.8 read as a whole rather than one feature at a time, starting with what a model is actually told. Four of these had shipped as correct. The date line carried a timezone variable that resolves to nothing until somebody chooses one -- so every default account was told times were "in unless they say otherwise", while two comments asserted the line disappeared instead. The prompt preview built its variables without a chat, which is what eleven fragments are gated on, so the whole agent surface was absent from it whatever was ticked. Plan mode was instructed to keep its plan current with a tool that mode withdraws. And knowledge_get returned a document whole where every sibling reader caps and says so, its description promising exactly that. The subagent guidance was wrong in both directions at once: it denied a documented parameter and named seven of twenty-three allowed commands. Both halves are pinned by tests against the real list and the real schema now, because prose and a constant drift the moment one is edited alone. docs/notes/audit-0.9.md carries the findings that are not fixed here, with why -- the ones whose fix would change what a feature does are the user's call, not this pass's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -188,6 +188,21 @@ RISKS = (RISK_READ, RISK_WRITE, RISK_EXECUTE, RISK_ASK)
|
||||
# single page. Cut with the model told so, rather than refused.
|
||||
MAX_FETCH_CHARS = 20_000
|
||||
|
||||
# The same bound for a knowledge document, and it was missing. `knowledge_get`
|
||||
# returned `extracted_text` whole while every sibling reader capped and said so
|
||||
# -- `fetch` above, `file_read`, the memories block, the skill index, the
|
||||
# project listing. `MAX_EXTRACTED_CHARS` is 120_000 by default and an
|
||||
# administrator can raise it, so one call on a long PDF filled an ordinary
|
||||
# window with nothing anywhere reporting that it had.
|
||||
#
|
||||
# Larger than a fetched page on purpose. Somebody put this document in the
|
||||
# library deliberately and named it in a search; a page the model followed a
|
||||
# link to is a guess. Cut with the model told so rather than refused, which is
|
||||
# what `fetch` and `file_read` both do -- a reader that fails on exactly the
|
||||
# documents worth reading is worse than one that hands back the first part and
|
||||
# says there was more.
|
||||
MAX_DOCUMENT_CHARS = 40_000
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToolContext:
|
||||
@@ -471,6 +486,14 @@ async def _run_knowledge_get(context: ToolContext, args: dict[str, Any]) -> Tool
|
||||
"results": [{"title": document.title, "id": document.id}],
|
||||
}
|
||||
body = document.extracted_text or document.extraction_error or "(no text)"
|
||||
if len(body) > MAX_DOCUMENT_CHARS:
|
||||
body = (
|
||||
f"{body[:MAX_DOCUMENT_CHARS]}\n\n"
|
||||
f"[Cut off here. This document is {len(document.extracted_text or ''):,} "
|
||||
f"characters and the first {MAX_DOCUMENT_CHARS:,} are above. Search it "
|
||||
"with knowledge_search to find the part you need.]"
|
||||
)
|
||||
event["truncated"] = True
|
||||
return ToolOutcome(f"{document.title}\n\n{body}", event)
|
||||
|
||||
|
||||
@@ -912,8 +935,21 @@ async def _run_ask_user(context: ToolContext, args: dict[str, Any]) -> ToolOutco
|
||||
session-free snapshot that deliberately holds no way to reach one. Getting
|
||||
here means some other path called `run_tool` directly, and saying so is
|
||||
better than returning an empty answer the model would treat as a reply.
|
||||
|
||||
It read `args["question"]`, singular, against a schema that declares
|
||||
`questions` and a list -- so the event it built always carried an empty
|
||||
`query`, and the card showed a refusal with no sign of what had been asked.
|
||||
Harmless only because this path is unreachable, which is exactly why nothing
|
||||
caught it: schema drift on a branch no test exercises. Tolerant of the same
|
||||
spellings `generation._questions_in` accepts, rather than importing it,
|
||||
which would be a circular import for one field on a dead path.
|
||||
"""
|
||||
question = str(args.get("question") or "").strip()
|
||||
asked: Any = args.get("questions") or args.get("question") or ""
|
||||
if isinstance(asked, list):
|
||||
asked = asked[0] if asked else ""
|
||||
if isinstance(asked, dict):
|
||||
asked = asked.get("question") or ""
|
||||
question = str(asked).strip()
|
||||
return ToolOutcome(
|
||||
"That question could not be put to anyone, so it has gone unanswered. "
|
||||
"Carry on without it, or say what you need.",
|
||||
@@ -997,7 +1033,11 @@ REGISTRY: dict[str, ToolDef] = {
|
||||
ToolDef(
|
||||
name="knowledge_get",
|
||||
family=FAMILY_KNOWLEDGE,
|
||||
description="Read one knowledge document in full, by the id a search returned.",
|
||||
description=(
|
||||
"Read one knowledge document, by the id a search returned. A "
|
||||
"long one is cut off at the end rather than refused, and you "
|
||||
"are told when that happened."
|
||||
),
|
||||
parameters=_object({"id": _STRING}, ["id"]),
|
||||
run=_run_knowledge_get,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user