Skip to content

feat(output)!: replace agent mode with a default JSON envelope for everyone - #899

Merged
platinummonkey merged 1 commit into
mainfrom
refactor/remove-agent-mode
Oct 15, 2026
Merged

platinummonkey merged 1 commit into
mainfrom
refactor/remove-agent-mode

Conversation

@platinummonkey

@platinummonkey platinummonkey commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Breaking change. Removes agent mode. Output and help are now identical for humans and AI agents, and the {status, data, metadata} JSON envelope is the default output for everyone. Agent detection now drives two things only: the ai-agent <name> User-Agent token, and auto-approving CLI prompts (unchanged from main).

Breaking changes

  • JSON and YAML output are enveloped by default, for humans too. On main, humans got the raw body and only detected agents got the (JSON-only) envelope.
    • A response with a top-level data keeps it in place. Its sibling keys (meta, links, included) are now kept; the old envelope dropped them, losing cursors. status and metadata are added alongside.
    • Any other response (v1 arrays, the metrics query body, etc.) becomes data. Example: monitors list | jq '.[]' becomes jq '.data[]'.
    • A response that already has its own status or metadata key is wrapped whole. For example, monitors search keeps its API pagination at data.metadata.
  • Table/CSV/TSV print only rows; metadata goes to stderr. CSV and TSV now honor each command's row and column hints the same way the table does, so monitors search -o csv prints one row per monitor (it used to print the whole {counts, metadata, monitors} body as a single row). Table, CSV and TSV always print the metadata to stderr as one block:
    metadata: command="monitors search" count=30 truncated=true next_page=1
    next_action: More results available; rerun with --page=1
    warning: <when present>
    
    The metadata: line is always printed, with keys in a fixed order (command, count, truncated, next_cursor, next_offset, next_page), absent keys omitted and strings JSON-quoted. With --no-envelope only warning: lines are printed. CSV/TSV changed for commands with hints: monitors list/search, logs search, slos list, users list, incidents list, dashboards list, and pup format --rows-at/--row-at/--columns. Commands with explicit columns now print only those columns. --jq with table/CSV/TSV renders the jq result as-is.
  • --jq results are wrapped too. On main, humans got the bare filter result; now it becomes the envelope's data. So id=$(pup monitors get 1 --jq '.id') yields an envelope instead of 1. Use --no-envelope (or PUP_NO_ENVELOPE=1) to get the bare result.
  • pup api output is enveloped, including with -i: the response headers are printed first and describe the raw body, then the envelope follows.
  • Envelope shape changed.
    • metadata.note is gone.
    • metadata.truncated is always present.
    • New fields: next_cursor, next_offset, next_page, warnings.
    • After --jq, count describes the filtered data; truncated and next_cursor are kept.
  • Opt-out: --no-envelope (or PUP_NO_ENVELOPE=1) prints the raw response. The legacy --no-agent also prints the raw response, so scripts written to the old guidance keep working, and, as on main, it opts out of agent auto-approve. --no-envelope does not affect auto-approve.
  • Help is always text. --help no longer returns a JSON schema for agents; use pup agent schema.
  • Agent mode env vars removed. FORCE_AGENT_MODE and PUP_AGENT_MODE are no longer read or set. --agent is a hidden no-op.
  • ddsql schema tables / columns: data is the list; next_offset and warnings are in metadata. Reference-table discovery now requests sort=table_name, so offsets are stable across pages.

Changes

  • src/output.rs:
    • Metadata (now Deserialize) and build_envelope.
    • Metadata is derived from the response: count, command (the running command path), next_cursor and truncated. Commands only set what the response can't say.
    • Recognized cursors: meta.page.after, meta.page.next_cursor, meta.after, meta.next_cursor, meta.pagination.next_page (cicd flaky tests), page.next_cursor, data.next_cursor (llm-obs MCP endpoints), next_cursor, next_page_token (llm-obs patterns). Only fields that always mean "next page" are used; deployment gates' meta.page.cursor is the current page and is ignored.
    • Truncation also comes from meta.page.total_filtered_count exceeding the returned items (security findings), and from page pagination: metadata.page + 1 < metadata.page_count gives next_page (monitors search). truncated: false means "not known to be truncated": some APIs (e.g. incidents search) don't report it reliably.
    • next_action names the command's own cursor or page flag, read from its clap definition (--cursor, --page-cursor, --page-token, --page); commands without one say so.
    • The stderr metadata block (stderr_block) replaces the earlier ad-hoc stderr hints. Resource-diff hints print separately only when there are no changes or with --no-envelope.
  • src/formatter.rs: re-exports Metadata and adds format_and_print_with and output_with_meta. There is no agent_mode parameter.
  • Agent auto-approve (useragent::agent_auto_approves) is applied in main_inner and in the extension pre-parse path (PreParsedGlobals::apply_to), so extension children still get PUP_AUTO_APPROVE. Confirm-prompt behavior is otherwise unchanged from main.
  • --no-envelope is forwarded to extensions via PUP_NO_ENVELOPE.
  • Command metadata restored:
    • logs/traces cursor guidance;
    • idp suggestions, counts, entity-query "Continue with" (addresses the Bits --jq '.results' finding);
    • kinds, register, summary sampling, change-stories, traces get span count, resource-diff next step.
  • pup format: --count, --command and --next-action populate metadata again. Piped pup envelopes print as-is for JSON/YAML (lossless, so format | format is idempotent), while rows and --jq use their payload; a look-alike {status, data, metadata} document is only unwrapped if its metadata has a boolean truncated and only pup's own keys, otherwise it is left whole.
  • pup agent schema: envelope contract in the full, compact and filtered schemas, with --no-envelope in global_flags. returns.data describes envelope data, and the conformance test runs bodies through the real envelope builder.
  • Runbooks: pup steps run the child with --output json --no-envelope, so until conditions and capture: see the raw payload. The parent renders that payload for display in the user's format and envelope choice (metadata on stderr for table/CSV/TSV), using generic row selection; plain-text output passes through unchanged. No temp files or path env vars are involved.
  • Test harness: the agent pass sets CLAUDECODE=1, and command discovery uses pup agent schema. Both modes' baselines are the envelope shape. id_from paths were audited against each list response's shape (merged vs wrapped whole, e.g. slos list → data.data, events list → data.events; incidents list and synthetics suites list were already wrong on main), and a path that doesn't match the output is reported as a defect instead of a silent skip.
  • Docs, skills and agent markdown are updated, including jq examples for bare-array commands.

