Beyond Clicking and Shell Commands: API-Native Computer Control An AI researcher proposes API-native computer control, where agents write short JavaScript programs against validated application APIs instead of relying solely on GUI automation or shell commands. This approach reduces inference steps by handling loops and conditions locally, and is most effective when applications already have structured data and domain rules. ← All writing / Essay Beyond Clicking and Shell Commands: API-Native Computer Control A practical exploration of letting AI agents write short JavaScript programs against narrow, validated application APIs. An AI agent can draft an email, summarize a repository, or propose edits. The more difficult question is what happens next: how should it operate an application? GUI automation and shell access are two practical answers. I use both, but I have also been experimenting with another option: give the agent a small application API and let it write a short JavaScript program for each task. I call this API-native computer control . The name is more ambitious than the current implementation. I do not know whether it is the best general interface for agents, but it seems promising when an application already has structured data, domain rules, and a meaningful API. When tool calls become the control loop Tool calling often exposes actions such as: create rectangle ... move object ... set fill color ... delete object ... This works well for a few independent actions. It becomes less convenient when a task needs iteration or branching. Imagine a slide editor, canvas, or UI board where the user asks: Find every text object smaller than 12 px, increase it to 12 px, and move any overflow into a new text box below the original. If the model calls one tool for every object, every observation and action may require another inference step. A short program keeps the ordinary computation local: js const objects = canvas.listObjects { type: "text" } ; for const object of objects { if object.fontSize = 12 continue; const result = canvas.updateText object.id, { fontSize: 12 } ; if result.overflowText { canvas.createText { content: result.overflowText, x: object.x, y: object.y + object.height + 8, fontSize: 12, } ; } } The model still chooses the operation, but loops, conditions, and intermediate values do not each need another model turn. Generated code is not a replacement for tools; it is a way to compose approved tools. GUI control recovers semantics from pixels Graphical interfaces are excellent for people. We can scan a canvas, recognize an icon, and point at an object without naming every part of the scene. For an agent, the same action often takes a longer translation path: php flowchart LR subgraph GUI "GUI control" A Render -- B Interpret pixels -- C Find control -- D Click or type -- E Check result end subgraph API "Semantic API control" F Read state -- G Call named action -- H Verify result end E -- S Application state H -- S classDef gui fill: fff0df,stroke: c76b16,color: 17202a classDef api fill: e8f0fb,stroke: 2563a7,color: 17202a class A,B,C,D,E gui class F,G,H api GUI control is indispensable when no structured interface exists. Vision also remains important when appearance is the result, such as editing slides, graphics, or a web page. My concern is using pixels and coordinates as the primary control plane when the application already knows the content, bounds, transform, and identity of each object. A useful split is to use vision to understand and judge the output, while using semantic operations to change application state when available. Why the shell works so well The shell removes much of the visual interpretation work. It is textual, scriptable, composable, and supported by decades of public examples. Models have seen large amounts of Bash, git , ffmpeg , and similar tools during training. Some of that operational experience is encoded in the model’s learned parameters, so common commands can feel almost native. That is a substantial advantage: ffmpeg -i input.mov -vf "scale=1920:-2,fps=30" -c:v libx264 -crf 20 output.mp4 A model familiar with ffmpeg may produce this without first studying a manual. The shell is therefore hard to beat for developer environments and established utilities. The tradeoff is authority and precision. A process launcher plus filesystem access is broader than a narrowly scoped application operation: video.resize { assetId, width: 1920, frameRate: 30, destination: approvedOutput, } ; CLI syntax also depends on conventions that are not fully captured by a machine-checkable schema. Containers and operating-system sandboxes still matter. A narrow API does not replace them, but it can reduce the authority placed inside the boundary. For application-level automation, that may also allow a lighter runtime than a general shell environment. From fixed handlers to generated programs In a React application, a button usually invokes code that a developer prepared in advance: js function AlignButton { selectedIds } { const onClick = = { const objects = selectedIds.map id = editor.getObject id ; const left = Math.min ...objects.map object = object.x ; for const object of objects { editor.updateTransform object.id, { x: left } ; } editor.commitHistory "Align left" ; }; return