Skip to content

feat(dev): OTLP trace storage for local dev - #2039

Closed
tejaskash wants to merge 174 commits into
mainfrom
feat/dev-otel-storage
Closed

feat(dev): OTLP trace storage for local dev#2039
tejaskash wants to merge 174 commits into
mainfrom
feat/dev-otel-storage

Conversation

@tejaskash

Copy link
Copy Markdown
Contributor

First slice of the project dev tracing work (#1980 restacks onto this; see #2013's closing comment for the full split plan).

What this does

The storage half of local trace collection, with no I/O beyond the filesystem:

  • otel/transforms.ts — pure OTLP wire handling: split one export batch into per-trace payloads (SDKs batch by time, not trace — attributing a whole batch to its first trace id corrupts trace identity), normalize protobuf/JSON ids to hex, and shape spans/logs for display (attribute flattening, transport-noise filtering).
  • otel/store.ts — append-only storage: one JSON Lines file per trace under a directory, raw reads back. Malformed lines are skipped, never fatal (a half-written line must not break reads).

Ported from the current CLI's operations/dev/otel/transforms.ts with one correctness fix over the original: the per-trace batch partitioning described above.

What consumes it

#1980 (the OTLP/HTTP collector + project dev wiring) stacks directly on this branch — the consumer is one PR up, visible now.

Verification

23 tests (happy + unhappy paths per layer, audited for redundancy), tsc/lint/format clean. End-to-end proof of the full pipeline lives in #1980's stack (real project, real Bedrock invocation, traces on disk).

AlexanderRichey and others added 30 commits June 23, 2026 14:23
fix(style): add .tsx to precommit hook + align on testing standard.
feat: add config command with positional argument support
Add Router.default(fn) to register a handler that runs when a group is
selected without a subcommand (e.g. `agentcore` or `agentcore harness`).

The leaf action-building logic is extracted into a shared attachAction
helper so leaves and default handlers execute uniformly: same middleware
wrapping, global-flag injection into context, and flag/arg parsing. A
group's default handler runs with the group's own middleware and can read
both inherited and its own group-level flags from the context. It has no
own flags/arguments (globals-only).
Three group-level flag tests passed flags as a third constructor argument
(`new Router("app", "", [RegionKey])`), a form the constructor does not
accept, so the globals were never registered. Commander then rejected the
flags as unknown options and called process.exit, aborting the suite.
Switch to the chainable .groupFlags(...) API so the flags register.
Add a React + Ink TUI, mounted through the router's default-handler support:

- renderTui(core) returns a DefaultHandle that reads the command path from
  the context and renders the Root component with the path and injected core
  clients. Wired onto the `agentcore` root and the `harness` group so bare
  invocations (`agentcore`, `agentcore harness`) open the TUI.
- withTuiOnEmptyFlagsAndArgs(core) is a middleware that opens the TUI when a
  leaf command is invoked with no flags or arguments (and not --json),
  otherwise delegates to the real handler.
- Hoist the Core type into handlers/types.tsx and thread it (rather than the
  bare CoreHarnessClient) through the harness handlers.

Add react, ink, and @types/react as dependencies.
Relocate renderTui from components/index.tsx to a new src/tui/ directory to
keep the TUI entry points organized, and update its three importers
(middleware, root handler, harness handler). This is where the upcoming
renderJson() will also live. Root stays in components/ and is imported by
tui/ from ../components/Root.
useEffect(exit) with no dependency array ran on every render, so exit()
fired repeatedly. Add an empty deps array so the effect runs a single time
when HelpScreen mounts.
Add renderJson(data) alongside renderTui in src/tui/, which prints an
indented JSON representation of a value to stdout. Use it in the harness
get/list handlers in place of raw console.log so their output is
consistently pretty-printed.
tejaskash and others added 21 commits August 13, 2026 12:26
* feat(project): implement project build

`agentcore project build` compiles the project's CDK app and synthesizes its
CloudFormation templates, so the deployable artifacts exist before deploy.

Synthesis runs offline: each stack's environment comes from aws-targets.json,
so no credentials are needed. An empty targets file makes the CDK app fail with
its own actionable message rather than the CLI guessing an account.

The generated package.json defines `cdk` as "npm run build && cdk", so one
`npm run cdk -- synth --quiet` covers both compile and synthesis.

Also puts withProject to work for the first time: it resolves the enclosing
project and hands it to the handler through ProjectKey, and it wraps only build
so that `create` (which refuses to nest inside a project) stays unaffected. Its
cwd is now resolved per invocation instead of at wiring time, so the directory
the user actually ran in is the one searched.

* fix(project): set runtimeVersion on the CodeZip template

The CDK construct library rejects a CodeZip runtime that declares no
runtimeVersion ("runtimeVersion is required for CodeZip builds"), and it is the
field that selects the packager. Without it, synthesizing a freshly created
python project fails on its own scaffolded config.

Container builds take their version from the image, so the container template
is unaffected.

* feat(project): dispatch build on the project's managedBy backend

agentcore.json already records `managedBy` (CDK is the only value today), but
nothing read it: build() hardcoded the CDK path, so adding a terraform or
no-IaC backend later would have meant editing that path instead of adding
alongside it.

Carry managedBy on Project and switch on it in build(), delegating the CDK
work to a private buildWithCdk(). The default arm assigns to `never`, so a new
ManagedBySchema member fails to compile until it has an arm here.
* feat(gateway): add delete commands

* feat(gateway): complete delete command surface

* test(gateway): record delete fixtures with e2e profile

* test(gateway): clean up partial delete fixtures

* test(gateway): inject logs client in delete fixtures
* feat: add JSONL draft diff logic and update command support

* fix: precompute all batches and add incremental ID persistence

* fix: model MalformedResponseError

* chore: add onProgress pattern to dataset update batches

* chore: add fixture test for dataset update flow

* fix: remove first-response behavior, sanitize URLs, and verify draft state when writing

* fix: add missing logs stub in update test
* feat(dev): add container dev runner

* fix(dev): address container runner review

* fix(dev): address container runtime feedback
* feat(gateway): add headless invoke command

* fix(gateway): preserve responses and redact paths

* fix(gateway): sign authenticate-only invocations

* test(gateway): update client construction after rebase

* fix(gateway): allow empty no-content responses on TTY

* test(gateway): update mutation helper dependencies

* fix(gateway): preserve raw query values for SigV4

* fix(gateway): remove rebase conflict markers
* feat: add read-only datasets TUI

* chore: replace OMIT parameter with supportedTUICommands handler
* feat(runtime): add contextual TUI actions

* fix(runtime): show endpoint failure reason
Co-authored-by: jariy17 <tjariy+jariy17@users.noreply.github.com>
* feat(project): implement add harness scaffolding

* refactor(test): clean up tests

* refactor(project): rename add to addResource
* feat(tel): implement otel sink

* docs(tel): add docstring comments to otel sink

* test(tel): add a test of a local server for the collector sink

* chore: remove extra space from telemetry test file

* docs: add note in comment about default value

* fix(tel): avoid letting flush failures stop shutdown

* feat(tel): swap to cli from error method

* fix(tel): remove duplicate resource attributes

* feat(tel): add installationId header and temporarily preference

* test(tel): adjust test to use passed down resource attributes
* feat(gateway): add interactive invoke console

* fix(tui): keep long multiline input within its rows

* fix(gateway): refine path editor and session labels

* style(gateway): use neutral path dialog border

* fix(gateway): make idle escape navigate back

* fix(gateway): allow no-content responses in TUI

* refactor(invoke): simplify TUI launch errors

* test(gateway): allow TUI startup on slow runners

* test(gateway): inject invoke TUI renderer
* feat(project): implement add harness scaffolding

* refactor(test): clean up tests

* refactor(project): rename add to addResource

* feat(project): implement full add functionality for harness

* feat(project): finish add implementation

* refactor(proj): config --> spec

* fix(proj): fail runtime early

* fix(proj): swap to relative path for harness.json path reference

* feat(proj): wire up dockerfile support

* fix(proj): wire in gateway outbound auth

* feat(schemas): add credentialArn for harness skills for non-project credentials

* fix(harness): add flag for explicit vpc id to support vpc + dockerfile edgecase

* feat(proj): handle partial failures of harnesss scaffolding

* fix(harness): strip system prompt from config to ensure file is source of truth

* fix(test): use path module to build path for windows support

* test(harness): add case for unrecognized add flags

* fix(add): reject vpc id if networkConfig is not present

* test(harness): verify duplicate names are rejected

* fix(add): log failed rollback cleanup errors

* fix(add): add scaffolded paths before scaffolding

* docs: add comments above long paramtrized tests
* feat(project): wire dev handler

* fix(dev): read runtimes from Project.spec after #2004

* fix(dev): address project dev review feedback
`project create` scaffolds `agentcore/aws-targets.json` as an empty list, but
the generated CDK app threw on an empty list and synthesized one stack per
target, so a freshly created project had nothing to synthesize and
`project build` failed:

    AgentCore CDK synthesis failed: No deployment targets configured.
    Please define targets in agentcore/aws-targets.json

Build does not need to know where a project deploys. An empty target list now
synthesizes a single environment-agnostic stack, which resolves its account and
region from CloudFormation pseudo-parameters instead of pinning them at synth
time. That is enough to compile the app and produce a template, which is what
build is for.

The fallback stack is named `AgentCore-<project>` and carries no
`agentcore:target-name` tag, so it is not a deploy candidate: only a stack
synthesized for a real target is tagged, and selecting on that tag is how a
stack is chosen to ship.

A project that does configure targets is unaffected, still synthesizing one
environment-pinned, target-tagged stack per entry.
* refactor(eval): address on-demand review feedback

* test(eval): consolidate on-demand regression coverage

* fix(eval): restore semicolon and document runWithRealCore rationale

- Restore dropped semicolon in src/core/types.tsx that failed format:check
- Explain why runWithRealCore stubs the SDK .send() seam inline instead of
  using the golden fixture harness (failure conditions the recorder can't
  provoke against a live account)

---------

Co-authored-by: jariy17 <tjariy+jariy17@users.noreply.github.com>
* fix(harness): avoid writing a corrupted spec to disk

* fix(error): make deserialization error a user error

* fix(test): update failing tests

---------

Co-authored-by: Hweinstock <hkobew@amazom.com>
Pure OTLP wire handling (per-trace batch partitioning, id normalization,
frontend shaping) and append-only per-trace JSONL storage. A batch routinely
carries spans from several traces, so persistence partitions by trace id —
writing whole batches under the first id corrupts trace identity. Consumed by
the OTLP collector in #1980, which stacks on this.
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.95918% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.14%. Comparing base (1a21206) to head (722149a).

Files with missing lines Patch % Lines
src/core/dev/otel/transforms.ts 97.32% 6 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           refactor    #2039      +/-   ##
============================================
+ Coverage     97.13%   97.14%   +0.01%     
============================================
  Files           381      383       +2     
  Lines         22782    23076     +294     
============================================
+ Hits          22130    22418     +288     
- Misses          652      658       +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot removed the agentcore-harness-reviewing AgentCore Harness review in progress label Aug 19, 2026
@tejaskash
tejaskash changed the base branch from refactor to main August 19, 2026 19:22
@tejaskash tejaskash closed this Aug 19, 2026
@github-actions github-actions Bot added the size/l PR size: L label Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/l PR size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants