Skip to content

docs: lead the integration story with the engine library - #157

Merged
David Koleczek (DavidKoleczek) merged 1 commit into
mainfrom
docs/lib-first-integration
Aug 26, 2026
Merged

docs: lead the integration story with the engine library#157
David Koleczek (DavidKoleczek) merged 1 commit into
mainfrom
docs/lib-first-integration

Conversation

@DavidKoleczek

Copy link
Copy Markdown
Collaborator

Why

The docs presented five integration surfaces as peers, which understated the one that is actually the contract. amplifier_agent_lib is the engine; the CLI, the HTTP face, and both wrapper SDKs are adapters over it.

Weighting them equally has a measurable consequence: a Python host reading these docs reaches for a subprocess when it could have imported the library. The in-process path was documented in five lines with no code, while the wire protocol got 47 lines with full argv and envelope tables, and all seven checklist items were subprocess-only.

What changed

docs/INTEGRATION.md and skills/amplifier-agent/SKILL.md now open with a complete, runnable embedding, then reframe the wrappers as what you reach for when your host cannot import Python in-process.

  • Install is one shared section. A single distribution ships the library and the binary together, and the binary is presented as the setup and diagnostics surface (doctor, auth set, models list) rather than the runtime path.
  • Checklists and error codes are split by surface, so an embedder is not reading binary_not_found and exit codes as their own concern.
  • Provider selection is documented via enumerate_resolvable_providers() rather than left implicit.

docs/spec/engine-api.md was not followable end to end. It documented Engine but never named make_turn_handler, so a reader following it exactly could not construct one. It now carries the turn assembly sequence and names the two symbols on that path that sit outside the lib's public surface.

Three claims in it were stale and are corrected against observed behavior:

Claim Reality
"TurnSubmitResult carries exactly reply, turnId, sessionId" Eight keys. The five usage fields landed with per-turn usage accounting.
"bundle_override exists for tests; production callers leave it None" It does not decide which bundle serves the turn; the make_turn_handler closure does. Passing it avoids a redundant re-prepare inside boot(), which is why the CLI passes it in production.
Child-to-parent cost bridging listed as unsupported Implemented.

The "no second session-factory path" non-goal is narrowed to embedders, since amplifier_agent_http reaches the runtime through a private path of its own.

Newly documented behavior that embedders hit and had nowhere to read about:

  • Continuity is a function of session id and workspace, not object lifetime. A second submit_turn on one booted Engine succeeds but does not see the first turn.
  • Clearing mount_plan["providers"] before injection is load-bearing. inject_provider is a no-op when a provider is already mounted, so skipping it silently discards the injection.
  • CliApprovalSystem() with no arguments declines everything.
  • Importing the library overwrites AMPLIFIER_HOME in os.environ.
  • costUsd is a Decimal, so json.dumps on the result needs default=str.

README.md, docs/ARCHITECTURE.md, docs/LAYERS_AND_RELEASES.md, docs/SPEC.md, docs/ECOSYSTEM.md are updated only where they contradicted the above. SPEC.md moves engine-api.md from "The protocol" into "Surfaces callers drive".

Verification

Both published code samples were extracted back out of the finished docs and executed verbatim against a fresh install from git, each returning a real agent reply:

uv pip install "git+https://github.com/microsoft/amplifier-agent"
  -> amplifier-agent==0.16.0, installs the lib and the binary

embedded turn -> exit 0
{"reply":"pong","turnId":"turn-1","sessionId":"embed-demo-1",
 "tokensIn":12776,"tokensOut":4,"cacheReadTokens":11904,
 "cacheWriteTokens":0,"costUsd":Decimal("0.0062472")}

The multi-turn and resume claims were tested rather than assumed: two submit_turn calls on one Engine both return, but turn two does not recall turn one; a fresh Engine per turn with is_resumed=True and the same session id does.

make verify passes clean (verify: ALL GATES PASSED).

Scope of impact

Docs-only. No engine, wrapper, or protocol change. No CHANGELOG entry, matching the convention for docs-only changes in this repo.

Follow-ups, not addressed here

Two defects surfaced while verifying and are left for separate changes:

  • foundation_home.bind() sets os.environ["AMPLIFIER_HOME"] unconditionally, discarding a value the host already set. setdefault would fix it. Documented as a footgun in the meantime.
  • The gemini provider passes credential detection and reaches the LLM, then crashes in session_store.py:52 with TypeError: Object of type SimpleNamespace is not JSON serializable. This is why the docs advise choosing from enumerate_resolvable_providers() against your own preference order rather than taking the first entry.

The docs presented five integration surfaces as peers, which understated the
one that is actually the contract. `amplifier_agent_lib` is the engine; the
CLI, the HTTP face, and both wrapper SDKs are adapters over it. Weighting them
equally led Python hosts to spawn a subprocess when they could have imported
the library, and left the in-process path documented in five lines with no
code.

INTEGRATION.md and skills/amplifier-agent/SKILL.md now open with a complete,
runnable embedding and reframe the wrappers as what you reach for when your
host cannot import Python in-process. Install becomes one shared section,
since a single distribution ships the library and the binary together, and the
binary is presented as the setup and diagnostics surface (doctor, auth set,
models list) rather than the runtime path. Checklists and error codes are split
by surface so an embedder is not reading binary_not_found and exit codes as
their own concern.

spec/engine-api.md was not followable end to end: it documented `Engine` but
never named `make_turn_handler`, so a reader following it exactly could not
construct one. It now carries the turn assembly sequence and names the two
symbols on that path that sit outside the lib's public surface. Three claims in
it were also stale and are corrected against observed behavior:

- `TurnSubmitResult` carries eight keys, not three. The five usage fields
  landed with per-turn usage accounting.
- `bundle_override` does not decide which bundle serves the turn; the
  `make_turn_handler` closure does. Passing it avoids a redundant
  re-prepare inside `boot()`, which is why the CLI passes it in production.
- Child-to-parent cost bridging is implemented and no longer a limitation.

The "no second session-factory path" non-goal is narrowed to embedders, since
amplifier_agent_http reaches the runtime through a private path of its own.

Also documents behavior that embedders hit and had nowhere to read about:
continuity is a function of session id and workspace rather than object
lifetime (a second submit_turn on one booted Engine succeeds but does not see
the first turn), clearing mount_plan["providers"] before injection is
load-bearing, CliApprovalSystem() with no arguments declines, and importing
the library overwrites AMPLIFIER_HOME in os.environ.

README, ARCHITECTURE, LAYERS_AND_RELEASES, SPEC, and ECOSYSTEM are updated
only where they contradicted the above.

Both published code samples were executed verbatim against a fresh install
from git and returned a real agent reply.

Scope of impact: docs-only. No engine, wrapper, or protocol change.

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
@DavidKoleczek
David Koleczek (DavidKoleczek) merged commit 8280387 into main Aug 26, 2026
4 checks passed
@DavidKoleczek
David Koleczek (DavidKoleczek) deleted the docs/lib-first-integration branch August 26, 2026 21:49
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.

1 participant