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>
This commit is contained in:
1 parent
1943539a7a
commit
0d8fd6ff52
26 files changed
+2585
-672
No files matched your search
@@ -0,0 +1,114 @@
|
||||
import type { Command } from '../../../core/commands/Command';
|
||||
import type { AnnotationKind } from '../../../core/graph';
|
||||
import type { Project } from '../../../core/model/Project';
|
||||
import type { MeasureNode } from '../../graph/graphLayout';
|
||||
import type { ArgSchema } from '../argCheck';
|
||||
|
||||
/** What a command is built from: the project as it stands before the edit, and where new nodes go. */
|
||||
export interface BuildContext {
|
||||
project: Project;
|
||||
/** A free place for a node the agent gave none, right of what the graph and this edit hold. */
|
||||
spot(assetId: string): { x: number; y: number };
|
||||
/** How the nodes of a graph are measured for arranging. */
|
||||
measure(assetId: string): MeasureNode;
|
||||
}
|
||||
|
||||
/** The command, and the ids of what it makes – what later steps of the same edit can name. */
|
||||
export interface Built {
|
||||
command: Command;
|
||||
/** A function for what is only known once the command has run. */
|
||||
made?: Record<string, string> | (() => Record<string, string>);
|
||||
/** Nodes it added without a place: they are arranged when the edit's steps have run. */
|
||||
unplaced?: { asset: string; nodes: string[] };
|
||||
/** A group or comment it put at nodes: fitted to them again once the edit has arranged its nodes. */
|
||||
frames?: Frame;
|
||||
}
|
||||
|
||||
export interface Frame {
|
||||
asset: string;
|
||||
kind: AnnotationKind;
|
||||
id: string;
|
||||
nodes: readonly string[];
|
||||
}
|
||||
|
||||
export type Args = Record<string, unknown>;
|
||||
|
||||
/** What a step worked on, for the editor to show the user: an asset, objects of a holder, nodes of a graph. */
|
||||
export type StepTarget =
|
||||
| { kind: 'asset'; asset: string }
|
||||
| { kind: 'objects'; in: string; ids: string[] }
|
||||
| { kind: 'nodes'; asset: string; ids: string[] };
|
||||
|
||||
/** One command the agent can run (#429): what it does, its arguments, and how it is made. */
|
||||
export interface CatalogCommand {
|
||||
name: string;
|
||||
description: string;
|
||||
args: ArgSchema;
|
||||
/** Deletes something the user has to agree to first – decision 4 of #429. */
|
||||
confirms?: true;
|
||||
build(args: Args, context: BuildContext): Built;
|
||||
/** What it worked on, from its arguments and what it made; none for a step there is nothing to show of. */
|
||||
shows?(args: Args, made: Record<string, string>): StepTarget;
|
||||
}
|
||||
|
||||
export const id = (description: string): ArgSchema => ({ type: 'string', minLength: 1, description });
|
||||
export const number = (description: string): ArgSchema => ({ type: 'number', description });
|
||||
export const flag = (description: string): ArgSchema => ({ type: 'boolean', description });
|
||||
export const ids = (description: string): ArgSchema => ({
|
||||
type: 'array',
|
||||
items: { type: 'string', minLength: 1 },
|
||||
minItems: 1,
|
||||
description,
|
||||
});
|
||||
export const oneOf = (values: readonly string[], description: string): ArgSchema => ({
|
||||
type: 'string',
|
||||
enum: values,
|
||||
description,
|
||||
});
|
||||
export const anyValue: ArgSchema = {
|
||||
description: 'Any JSON value; it is checked against the kind of the property.',
|
||||
};
|
||||
export const object = (properties: Record<string, ArgSchema>, required: string[]): ArgSchema => ({
|
||||
type: 'object',
|
||||
properties,
|
||||
required,
|
||||
additionalProperties: false,
|
||||
});
|
||||
|
||||
export const ASSET = id('Id of the asset.');
|
||||
export const HOLDER = id('Id of the scene or container the object is in.');
|
||||
export const OBJECT = id('Id of the placed object.');
|
||||
export const GRAPH = id('Id of the asset holding the graph (behavior, state machine, shader, ...).');
|
||||
export const NODE = id('Id of the node.');
|
||||
export const PATH: ArgSchema = {
|
||||
type: 'array',
|
||||
items: { type: 'string', minLength: 1 },
|
||||
description:
|
||||
'Field keys leading into a struct variable, outermost first; leave out for the variable itself.',
|
||||
};
|
||||
|
||||
export const text = (args: Args, key: string): string => args[key] as string;
|
||||
export const numberOf = (args: Args, key: string): number => args[key] as number;
|
||||
export const listOf = (args: Args, key: string): string[] => args[key] as string[];
|
||||
|
||||
export const theAsset = (args: Args): StepTarget => ({ kind: 'asset', asset: text(args, 'asset') });
|
||||
export const madeAsset = (_args: Args, made: Record<string, string>): StepTarget => ({
|
||||
kind: 'asset',
|
||||
asset: made.id!,
|
||||
});
|
||||
export const theObject = (args: Args): StepTarget => ({
|
||||
kind: 'objects',
|
||||
in: text(args, 'in'),
|
||||
ids: [text(args, 'object')],
|
||||
});
|
||||
export const theObjects = (args: Args): StepTarget => ({
|
||||
kind: 'objects',
|
||||
in: text(args, 'in'),
|
||||
ids: listOf(args, 'objects'),
|
||||
});
|
||||
export const theNode = (args: Args): StepTarget => ({
|
||||
kind: 'nodes',
|
||||
asset: text(args, 'asset'),
|
||||
ids: [text(args, 'node')],
|
||||
});
|
||||
export const theGraph = (args: Args): StepTarget => ({ kind: 'nodes', asset: text(args, 'asset'), ids: [] });
|
||||
Reference in new issue
Block a user