Skip to content

feat(tools): export provider-neutral knowledge tools and record a no-use retrieval - #168

Merged
drewstone merged 1 commit into
mainfrom
feat/knowledge-tools
Aug 21, 2026
Merged

feat(tools): export provider-neutral knowledge tools and record a no-use retrieval#168
drewstone merged 1 commit into
mainfrom
feat/knowledge-tools

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

Why

Two gaps, both from #151, and one of them is what makes every earlier PR in this wave reachable by an agent.

No tool adapter. Knowledge exported no ToolDefinition, and agent-runtime exported none either (grep'd: it imports agent-knowledge in 5 files, none of which expose search, read, record, or resolve). Every consumer had to write its own adapter, which is how the discovery-lab fork started.

No no-use disposition. A use receipt requires a selected rank, so a retrieval that influenced nothing left no record at all. "No evidence of use" and "evidence of no use" were indistinguishable in the ledger — and the second is the interesting one when you are measuring whether retrieval changes behaviour.

What

createKnowledgeTools({ stores, runId, retrieverVersion, actorId?, pagesDirectory?, intake?, brief?, recordRetrieval? }) returns four ToolDefinitions using the neutral shape from @tangle-network/agent-interface. Knowledge owns every handler; a runtime transports the definitions and the calls and runs no knowledge loop.

  • knowledge_searchbuildKnowledgeBrief over the run's chain, then a retrieval receipt on every call, handed to recordRetrieval and returned in the result. Retrieval is recorded by the infrastructure, not claimed by the run.
  • knowledge_read → chain read by qualified id. An id visible at two origins is reported ambiguous with both candidates and is never silently resolved to one.
  • knowledge_recordapplyKnowledgeWriteBlocks into this run's store, with the intake gate and the inherited chain entries as its corpus.
  • knowledge_resolveresolveRunScopedCitations, returning each reference's status without hiding an ambiguity.

retrieverVersion is required rather than inferred: a bundled build cannot read its own manifest, and a receipt that guessed the version would be a receipt that lies about what ranked the results.

createKnowledgeRetrievalDisposition / verifyKnowledgeRetrievalDisposition record a retrieval that influenced nothing. The disposition binds to one retrievalReceiptDigest, carries relation: 'irrelevant' | 'no-use', and has no rank — that absence is the whole point. irrelevant means the returned pages did not bear on the question; no-use means they did and the consumer used none of them.

Simplification

Simplification: the block that copies actor identity from a retrieval receipt into a downstream record (runId, actorId, profileDigest, executionRef, retrievalReceiptDigest) was about to exist twice. It is now retrievalIdentity(retrieval), and both useMaterial and dispositionMaterial route through it, so a downstream record cannot disagree with the receipt it references.
Net: +531 / -10 lines, 9 files, 1 identity-copy block collapsed to 1 owner before a second copy existed, 0 new schemas beyond the disposition itself (it reuses the receipt module's version, digest algorithm, consumer ref, evidence refs, and attribute normalizers).
Not done here: no MCP or provider-specific packaging; ToolDefinition is the neutral contract and adapting it is the runtime's job. No sensitivity-policy seam on the tools; the application still filters by supplying roots and brief options.
Tests: +4 (a search mints a receipt that the visibility snapshot verifies and joins, which is the contract that makes retrieval provable through the tool rather than only through the API; a recorded proposal with a dangling cite is refused, which proves the intake gate is actually wired into the tool and not merely typed; an id visible at two origins reports both candidates and resolves only when qualified, the run-scoped invariant that is easy to break by "helpfully" picking one; a disposition verifies against its own retrieval and is refused against another, the binding that stops a no-use record from being reattached), -0 deleted. No shape assertions.

Proof

pnpm run typecheck   pass (src + contracts)
pnpm run lint        pass, 241 files
pnpm run build       pass
pnpm api:surface     984 exports across 6 entry points, regenerated and committed
check:version-bump   7 export changes needing a minor bump, paid for by 10.5.0 -> 10.6.0
vitest src/knowledge-tools.test.ts src/knowledge-use-receipts.test.ts   26 passed
pnpm test (full)     128 failed | 654 passed | 16 skipped (798)

128 local failures — the same number and the same files as the four merged PRs before this one and as clean origin/main on this macOS host. Every one raises knowledge transaction directory escaped its root from the /var -> /private/var symlink under tmpdir(). Linux CI has no such symlink; the new tests realpath their roots and are green locally.

Closes #157
Refs #151

@tangletools tangletools left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Auto-approved drewstone PR — 0c2281ab

This PR was opened by the trusted drewstone account.

This approval is provisional and was applied by the local stand-in because the pr-reviewer webhook host is unreachable (2026-08-21). CI on this head is fully green. The full PR reviewer audit re-runs via the resweep when the service returns and will publish findings if it detects issues.

@drewstone
drewstone merged commit d261b09 into main Aug 21, 2026
2 checks passed
@drewstone
drewstone deleted the feat/knowledge-tools branch August 21, 2026 05:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants