Skip to content

refactor(ai): remove unused metadata fields - #61018

Merged
rekram1-node merged 1 commit into
devfrom
ai-metadata-cleanup
Nov 2, 2026
Merged

rekram1-node merged 1 commit into
devfrom
ai-metadata-cleanup

Conversation

@rekram1-node

@rekram1-node rekram1-node commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

@opencode/ai had a free-form metadata bag on almost every message type. Most of them were never read by the package or anything else. Core copied its own session metadata into some of them, but nothing read it back.

Change

Removed the metadata fields nothing reads, and the writes that fed them.

Field Before Now
Message.metadata core copied session metadata in, nothing read it removed
TextPart.metadata core wrote attachment info, nothing read it removed
ToolCallPart.metadata never written or read removed
ToolResultPart.metadata only copied by ToolResultPart.make removed
ReasoningPart.metadata never written or read removed
ToolDefinition.metadata never written or read removed
LLMRequest.metadata accepted, never sent anywhere removed
SystemPart.metadata read by the Claude agent plugin, documented plugin API kept
MediaPart.metadata read by Anthropic lowering (file_id, citations, title, context, transformations) kept, core no longer writes an unused description into it
 core to-llm-message
-  Message.make({ role: "user", content, metadata: { ...session.metadata, agents } })
+  Message.make({ role: "user", content })
-  { type: "text", text, metadata: { attachment: { source, name, description } } }
+  { type: "text", text }

Stored native compaction windows that contain the removed fields still decode. The extra keys are dropped.

Breaking change

@opencode/ai is published, and plugins receive Message and part objects through session hooks. Code that sets or reads the removed fields needs to drop them.

Out of scope

  • Moving the Anthropic options on MediaPart.metadata to a typed provider option.

Verification

  • bun test in packages/ai (2013 passed, 0 failed)
  • bun run script/test.ts in packages/core (7188 passed; 3 shell tests fail the same way on dev locally)
  • bun run typecheck:tsc in packages/ai and packages/core
  • bun run check from repo root (35/35)

Added by the opencode bot (AI-generated).

Public API: removes metadata from @opencode/ai Message, TextPart, ToolCallPart, ToolResultPart, ReasoningPart, ToolDefinition and LLMRequest (breaking for plugins and SDK callers that set or read it)

@opencodejr opencodejr 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.

The change looks sound to me. I found no remaining readers of the removed fields in packages/ai or packages/core. The kept MediaPart.metadata and SystemPart.metadata are still read where expected. A stored Message whose message and text part carry the old metadata still decodes, with the extra keys dropped. The changed tests pass, and typecheck:tsc is clean in ai, core and plugin.

Public API (breaking for @opencode/ai users and plugins):

  • Message.metadata: removed
  • TextPart.metadata: removed (core no longer writes metadata.attachment to it)
  • ToolCallPart.metadata, ToolResultPart.metadata, ReasoningPart.metadata: removed
  • ToolDefinition.metadata: removed
  • LLMRequest.metadata and the metadata request input: removed
  • MediaPart.metadata: kept, but core no longer writes description into it

Plugins get these objects through SessionRequest.messages, which the session request, context and compaction hooks expose. A plugin that read message.metadata (session metadata or agents) or metadata.attachment on text parts now gets undefined. TypeScript callers that set these fields get a compile error. Old stored data still decodes.

No other concerns.

@opencodejr opencodejr 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.

This push only rebases the change onto newer dev; the patch is identical to the one I reviewed. The new code from dev doesn't read any of the removed fields. typecheck:tsc is clean in ai, core and plugin, and the changed tests pass.

The public API impact is the same as before. metadata is removed from Message, TextPart, ToolCallPart, ToolResultPart, ReasoningPart, ToolDefinition and LLMRequest. This breaks plugins and SDK callers that set or read those fields. Old stored data still decodes.

No new concerns.

@opencodejr opencodejr 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.

This push only rebases the change onto newer dev; the patch is identical to the one I reviewed. typecheck:tsc is still clean in ai, core and plugin, and the changed tests pass.

The public API impact is the same as before. metadata is removed from Message, TextPart, ToolCallPart, ToolResultPart, ReasoningPart, ToolDefinition and LLMRequest. This breaks plugins and SDK callers that set or read those fields. Old stored data still decodes.

No new concerns.

@rekram1-node
rekram1-node merged commit e5c9b01 into dev Nov 2, 2026
34 of 35 checks passed
@rekram1-node
rekram1-node deleted the ai-metadata-cleanup branch November 2, 2026 04:06
pascalandr added a commit to NeuralNomadsAI/CodeNomad that referenced this pull request Nov 7, 2026
…penCode 2.2.6) (#1350)

## Problem

On OpenCode 2.2.6, scheduled Mission wakes no longer start their
passage. Upstream anomalyco/opencode#61018 removed the `metadata` copy
from the LLM messages plugins see in session context hooks. CodeNomad
classified wakes from `metadata.schedule` on that projection, so a 2.2.6
wake looked inconsistent: the drain failed with `schedule-carrier-wake`
and the passage was never admitted.

## Fix

The context projection still carries each native message `id`, and the
stored native message still carries `metadata.schedule`. Wake
classification now resolves user-role context IDs through the existing
bounded native message reader and classifies the authoritative stored
records by type and status, ignoring projected metadata. Resolved
records are reused for the candidate proof, so a wake row is read once.
Classification and recovery reads have separate read and byte budgets,
and exceeding a bound surfaces a coverage error instead of dropping the
wake. Placement, mapping, deterministic identities, source-byte fences,
RPC checks and CAS/high-water behavior are unchanged. The minimum stays
2.2.3.

The canonical authority and bounds contract is in
`dev-docs/MISSIONS_NATIVE_SCHEDULE_TRANSPORT.md`.

Native facts, checked on isolated CLIs:

| CLI | Context message ID | Context `metadata.schedule` | Stored
`metadata.schedule` |
|---|---|---|---|
| 2.2.3 | kept | present | present |
| 2.2.5 | kept | present | present |
| 2.2.6 | kept | absent | present |

## Tests

- Unit coverage for metadata-free and conflicting projections, notices,
compactions, invalid native records, a context of 128 wake entries,
eight recovery reads and independent byte budgets.
- `tests-runtime-contracts` gains a native context/storage check and the
cold scheduled-Mission product check on minimum and latest, and uploads
the fixtures' receipts and failure diagnostics when a step fails. The
schedule fixtures read the actual CLI version instead of a hardcoded
2.2.3, poll for the PID-matching ready registration, and set the `at`
time after activation, just before Play. The cold wait allows 120 s
(leader-lease recovery exceeds 45 s on dev too).
- On 2.2.6, an `origin/dev` snapshot fails the cold product check
(`schedule-carrier-wake`); this branch passes `at` and `in` with the
exact CI command (product step: 102 s on 2.2.3, 208 s on 2.2.6).
- Foundation fixtures pass on 2.2.3 and 2.2.6 (3 CAS applications, cold
recovery, no provider requests); an earlier revision also passed them on
2.2.5. Server Missions suites: 1,406 passed. Server/UI typechecks and
native bundle builds pass. The full runtime-contract sequence passes
locally on 2.2.3 (15 min) and 2.2.6 (23 min 26 s, against the job's
25-minute timeout).

## Known limitation

`experimental.part_rows` (off by default) stores assistant parts in
`session_part`, which CodeNomad's history, search, outline, gallery and
pruning readers do not read. Documented in
`dev-docs/SESSION_HISTORY_QUERIES.md`, not addressed here.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant