enhancement
major
minor
defect
critical
major
minor
Problem
Since #30149 a chat agent runs the commands its <chat-conversation> lists in <agent-commands> (page-commands.runPageCommand). The tool takes only the command's name, so a command can act only on what the page already holds. An agent cannot say which object a command is for: "show the chat of the conversation I just handed over", "open the form of a new git connection for tenant X", "show the project whose application the user wants to open".
The building blocks exist: a command takes its inputs from channels, and a view action binds channel values (<show-views>). The agent's one write into the page, present, does not combine with them. Its tool contract ends with "After it, answer in one or two sentences what the user now sees, and call no further tool.", and its answer says "The user now sees the form of this object". For a channel that is only a command's input, both are false.
Change
An <agent-command> names the inputs an agent may set (deny by default, like the present channels). runPageCommand takes values for them as object ids. The tool reads them with the user's rights, as present reads its ids (an object the user cannot read is refused), and binds them to those input channels for the command. The command's executability and access rules then decide over the bound values exactly as for a click, and a refused command answers with their reason. pageCommands lists each command's settable inputs with their descriptions.
Whether a bound value stays on the channel after the command, or is only seen by the command, is decided in the implementation and documented in the article ai/chat-ui.
Use
tl-factory (Gitea TopLogic/tl-factory #36, #41): the agent of the start page opens the git connection form for a named tenant, the coding agent's chat for the conversation it handed over, and the project whose preview the user wants to open.
Lösung
- An <agent-command> takes the optional property input-description (English, for the model): what object the command is for. Only a command with it accepts an object from the agent (deny by default); an object for a command without it is refused.
- runPageCommand(command, input) takes the id of that object. The tool reads it with the user's rights through the same resolution as present (TidResolver with the read filter; an object the user cannot read is refused) and runs the command with it as main argument through ViewCommandModel.execute(context, input) (PageCommand.run(Object)) - the way a table row runs a command for its row. The command's executability and access rules decide over that object exactly as for a click.
- A refusal by the command's rules is an ApplicationRefusal (#30166) with command, input (only when an object was passed) and the application's reasons in the user's language. The tool's own refusals (object for a command that takes none, unknown or unreadable object, unlisted name, busy, no page) are English sentences.
- The value is seen by this run of the command only and is not written into the input channel, so a command has no side effect on the page beyond what its chain does (e.g. a <show-views> binding the argument to the opened view, or a <write-channel> storing it).
- pageCommands lists for each command whether it takes an object, with its input-description.
- One object per command. Several values are set by several commands: one command writes its argument into a channel with <write-channel>, another one reads that channel as its input. The article ai/chat-ui describes the input, the refusal format and this pattern.
- Tests (TestPageCommandTool): with an object the command sees it; an object the user cannot read is refused; an object for a command without input-description is refused; an executability rule refusing the object answers with its reason.