fix: scope observation to current turn
Require assistant natural language after the latest user message so a completed prompt cannot reuse historical replies when queuing the observation snapshot.
This commit is contained in:
1 parent
7a15b1b49c
commit
16f5c57f17
3 files changed
+65
-7
No files matched your search
@@ -120,13 +120,14 @@
|
|||||||
noise remains.
|
noise remains.
|
||||||
- `pnpm run typecheck`: passed, including after the pinned-pnpm dependency rebuild.
|
- `pnpm run typecheck`: passed, including after the pinned-pnpm dependency rebuild.
|
||||||
- `pnpm exec eslint . --quiet`: passed.
|
- `pnpm exec eslint . --quiet`: passed.
|
||||||
- Full unit suite after planner fixes: 174 files passed; 1495 tests passed,
|
- Full unit suite after planner fixes: 174 files passed; 1496 tests passed,
|
||||||
2 skipped (1497 total).
|
2 skipped (1498 total).
|
||||||
- Pressure suite: 1 file and 1 test passed.
|
- Pressure suite: 1 file and 1 test passed.
|
||||||
- Focused post-review suite: 3 files and 33 tests passed for migration,
|
- Focused post-review suite: 3 files and 34 tests passed for migration,
|
||||||
session-observation sync, and Conversation store settlement behavior. It covers
|
session-observation sync, and Conversation store settlement behavior. It covers
|
||||||
valid-v2 no-legacy-read, unknown/modified Agent backup, live ordinary-prompt
|
valid-v2 no-legacy-read, unknown/modified Agent backup, live ordinary-prompt
|
||||||
settlement, no hydrate/follow-up replay, and assistant-natural-language gating.
|
settlement, no hydrate/follow-up replay, and current-turn assistant natural-
|
||||||
|
language gating that cannot reuse an older assistant reply.
|
||||||
- Electron E2E: 3 tests passed for first Pi conversation latency/submit ordering,
|
- Electron E2E: 3 tests passed for first Pi conversation latency/submit ordering,
|
||||||
Pi product UI, and bundled Skill project configuration.
|
Pi product UI, and bundled Skill project configuration.
|
||||||
- `pnpm run build:vite`: passed (Renderer, Electron Main, Preload, and utility).
|
- `pnpm run build:vite`: passed (Renderer, Electron Main, Preload, and utility).
|
||||||
|
|||||||
@@ -363,8 +363,22 @@ export function queueAgentSessionSync(
|
|||||||
|
|
||||||
/** Queue the final product Conversation snapshot after a completed prompt turn. */
|
/** Queue the final product Conversation snapshot after a completed prompt turn. */
|
||||||
export function queueCodingConversationSessionSync(snapshot: ConversationSnapshot): void {
|
export function queueCodingConversationSessionSync(snapshot: ConversationSnapshot): void {
|
||||||
const messages: RawMessage[] = snapshot.nodes.flatMap((node) => {
|
const messageNodes = snapshot.nodes.filter((node) => node.kind === 'message');
|
||||||
if (node.kind !== 'message') return [];
|
let lastUserIndex = -1;
|
||||||
|
for (let index = messageNodes.length - 1; index >= 0; index -= 1) {
|
||||||
|
if (messageNodes[index].role === 'user') {
|
||||||
|
lastUserIndex = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastUserIndex < 0) return;
|
||||||
|
const currentTurnAssistantIds = new Set(messageNodes
|
||||||
|
.slice(lastUserIndex + 1)
|
||||||
|
.filter((node) => node.role === 'assistant')
|
||||||
|
.map((node) => node.id));
|
||||||
|
if (currentTurnAssistantIds.size === 0) return;
|
||||||
|
|
||||||
|
const messages: RawMessage[] = messageNodes.flatMap((node) => {
|
||||||
return [{
|
return [{
|
||||||
id: node.id,
|
id: node.id,
|
||||||
role: node.role,
|
role: node.role,
|
||||||
@@ -384,7 +398,11 @@ export function queueCodingConversationSessionSync(snapshot: ConversationSnapsho
|
|||||||
messages,
|
messages,
|
||||||
updatedAt,
|
updatedAt,
|
||||||
);
|
);
|
||||||
if (!data?.messages.some((message) => message.role === 'assistant')) return;
|
if (!data?.messages.some((message) => (
|
||||||
|
message.role === 'assistant'
|
||||||
|
&& typeof message.id === 'string'
|
||||||
|
&& currentTurnAssistantIds.has(message.id)
|
||||||
|
))) return;
|
||||||
queueAgentSessionData(data);
|
queueAgentSessionData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -220,6 +220,45 @@ describe('local Agent session sync', () => {
|
|||||||
expect(pushSessionDataMock).not.toHaveBeenCalled();
|
expect(pushSessionDataMock).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not reuse an older assistant reply when the current turn has no reply', async () => {
|
||||||
|
const base = createProductSnapshot('conversation-1');
|
||||||
|
queueCodingConversationSessionSync({
|
||||||
|
...base,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
kind: 'message',
|
||||||
|
id: 'old-user',
|
||||||
|
role: 'user',
|
||||||
|
status: 'complete',
|
||||||
|
blocks: [{ kind: 'text', id: 'old-question', text: '旧问题', status: 'complete' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: 'message',
|
||||||
|
id: 'old-assistant',
|
||||||
|
role: 'assistant',
|
||||||
|
status: 'complete',
|
||||||
|
blocks: [{ kind: 'text', id: 'old-answer', text: '旧回答', status: 'complete' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: 'message',
|
||||||
|
id: 'current-user',
|
||||||
|
role: 'user',
|
||||||
|
status: 'complete',
|
||||||
|
blocks: [{ kind: 'text', id: 'current-question', text: '新问题', status: 'complete' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
run: {
|
||||||
|
status: 'idle',
|
||||||
|
runId: 'run-2',
|
||||||
|
settledAt: Date.parse('2026-08-24T08:01:00.000Z'),
|
||||||
|
terminalReason: 'completed',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await flushPendingAgentSessionSync();
|
||||||
|
|
||||||
|
expect(pushSessionDataMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('never uploads a pending snapshot under a different authenticated account', async () => {
|
it('never uploads a pending snapshot under a different authenticated account', async () => {
|
||||||
queueAgentSessionSync('prj_1', 'ses_1', [
|
queueAgentSessionSync('prj_1', 'ses_1', [
|
||||||
{ id: 'user-1', role: 'user', content: '账号 A 的问题' },
|
{ id: 'user-1', role: 'user', content: '账号 A 的问题' },
|
||||||
|
|||||||
Reference in new issue
Block a user