Skip to content

fix(eve): pass the settled history to turn.completed so memory capture runs - #3465

Open
gugu91 wants to merge 1 commit into
vercel:mainfrom
gugu91:fix/memory-capture-turn-completed
Open

gugu91 wants to merge 1 commit into
vercel:mainfrom
gugu91:fix/memory-capture-turn-completed

Conversation

@gugu91

@gugu91 gugu91 commented Sep 17, 2026 •

Copy link
Copy Markdown

Summary

Fixes #3223 (p0). Since 0.51.0 no memory provider's capture["turn.completed"] has run. This restores the one argument that was lost and adds a regression test.

The problem

turn.completed skips memory capture because it arrives without the turn's messages. handleEvent(event, messages) in execution/workflow-steps.ts forwards both arguments to dispatchMemoryLifecycleEvent, which only captures when messages are present:

} else if (input.event.type === "turn.completed" && input.messages !== undefined) {
  try { await dispatchMemoryTurnCompleted({ ... }); } catch (error) { log.error(...); }
}

#2690 (aae26311, "graduate background subagents to workflow tools") removed the messages parameter from emitTurnEpilogue in harness/emission.ts and the session.history arguments at its call sites in tool-loop.ts. Since then messages is always undefined, the guard returns before the try, and nothing is logged. Recall kept working because turn.started has no such guard, and compaction-time capture kept working because compaction.requested still passes messages, so the regression stayed hidden.

How we hit it

We integrated @supermemory/eve on 0.52.4 and the agent could only recall facts it had saved with the remember tool. A recorder provider in our fixture stack logged every turn.started and no turn.completed across many turns, and the live Supermemory container held only source_type: "memory" documents, none of type "conversation". Reading the shipped dist led to the guard above and then to #2690. #3223 already reports the same diagnosis with reproductions on 0.51.0, 0.52.x, 0.54.3 and 0.55.0. The maintainer-bot draft #3224 has only the failing test and has been idle since 2026-09-10, so this PR carries that test forward with the fix.

Why this fixes it

emitTurnEpilogue takes an optional messages again and passes it as the second argument of the emit, as it did before #2690. The four places that settle a turn pass session.history:

  • finishConversationTurn and finishTaskTurn, plain path
  • finishConversationTurn and finishTaskTurn, structured path, through emitStructuredResult, which gains the same parameter

Each site reads the history after persistStructuredAssistantTurn and clearTurnClientContextState, so the provider receives the settled history including the assistant response. The five other callers of emitTurnEpilogue end a turn without a model response (session limits, input requests, authorization parking, proxied subagent input and authorization events). They keep omitting messages on purpose, so a turn with no answer captures nothing. The public event stream does not change: messages is the side-channel argument of HarnessEmitFn, not part of the event.

Testing

Unit, this repo

  • New test in harness/tool-loop.test.ts: a conversation turn through createToolLoopHarness with a mock model, asserting the turn.completed emit receives the same array as result.session.history. On main it fails with expected undefined to deeply equal [ { content: 'Hi', … }, … ]; with the fix it passes.
  • pnpm test:unit: 8756 passed, 1 skipped, 4 failed. The 4 failures (execution/session-callback-request.test.ts ×3, self-modification/registry-add.test.ts ×1) fail the same way on a clean main checkout in this environment and do not touch memory or the harness.
  • pnpm test:integration: 871 passed across 112 files.
  • pnpm lint: 0 errors, only pre-existing warnings in apps/templates. pnpm typecheck: 47/47 tasks. oxfmt --check clean on the touched files.

Live, our app against Supermemory with a real model

  • Before: after about 10 turns the member's container held 2 documents, both source_type: "memory" from the remember tool, and none of type "conversation".
  • After: one chat with a passing remark (my dog is called kestrel-…) and no tool calls in the stream. About 20 seconds later the container had a new source_type: "conversation" document with status: "done". A new chat asking "what's my dog called?" answered kestrel-… with no tool calls, so the provider's turn-start search found the captured turn.

Checklist

…e runs

Since vercel#2690 emitTurnEpilogue emitted turn.completed without the turn's
messages. dispatchMemoryLifecycleEvent only runs completed-turn capture when
messages are present, so every memory provider's capture["turn.completed"]
was skipped silently from 0.51.0 onward; only compaction-time capture still
ran. Restore the optional messages parameter and pass session.history from
the four paths that settle a turn (conversation and task, plain and
structured), as before vercel#2690.

Fixes vercel#3223

Signed-off-by: Guglielmo Porcellini <5994936+gugu91@users.noreply.github.com>
@vercel

vercel Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

@gugu91 is attempting to deploy a commit to the Vercel Team on Vercel.

A member of the Team first needs to authorize it.

This branch has not been deployed

No deployments
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.

Memory capture silently stops running after upgrading to 0.51.0

1 participant