-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat(coding-agent): add namespaced todo projections #6522
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
panosAthDBX
wants to merge
34
commits into
can1357:main
Choose a base branch
from
panosAthDBX:feat/namespaced-todo-projection
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
ba85073
feat(coding-agent): add namespaced todo projections
panosAthDBX d2b3a7e
Merge remote-tracking branch 'upstream/main' into feat/namespaced-tod…
panosAthDBX 82937b7
fix(coding-agent): harden todo projection rendering
panosAthDBX 2c4eb4b
Merge upstream main into feat/namespaced-todo-projection
panosAthDBX 2fa6faf
fix(coding-agent): format todo projection changes
panosAthDBX 71a5b82
fix(coding-agent): stabilize todo projection lifecycle
panosAthDBX 218a311
fix(coding-agent): retarget todos with focused session
panosAthDBX 50b2066
fix(coding-agent): render startup todo projections
panosAthDBX 149e84c
fix(coding-agent): refresh projections in mode startup
panosAthDBX ab8b97d
fix(coding-agent): forward todo projection RPC events
panosAthDBX aa06c10
fix(coding-agent): publish RPC todo projection snapshots
panosAthDBX 89be10e
Merge upstream main into feat/namespaced-todo-projection
panosAthDBX 023d674
fix(ai): avoid Anthropic OAuth export cycle
panosAthDBX 8b3efca
fix(coding-agent): repair projection transport regressions
panosAthDBX 12d1272
fix(coding-agent): preserve projection contracts
panosAthDBX 90f5be7
fix(acp): retain projection terminal states
panosAthDBX 4d112b1
Merge upstream main into feat/namespaced-todo-projection
panosAthDBX 4e7f108
test: stabilize loaded CI lifecycle checks
panosAthDBX 9bce746
ci: retry transient bun downloads
panosAthDBX e60f8c6
test: stabilize detached AWS and model probes
panosAthDBX d2fb602
fix: preserve startup projections and stabilize scrollback test
panosAthDBX 8998366
fix(coding-agent): flush passive RPC startup projections
panosAthDBX 34a75e9
fix(coding-agent): flush passive RPC startup projection after initial…
panosAthDBX a9143e5
fix(todo): harden sparse projections and RPC startup negotiation
panosAthDBX fa5fee6
fix(rpc): make startup projection negotiation deterministic
panosAthDBX 1a21cf2
fix(tui): shorten projected home paths and rebuild PR install natives
panosAthDBX c65715f
test(coding-agent): bound and reap profile CLI probes
panosAthDBX c0ff0db
Merge upstream main into feat/namespaced-todo-projection
panosAthDBX 6f7abe5
Merge remote-tracking branch 'upstream/main' into feat/namespaced-tod…
panosAthDBX 19500e5
merge: update namespaced todo projections with main
panosAthDBX 11c8777
Merge concurrent PR publication
panosAthDBX 3fe3470
fix(coding-agent): flush startup JSON events before prompting
panosAthDBX 01d3412
test(coding-agent): provide projection snapshot in executor session d…
panosAthDBX 6d76c3a
ci: retry transient browser relay timeout
panosAthDBX File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
144 changes: 144 additions & 0 deletions
144
packages/coding-agent/src/extensibility/extensions/todo-projection.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| export type TodoProjectionStatus = "pending" | "in_progress" | "completed" | "failed" | "cancelled" | "abandoned"; | ||
|
|
||
| /** A host-rendered todo owned by an extension namespace. */ | ||
| export interface TodoProjectionItem { | ||
| /** Stable within the namespace for the lifetime of the projected work item. */ | ||
| readonly id: string; | ||
| readonly content: string; | ||
| readonly status: TodoProjectionStatus; | ||
| } | ||
|
|
||
| /** A display group for extension-owned projected todos. */ | ||
| export interface TodoProjectionPhase { | ||
| /** Stable within the namespace for the lifetime of the projected phase. */ | ||
| readonly id: string; | ||
| readonly name: string; | ||
| readonly tasks: readonly TodoProjectionItem[]; | ||
| } | ||
|
|
||
| /** Read-only snapshot exposed by the host for rendering and inspection. */ | ||
| export interface NamespacedTodoProjection { | ||
| readonly namespace: string; | ||
| readonly phases: readonly TodoProjectionPhase[]; | ||
| } | ||
|
|
||
| const TODO_PROJECTION_STATUSES: Record<TodoProjectionStatus, true> = { | ||
| pending: true, | ||
| in_progress: true, | ||
| completed: true, | ||
| failed: true, | ||
| cancelled: true, | ||
| abandoned: true, | ||
| }; | ||
|
|
||
| function requireStableId(value: string, kind: "phase" | "task"): string { | ||
| const id = value.trim(); | ||
| if (!id) throw new Error(`Todo projection ${kind} id must be non-empty`); | ||
| return id; | ||
| } | ||
|
|
||
| export function normalizeTodoProjectionNamespace(namespace: string): string { | ||
| const normalized = namespace.trim(); | ||
| if (!normalized) throw new Error("Todo projection namespace must be non-empty"); | ||
| return normalized; | ||
| } | ||
|
|
||
| function assertDenseTodoProjectionArray(values: readonly unknown[], label: "phase" | "task"): void { | ||
| for (let index = 0; index < values.length; index++) { | ||
| if (!Object.hasOwn(values, index)) { | ||
| throw new Error(`Todo projection ${label} array must not contain holes (missing index ${index})`); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Validate and clone extension-owned data at the public API boundary. The | ||
| * returned value contains only fields the host renders; extra caller metadata | ||
| * is deliberately discarded. | ||
| */ | ||
| export function cloneTodoProjection(phases: readonly TodoProjectionPhase[]): TodoProjectionPhase[] { | ||
| const phaseIds = new Set<string>(); | ||
| const taskIds = new Set<string>(); | ||
| assertDenseTodoProjectionArray(phases, "phase"); | ||
| for (const phase of phases) assertDenseTodoProjectionArray(phase.tasks, "task"); | ||
| return phases.map(phase => { | ||
| const id = requireStableId(phase.id, "phase"); | ||
| if (phaseIds.has(id)) throw new Error(`Duplicate todo projection phase id: ${id}`); | ||
| phaseIds.add(id); | ||
| const name = phase.name.trim(); | ||
| if (!name) throw new Error(`Todo projection phase ${id} must have a non-empty name`); | ||
| const tasks = phase.tasks.map(task => { | ||
| const taskId = requireStableId(task.id, "task"); | ||
| if (taskIds.has(taskId)) throw new Error(`Duplicate todo projection task id: ${taskId}`); | ||
| taskIds.add(taskId); | ||
| const content = task.content.trim(); | ||
| if (!content) throw new Error(`Todo projection task ${taskId} must have non-empty content`); | ||
| if (TODO_PROJECTION_STATUSES[task.status] !== true) { | ||
| throw new Error(`Invalid todo projection status for ${taskId}: ${String(task.status)}`); | ||
| } | ||
| return { id: taskId, content, status: task.status }; | ||
| }); | ||
| return { id, name, tasks }; | ||
| }); | ||
| } | ||
|
|
||
| export function cloneNamespacedTodoProjections( | ||
| projections: ReadonlyMap<string, readonly TodoProjectionPhase[]>, | ||
| ): NamespacedTodoProjection[] { | ||
| return [...projections.entries()] | ||
| .sort(([left], [right]) => left.localeCompare(right)) | ||
| .map(([namespace, phases]) => ({ namespace, phases: cloneTodoProjection(phases) })); | ||
| } | ||
|
|
||
| function todoProjectionPhasesEqual( | ||
| left: readonly TodoProjectionPhase[], | ||
| right: readonly TodoProjectionPhase[], | ||
| ): boolean { | ||
| if (left.length !== right.length) return false; | ||
| for (let phaseIndex = 0; phaseIndex < left.length; phaseIndex++) { | ||
| const leftPhase = left[phaseIndex]!; | ||
| const rightPhase = right[phaseIndex]!; | ||
| if (leftPhase.id !== rightPhase.id || leftPhase.name !== rightPhase.name) return false; | ||
| if (leftPhase.tasks.length !== rightPhase.tasks.length) return false; | ||
| for (let taskIndex = 0; taskIndex < leftPhase.tasks.length; taskIndex++) { | ||
| const leftTask = leftPhase.tasks[taskIndex]!; | ||
| const rightTask = rightPhase.tasks[taskIndex]!; | ||
| if ( | ||
| leftTask.id !== rightTask.id || | ||
| leftTask.content !== rightTask.content || | ||
| leftTask.status !== rightTask.status | ||
| ) { | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| /** | ||
| * Session-owned store for display-only projections. It has no dependency on | ||
| * canonical todo state or transcript persistence. | ||
| */ | ||
| export class TodoProjectionStore { | ||
| #projections = new Map<string, TodoProjectionPhase[]>(); | ||
|
|
||
| set(namespace: string, phases: readonly TodoProjectionPhase[] | undefined): boolean { | ||
| const key = normalizeTodoProjectionNamespace(namespace); | ||
| if (phases === undefined) return this.#projections.delete(key); | ||
| const next = cloneTodoProjection(phases); | ||
| const current = this.#projections.get(key); | ||
| if (current && todoProjectionPhasesEqual(current, next)) return false; | ||
| this.#projections.set(key, next); | ||
| return true; | ||
| } | ||
|
|
||
| snapshot(): NamespacedTodoProjection[] { | ||
| return cloneNamespacedTodoProjections(this.#projections); | ||
| } | ||
|
|
||
| clear(): boolean { | ||
| if (this.#projections.size === 0) return false; | ||
| this.#projections.clear(); | ||
| return true; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.