Skip to content

feat(core): add session metadata support#23068

Merged
rekram1-node merged 12 commits into
anomalyco:devfrom
shantur:session-metadata
May 30, 2026
Merged

feat(core): add session metadata support#23068
rekram1-node merged 12 commits into
anomalyco:devfrom
shantur:session-metadata

Conversation

@shantur
Copy link
Copy Markdown
Contributor

@shantur shantur commented Apr 17, 2026

Issue for this PR

Closes #

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Introduces a metadata field in sessions that can be used with SDK to persist client controlled JSON metadata for session. This is simlar to metadata available in Message.Parts
Session APIs and SDK exposes metadata for create, get, update, list, and fork operations.

Support full metadata replacement on session update, add copyMetadata to session forks with default copy behavior,

If you paste a large clearly AI generated description here your PR may be IGNORED or CLOSED!

How did you verify your code works?

Added tests and manual testing.

Screenshots / recordings

If this is a UI change, please include a screenshot or recording.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

If you do not follow this template your PR will be automatically rejected.

shantur added 2 commits May 11, 2026 18:41
Persist session metadata in the session store and expose it across the session APIs and SDK so clients can round-trip metadata during create, get, update, list, and fork operations.

Add a JSON metadata column to the session table, thread metadata through the session model/projectors, and normalize empty metadata responses to {}.

Support full metadata replacement on session update, add copyMetadata to session forks with default copy behavior, regenerate the session SDK surface, and cover the behavior with server/session tests plus a migration for existing databases.
@shantur shantur force-pushed the session-metadata branch from 8c06b4f to c3351cb Compare May 11, 2026 18:09
@rekram1-node
Copy link
Copy Markdown
Collaborator

/review

@@ -33,6 +33,7 @@ export const SessionTable = sqliteTable(
summary_deletions: integer(),
summary_files: integer(),
summary_diffs: text({ mode: "json" }).$type<Snapshot.FileDiff[]>(),
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.

Suggestion (style): this new metadata type uses any, which the repo style guide asks us to avoid. Since this is client-controlled JSON and the generated SDK exposes unknown, consider carrying Record<string, unknown> through the storage/service types instead of Record<string, any>.

export type SessionUpdateData = {
body?: {
title?: string
metadata?: {
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.

Suggestion: the update route accepts metadata: null to clear metadata, but the generated SDK type only allows an object here. That means SDK callers cannot type-safely use the clear path covered by the API/tests. Since this file is generated, the source fix may need to make the nullable schema survive generation so this becomes { [key: string]: unknown } | null.

# Conflicts:
#	packages/opencode/src/session/session.ts
@rekram1-node
Copy link
Copy Markdown
Collaborator

/review

const Metadata = Schema.Record(Schema.String, Schema.Any)

export const Info = Schema.Struct({
id: SessionID,
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.

Suggestion for the human to consider: this new field introduces any, which is avoidable here and the PR describes this as JSON metadata. Using a JSON schema keeps service-level callers from passing non-serializable values that HTTP clients could never send.

Suggested change
id: SessionID,
const Metadata = Schema.Record(Schema.String, Schema.Json)

export type SessionUpdateData = {
body?: {
title?: string
metadata?: {
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.

Suggestion for the human to consider: the server schema and new route test both allow PATCH /session/:id with metadata: null to clear metadata, but the generated SDK update payload does not include null. If clearing with null is intended to be public SDK behavior, this generated type should include | null after the source/schema change and SDK regeneration.

summary_deletions: integer(),
summary_files: integer(),
summary_diffs: text({ mode: "json" }).$type<Snapshot.FileDiff[]>(),
metadata: text({ mode: "json" }).$type<Record<string, any>>(),
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.

Suggestion for the human to consider: this new DB type introduces any, which the style guide asks us to avoid. If the runtime schema is narrowed to JSON, this can also avoid widening the persisted shape to any here.

Suggested change
metadata: text({ mode: "json" }).$type<Record<string, any>>(),
metadata: text({ mode: "json" }).$type<Record<string, unknown>>(),

rekram1-node and others added 3 commits May 13, 2026 23:27
# Conflicts:
#	packages/opencode/src/session/projectors.ts
#	packages/opencode/src/session/session.sql.ts
#	packages/opencode/src/session/session.ts
#	packages/opencode/test/server/session-actions.test.ts
#	packages/opencode/test/server/session-list.test.ts
#	packages/opencode/test/session/session.test.ts
@shantur
Copy link
Copy Markdown
Contributor Author

shantur commented May 14, 2026

Addressed the latest session metadata review feedback in 2c9c3ac.

Changes made:

  • Default legacy imported sessions with missing metadata to {}, fixing the bun run dev import session-export.json missing-key failure.
  • Removed copyMetadata from the fork API and SDK; forks now keep the default copy behavior.
  • Kept PATCH /session/:id full metadata replacement and metadata: null clear behavior; SDK update payload now allows null.
  • Tightened the DB metadata type to Record<string, unknown>.
  • Verified locally:
  • bun test test/session/session.test.ts test/session/schema-decoding.test.ts test/server/session-actions.test.ts test/server/session-list.test.ts test/cli/import.test.ts test/cli/cmd/tui/notifications.test.ts from packages/opencode: 53 pass.
  • bun typecheck from packages/opencode: pass.
  • bun typecheck from packages/sdk/js: pass.
  • Push hook ran full bun turbo typecheck: pass.
  • Ran serve against an isolated temp SQLite DB and verified create/get/list/update/clear/default/fork metadata behavior with curl.
  • Reproduced legacy import payload without metadata against the temp DB and confirmed imported sessions default metadata to {}.

@github-actions
Copy link
Copy Markdown
Contributor

Automated PR Cleanup

Thank you for contributing to opencode.

Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions.

This PR was closed because it matched the following cleanup criteria:

  • The PR was created more than 1 month ago
  • The PR had fewer than 2 positive reactions
  • Positive reactions are counted as thumbs-up, heart, celebration, or rocket reactions on the PR

PRs created within the last month are not affected by this cleanup.

If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate.

Thanks again for taking the time to contribute.

@rekram1-node
Copy link
Copy Markdown
Collaborator

/review

path: sessionPath(ctx.worktree, ctx.directory),
workspaceID: original.workspaceID,
title,
metadata: structuredClone(original.metadata),
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.

Suggestion: if copyMetadata is still intended for this feature, this always-copy behavior leaves no API/SDK path to opt out. The final fork schema only accepts messageID, and the generated SDK no longer includes copyMetadata, so clients cannot request the documented non-copying fork behavior. If that behavior was intentionally dropped, updating the PR/API expectation would avoid the mismatch.

@shantur
Copy link
Copy Markdown
Contributor Author

shantur commented May 29, 2026

@rekram1-node - Apologies for chasing but can this please be merged in?
If anything can expedite this please let me know

Match the optional treatment of every comparable schema field (Session.Info.permission, CreateInput, message-v2 parts). metadata is now optionalOmitUndefined and omitted when unset/cleared rather than materialized as {}. Lets unrelated session fixtures drop their forced metadata: {} entries.
@rekram1-node rekram1-node merged commit ddc30cd into anomalyco:dev May 30, 2026
8 checks passed
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.

2 participants