Notes for reviewers

  • src/config.rs, src/client.rs and src/formatter.rs carry the openapi-transformer "generated" header. Config now lives in the hand-maintained src/settings.rs (since refactor(config): split generator-owned contract from settings #913), and src/config.rs only re-exports it. The generator must emit the same contract: no agent_mode in src/client.rs test fixtures, format_and_print(data, format, jq), format_and_print_with_table(data, format, jq, table), format_and_print_with(data, format, jq, meta, table), output, output_with_table, output_with_meta, and the Metadata re-export.
  • Harness baselines were converted from the human (raw) shapes rather than captured from a live run (that needs credentials). Optional metadata keys auto-merge on the first live run.
  • agent schema, auth status, runbooks and bits print their own JSON and are not enveloped (unchanged from main).

Testing

  • cargo test -- --test-threads=1: 2,211 unit tests plus all integration tests pass.
  • cargo clippy --all-targets -- -D warnings and cargo fmt --check pass, and the wasm32-wasip2 build is clean.
  • Tests cover:
    • envelope shape, sibling preservation, collision fallback, derived truncation and cursors, and --jq keeping metadata;
    • --no-envelope / --no-agent / PUP_NO_ENVELOPE;
    • agent auto-approve: a marker such as CLAUDECODE=1 auto-approves, no marker does not, --no-agent opts out, --no-envelope does not, and the extension pre-parse path matches;
    • format flags and envelope round-trips;
    • ddsql next_offset;
    • the Bits scenario end to end (tests/idp_query_cli.rs);
    • real-binary pagination (tests/pagination_cli.rs), including a command whose cursor flag is --page-token;
    • real-binary runbooks (tests/runbook_cli.rs): poll until on a raw --jq result, a timeout negative, and capture of the raw payload, table display, a plain-text step, and PUP_PAYLOAD_FILE not being honored;
    • real-binary output formats (tests/output_formats_cli.rs): pup format idempotence for four payload shapes, one CSV/TSV row per monitor with parseable stdout and metadata on stderr, the table, the YAML envelope, raw --no-envelope YAML/CSV, --jq with CSV, an empty result, and a bare v1 array;
    • stderr notes (cursor named alongside a custom next_action, warnings), new cursor/truncation locations, look-alike envelopes left whole, and per-command cursor flags.

🤖 Generated with Claude Code

@platinummonkey platinummonkey changed the title refactor!: remove agent mode and the JSON output envelope [breaking change] remove agent mode and the JSON output envelope Oct 8, 2026
@platinummonkey
platinummonkey marked this pull request as ready for review October 9, 2026 14:05
@platinummonkey
platinummonkey requested a review from a team as a code owner October 9, 2026 14:05
@datadog-prod-us1-4

This comment has been minimized.

@datadog-prod-us1-4 datadog-prod-us1-4 Bot 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.

Bits Code Review: FAIL

When reference-table discovery fails (e.g. HTTP 429), the code incorrectly emits a continuation offset into an incomplete result set, causing callers that resume from that offset to skip entries. The fix is to suppress next_offset whenever the discovery is incomplete so callers retry the original page.

Open Bits AI session

🤖 Bits Code Review · Commit b4284b1

Comment thread src/commands/ddsql.rs Outdated
@platinummonkey
platinummonkey marked this pull request as draft October 14, 2026 13:51
@platinummonkey
platinummonkey marked this pull request as ready for review October 14, 2026 13:51

@datadog-prod-us1-4 datadog-prod-us1-4 Bot 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.

Bits Code Review: FAIL

Entity queries filtered with --jq '.results' lose all continuation information, allowing partial inventories to appear complete. The existing cursor-hint helper can preserve that information on stderr.

Open Bits AI session

🤖 Bits Code Review · Commit 68b7a73

Comment thread src/commands/idp/entity_query.rs Outdated
@platinummonkey
platinummonkey marked this pull request as draft October 14, 2026 14:54
@platinummonkey platinummonkey changed the title [breaking change] remove agent mode and the JSON output envelope feat!: remove agent mode; make the JSON envelope the default output Oct 14, 2026
@platinummonkey
platinummonkey force-pushed the refactor/remove-agent-mode branch 2 times, most recently from 33fb704 to a5cb724 Compare October 14, 2026 16:37
@platinummonkey platinummonkey changed the title feat!: remove agent mode; make the JSON envelope the default output feat(output)!: replace agent mode with a default JSON envelope for everyone Oct 14, 2026
@platinummonkey
platinummonkey force-pushed the refactor/remove-agent-mode branch 2 times, most recently from 0683116 to 3911b3c Compare October 14, 2026 22:33
@platinummonkey
platinummonkey marked this pull request as ready for review October 14, 2026 22:40

@datadog-prod-us1-4 datadog-prod-us1-4 Bot 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.

Bits Code Review: FAIL

Two production-impacting bugs were identified: envelope re-rendering silently mutates payload structure by double-promoting the data key through unwrap_envelope/build_envelope, and runbook polling evaluates enveloped subprocess output directly against conditions (instead of unwrapping first), causing all poll conditions to time out rather than resolve. A third issue affects the test harness SLO ID lookup paths, which must be updated to account for the new data.data nesting introduced by collision wrapping.

Open Bits AI session

🤖 Bits Code Review · Commit 3911b3c

Comment thread src/output.rs
Comment thread src/output.rs
Comment thread scripts/test_harness.py
@platinummonkey
platinummonkey force-pushed the refactor/remove-agent-mode branch 2 times, most recently from 0fcc8b7 to 287ec02 Compare October 14, 2026 23:11
…eryone

Agent mode changed pup's behavior when an AI agent was detected: JSON was
wrapped in {status, data, metadata} (hoisting `data` and dropping `meta`,
`links`, and `included`, which lost pagination cursors), --help returned
a JSON schema, and prompts were auto-approved. Humans and agents saw
different shapes. Now every caller gets the same output; agent detection
only adds the User-Agent token and auto-approves CLI prompts.

- Remove agent mode: no FORCE_AGENT_MODE / PUP_AGENT_MODE, --help is always
  text. Agent detection adds `ai-agent <name>` to the User-Agent and still
  auto-approves CLI prompts (also in the extension pre-parse path, so
  extension children get PUP_AUTO_APPROVE). The legacy --no-agent opts out
  of that auto-approval, as it did before. --agent is a hidden no-op.
- Wrap JSON and YAML output in {status, data, metadata} by default. A body
  with a top-level `data` keeps it and its siblings (meta, links, included);
  any other body (or one with its own status/metadata key), and any --jq
  result, becomes `data`.
- Table, CSV, and TSV print only rows on stdout. CSV/TSV now honor the
  command's row/column hints like the table (e.g. monitors search prints
  one row per monitor), and render a --jq result as-is.
