mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-09 16:02:36 +01:00
## Thinking Path > - Paperclip manages work across agent runtimes. > - Company observability sends allowed records to the operator's OTLP receiver. > - Native OpenCode exports tool results but omits submitted argument diagnostics. > - Exact argument values are excluded by the current capture policy. > - Safe type information can distinguish malformed envelopes without copying private values. > - This change exports a fixed diagnostic shape from authenticated dispatch and schema-rejection events. > - Operators can compare rejected and accepted calls under the existing capture controls. ## Linked Issues or Issue Description **What happened?** OpenCode tool spans omit argument diagnostics. The exporter reads native tool events. The authenticated MCP boundary has the submitted arguments, but its dispatch item events do not create tool spans. Calls rejected by its schema check do not emit dispatch items. **Expected behavior** With tool details and tool content enabled, Paperclip tool spans include the method, diagnostic schema version and allowed argument field types. Exact values and arbitrary field names stay excluded. **Steps to reproduce** 1. Configure a company OTLP receiver and enable native OpenCode export. 2. Enable tool details and tool content. 3. Submit a rejected Paperclip tool call and then a valid call. 4. Inspect the input diagnostics and outcome of each span. Related work: #17989 proposes metadata-only native export. #18098 added the normalized content exporter. This change repairs argument diagnostics in that exporter. ## What Changed - Export `paperclip.opencode.tool-diagnostics.v1` as `braintrust.input` for authenticated Paperclip tool calls. - Include `tools/call`, the argument type and types for a fixed list of envelope fields. - Use dispatch IDs for Paperclip tool spans. Keep separately named `opencode tool ...` provider records for failures before dispatch. Mark each span source. - Preserve schema-rejected calls as failed items without invoking the controller handler. Keep authentication and the closed tool catalog. - Bound schema-rejection error text in stored events and transcripts with the existing redaction and 8 KiB limit. Keep the provider response unchanged. - Preserve the controller opt-in, tool-detail and tool-content switches, redaction, limits and run identity. - Add MCP-to-receiver regression coverage and update the capture policy and capability maps. ### Hypothesis and thesis for a fix The exporter drops the dispatch diagnostic signal. Raw input omission is intentional. The repair belongs in the OpenCode driver and exporter. Shared protocol, tool permissions and task completion stay unchanged. The regression sends schema-rejected, handler-rejected and valid argument envelopes through a fixture OpenCode subprocess and the authenticated MCP bridge. It checks the downstream rejection and receipt, completion event and actual local OTLP request. The baseline has no dispatch spans. The candidate has separate spans with safe type diagnostics. Two warm runs and disabled capture use the same path. Source: [OpenCode diagnostic pattern](https://www.braintrust.dev/app/Paperclip/p/Paperclip%20Staging/patterns/f172172e-9c65-453f-bb3a-7c6ba4f5a400). ## Verification - Baseline: `a92f616cd0a457c8ba85d994b24e1f92c2e4d1de`. The new integration regression fails because tool spans are absent. The same regression passes with this change. - Exporter and MCP bridge suites: 37 tests pass. Three final driver controls pass. Ten selected controls also passed before the final provider-record repair. Runner TypeScript typecheck and build pass after the review repair. - The review regression failed with two spans before the repair. It passes with three spans after the repair. Unauthenticated and unassigned calls do not reach the rejection observer. A broken observer cannot change the schema rejection. - The full OpenCode/exporter/MCP run passes 94 tests and fails one existing final-answer selection test. The same failure occurs in an untouched baseline checkout. It is not claimed as a pass. - A further filtered run reached the default five-second timeout in two existing continuation tests during concurrent repository checks. All five selected dispatch, warm-session and continuation controls pass with a 15-second test deadline. The earlier timeouts remain recorded. - `node scripts/check-maps.mjs` and `git diff --check` pass. - Runner TypeScript checks and build pass. Full repository typecheck and build stop at Rust because `cargo` is not installed. The full repository test command was stopped before workspace handoff. No full-suite pass is claimed. Local source commits: `c48a8af1c186e415c802fa0ac95209b16b4a3122` `6744991f6`, and `a8ccf4693`. The remote branch is based on newer master `4814d1ab691c176170510a93a175dafdc9e021c7`. All initial eight changed files match between those bases before the patch. - The integration provider and controller are fixtures. The OTLP receiver is a real local HTTP server. A separate real OpenCode 1.18.34 probe opened a session. The remote free model returned `Free usage limit reached`. No tool call or receipt was produced. No live recovery is claimed. - Review follow-up: a real MCP request with 12,000 invalid array elements produced a 264,908-character event error before the repair. The same regression now stores 8,192 characters in both events and transcript. The MCP error receipt stays unchanged and no handler runs. All 37 exporter/MCP tests and 12 selected driver/exporter/MCP controls pass. Runner TypeScript checks and build pass. Local follow-up commit: `64d4fa583`. - The previous remote head passed Runner, typecheck, build and E2E. Its unchanged attention-retention shard failed twice with 15-second timeouts, then cleanup deadlock and duplicate fixture IDs. Both affected retention suites passed all 70 tests locally unchanged. The new head needs fresh CI and review; no full CI pass is claimed. - Current-master qualification (2026-11-06, merge `2959ecdb8d`): normally merge master `53645a0104`, including the existing test-only retention-fixture repair from #18774 reused by #18770. Keep the OpenCode diagnostics feature and upstream durable MCP and completion-feedback behavior. No test deadline or assertion changed. The three old CI timeouts did not reproduce locally: the four completed-blocker-history cases passed on the old head in 29.52 seconds, then passed on the merged head in 13.23 seconds. The fixture rollback/provenance regression also passed. All 116 exporter, MCP bridge and OpenCode driver fixture tests passed. Runner TypeScript checks and both TypeScript compilation targets passed; ACPX profiles match the release manifest and protocol-contract checks pass. These checks use local PostgreSQL, HTTP and process fixtures. No hosted provider call or deployed recovery is claimed. Fresh CI and review are pending for this merge. - Map-scope review follow-up (`61e662e179`): remove only the inherited diagnostics paragraphs from the company capability maps. This change adds logging detail within an existing capability. `node scripts/check-maps.mjs` and `git diff --check` pass. Production and test inputs are unchanged from the qualified merge; fresh CI and review are pending for the final head. - Test compiler follow-up (`6675b46377`): reproduce CI TS2722 with `tsc -p packages/paperclip-runner/tsconfig.test.json --noEmit`. The harness contract makes transcript retrieval optional. The OpenCode bounds regression now fails explicitly if that capability is absent, then inspects the narrowed method. The same complete Runner test compiler project passes, and the actual local schema-rejection fixture passes with all receipt and 8 KiB assertions retained. Production inputs are unchanged. Fresh final-head CI and review are pending. - Final-head qualification: `6675b4637743aa269e5214756b158e1e0a4561a5` passes all 56 applicable CI checks (three skipped, one neutral review). [CI result](https://github.com/paperclipai/paperclip/actions/runs/42253119577). Greptile gives 5/5 with no open findings. Hosted task recovery remains pending verified rollout. ## Risks - Authenticated spans use dispatch IDs. Provider records use separate names and a source attribute. Consumers must choose the source they need to avoid counting two observations of the same tool action. - Safe types cannot reconstruct nested arguments or explain every value-level validation failure. - Export remains best effort and bounded. Lost native frames no longer remove dispatch diagnostics, but abrupt process termination can still lose a turn. - Full local Rust checks and a deployed product journey are not yet verified. The pull request is unmerged and not deployed. ## Model Used OpenAI GPT-6 through Codex, with reasoning, tool use and code execution. The exact runtime variant and context-window size are not exposed to this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
141 lines
9.0 KiB
Markdown
141 lines
9.0 KiB
Markdown
# Company agent observability
|
|
|
|
Company Settings → General → Agent observability configures one company-owned
|
|
OTLP destination for Paperclip-managed Codex, Claude Code and native OpenCode executions. Export
|
|
is disabled by default. Choose Braintrust (US) or a custom HTTP(S) OTLP base URL;
|
|
use your own collector when you need fan-out. Braintrust EU and self-hosted
|
|
installations can use Custom OTLP with their required routing/authentication
|
|
headers. The receiver must be reachable from every execution environment;
|
|
`localhost` refers to the execution host, including a remote sandbox.
|
|
|
|
This is separate from Paperclip's first-party Telemetry and its content-free
|
|
[infrastructure tracing](observability.md). Configuring it does not change either data path.
|
|
|
|
## Capture and supported runtimes
|
|
|
|
The qualified versions are Codex 0.160.0 and Claude Code through ACP bridge
|
|
0.73.0 with Agent SDK 0.3.286. Both logs and traces use HTTP/protobuf. Prompts,
|
|
assistant responses and tool content default on. Additional tool details are a
|
|
Claude Code and native OpenCode control; Codex has no equivalent switch. Codex requests up to 64 KiB
|
|
of tool output (or zero when disabled); native event availability, truncation
|
|
and response caps still apply. Claude Code applies its own native limits.
|
|
Metrics, raw API bodies and Codex Guardian assessments are disabled.
|
|
|
|
Native OpenCode 1.18.34 uses a runner-owned HTTP/JSON exporter. Each completed,
|
|
failed or cancelled turn produces a trace with Paperclip company, agent, run and
|
|
turn IDs. Authenticated Paperclip tool dispatch outcomes, native tool outcomes and
|
|
message-level token/cost receipts are child spans;
|
|
repeated usage updates replace the same message receipt. Missing prices remain
|
|
unknown. This projection excludes hidden reasoning, raw provider payloads and
|
|
system instructions. Prompt, response, tool-detail and tool-content switches
|
|
apply before export. Content is bounded to 8 KiB per field and 64 tools plus 64
|
|
usage records per turn; truncation is marked. With both tool-detail and tool-content
|
|
capture enabled, Paperclip tool spans include `braintrust.input` diagnostics
|
|
(`paperclip.opencode.tool-diagnostics.v1`): the upstream `tools/call` method,
|
|
argument JSON type and types for a fixed list of envelope fields. These are
|
|
shape diagnostics, not a copy of the arguments. Arbitrary field names, argument
|
|
values, nested payloads and full model request bodies are not captured.
|
|
Authenticated schema rejections also produce failed tool spans without invoking
|
|
the controller handler. Conflicting request IDs produce separate failed items,
|
|
so a rejection cannot overwrite a previous dispatch outcome.
|
|
Paperclip tool spans use authenticated dispatch IDs and the name `tool <name>`.
|
|
Provider SSE records for those declared tools use `opencode tool <name>` and
|
|
remain available when failure occurs before dispatch, such as an oversized
|
|
request. These are separate observations, not correlated calls. The
|
|
`paperclip.tool.source` attribute distinguishes dispatch and provider records.
|
|
Native tools retain their provider spans without submitted-argument diagnostics. OpenCode's performance-only OTLP sink stays separate.
|
|
|
|
The exporter sends once at turn completion with a three-second deadline, at most
|
|
four requests in flight and no idle polling or persistent queue. Closing drains
|
|
pending requests and marks an unfinished turn failed. Export failures emit a
|
|
sanitized diagnostic and do not change task outcomes. Abrupt process termination
|
|
can lose an unexported turn. Recovered in-flight turns without a new submission
|
|
are not replayed just to produce an observability record.
|
|
|
|
The native runner and legacy local adapters receive transient launch material.
|
|
Native Codex writes a private managed config.toml; legacy exec uses a unique
|
|
invocation profile, preserving a selected source profile. Its profile injection
|
|
requires the qualified executable version. Codex ACPX writes its isolated home.
|
|
The legacy ACPX adapter keeps export configuration in a private home per agent
|
|
and settings compatibility key, and shares conversation files with its stable managed home;
|
|
export changes do not change its logical conversation fingerprint.
|
|
Codex receives authentication through `OTEL_EXPORTER_OTLP_LOGS_HEADERS` and
|
|
`OTEL_EXPORTER_OTLP_TRACES_HEADERS` in its process environment. Its managed
|
|
configuration and invocation profiles contain empty header maps, so exporting
|
|
does not save the ingestion credential in those files. The bundled Rust OTLP
|
|
exporter percent-decodes environment header values; launch material encodes
|
|
them once to preserve literal percent sequences. Credentials remain literal
|
|
values in the company secret; config-file environment interpolation is not used.
|
|
A caller-owned/preconfigured Codex home cannot be automatically configured in
|
|
this version: its files remain untouched and execution emits a diagnostic.
|
|
Legacy OpenCode, other harnesses, personal Codex desktop sessions and hosted claude_managed are
|
|
outside this feature.
|
|
|
|
## Changes and process lifetime
|
|
|
|
Paperclip reads settings once when an execution begins preparation. That
|
|
snapshot governs instruction reservation, compatibility and launch. Already
|
|
preparing or running work retains its configuration. A changed settings
|
|
revision or resolved credential version retires an idle incompatible warm
|
|
process using existing ownership checks. Conversation compatibility is
|
|
separate, so a safe replacement can resume the saved conversation. Recovery
|
|
never replays an uncertain turn merely to apply new export settings.
|
|
|
|
Missing, disabled, invalid or unavailable configuration runs without company
|
|
export and reports a sanitized availability diagnostic where relevant. Native
|
|
export rejection/timeouts do not determine task success; exporter deadlines
|
|
are three seconds. OpenCode starts its bounded asynchronous request when the terminal event is
|
|
observed; event delivery and task settlement do not wait for the receiver.
|
|
|
|
## Credentials and access
|
|
|
|
Only board users with Manage connections permission can read or change this
|
|
configuration. Agents cannot retrieve it through the settings API. The destination stores no credential
|
|
values. Authentication headers live in one encrypted Paperclip-managed company
|
|
secret, bound to the system consumer company-observability. Updates rotate that
|
|
secret; removal uses the secret service to retire it. Braintrust project
|
|
routing is derived from the current project ID, independently of the API key.
|
|
|
|
Reads return configured status and header names. Blank existing custom header
|
|
values retain the saved secret at the same HTTP origin; a change of scheme, host,
|
|
or port requires every retained header value to be entered again. New/renamed headers require values. Removing a
|
|
row removes that header. API-key replacement is explicit, and Cancel clears
|
|
entered secrets. Revisions prevent another administrator's edit being silently
|
|
overwritten. Credential values are excluded from task environment manifests,
|
|
run/config snapshots, argv, activity records, recovery configuration and query
|
|
cache data. OpenCode keeps exporter credentials in the runner wrapper and excludes them
|
|
from its child environment and config. Codex and Claude Code receive headers through their
|
|
process environment, which their tools can inherit or inspect.
|
|
This feature does not isolate export credentials from agent code. Use a credential
|
|
limited to telemetry ingestion for the selected destination; do not supply a broad
|
|
organization administration key. A user-operated collector can hold upstream
|
|
credentials when a deployment requires a separate trust boundary.
|
|
|
|
## Connection tests
|
|
|
|
Saving never contacts the destination. Test saved settings sends one synthetic
|
|
root span and a correlated log with a unique marker from the control plane,
|
|
even when agent export is disabled. The receiver receives no production task
|
|
content. Both requests share a ten-second deadline, cannot redirect, and use
|
|
the deployment's guarded HTTP policy with DNS pinning and response-size limits.
|
|
Receipts contain sanitized HTTP/signal results and no receiver body text.
|
|
A receipt is retained only for the revision that was tested.
|
|
|
|
“Test payload accepted” means the server's synthetic payload was accepted. It
|
|
does not prove agent export, remote network reachability, downstream indexing,
|
|
or that a provider UI rendered the records. Harness exporters use their own
|
|
network clients under the execution environment's policy; the server's DNS
|
|
pinning applies only to the explicit control-plane test. Verify real tasks and
|
|
the destination's records before relying on a deployment.
|
|
|
|
## Verification
|
|
|
|
The focused suites cover schema bounds, concurrent saves, secret/header
|
|
lifecycle, authorization, protobuf transport and rejection, private-network
|
|
policy, timeout, revision fencing, environment projections, private native
|
|
configuration, warm compatibility, and UI conflict/company-switch behavior.
|
|
Storybook IDs agent-observability-01-settings, agent-observability-02-states and
|
|
agent-observability-03-journey use fake credentials and production components.
|
|
Real local/remote native export evidence must be reported separately from
|
|
these automated and synthetic checks.
|