From 5a2890f854ee0414701ecd553f5b511744ee2760 Mon Sep 17 00:00:00 2001 From: Endri Bezati Date: Mon, 9 Nov 2026 07:46:14 +0100 Subject: [PATCH 1/2] feat: the stock bundle no longer loads the agent workflow UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The @tool and @agent annotation editors, the agent sections of a node's panel and the wizard's @tool and @viewer templates are wfpy's, yet every product's diagram showed them, because the stock feature module bound them. It binds none now: they are agentUiModule (#136), which wfpy-ide's own webview bundle loads (endrix/wfpy-ide#24). sv, vhdl, ndf and calpy get the plain annotation card, no Skill row, no Agent Tools, no Agent Context, and only the wizard's own "Custom annotation…". The stock bundle drops the agent UI's code (141 KB). The parity baseline loses the five bindings; the seam tests that relied on the stock module for them load agentUiModule, and each now also pins that the stock diagram has none. Ship after wfpy-ide#24, in the release that bumps the API to 0.9. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv --- CHANGELOG.md | 10 ++++++++ packages/diagram-client/README.md | 18 +++++++------ packages/diagram-client/src/agent-ui/index.ts | 9 +++---- packages/diagram-client/src/node-section.ts | 2 +- .../src/stock-features.module.ts | 9 +++---- .../test/agent-ui/annotation-wizard.test.ts | 2 +- .../diagram-client/test/agent-ui/panel.ts | 2 +- .../test/annotation-editor-seam.test.ts | 14 ++++++----- .../test/annotation-template-seam.test.ts | 12 ++++++--- .../test/container-parity.test.ts | 5 +--- .../fixtures/container-parity.baseline.json | 25 ------------------- .../test/node-section-seam.test.ts | 13 +++++++--- 12 files changed, 57 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35ff02d..bf88aa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,16 @@ platform API version. ### Changed +- **The stock bundle no longer loads the agent workflow UI.** The `@tool` and + `@agent` annotation editors, the agent sections of a node's panel (Skill + row, Agent Tools, live Agent Context) and the wizard's `@tool` and + `@viewer` templates were wfpy's, yet sv, vhdl, ndf and calpy showed them + too. They are `agentUiModule`, which wfpy-ide's own webview bundle loads + (endrix/wfpy-ide#24). The stock bundle is 141 KB smaller. **Ship after + wfpy-ide#24, in the release that bumps `DIALOGRAM_API_VERSION` to 0.9**, so + a wfpy-ide still at 0.8 (and still on the stock bundle) fails its version + check instead of losing its agent UI. + - **Breaking for a profile that relied on the defaults: the Entities palette, navigation and the source MIME type are the product's.** The tool-palette provider no longer hard-codes wfpy's five rows (Task, Tool, Agent, Viewer, diff --git a/packages/diagram-client/README.md b/packages/diagram-client/README.md index 6dbf2ad..ed6cc75 100644 --- a/packages/diagram-client/README.md +++ b/packages/diagram-client/README.md @@ -208,8 +208,8 @@ Without `formatAnnotation`, Save writes `@name(key=value, ...)`. With ones the card does not show. `guiOnlyArgs` are keys your editor keeps in `args` for its own state; Save writes them nowhere. `quickAdd` puts an "add from template" button in the Annotations header. There is one editor per -name: a later binding replaces an earlier one, so your module can replace one -of the stock editors (`@tool`, `@agent`). +name: a later binding replaces an earlier one, so a module loaded later can +replace an editor an earlier one bound. **Node sections.** To show more about a node than the panel's own sections, bind a `NodeSectionContribution`. It needs an `id`; every hook is optional: @@ -241,9 +241,7 @@ export const myFeaturesModule = new ContainerModule(bind => { again, for an answer that arrives later. Contributions are asked in the order their ids were first bound. A later -binding for an id replaces the earlier one, so your module can replace the -stock bundle's agent sections (id `agent`: the Skill row, Agent Tools and the -live Agent Context). +binding for an id replaces the earlier one. **Annotation templates.** The Annotations header's "Wizard…" adds an annotation from a template, or from a name and arguments typed in. To offer a @@ -267,8 +265,14 @@ export const myFeaturesModule = new ContainerModule(bind => { There is one template per annotation name, and a later binding replaces an earlier one. The wizard lists them before its own "Custom annotation…" entry -(`custom`), which a template cannot replace. The stock bundle binds `@tool` -and `@viewer`. +(`custom`), which a template cannot replace. + +**The agent workflow UI.** The stock bundle binds no editors, sections or +templates. wfpy's are `agentUiModule`, imported by path as +`@dialogram/diagram-client/agent-ui/index`: the `@tool` and `@agent` +editors, the agent sections (id `agent`: Basic Info's Skill row, Agent Tools +and the live Agent Context) and the `@tool` and `@viewer` templates. A bundle +that wants them passes it in `features`, as wfpy-ide's does. **A diagram of your own.** When you compose the container yourself: diff --git a/packages/diagram-client/src/agent-ui/index.ts b/packages/diagram-client/src/agent-ui/index.ts index 0842349..bc7b96f 100644 --- a/packages/diagram-client/src/agent-ui/index.ts +++ b/packages/diagram-client/src/agent-ui/index.ts @@ -2,11 +2,10 @@ * The agent workflow UI: the `@tool` and `@agent` annotation editors, the agent * sections of a node's panel, and the wizard's `@tool` and `@viewer` templates. * - * `agentUiModule` binds all of it. A product's own webview bundle passes it to - * `startStockDiagram({ features: [agentUiModule] })` (imported by path, - * `@dialogram/diagram-client/agent-ui/index`); the stock feature module binds - * the same today, and each contribution point keeps the last binding per - * name or id, so binding it twice draws everything once. + * `agentUiModule` binds all of it. The stock bundle does not load it: a + * product's own webview bundle passes it to + * `startStockDiagram({ features: [agentUiModule] })`, imported by path as + * `@dialogram/diagram-client/agent-ui/index`. */ import { ContainerModule, type interfaces } from 'inversify'; import { AnnotationEditor } from '../annotation-editor'; diff --git a/packages/diagram-client/src/node-section.ts b/packages/diagram-client/src/node-section.ts index f7bb13a..4f63e8f 100644 --- a/packages/diagram-client/src/node-section.ts +++ b/packages/diagram-client/src/node-section.ts @@ -47,7 +47,7 @@ export interface NodeSectionHost { } export interface NodeSectionContribution { - /** What the contribution is, for a later binding to replace it: the stock one is `'agent'`. */ + /** What the contribution is, for a later binding to replace it (the agent sections are `'agent'`). */ readonly id: string; /** Once, when the panel starts. */ diff --git a/packages/diagram-client/src/stock-features.module.ts b/packages/diagram-client/src/stock-features.module.ts index f2705d8..a526758 100644 --- a/packages/diagram-client/src/stock-features.module.ts +++ b/packages/diagram-client/src/stock-features.module.ts @@ -13,7 +13,6 @@ import { ContainerModule } from 'inversify'; import { ChangeBoundsTool, TYPES, bindAsService, configureActionHandler } from '@eclipse-glsp/client'; import { MoveAction } from '@eclipse-glsp/sprotty'; import { PropertyPanel } from './property-panel'; -import { bindAgentUi } from './agent-ui'; import { ChatPanel } from './chat-panel-integrated'; import { clientBehavior } from './profile'; import { WorkflowNavigationUi } from './navigation-ui'; @@ -72,11 +71,9 @@ export const workflowFeaturesModule = new ContainerModule((bind, unbind, isBound bind(PropertyPanel).toSelf().inSingletonScope(); bind(TYPES.ISelectionListener).toService(PropertyPanel); bind(TYPES.IGModelRootListener).toService(PropertyPanel); - // The agent workflow UI (agent-ui/): the panel's `@tool` and `@agent` - // editors, the agent sections of a node's panel (Basic Info's Skill row, - // Agent Tools, the live Agent Context) and the wizard's `@tool` and - // `@viewer` templates. A product's module binds its own the same way. - bindAgentUi(bind); + // No annotation editors, node sections or wizard templates of its own: a + // product binds those in its feature module. The agent workflow UI is + // `agentUiModule` (agent-ui/), which a product's own bundle loads. // The hierarchy outline, fed by the root model's `wf:hierarchy`. Shown only // when the product navigates nested workflows in place and the model diff --git a/packages/diagram-client/test/agent-ui/annotation-wizard.test.ts b/packages/diagram-client/test/agent-ui/annotation-wizard.test.ts index eea37e4..2660fb1 100644 --- a/packages/diagram-client/test/agent-ui/annotation-wizard.test.ts +++ b/packages/diagram-client/test/agent-ui/annotation-wizard.test.ts @@ -16,7 +16,7 @@ class TestWizard extends WorkflowEditAnnotationsActionHandler { readonly dispatched: any[] = []; constructor(node: any) { super(); - // What the stock feature module binds. + // What `agentUiModule` binds. (this as any).contributedTemplates = stockAnnotationTemplates(); (this as any).editorContext = { modelRoot: rootOf(node) }; (this as any).actionDispatcher = { dispatch: async (a: unknown) => { this.dispatched.push(a); } }; diff --git a/packages/diagram-client/test/agent-ui/panel.ts b/packages/diagram-client/test/agent-ui/panel.ts index 5351a14..0a12262 100644 --- a/packages/diagram-client/test/agent-ui/panel.ts +++ b/packages/diagram-client/test/agent-ui/panel.ts @@ -35,7 +35,7 @@ export class TestPanel extends PropertyPanel { constructor() { super(); - // What the stock feature module binds. + // What `agentUiModule` binds. (this as any).contributedAnnotationEditors = stockAnnotationEditors(); (this as any).contributedNodeSections = stockNodeSections(); (this as any).actionDispatcher = { diff --git a/packages/diagram-client/test/annotation-editor-seam.test.ts b/packages/diagram-client/test/annotation-editor-seam.test.ts index b53315b..a1b9d4e 100644 --- a/packages/diagram-client/test/annotation-editor-seam.test.ts +++ b/packages/diagram-client/test/annotation-editor-seam.test.ts @@ -12,6 +12,7 @@ import { ChangeBoundsTool, TYPES } from '@eclipse-glsp/client'; import { AnnotationEditor, type AnnotationEditorContext } from '../src/annotation-editor'; import { PropertyPanel } from '../src/property-panel'; import { workflowFeaturesModule } from '../src/stock-features.module'; +import { agentUiModule } from '../src/agent-ui'; import { installPage, taskNode } from './agent-ui/harness'; import { TestPanel, button, card, resetAgentUi, rootOf, section, tick } from './agent-ui/panel'; @@ -179,23 +180,24 @@ describe('the editors the panel is given', () => { const names = (panel: PropertyPanel) => (panel as any).annotationEditors().map((e: AnnotationEditor) => e.annotationName); - it('are the stock @tool and @agent editors, in that order', () => { - expect(names(stockContainer().get(PropertyPanel))).toEqual(['tool', 'agent']); + it('are none in the stock diagram', () => { + expect(names(stockContainer().get(PropertyPanel))).toEqual([]); }); - it('include a product’s, after the stock ones', () => { + it('are the agent UI’s @tool and @agent when a bundle loads it, then another module’s', () => { const product = new ContainerModule(bind => { bind(AnnotationEditor).toConstantValue(probeEditor()); }); - expect(names(stockContainer(product).get(PropertyPanel))).toEqual(['tool', 'agent', 'probe']); + expect(names(stockContainer(agentUiModule).get(PropertyPanel))).toEqual(['tool', 'agent']); + expect(names(stockContainer(agentUiModule, product).get(PropertyPanel))).toEqual(['tool', 'agent', 'probe']); }); - it('take a product’s editor in place of the stock one for its annotation', () => { + it('take a later module’s editor in place of an earlier one’s for its annotation', () => { const mine = probeEditor([], 'agent'); const product = new ContainerModule(bind => { bind(AnnotationEditor).toConstantValue(mine); }); - const editors = (stockContainer(product).get(PropertyPanel) as any).annotationEditors(); + const editors = (stockContainer(agentUiModule, product).get(PropertyPanel) as any).annotationEditors(); expect(editors.map((e: AnnotationEditor) => e.annotationName)).toEqual(['tool', 'agent']); expect(editors[1]).toBe(mine); }); diff --git a/packages/diagram-client/test/annotation-template-seam.test.ts b/packages/diagram-client/test/annotation-template-seam.test.ts index 6033c70..47df9b1 100644 --- a/packages/diagram-client/test/annotation-template-seam.test.ts +++ b/packages/diagram-client/test/annotation-template-seam.test.ts @@ -9,7 +9,7 @@ import { Container, ContainerModule } from 'inversify'; import { ChangeBoundsTool, EditorContextService, TYPES } from '@eclipse-glsp/client'; import { AnnotationTemplate, type AnnotationTemplateContext } from '../src/annotation-template'; import { WorkflowEditAnnotationsActionHandler } from '../src/editing-action-handlers'; -import { stockAnnotationTemplates } from '../src/agent-ui'; +import { agentUiModule, stockAnnotationTemplates } from '../src/agent-ui'; import { workflowFeaturesModule } from '../src/stock-features.module'; import { ui, installPage, taskNode } from './agent-ui/harness'; import { resetAgentUi, rootOf } from './agent-ui/panel'; @@ -149,12 +149,16 @@ describe('the templates the wizard is given', () => { const names = (h: any) => h.annotationTemplates().map((t: AnnotationTemplate) => t.annotationName); - it('are the stock @tool and @viewer, then a product’s', () => { + it('are none in the stock diagram', () => { + expect(names(container().resolve(WorkflowEditAnnotationsActionHandler))).toEqual([]); + }); + + it('are the agent UI’s @tool and @viewer when a bundle loads it, then another module’s', () => { const product = new ContainerModule(bind => { bind(AnnotationTemplate).toConstantValue(probe('retry', '@retry')); }); - expect(names(container().resolve(WorkflowEditAnnotationsActionHandler))).toEqual(['tool', 'viewer']); - expect(names(container(product).resolve(WorkflowEditAnnotationsActionHandler))).toEqual(['tool', 'viewer', 'retry']); + expect(names(container(agentUiModule).resolve(WorkflowEditAnnotationsActionHandler))).toEqual(['tool', 'viewer']); + expect(names(container(agentUiModule, product).resolve(WorkflowEditAnnotationsActionHandler))).toEqual(['tool', 'viewer', 'retry']); }); it('are none when nothing is bound', () => { diff --git a/packages/diagram-client/test/container-parity.test.ts b/packages/diagram-client/test/container-parity.test.ts index ad15ffc..d680fda 100644 --- a/packages/diagram-client/test/container-parity.test.ts +++ b/packages/diagram-client/test/container-parity.test.ts @@ -32,10 +32,7 @@ * states, and the read-only view marker (hides the palette's tools while the view shown is * read-only, bound to itself and as a model-root listener), and the selection trace (its listener, * bound to itself and as a selection listener, and the `SelectionTraceFeedbackCommand` that draws - * the path), and the property panel's stock annotation editors (`@tool` and `@agent`, - * bound as constants under `AnnotationEditor`), its agent sections (`AgentNodeSections`, - * bound as a constant under `NodeSectionContribution`) and the annotation wizard's templates - * (`@tool` and `@viewer`, bound as constants under `AnnotationTemplate`) — plus one change: `label:boundary:type` is bound to + * the path) — plus one change: `label:boundary:type` is bound to * the non-editable `BoundaryLabel`, because editing it renamed the port. * The oracle still does its job: it fails on any binding this composition gains * or loses, and updating the fixture is the deliberate act of accepting one. diff --git a/packages/diagram-client/test/fixtures/container-parity.baseline.json b/packages/diagram-client/test/fixtures/container-parity.baseline.json index 4871005..23764d1 100644 --- a/packages/diagram-client/test/fixtures/container-parity.baseline.json +++ b/packages/diagram-client/test/fixtures/container-parity.baseline.json @@ -96,31 +96,6 @@ "op": "rebind", "to": "ViewportPreservingSetModelCommand" }, - { - "id": "Symbol(dialogram.AnnotationEditor)", - "op": "bind", - "to": "const:ToolAnnotationEditor" - }, - { - "id": "Symbol(dialogram.AnnotationEditor)", - "op": "bind", - "to": "const:AgentAnnotationEditor" - }, - { - "id": "Symbol(dialogram.AnnotationTemplate)", - "op": "bind", - "to": "const:ToolAnnotationTemplate" - }, - { - "id": "Symbol(dialogram.AnnotationTemplate)", - "op": "bind", - "to": "const:ViewerAnnotationTemplate" - }, - { - "id": "Symbol(dialogram.NodeSectionContribution)", - "op": "bind", - "to": "const:AgentNodeSections" - }, { "id": "Symbol(stub.ICopyPasteHandler)", "op": "bind", diff --git a/packages/diagram-client/test/node-section-seam.test.ts b/packages/diagram-client/test/node-section-seam.test.ts index 765daf2..71c8e37 100644 --- a/packages/diagram-client/test/node-section-seam.test.ts +++ b/packages/diagram-client/test/node-section-seam.test.ts @@ -14,6 +14,7 @@ import { NodeSectionContribution, type NodeSectionHost } from '../src/node-secti import { createPropertySection } from '../src/property-section'; import { PropertyPanel } from '../src/property-panel'; import { workflowFeaturesModule } from '../src/stock-features.module'; +import { agentUiModule } from '../src/agent-ui'; import { installPage, taskNode, ANALYST_AGENT } from './agent-ui/harness'; import { TestPanel, resetAgentUi, rootOf, section } from './agent-ui/panel'; @@ -196,23 +197,27 @@ describe('the node sections the panel is given', () => { const kinds = (panel: PropertyPanel) => (panel as any).nodeSections().map((s: NodeSectionContribution) => s.constructor?.name === 'Object' ? 'product' : s.constructor?.name); - it('are the stock agent sections, then a product’s, each installed once after injection', () => { + it('are none in the stock diagram', () => { + expect(kinds(stockContainer().get(PropertyPanel))).toEqual([]); + }); + + it('are the agent UI’s sections when a bundle loads it, then another module’s, each installed once after injection', () => { const calls: Call[] = []; const product = new ContainerModule(bind => { bind(NodeSectionContribution).toConstantValue(probe(calls)); }); - const panel = stockContainer(product).get(PropertyPanel); + const panel = stockContainer(agentUiModule, product).get(PropertyPanel); expect(kinds(panel)).toEqual(['AgentNodeSections', 'product']); expect(calls).toEqual([['install']]); }); - it('take a product’s agent sections in place of the stock ones', () => { + it('take a later module’s agent sections in place of the agent UI’s', () => { const calls: Call[] = []; const mine = probe(calls, 'mine', 'agent'); const product = new ContainerModule(bind => { bind(NodeSectionContribution).toConstantValue(mine); }); - const panel = stockContainer(product).get(PropertyPanel); + const panel = stockContainer(agentUiModule, product).get(PropertyPanel); expect((panel as any).nodeSections()).toEqual([mine]); expect(calls).toEqual([['install']]); }); From 1c0a608b264dad07d971ac9cb4d5664528a046f5 Mon Sep 17 00:00:00 2001 From: Endri Bezati Date: Mon, 9 Nov 2026 07:53:24 +0100 Subject: [PATCH 2/2] fix: the agent UI module brings its discovery handlers; no stock leftovers in the docs Review of #137: - The handlers of the server's skill and agent-preset answers (WorkflowShowAgentSkills/ClaudeAgentsActionHandler) were still bound by the stock module, though only the agent UI reads their caches: agentUiModule worked only because the stock module bound them for it. bindAgentUi configures them now (it takes the module's context), and the stock module does not; the parity baseline loses the two. - Each seam now also pins the stock diagram plus a product's module without the agent UI, what sv, vhdl, ndf and calpy run: only the product's editor, section (installed once) or template. - The contribution points' docs no longer speak of "stock" editors, sections and templates, and the panel's model-root listener is justified by in-place node-section updates rather than agent context; the lists are agentUiEditors/agentUiNodeSections/agentUiTemplates. - CHANGELOG: the entry leads with what breaks and for whom, and [Unreleased] opens with the 0.9 API bump this release needs. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv --- CHANGELOG.md | 26 ++++++++---- packages/diagram-client/src/agent-ui/index.ts | 41 +++++++++++++++---- .../diagram-client/src/annotation-editor.ts | 4 +- .../diagram-client/src/annotation-template.ts | 4 +- packages/diagram-client/src/contributions.ts | 6 +-- packages/diagram-client/src/node-section.ts | 4 +- packages/diagram-client/src/property-panel.ts | 4 +- .../src/stock-features.module.ts | 13 ++---- .../test/agent-ui/annotation-wizard.test.ts | 4 +- .../diagram-client/test/agent-ui/panel.ts | 6 +-- .../test/annotation-editor-seam.test.ts | 6 ++- .../test/annotation-template-seam.test.ts | 14 ++++--- .../fixtures/container-parity.baseline.json | 10 ----- .../test/node-section-seam.test.ts | 8 +++- .../test/stock-feature-modules.test.ts | 12 +++--- 15 files changed, 97 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf88aa5..c33eab9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ platform API version. ## [Unreleased] +**Release:** this release must bump `DIALOGRAM_API_VERSION` to 0.9 and ship +with products built against it: its API removals (#127, #129) and the stock +bundle's dropping the agent workflow UI (below) need a product still at 0.8 +to fail its version check rather than run against what it no longer has. + ### Removed - **The `@path` annotation editor and its wizard template.** No producer has @@ -96,15 +101,18 @@ platform API version. ### Changed -- **The stock bundle no longer loads the agent workflow UI.** The `@tool` and - `@agent` annotation editors, the agent sections of a node's panel (Skill - row, Agent Tools, live Agent Context) and the wizard's `@tool` and - `@viewer` templates were wfpy's, yet sv, vhdl, ndf and calpy showed them - too. They are `agentUiModule`, which wfpy-ide's own webview bundle loads - (endrix/wfpy-ide#24). The stock bundle is 141 KB smaller. **Ship after - wfpy-ide#24, in the release that bumps `DIALOGRAM_API_VERSION` to 0.9**, so - a wfpy-ide still at 0.8 (and still on the stock bundle) fails its version - check instead of losing its agent UI. +- **Breaking for a product that showed the agent workflow UI from the stock + bundle: it no longer loads it.** The `@tool` and `@agent` annotation + editors, the agent sections of a node's panel (Skill row, Agent Tools, live + Agent Context), the wizard's `@tool` and `@viewer` templates and the + handlers of the server's skill and agent-preset answers were wfpy's, yet + every stock-bundle product showed the editors, the header's "Add @tool" and + "Add @agent" and the templates. They are `agentUiModule`, which wfpy-ide's + own webview bundle loads (endrix/wfpy-ide#24, merged). sv, vhdl, ndf and + calpy get the plain annotation card and only the wizard's "Custom + annotation…"; none of them has `@agent` nodes, so Agent Tools, the Skill + row and Agent Context never appeared for them. The stock bundle is 141 KB + smaller. Ship in the 0.9 release (above). - **Breaking for a profile that relied on the defaults: the Entities palette, navigation and the source MIME type are the product's.** The tool-palette diff --git a/packages/diagram-client/src/agent-ui/index.ts b/packages/diagram-client/src/agent-ui/index.ts index bc7b96f..51b2faa 100644 --- a/packages/diagram-client/src/agent-ui/index.ts +++ b/packages/diagram-client/src/agent-ui/index.ts @@ -8,9 +8,16 @@ * `@dialogram/diagram-client/agent-ui/index`. */ import { ContainerModule, type interfaces } from 'inversify'; +import { configureActionHandler } from '@eclipse-glsp/client'; import { AnnotationEditor } from '../annotation-editor'; import { AnnotationTemplate } from '../annotation-template'; import { NodeSectionContribution } from '../node-section'; +import { + WorkflowShowAgentSkillsAction, + WorkflowShowAgentSkillsActionHandler, + WorkflowShowClaudeAgentsAction, + WorkflowShowClaudeAgentsActionHandler +} from '../editing-action-handlers'; import { AgentAnnotationEditor } from './agent-editor'; import { AgentNodeSections } from './agent-sections'; import { ToolAnnotationEditor } from './tool-editor'; @@ -18,32 +25,48 @@ import { ToolAnnotationTemplate } from './tool-template'; import { ViewerAnnotationTemplate } from './viewer-template'; /** Fresh instances, in the order the panel shows them. */ -export function stockAnnotationEditors(): AnnotationEditor[] { +export function agentUiEditors(): AnnotationEditor[] { return [new ToolAnnotationEditor(), new AgentAnnotationEditor()]; } /** A fresh instance: it keeps the chat history it last drew. */ -export function stockNodeSections(): NodeSectionContribution[] { +export function agentUiNodeSections(): NodeSectionContribution[] { return [new AgentNodeSections()]; } /** The wizard's templates, in its menu order. */ -export function stockAnnotationTemplates(): AnnotationTemplate[] { +export function agentUiTemplates(): AnnotationTemplate[] { return [new ToolAnnotationTemplate(), new ViewerAnnotationTemplate()]; } -/** Bind fresh instances of all of it into a feature module. */ -export function bindAgentUi(bind: interfaces.Bind): void { - for (const editor of stockAnnotationEditors()) { +/** What a feature module's registry is handed, as GLSP's helpers take it. */ +export interface ModuleContext { + bind: interfaces.Bind; + unbind: interfaces.Unbind; + isBound: interfaces.IsBound; + rebind: interfaces.Rebind; +} + +/** + * Bind fresh instances of all of it into a feature module, and the handlers + * of the server's answers to the `@agent` editor's discovery requests (the + * skills and agent presets it found), whose caches the editor and the Skill + * row read. + */ +export function bindAgentUi(context: ModuleContext): void { + const { bind } = context; + configureActionHandler(context, WorkflowShowAgentSkillsAction.KIND, WorkflowShowAgentSkillsActionHandler); + configureActionHandler(context, WorkflowShowClaudeAgentsAction.KIND, WorkflowShowClaudeAgentsActionHandler); + for (const editor of agentUiEditors()) { bind(AnnotationEditor).toConstantValue(editor); } - for (const sections of stockNodeSections()) { + for (const sections of agentUiNodeSections()) { bind(NodeSectionContribution).toConstantValue(sections); } - for (const template of stockAnnotationTemplates()) { + for (const template of agentUiTemplates()) { bind(AnnotationTemplate).toConstantValue(template); } } /** The agent workflow UI as a feature module of its own. */ -export const agentUiModule = new ContainerModule(bind => bindAgentUi(bind)); +export const agentUiModule = new ContainerModule((bind, unbind, isBound, rebind) => bindAgentUi({ bind, unbind, isBound, rebind })); diff --git a/packages/diagram-client/src/annotation-editor.ts b/packages/diagram-client/src/annotation-editor.ts index 314d8a1..461e478 100644 --- a/packages/diagram-client/src/annotation-editor.ts +++ b/packages/diagram-client/src/annotation-editor.ts @@ -12,8 +12,8 @@ * * and passes the module to `startStockDiagram({ features })`. There is one * editor per annotation name: a later binding for a name replaces the earlier - * one, so a product's module, which loads after the stock one, can replace a - * stock editor. The section header's quick-add buttons follow the order in + * one, so a module that loads later can replace an editor an earlier one + * bound (the stock diagram binds none). The section header's quick-add buttons follow the order in * which the names were first bound. */ import type { IActionDispatcher } from '@eclipse-glsp/client'; diff --git a/packages/diagram-client/src/annotation-template.ts b/packages/diagram-client/src/annotation-template.ts index 7fb2d42..611b199 100644 --- a/packages/diagram-client/src/annotation-template.ts +++ b/packages/diagram-client/src/annotation-template.ts @@ -10,8 +10,8 @@ * * and passes the module to `startStockDiagram({ features })`. There is one * template per annotation name: a later binding for a name replaces the - * earlier one, so a product's module, which loads after the stock one, can - * replace a stock template. The wizard lists them in the order the names were + * earlier one, so a module that loads later can replace a template an + * earlier one bound (the stock diagram binds none). The wizard lists them in the order the names were * first bound, before its own "Custom annotation…" entry; a template without a * name is ignored. */ diff --git a/packages/diagram-client/src/contributions.ts b/packages/diagram-client/src/contributions.ts index 1504ab5..9310015 100644 --- a/packages/diagram-client/src/contributions.ts +++ b/packages/diagram-client/src/contributions.ts @@ -1,8 +1,8 @@ /** * How a contribution point picks among what products bound: one per key, a - * later binding replacing an earlier one in the earlier one's place. A - * product's feature module loads after the stock one, so it can replace a - * stock contribution. An empty key drops the contribution. + * later binding replacing an earlier one in the earlier one's place, so a + * module that loads later can replace what an earlier one bound. An empty + * key drops the contribution. */ export function lastPerKey(bound: readonly T[] | undefined, keyOf: (item: T) => string): T[] { const byKey = new Map(); diff --git a/packages/diagram-client/src/node-section.ts b/packages/diagram-client/src/node-section.ts index 4f63e8f..ce47d7a 100644 --- a/packages/diagram-client/src/node-section.ts +++ b/packages/diagram-client/src/node-section.ts @@ -12,8 +12,8 @@ * * and passes the module to `startStockDiagram({ features })`. There is one * contribution per `id`: a later binding for an id replaces the earlier one, so - * a product's module, which loads after the stock one, can replace a stock - * contribution. Contributions are asked in the order their ids were first + * a module that loads later can replace a contribution an earlier one bound + * (the stock diagram binds none). Contributions are asked in the order their ids were first * bound, so their rows and sections come in that order. */ import type { PropertyElement } from './property-model'; diff --git a/packages/diagram-client/src/property-panel.ts b/packages/diagram-client/src/property-panel.ts index 7491d7c..8d0e1e8 100644 --- a/packages/diagram-client/src/property-panel.ts +++ b/packages/diagram-client/src/property-panel.ts @@ -167,8 +167,8 @@ export class PropertyPanel implements ISelectionListener, IGModelRootListener { /** * One editor per annotation name, in the order the names were first bound. - * A later binding for a name replaces the earlier one, so a product's - * module, loaded after the stock one, can replace a stock editor. + * A later binding for a name replaces the earlier one, so a module that + * loads later can replace an editor an earlier one bound. */ protected annotationEditors(): readonly AnnotationEditor[] { return lastPerKey(this.contributedAnnotationEditors, editor => editor.annotationName); diff --git a/packages/diagram-client/src/stock-features.module.ts b/packages/diagram-client/src/stock-features.module.ts index a526758..162f52c 100644 --- a/packages/diagram-client/src/stock-features.module.ts +++ b/packages/diagram-client/src/stock-features.module.ts @@ -48,11 +48,7 @@ import { WorkflowEditParametersAction, WorkflowEditParametersActionHandler, WorkflowEditAnnotationsAction, - WorkflowEditAnnotationsActionHandler, - WorkflowShowAgentSkillsAction, - WorkflowShowAgentSkillsActionHandler, - WorkflowShowClaudeAgentsAction, - WorkflowShowClaudeAgentsActionHandler + WorkflowEditAnnotationsActionHandler } from './editing-action-handlers'; /** @@ -66,8 +62,9 @@ export const workflowFeaturesModule = new ContainerModule((bind, unbind, isBound bind(WorkflowNavigationUi).toSelf().inSingletonScope(); // Register property panel as both selection listener and model-root listener. - // IGModelRootListener fires on every SetModelAction, enabling the panel to - // update agent context in real time even when the selection hasn't changed. + // IGModelRootListener fires on every SetModelAction, so a node section can + // update itself in place while a run streams, even when the selection + // hasn't changed (`NodeSectionContribution.modelChanged`). bind(PropertyPanel).toSelf().inSingletonScope(); bind(TYPES.ISelectionListener).toService(PropertyPanel); bind(TYPES.IGModelRootListener).toService(PropertyPanel); @@ -133,8 +130,6 @@ export const workflowFeaturesModule = new ContainerModule((bind, unbind, isBound configureActionHandler(context, WorkflowOpenViewAction.KIND, WorkflowOpenViewActionHandler); configureActionHandler(context, WorkflowEditParametersAction.KIND, WorkflowEditParametersActionHandler); configureActionHandler(context, WorkflowEditAnnotationsAction.KIND, WorkflowEditAnnotationsActionHandler); - configureActionHandler(context, WorkflowShowAgentSkillsAction.KIND, WorkflowShowAgentSkillsActionHandler); - configureActionHandler(context, WorkflowShowClaudeAgentsAction.KIND, WorkflowShowClaudeAgentsActionHandler); // The profile's status source on the canvas's context menu ("Show