Skip to content

feat(knowledge): section action docs by header and serve a digest to agents (v1.57.0) - #197

Merged
paulkr merged 8 commits into
mainfrom
feat/knowledge-sections
Sep 15, 2026
Merged

paulkr merged 8 commits into
mainfrom
feat/knowledge-sections

Conversation

@moekatib

Copy link
Copy Markdown
Contributor

Summary

one --agent actions knowledge now returns a digest instead of the whole document. Action docs are split on markdown headings; the sections an agent needs to build a correct request (method/URL, headers, description, enforcement rules, required + optional parameters, sample request, gotchas, error handling) come back in full, and the size drivers (success response sample, response fields table, worked examples, scraped reference schemas) are listed by name and size so the agent can ask for exactly what it needs. Docs at or under 8k chars are returned whole.

one --agent actions knowledge <platform> <actionId>                    # digest (default)
one --agent actions knowledge <platform> <actionId> --section response # by heading, id, or alias; comma-separated
one --agent actions knowledge <platform> <actionId> --toc              # every heading, no document
one --agent actions knowledge <platform> <actionId> --full             # previous behaviour

Section and --toc calls are served from the disk cache with no network round trip. Human (non-agent) output is unchanged.

Backward compatible. knowledge and method keep their shape and the execution-guide preamble still leads the markdown. A truncated digest carries a one-line banner at the top and a notice at the end naming the omitted sections and the exact commands to load them, so an agent that only reads the text still knows the document continues. The JSON adds truncated, sections[] (omitted sections only, with id, heading, chars), omitted, omittedChars, and more. An unknown section name returns an error listing every available section.

Results

Measured on the full JSON an agent receives, across all 359 action docs cached locally (46 platforms). Full report with charts, per-platform breakdown, and the ten biggest wins: https://claude.ai/code/artifact/1ded5790-c441-4120-b005-fdfd73f6dcc1

Measure Before After Saving
Total bytes, whole corpus 4.93M chars 3.13M chars −36%
Average per doc, unweighted −12%
Average per doc the digest trims (180 docs over 8k) −26%
Median response 9.6k 8.9k −7%
90th percentile response 19.7k 11.2k −43%
99th percentile response 83.9k 14.5k −83%
Largest response 380k 16.2k −96%

By source-document size:

Source size Docs Avg before Avg after Saving
≤ 8k (returned whole) 179 6.9k 7.0k −1%
8–12k 106 11.3k 10.0k 11%
12–20k 47 17.1k 10.9k 37%
20–40k 19 27.0k 10.7k 61%
> 40k 8 147.5k 13.9k 91%

No bucket gets worse. The ceiling is the number that matters for agent reliability: nothing exceeds 16.2k chars now, where seven docs used to exceed 50k.

Subagent trials

Five fresh subagents, none told the feature existed, each ran a real search → knowledge → execute loop through the dev build on read-only actions.

Trial Task Digest Full Extra sections needed Executes
GitHub 3 most recently updated open issues 8.4k 23.2k none 1
Stripe 5 recent customers, then 3 recent charges 9.4k 16.2k none to execute; loaded Response Fields afterwards to confirm the amount unit 1 + 1
Gmail 5 most recent unread inbox messages 11.8k 19.1k one (Response), worked first time completed; retries were caused by two unrelated backend defects, below
Notion search pages by title 10.5k 17.3k none 1
Notion, rerun on final build same search + probes on the 134k Append Block Children doc 16.4k 140.5k one section 1.4k; not-found error 7.3k; --toc 39k 1

Every agent recognised the digest as partial and quoted truncated and the closing notice. No execute failed because of missing documentation.

What the trials found and fixed

  • Nested required fields were dropped. Notion's page-update doc puts Required fields under an "Option B" subheading, which was deferred wholesale. A deferred H3+ node containing essentials now keeps its own text and decides its children individually.
  • The table of contents outweighed the document. On a 414-heading doc the list was 45 KB and was re-sent on every --section call (a 795-char section cost 47 KB). It is now collapsed for mega-docs, sent only on truncated responses, and lists only omitted sections. That section now costs 1.4 KB.
  • --section responses said truncated: true, which an agent looping until the flag clears would never escape. Now false, with a resolved list showing what each name matched.
  • Duplicate headings returned only the first copy. Both ## Response blocks in a scraped doc now come back.
  • Metadata overhead made the median doc larger. The first measurement showed 258/359 docs growing by ~1.9k chars because every response listed every heading. Fixed by sending the list only when truncated and only for omitted sections.