- Built-in metadata: count, truncated, command, next_cursor, next_offset,
  next_page, next_action, warnings. Continuation is lifted from the
  response (cursors, totals, and page/page_count pagination such as
  monitors search's own `metadata`), survives --jq, and next_action names
  each command's own flag (read from clap, e.g. --page-cursor, --page).
- For table, CSV, and TSV, always print the metadata to stderr as one
  block: `metadata: key=value ...`, then `next_action:` and `warning:`
  lines. With --no-envelope only `warning:` lines are printed.
- Add --no-envelope (env PUP_NO_ENVELOPE, inherited by extensions) to print
  the raw response body; the legacy --no-agent also prints it raw.
- pup format: --count/--command/--next-action populate metadata. A piped
  pup envelope prints as-is for JSON/YAML (lossless: `format | format` is
  idempotent); rows and --jq use its payload. Look-alike documents are
  left whole.
- Runbooks: pup steps run the child with --output json --no-envelope, so
  `until` conditions and `capture:` see the raw payload; the parent renders
  that payload for display in the user's format and envelope choice
  (metadata on stderr for table/CSV/TSV). Plain-text output passes through.
- ddsql schema: list in `data`, next_offset/warnings in metadata; no
  next_offset after a rate-limited lookup; reference tables sorted by name.
- pup agent schema: envelope contract in full, compact, and filtered
  schemas; --no-envelope listed in global_flags.
- traces get / get --pruned validate the response shape.
- Test harness: id_from paths audited against each list response's shape
  (merged vs wrapped whole, e.g. slos list -> data.data, events list ->
  data.events); a stale path is a defect, not a silent skip. Snapshot
  baselines updated for both modes.

BREAKING CHANGE: JSON and YAML output are wrapped in {status, data,
metadata} for every caller, including --jq results (`$(pup ... --jq '.id')`
now yields an envelope). CSV/TSV follow each command's row and column
selection, and table/CSV/TSV print a metadata block on stderr. Pass --no-envelope or set PUP_NO_ENVELOPE=1 for the raw body.
--help no longer returns JSON, and FORCE_AGENT_MODE / PUP_AGENT_MODE are
ignored.

The openapi-transformer generator must emit the new formatter contract
(src/formatter.rs: format_and_print, format_and_print_with_table,
format_and_print_with, output, output_with_table, output_with_meta, and the
Metadata re-export; src/client.rs test fixtures without agent_mode;
src/config.rs stays a re-export of crate::settings, which holds Config).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@platinummonkey
platinummonkey force-pushed the refactor/remove-agent-mode branch from 287ec02 to 384a030 Compare October 15, 2026 13:29
@platinummonkey
platinummonkey merged commit ab7d911 into main Oct 15, 2026
9 checks passed
@platinummonkey
platinummonkey deleted the refactor/remove-agent-mode branch October 15, 2026 14:23
platinummonkey added a commit that referenced this pull request Oct 15, 2026
PR #908 regenerated src/generated/downtime.rs against the pre-#899
formatter API (agent_mode arg, four-field Metadata). Both merged to main
back to back, leaving main and this release branch unable to compile.

- Build Metadata with ..Default::default() for the new pagination fields
- Print via formatter::output_with_meta instead of the removed signature

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants