Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
09b3d07
feat(cli): adopt Goal v3 in ACP sessions
qqqys Aug 8, 2026
b1689bb
test(cli): give the ACP Session fakes a Goal runtime
qqqys Aug 8, 2026
e6a3735
fix(cli): settle Goal turns that fail, and answer goal RPCs without p…
qqqys Aug 8, 2026
b0f78ad
fix(cli): stop Goal turns leaking permits, and show a paused goal as …
qqqys Aug 8, 2026
69a27b3
fix(cli): unblock deadlocked Goal turns and stop a capped goal loop
qqqys Aug 9, 2026
086406f
fix(cli): keep Goal degradation working headless and re-subscribe aft…
qqqys Aug 9, 2026
5de2229
Merge upstream/main into agent/goal-v3-acp
qqqys Aug 9, 2026
feb90d7
merge: resolve acpAgent conflict with main's session-restore profiler
qqqys Aug 9, 2026
6d25cbb
fix(cli): repair Goal recovery on ACP resume
qqqys Aug 9, 2026
c5c90ff
fix(acp-bridge): replay paused goal cards instead of dropping them
qqqys Aug 9, 2026
8dcbe4b
fix(core): report a lost session writer as a Goal persistence failure
qqqys Aug 10, 2026
985d0b1
fix(cli): deliver the recovered Goal card on the bulk load and resume…
qqqys Aug 10, 2026
d178b96
fix(cli): stop Goal permits leaking on turns that never reached the m…
qqqys Aug 10, 2026
ea64631
Merge branch 'main' into agent/goal-v3-acp
qwen-code-dev-bot Aug 10, 2026
e69b5d2
Merge branch 'main' into agent/goal-v3-acp
qwen-code-dev-bot Aug 10, 2026
8218067
fix(goals): surface failed clear persistence
qqqys Aug 10, 2026
56d9d4f
fix(goal): surface persistence recovery failures
qqqys Aug 11, 2026
347f30c
fix(goal): clean up failed permit claims
qqqys Aug 11, 2026
d1bb047
test(cli): align degraded Goal CI setup
qqqys Aug 11, 2026
3c8c848
fix(cli): stop Goal continuations after cancel
qqqys Aug 11, 2026
8c5dc82
test(webui): cover paused Goal status normalization
qqqys Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions packages/acp-bridge/src/bridgeTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import type {
ApprovalMode,
GoalSnapshotV2,
SessionGroupPresetColor,
} from '@qwen-code/qwen-code-core';
import type {
Expand Down Expand Up @@ -601,16 +602,14 @@ export interface BridgeSessionSummary {
}

/**
* A session's live `/goal` state, as reported by the `qwen --acp` child.
*
* Only the active goal crosses the bridge. The child also caches the most
* recent goal that ended on its own, but nothing on this side reads it, so it
* is not part of the wire shape — add it back alongside the first consumer.
* A session's live canonical Goal state, as reported by the `qwen --acp`
* child. `active` remains as a compatibility projection for existing hosts.
*/
export interface BridgeSessionGoal {
snapshot: GoalSnapshotV2;
active: {
condition: string;
/** Judge turns completed so far; 0 before the first stop-hook evaluation. */
/** Canonical Goal turns completed so far. */
iterations: number;
setAt: number;
/** The judge's verdict on the most recent turn, when it has run. */
Expand Down Expand Up @@ -1450,9 +1449,8 @@ export interface AcpSessionBridge {
): Promise<{ cleared: boolean; condition?: string }>;

/**
* Read a live session's goal state. Throws `SessionNotFoundError` when the
* session is not resident — goals live in the child's memory, so a
* non-resident session has no goal to report.
* Read a live session's Goal state. Throws `SessionNotFoundError` when the
* session is not resident because this route addresses the selected runtime.
*/
getSessionGoal(sessionId: string): Promise<BridgeSessionGoal>;

Expand Down
45 changes: 45 additions & 0 deletions packages/acp-bridge/src/transcript-replay.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ function goalStateRecord(
});
}

function goalCardRecord(
uuid: string,
...items: ReadonlyArray<Record<string, unknown>>
): TranscriptRecordInput {
return record(uuid, 'system', {
subtype: 'slash_command',
systemPayload: { phase: 'result', outputHistoryItems: items },
});
}

describe('createTranscriptReplayMachine', () => {
it('does not replay internal Goal runtime prompts as user messages', () => {
expect(
Expand Down Expand Up @@ -112,6 +122,41 @@ describe('createTranscriptReplayMachine', () => {
});
});

it('replays a legacy paused goal card instead of leaving the set card newest', () => {
const machine = createTranscriptReplayMachine();
expect(
updates(
machine,
goalCardRecord('goal-set', {
type: 'goal_status',
kind: 'set',
condition: GOAL.objective,
}),
),
).toHaveLength(1);

const projected = updates(
machine,
goalCardRecord('goal-paused', {
type: 'goal_status',
kind: 'paused',
condition: GOAL.objective,
iterations: 4,
lastReason: 'paused by the user',
}),
);

expect(projected).toHaveLength(1);
expect(projected[0]?._meta).toMatchObject({
goalStatus: {
kind: 'paused',
condition: GOAL.objective,
iterations: 4,
lastReason: 'paused by the user',
},
});
});

it('emits legacy goalTerminal metadata for a terminal goal_state', () => {
const projected = updates(
createTranscriptReplayMachine(),
Expand Down
6 changes: 6 additions & 0 deletions packages/acp-bridge/src/transcript-replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ const TRANSCRIPT_GOAL_STATUS_KINDS = new Set([
'cleared',
'failed',
'aborted',
// A paused goal is not running, and dropping the card here is not neutral:
// the replay stream is what feeds the goal renderer, so the older `set` card
// stays newest and every surface keeps claiming autonomous work is under way.
// Kept in step with `GOAL_STATUS_KINDS`, which the daemon-side reader
// (`parseGoalStatusItem`) validates the same on-disk cards against.
'paused',
'checking',
]);

Expand Down
Loading
Loading