Not in this PR

  • The MCP server lives in the external @withone/mcp package and still returns the full document. src/lib/knowledge-sections.ts is pure (no I/O) so it can be imported there for parity.
  • Two defects the Gmail trial exposed, outside the CLI: the passthrough keeps only the last value of a repeated query parameter (so metadataHeaders arrays silently lose entries even though the docs recommend them), and the custom Gmail actions require the connection key inside the body as well as positionally.

Trade-off

Response Fields is deferred by default. For reporting tasks it sometimes holds the one detail that matters (Stripe: "amount is in the smallest currency unit"). Agents in the trials loaded it when needed, which is the intended path and costs one cached call.

Test plan

  • 39 new unit tests in src/lib/knowledge-sections.test.ts against three real docs as fixtures (canonical, custom-action, and GraphQL templates; headings inside code fences; multi-H1 docs; size cap; nested required fields; TOC collapse; alias and duplicate-heading lookup)
  • Batch run of the digest over all 359 cached docs: zero parse errors, every doc keeps method/URL and required parameters, no bucket larger than before
  • Five end-to-end subagent trials (above)
  • Docs updated: src/lib/guide-content.ts, skills/one/SKILL.md, README.md, src/cli.ts help, src/commands/onboard.ts
  • Version bumped to 1.57.0 (hand-edited lockfile per repo rule)
  • npm test: 570 pass; the 6 failures in config.test.ts are pre-existing on main (macOS /var vs /private/var symlink)

🤖 Generated with Claude Code

https://claude.ai/code/session_016bU7frFFLis1hLrcR1w2HY

moekatib and others added 6 commits September 14, 2026 15:59
…agents (v1.57.0)

`one --agent actions knowledge` now returns a digest instead of the whole
document. Action docs are split on markdown headings; the sections an
agent needs to build a correct request (method/URL, headers, description,
enforcement rules, required + optional parameters, sample request,
gotchas, error handling) are returned in full, and the size drivers
(Success Response, Response Fields, worked examples) are listed in a
table of contents with ids and char counts. Docs at or under 8k chars
are returned whole.

The agent loads more by name, id, or alias with `--section` (repeatable
or comma-separated; a parent brings its children), or everything with
`--full`. Both are served from the disk cache with no network call. An
unknown name returns an error listing every available section.

Backward compatible: `knowledge` and `method` keep their shape, the CLI
execution guide preamble still leads the markdown, and a truncated
digest ends with a notice naming the omitted sections and the exact
commands to load them, so an agent that only reads the text still knows
the document continues. `truncated`, `sections[]`, `omitted`,
`omittedChars`, and `more` are added to the JSON envelope. Human output
is unchanged (full document).

Fixtures are three real action docs (gmail custom template, stripe,
github) with internal hostnames scrubbed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bU7frFFLis1hLrcR1w2HY
… add GraphQL headings

A batch run over all 359 cached action docs found three gaps in the
digest rules:

- Required fields nested under a deferred sub-heading (Notion's
  `Request Body > Option B > Required ...`) were dropped with their
  parent. A deferred H3+ node that contains essentials now keeps its own
  text and decides its children individually.
- The budget back-fill could pull random schema types out of appended
  "Data Models — Chunk N" H1 sections. Only H2+ sections are back-filled.
- GraphQL docs use `Request Variables`, `Path Parameters`, and `Query
  Parameters`; those are now essential. `Type` was removed from the
  essential set (it only appears inside schema appendices).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bU7frFFLis1hLrcR1w2HY
From a subagent trial of the digest:

- `--section` responses reported `truncated: true` because the doc as a
  whole was not present. An agent looping "while truncated, fetch more"
  would never stop. The requested sections are returned whole, so the
  flag is now false there; `sections[]` still shows what else exists.
- Add `resolved` (the section ids each name matched) so alias and
  prefix matches like `examples` -> `example-usage` are visible.
