Files
feuerwerk/tests/editor/agentTestHost.ts
T
adminandClaude Opus 5.5 0d8fd6ff52
feuerwerk / gate (push) Successful in 5m10s
feuerwerk / suite (push) Failing after 18m49s
Let the agent use the graph, scene and project commands (#429)
The agent knew 23 of the editor's commands. It could not point a
Variables node at a variable, group nodes, edit ports, transitions,
containers, colliders or the project settings. Stage 5a brings the
catalog to 85 commands in five areas and keeps every answer short.

- The catalog lists names and descriptions only. With a query (an
  area or command names) it adds the arguments. All schemas at once
  would pass the 60 000 character limit of one answer.
- add_port points a Variables node at a variable. The port takes the
  variable's kind, and an unknown name is refused with the list of
  the ones that exist.
- add_group and add_comment take nodes instead of coordinates. They
  follow the nodes after the arrange step, in the same undo step.
- The rule for which list a port picks from moved into portChoices.ts.
  Inspector and agent now share it.
- The frame of a group around nodes moved into annotationDrag.ts, so
  the button and the agent get the same frame.
- list_assets returns the project settings. get_asset shows the
  content of placed containers with the ids overrides need. get_graph
  shows declared ports and frame colors.
- A new node is placed at the height of its port, in the free spot
  whose links cross the fewest others. A source goes left of the node
  it feeds. Before, new nodes always went right of the rest.
- Arrange breaks a tie in crossings by putting the flow on top, and
  exec links pull harder when a column is sorted.
- A comment over a group keeps clear of the group's title bar.
- The agent's instructions and lint_graph ask to look at crossings
  too and to call arrange_graph when there are more than needed.

Left out on purpose: file assets (no upload), paste from the
clipboard, internal wrapper commands and switching plugins on.

Found on the way:
- Variable commands on a Struct asset did not run through
  WithStructTypes. New fields did not reach variables of that type.
- A query "variables" also matched set_variables_scope. A word that
  names an area now means only that area.

Tests:
- agent-catalog (new, 10), agent-edit, graph-arrange (3 new) and
  relay green. 11 bends of the code, all red.
- e2e 71 on atx: 22/22, the new check sees port speed and a group
  around all 4 drawn nodes.
- Live on a test project: a speed variable of 300 moved the player
  to x 150.03 in 0.5 s. A re-placed Variables node landed below Axis
  input with 4 crossings instead of 7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:01:15 +02:00

49 lines
1.7 KiB
TypeScript

import { type Command, CommandError } from '../../src/core/commands/Command';
import type { ToolHost, ToolResult } from '../../src/editor/agent/agentTools';
import type { AgentView } from '../../src/editor/agent/agentView';
import type { StepTarget } from '../../src/editor/agent/commandCatalog';
import type { SceneFixture } from '../helpers';
/** The editor behind the agent's tools, as the tests drive it: the fixture's project and undo stack. */
export function hostFor(fixture: SceneFixture, deleteAnswer: string | null = null, follows = false) {
const asked: string[][] = [];
const shown: StepTarget[] = [];
const host: ToolHost = {
view: () => ({ play: 'editing' }) as AgentView,
screenshot: () => Promise.resolve({ data: '', width: 0, height: 0 }),
project: () => fixture.project,
execute: (command: Command) => {
try {
fixture.stack.execute(command);
return null;
} catch (error) {
if (error instanceof CommandError) return error.message;
throw error;
}
},
confirmDeletes: (ids) => {
asked.push([...ids]);
return Promise.resolve(deleteAnswer);
},
open: () => null,
select: () => null,
play: () => null,
undo: () => {
const label = fixture.stack.undoLabel;
if (label !== null) fixture.stack.undo();
return label;
},
redo: () => {
const label = fixture.stack.redoLabel;
if (label !== null) fixture.stack.redo();
return label;
},
follows: () => follows,
show: (focus) => shown.push(focus),
};
return { host, asked, shown };
}
export const text = (result: ToolResult) =>
result.content.map((item) => (item.type === 'text' ? item.text : '')).join('\n');