Skip to content

Show recalled memories on transcript messages #1074

Description

@sentry-junior

Summary

Preserve the structured context that plugins add to a turn so the same content
can be rendered into the model prompt and inspected from the triggering user
message in the dashboard.

Memory recall is the first use case. A recalled-memory contribution should keep
the selected memory ids, content, timestamps, scope, and kind instead of
discarding that structure after producing prompt text.

Current behavior

  • packages/junior-memory/src/recall.ts selects memories and immediately renders
    them into a text-only PromptMessage.
  • packages/junior-plugin-api/src/prompt.ts defines PromptMessage as a strict
    { text } object.
  • Core inserts that text through getPluginUserPromptContributions(), but it
    cannot persist or report which records produced it.
  • The dashboard can only see an opaque memory block inside the model-facing
    prompt.

Product behavior

  • Show a subtle context indicator on a user transcript message when a plugin
    contributed context to that turn.
  • Opening it shows the context used for the turn. Memory recall gets a focused
    view with one row per recalled memory.
  • The memory view includes the exact selected content plus memory id, observed
    timestamp, scope/kind when available, and the time the snapshot was loaded.
  • Later memory edits, expiry, or deletion do not change the historical snapshot.
  • Context remains searchable in the transcript and has a compact transcript
    export representation.
  • Existing conversation payload authorization and redaction rules apply to all
    context content.

This is turn context, not a complete dump of every provider request. System
instructions, prior agent history, tool definitions, and later tool results are
outside this issue.

Proposed extension

Add one small, typed context contribution alongside the existing plugin prompt
hook. Follow the existing defineJuniorPlugin / definePluginTool style: plain
objects, a strict Zod schema, and at most one small inference helper.

Conceptually, a context definition owns:

{
  kind: "recall",
  version: 1,
  schema: memoryRecallSchema,
  renderPrompt(content) {
    return renderMemoryPrompt(content.memories);
  },
}

The memory hook creates a contribution from structured content:

{
  kind: "recall",
  version: 1,
  content: {
    memories: [
      {
        id,
        content,
        observedAtMs,
        scope,
        kind,
      },
    ],
  },
}

Core validates the content, calls the plugin-owned prompt renderer, and inserts
the resulting text through the existing user-prompt path. The prompt renderer
is not persisted.

Core also appends a bounded turn_context conversation event anchored by the
core-owned turn id:

{
  type: "turn_context",
  turnId,
  pluginName: "memory",
  kind: "recall",
  version: 1,
  loadedAtMs,
  content: {
    memories: [...],
  },
}

The event contains facts only. Do not persist field labels, icons, layout,
rendered prompt text, or other presentation instructions. Core supplies
pluginName, turnId, and loadedAtMs; the plugin cannot spoof them.

kind and version form the stored plugin context contract. Changing the
persisted shape or model-visible interpretation requires a new version.
Historical events are not rewritten. Unknown context kinds or versions may use
a safe generic structured view.

Dashboard

The reporting API projects authorized turn_context events and associates them
with the user message that started the turn.

The dashboard provides a safe generic structured-content view. It may also use
an optional specialized renderer keyed by plugin name, kind, and version. The
memory renderer consumes the stored memory structure; it does not query the
memory database or control authorization.

Presentation stays in dashboard code and can change without changing stored
events. A missing specialized renderer falls back to the generic view.

Limits and privacy

  • Apply strict per-contribution and aggregate serialized-size limits.
  • Limit nesting, collection length, and string length.
  • Memory persists only records actually selected for the prompt, not every
    search result or full database rows.
  • Treat strings as text; do not accept HTML or executable UI content.
  • Do not project context content when the caller cannot view conversation
    payloads.
  • Search and export operate only on the already-authorized report.

The schema can prevent executable or unbounded content. Secret handling remains
the responsibility of the contributing plugin and the conversation payload
authorization boundary.

Acceptance criteria

  • Memory recall produces one typed, versioned context contribution derived from
    the same selected records used to render the model prompt.
  • Core renders the plugin contribution into the existing model-visible user
    prompt without changing current recall wording.
  • Core durably stores the structured, presentation-free context against the
    turn.
  • No context event is emitted when recall returns no memories.
  • Rich transcript view shows a context indicator on the triggering user
    message and a memory-specific detail view.
  • The detail view includes memory id, selected content, observed time,
    scope/kind, and load time.
  • Later changes to memory storage do not alter historical context.
  • Unknown kinds or versions render safely without executing plugin UI code.
  • Redacted conversations do not expose context through the report, transcript
    search, or export.
  • Tests cover schema validation and budgets, prompt rendering, durable event
    projection, authorization, memory rendering, transcript search, and export.

Requested by David Cramer via Junior.

--

View Junior Session in Sentry

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions