fix(agent-core-v2): seed the activity view's lastTurn from the persisted turn.ended record - #2648
Conversation
…ted turn.ended record A cold-resumed agent seeded its activity view only from live loop/task state, so the last turn's outcome was lost on a server restart: sessions came back with no lastTurnReason, and clients could not surface a previously failed turn (e.g. a provider 429 that killed the turn before the restart). The loop already persists the terminal turn.ended record (reason, error, durationMs); fold the latest one into the TurnModel as lastEnded and have AgentActivityView.seedFromLoop adopt it when no turn is active, so the session work aggregate (and everything built on it) reflects the last turn's outcome again after a cold start.
🦋 Changeset detectedLatest commit: 64fbcea The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 301e1b6f4e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Review follow-up: the agent scope (and with it this view) is constructed before wire.restore() replays the journal, so a constructor-time read of TurnModel.lastEnded always saw the initial state on a cold resume. Move the wire-backed seed behind the onDidRestore hook (constructor seed kept for views built after a restore), and drop the inline comments in favor of the file header per the package comment convention.
|
Both review findings addressed in 2e2a69f:
Also added the missing changeset (@moonshot-ai/kimi-code patch). Full agent-core-v2 suite: 4716 passed. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2e2a69f207
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…rators Review follow-up: the previous revision narrated the restore-hook mechanics in the header; the package convention keeps headers at the module's external role plus collaborators, so drop the implementation narrative.
|
Header trimmed in 2f2b64f — now states only the module's role and the collaborator ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2f2b64f472
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Review follow-up: advanceTurnClock built a fresh state object without spreading, so a new prompt or a queued cancel silently dropped the stored last-ended outcome even though no new turn had ended — after a restart the activity view would again find nothing to seed. Spread the prior state and cover the prompt/queued-cancel/replace cycle with a model-level test.
|
Queued-cancel finding addressed in 4df2882: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4df2882475
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…tarts Review follow-up: with the clock advances preserving lastEnded, a prompt persisted without its turn ever starting would leave the previous turn's outcome to be seeded after a restart, reporting a stale result for a turn that never ended. The loop-event fold now drops lastEnded as soon as a newer turn's events land, while prompts and queued cancels keep it.
|
Stale-outcome finding addressed in 27eceb1: prompts and queued cancels still preserve |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 27eceb1436
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Review follow-up: the lastEnded keep/clear mechanics read as implementation narrative in the header; the convention there is role and collaborators only.
|
@codex review |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
|
@codex review |
|
Codex Review: Didn't find any major issues. More of your lovely PRs please. Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
|
@codex review |
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
…ted turn.ended record (MoonshotAI#2648) * fix(agent-core-v2): seed the activity view's lastTurn from the persisted turn.ended record A cold-resumed agent seeded its activity view only from live loop/task state, so the last turn's outcome was lost on a server restart: sessions came back with no lastTurnReason, and clients could not surface a previously failed turn (e.g. a provider 429 that killed the turn before the restart). The loop already persists the terminal turn.ended record (reason, error, durationMs); fold the latest one into the TurnModel as lastEnded and have AgentActivityView.seedFromLoop adopt it when no turn is active, so the session work aggregate (and everything built on it) reflects the last turn's outcome again after a cold start. * fix(agent-core-v2): seed lastTurn on wire restore and add a changeset Review follow-up: the agent scope (and with it this view) is constructed before wire.restore() replays the journal, so a constructor-time read of TurnModel.lastEnded always saw the initial state on a cold resume. Move the wire-backed seed behind the onDidRestore hook (constructor seed kept for views built after a restore), and drop the inline comments in favor of the file header per the package comment convention. * docs(agent-core-v2): trim the activityView header to role and collaborators Review follow-up: the previous revision narrated the restore-hook mechanics in the header; the package convention keeps headers at the module's external role plus collaborators, so drop the implementation narrative. * fix(agent-core-v2): keep TurnModel.lastEnded across clock advances Review follow-up: advanceTurnClock built a fresh state object without spreading, so a new prompt or a queued cancel silently dropped the stored last-ended outcome even though no new turn had ended — after a restart the activity view would again find nothing to seed. Spread the prior state and cover the prompt/queued-cancel/replace cycle with a model-level test. * fix(agent-core-v2): clear the stored turn outcome once a newer turn starts Review follow-up: with the clock advances preserving lastEnded, a prompt persisted without its turn ever starting would leave the previous turn's outcome to be seeded after a restart, reporting a stale result for a turn that never ended. The loop-event fold now drops lastEnded as soon as a newer turn's events land, while prompts and queued cancels keep it. * docs(agent-core-v2): keep the turnOps header at the domain role Review follow-up: the lastEnded keep/clear mechanics read as implementation narrative in the header; the convention there is role and collaborators only.
Problem
A cold-resumed agent seeds its
AgentActivityViewonly from live loop/task state (seedFromLoop/seedFromTasks/seedFromFullCompaction). ThelastTurnoutcome survives only as in-memory fold state, so a server restart wipes it: a session whose last turn failed (e.g. sustained provider 429 exhausting step retries) comes back fromresumewithlastTurnReason: undefined, and clients cannot surface the failure — the desktop/web conversation sits silently idle exactly like the original dead session did.Fix
Two small pieces on top of the existing persisted
turn.endedwire record:TurnModelfolds the latestturn.endedop into a newlastEndedstate field (turnId / reason / durationMs). The op was already persisted for the transcript cold fold; the model just didn't retain it.AgentActivityView.seedFromLoopadoptslastEndedaslastTurnwhen no turn is active, so the session work aggregate (ISessionActivityView→last_turn_reasonon the wire) reflects the last turn's outcome again after a cold start.Live behavior is untouched: a running turn still seeds the active slice and returns early; live
turn.endedevents keep overwritinglastTurnas before.Tests
test/agent/activityView/activityView.test.ts: seedslastTurnfrom the wire model on cold resume; stays empty when the wire has no ended turn.agent-core-v2suite: 4714 passed; repo lint 0 errors; kap-server typecheck clean.Downstream
The desktop app (kimi-code-app) renders a persistent failed-turn card off
lastTurnReason— this restores it across server restarts. A submodule bump there will follow once this lands.