- Add a one-line digest banner at the top of the markdown, right after
  the execution guide, so an agent that stops reading early still knows
  the document continues. The detailed notice stays at the end.
- `more.section` now says `<id or heading>` to match the footer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bU7frFFLis1hLrcR1w2HY
…uplicate headings

From a subagent trial against Notion's 134k-char Append Block Children
doc (414 headings):

- The `sections` list was 45 KB — more than the digest — and was
  re-sent on every `--section` call, so a 795-char section cost 47 KB.
  The digest now collapses the list to the action doc's own H2/H3s plus
  one entry per appended chunk (`children` counts the hidden ones,
  `sectionsCollapsed: true`, `sectionCount`). Section responses no
  longer carry the list at all. `--toc` returns the complete list.
  Not-found errors use the same collapse. Digest: 57 KB -> 19 KB;
  one section: 47 KB -> 1.6 KB.
- `--section Response` on a doc with two `## Response` headings
  returned only the first, silently. Exact id and exact heading matches
  are now unioned, so both copies come back.
- Appended H1 chunks are labelled `(appendix)` in the notice so a
  second copy of the endpoint doc no longer reads as if the core doc
  were missing. The notice names at most 12 omitted sections.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bU7frFFLis1hLrcR1w2HY
"mostly response shapes" understated what is omitted on schema-heavy
docs (Notion's append block children defers the block schemas). Name
the three kinds instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bU7frFFLis1hLrcR1w2HY
Measured over all 359 cached docs, the full-JSON payload had grown for
258 of them: the table of contents listed every heading (~1.9k chars)
even on small docs returned whole, wiping out the digest's savings on
the median doc. Untruncated responses now carry no section list;
truncated ones list only what was omitted, since included sections are
already the headings of the markdown.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bU7frFFLis1hLrcR1w2HY
@moekatib
moekatib requested a review from paulkr September 14, 2026 13:39

@siddharth-bhansali siddharth-bhansali left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review — approve ✅

Reviewed the approach, ran the tests, and independently stress-tested the digest across the full local corpus. This is a strong, well-scoped change that solves the CLI knowledge-truncation problem cleanly. Highlights of what I verified beyond the PR's own results:

Independent batch over all 203 locally-cached docs (46+ platforms):

  • 0 parse errors, 0 essential sections dropped, 0 digests larger than source, 40% total chars saved, ceiling ~8k. The core correctness invariant ("every doc keeps method/URL + required params") holds across the whole corpus, not just the fixtures.
  • Enumerated every deferred heading across the corpus — they're all appropriately non-essential (responses, response-fields, notes, examples, optional params, errors, filters/sorting). No request-shaping heading is being deferred, so the essential allowlist is well-tuned to the actual doc conventions.

Edge cases (all clean): no-heading doc returns whole; empty doc no-ops; a 30k essential section is capped to ~6k with a clear [truncated — N more chars, load with --section] marker instead of blowing the budget; a doc that is entirely deferred content collapses to a TOC. parseSections/buildDigest are pure and never threw.

Design: the section-tree + essential-allowlist + deferred-TOC + on-demand load is the right pattern — it's lossless (nothing removed, just deferred and retrievable) and deterministic/pure (hence the 39 solid unit tests). The truncated-digest banner + closing notice reliably tell an agent the document continues, which the subagent trials confirm.

Follow-ups (none blocking)

  1. MCP parity — the important one. The MCP path (@withone/mcp) still returns full docs. knowledge-sections.ts is pure by design so it can be imported there; this is the natural next PR and the bigger win for the benchmark (MCP is where a large tool surface hurts most).
  2. Allowlist is doc-structure-dependent. It validated clean on 203 docs, but it's tuned to current Intelliscan heading conventions — worth a periodic re-audit (the batch check above is a 30-line script) or a CI guard if heading conventions drift. Low priority.
  3. The two backend defects the Gmail trial surfaced (passthrough dropping repeated query params; custom Gmail actions needing the connection key in the body) are real and worth their own tickets — outside this PR.

Nice work. LGTM to merge.

@paulkr
paulkr merged commit 1b44d70 into main Sep 15, 2026
7 checks passed
@paulkr
paulkr deleted the feat/knowledge-sections branch September 15, 2026 13:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants