Files
chenmingxuan b780a60e8d feat(cua): release 0.38.0+artemis.1 with verified shell observation
Upgrade the pinned driver, validate menu and taskbar focus ownership, patch untitled Shell resolution, and remove duplicate scroll scaling. Publish package provenance, verification boundaries and matching marketplace checksum. Go tests, vet, build and byte-level package verification passed.
2026-10-27 01:13:43 +08:00

540 lines
17 KiB
Go

// ARTEMIS adapter for the pinned MIT Cua Driver. No model, scheduler, or recorder.
package main
import (
"bufio"
"bytes"
"crypto/sha256"
_ "embed"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"image"
_ "image/jpeg"
_ "image/png"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"time"
)
//go:embed tools.json
var toolsJSON []byte
const adapterVersion = "0.38.0+artemis.1"
type obj = map[string]any
type request struct {
JSONRPC string `json:"jsonrpc"`
ID json.RawMessage `json:"id"`
Method string `json:"method"`
Params obj `json:"params"`
}
type peer struct {
cmd *exec.Cmd
input io.WriteCloser
lines chan []byte
next int
mu sync.Mutex
dead bool
job uintptr
}
func object(v any) obj {
m, _ := v.(map[string]any)
if m == nil {
return obj{}
}
return m
}
func str(v any) string { s, _ := v.(string); return s }
func num(v any) int64 {
switch n := v.(type) {
case float64:
return int64(n)
case int64:
return n
case int:
return int64(n)
}
return 0
}
func items(v any) []any { a, _ := v.([]any); return a }
func textBlock(s string) obj { return obj{"type": "text", "text": s} }
func receipt(execution, side, code string) obj {
return obj{"version": 1, "execution": execution, "side_effect": side, "retryable": false, "error_code": code}
}
func failure(msg, execution, side, code string) obj {
return obj{"isError": true, "content": []any{textBlock(msg)}, "structuredContent": obj{"artemis": receipt(execution, side, code)}}
}
func writeTool(name string) bool {
return name != "list_windows" && name != "get_window_state" && name != "verify_state"
}
func startPeer(exe string) (*peer, error) {
c := exec.Command(exe, "mcp", "--direct")
c.Env = append(os.Environ(), "DO_NOT_TRACK=1", "CUA_TELEMETRY=0", "CUA_DRIVER_RS_TELEMETRY_ENABLED=false")
configureProcess(c)
in, err := c.StdinPipe()
if err != nil {
return nil, err
}
out, err := c.StdoutPipe()
if err != nil {
return nil, err
}
c.Stderr = os.Stderr
if err = c.Start(); err != nil {
return nil, err
}
job, err := ownProcess(c.Process.Pid)
if err != nil {
_ = c.Process.Kill()
_ = c.Wait()
return nil, err
}
p := &peer{cmd: c, input: in, lines: make(chan []byte, 32), job: job}
go func() {
defer close(p.lines)
s := bufio.NewScanner(out)
s.Buffer(make([]byte, 65536), 32<<20)
for s.Scan() {
p.lines <- append([]byte(nil), s.Bytes()...)
}
}()
_, err = p.call("initialize", obj{"protocolVersion": "2024-11-05", "clientInfo": obj{"name": "artemis-cua-adapter", "version": adapterVersion}, "capabilities": obj{}})
if err != nil {
p.stop()
return nil, err
}
err = json.NewEncoder(in).Encode(obj{"jsonrpc": "2.0", "method": "notifications/initialized"})
if err != nil {
p.stop()
return nil, err
}
return p, nil
}
func (p *peer) stop() {
p.mu.Lock()
defer p.mu.Unlock()
if p.dead {
return
}
p.dead = true
_ = p.input.Close()
closeJob(p.job)
_ = p.cmd.Process.Kill()
_ = p.cmd.Wait()
}
func (p *peer) call(method string, params obj) (obj, error) {
p.mu.Lock()
dead := p.dead
p.mu.Unlock()
if dead {
return nil, errors.New("driver closed")
}
p.next++
id := p.next
if err := json.NewEncoder(p.input).Encode(obj{"jsonrpc": "2.0", "id": id, "method": method, "params": params}); err != nil {
return nil, err
}
timer := time.NewTimer(45 * time.Second)
defer timer.Stop()
for {
select {
case raw, ok := <-p.lines:
if !ok {
return nil, errors.New("driver disconnected")
}
var r obj
if json.Unmarshal(raw, &r) != nil {
continue
}
if num(r["id"]) != int64(id) {
continue
}
if r["error"] != nil {
return nil, errors.New("driver protocol error")
}
return object(r["result"]), nil
case <-timer.C:
p.stop()
return nil, errors.New("driver response timed out; action not replayed")
}
}
}
func (p *peer) tool(name string, args obj) (obj, error) {
return p.call("tools/call", obj{"name": name, "arguments": args})
}
type adapter struct {
peer *peer
tools []any
schemas map[string]obj
captures map[string]pointerFrame
}
func newAdapter(p *peer) *adapter {
a := &adapter{peer: p, schemas: map[string]obj{}, captures: map[string]pointerFrame{}}
_ = json.Unmarshal(toolsJSON, &a.tools)
for _, v := range a.tools {
t := object(v)
a.schemas[str(t["name"])] = object(t["inputSchema"])
}
return a
}
func (a *adapter) validate(name string, args obj) error {
schema, ok := a.schemas[name]
if !ok {
return errors.New("tool is not exposed")
}
if name == "press_key" || name == "type_text" {
for _, key := range []string{"element_token", "x", "y"} {
if _, supplied := args[key]; supplied {
return errors.New(name + " uses the window's current focus; omit element_token/x/y. Focus with click/click_at first, or use set_value for a UIA field")
}
}
}
props := object(schema["properties"])
for key := range args {
if _, ok := props[key]; !ok {
return fmt.Errorf("unsupported argument: %s", key)
}
}
for _, key := range items(schema["required"]) {
if _, ok := args[str(key)]; !ok {
return fmt.Errorf("missing argument: %s", str(key))
}
}
if name == "list_windows" {
_, err := windowQuery(args)
return err
}
if num(args["pid"]) <= 0 || num(args["window_id"]) <= 0 {
return errors.New("a concrete pid and window_id are required")
}
if name == "set_value" && strings.TrimSpace(str(args["element_token"])) == "" {
return errors.New("set_value requires the latest element_token; use type_text for the current focus")
}
if name == "save_screenshot" && !validFilename(str(args["filename"])) {
return errors.New("filename must be a new plain PNG filename")
}
if name == "get_window_state" && (args["include_screenshot"] == false || args["include_accessibility_tree"] == false) {
return errors.New("candidate observations require both screenshot and accessibility tree")
}
return nil
}
func validFilename(s string) bool {
return s != "" && len(s) <= 160 && strings.EqualFold(filepath.Ext(s), ".png") && !strings.ContainsAny(s, "/\\:<>\"|?*\x00") && s != "." && s != ".." && !strings.HasSuffix(s, ".") && !strings.HasSuffix(s, " ")
}
func actionReceipt(r obj, write bool) obj {
if !write {
if r["isError"] == true {
return receipt("not_started", "none", "observation_failed")
}
return receipt("completed", "none", "")
}
s := object(r["structuredContent"])
// v0.36.0 class-level preflight refusal has no effect field. It occurs
// before input injection. Do not classify actuator failures with a cause
// as safe-to-repeat; retain their uncertain execution below.
if s["code"] == "background_unavailable" && s["cause"] == nil && s["event_kind"] != nil && r["isError"] == true {
return receipt("not_started", "none", "background_unavailable")
}
switch str(s["effect"]) {
case "confirmed":
return receipt("completed", "confirmed", "")
case "partial":
return receipt("partial", "confirmed", "partial_delivery")
case "refused":
return receipt("not_started", "none", "driver_refused")
case "unverifiable", "suspected_noop":
return receipt("completed", "possible", str(s["effect"]))
default:
return receipt("unknown", "possible", "unclassified_result")
}
}
func imageSize(r obj) (int, int) {
for _, v := range items(r["content"]) {
c := object(v)
if c["type"] == "image" {
raw, err := base64.StdEncoding.DecodeString(str(c["data"]))
if err == nil {
cfg, _, err := image.DecodeConfig(bytes.NewReader(raw))
if err == nil {
return cfg.Width, cfg.Height
}
}
}
}
return 0, 0
}
func source(r obj, args obj, started float64) obj {
s := object(r["structuredContent"])
w, h := imageSize(r)
if r["isError"] == true || w <= 0 || h <= 0 || num(s["pid"]) != num(args["pid"]) || num(s["window_id"]) != num(args["window_id"]) {
return nil
}
bounds := object(s["window_bounds"])
rect := obj{"left": num(bounds["x"]), "top": num(bounds["y"]), "width": num(bounds["width"]), "height": num(bounds["height"])}
screen, ok := verifyWindow(num(args["pid"]), num(args["window_id"]), rect)
if !ok {
return nil
}
return obj{"kind": "computer", "target_id": fmt.Sprint(num(args["window_id"])), "label": s["window_title"], "captured_at": started, "width": w, "height": h, "window": rect, "screen": screen}
}
func (a *adapter) observe(args obj) (obj, obj, error) {
delete(a.captures, targetKey(args))
started := float64(time.Now().UnixMilli()) / 1000
before := foregroundWindow()
focusBefore := keyboardFocus()
r, err := a.peer.tool("get_window_state", args)
if err != nil {
return nil, nil, err
}
annotateCaptureFocus(r, num(args["window_id"]), before, foregroundWindow())
object(r["structuredContent"])["keyboard_focus"] = obj{"before": focusBefore, "after": keyboardFocus()}
src := source(r, args, started)
if frame, ok := capturedFrame(r, src); ok {
a.captures[targetKey(args)] = frame
prependFrameHint(r, frame)
}
return r, src, nil
}
func observationArgs(args obj) obj {
return obj{"pid": args["pid"], "window_id": args["window_id"], "max_image_dimension": 1280, "max_elements": 250, "tree_format": "markdown"}
}
func (a *adapter) handle(name string, args obj) obj {
if err := a.validate(name, args); err != nil {
return failure(err.Error(), "not_started", "none", "invalid_input")
}
if name != "list_windows" {
if !windowOwned(num(args["pid"]), num(args["window_id"])) {
return failure("window is gone or belongs to a different process", "not_started", "none", "invalid_target")
}
}
if name == "save_screenshot" {
return a.save(args)
}
var r, src obj
var err error
if name == "get_window_state" {
for k, v := range observationArgs(args) {
if _, ok := args[k]; !ok {
args[k] = v
}
}
r, src, err = a.observe(args)
} else if name == "list_windows" {
query, _ := windowQuery(args) // Already validated above.
r, err = a.peer.tool(name, query)
if err == nil {
r = supplementShellWindows(r, query, nativeShellWindows())
r = windowListing(r, query)
}
} else {
if isPointerTool(name) {
frame, ok := a.captures[targetKey(args)]
if !ok {
return failure("No current screenshot; get_window_state before a pixel action", "not_started", "none", "invalid_coordinates")
}
if err := frame.validate(name, args); err != nil {
return failure(err.Error(), "not_started", "none", "invalid_coordinates")
}
if _, ok := verifyWindow(num(args["pid"]), num(args["window_id"]), frame.window); !ok {
delete(a.captures, targetKey(args))
return failure("Window moved or resized; get_window_state again", "not_started", "none", "stale_capture")
}
if name == "scroll" || name == "drag" {
// v0.38.0 maps screenshot pixels itself. Keep x/y unchanged.
args = withoutCaptureBinding(args)
}
}
upstream := name
if name == "click_at" {
upstream = "click"
}
if name == "verify_state" {
delete(a.captures, targetKey(args))
}
if writeTool(name) {
delete(a.captures, targetKey(args))
}
r, err = callWithForeground(a.peer.tool, upstream, args, foregroundWindow)
}
if err != nil {
a.peer.stop()
if writeTool(name) {
return failure(err.Error(), "unknown", "possible", "transport_lost")
}
return failure(err.Error(), "not_started", "none", "transport_lost")
}
structured := object(r["structuredContent"])
rec := object(structured["artemis"])
if len(rec) == 0 {
rec = actionReceipt(r, writeTool(name))
}
if writeTool(name) && rec["execution"] != "not_started" {
// Observe once after delivery; NEVER repeat an uncertain write.
focus := keyboardFocus()
next, reason := postActionTarget(args, nativeWindowInfo(num(args["window_id"])), focus, nativeShellWindows())
structured["post_action_focus"] = focus
structured["observation_target"] = obj{"requested": obj{"pid": args["pid"], "window_id": args["window_id"]},
"observed": obj{"pid": next["pid"], "window_id": next["window_id"]}, "reason": reason}
observation, origin, e := a.observe(next)
if e == nil && origin != nil {
src = origin
structured["observation"] = observation["structuredContent"]
r["content"] = append(items(r["content"]), items(observation["content"])...)
} else {
structured["observation_error"] = "Post-action evidence unavailable; inspect before another action."
if e != nil {
structured["observation_error"] = e.Error()
} else if observation != nil {
var errors []string
if observation["isError"] == true {
for _, item := range items(observation["content"]) {
block := object(item)
if block["type"] == "text" {
text := []rune(str(block["text"]))
if len(text) > 2000 {
text = text[:2000]
}
errors = append(errors, string(text))
}
}
}
// Failed identity/geometry validation must never forward its image
// or full tree as task evidence, even inside diagnostics.
structured["observation_diagnostics"] = obj{"driver_errors": errors,
"native_target": nativeWindowInfo(num(next["window_id"])), "isError": observation["isError"],
"image_size": func() []int { w, h := imageSize(observation); return []int{w, h} }()}
}
}
}
if name == "verify_state" { // Read-only verification may include a screenshot; it is not a task verdict.
src = source(r, args, float64(time.Now().UnixMilli())/1000)
if frame, ok := capturedFrame(r, src); ok {
a.captures[targetKey(args)] = frame
prependFrameHint(r, frame)
}
}
if src != nil {
rec["source"] = src
} else if name == "get_window_state" && r["isError"] != true {
return failure("capture target or geometry could not be validated", "not_started", "none", "invalid_capture")
}
if structured["effect"] == "refused" {
r["isError"] = true
}
structured["artemis"] = rec
r["structuredContent"] = structured
return r
}
func (a *adapter) save(args obj) obj {
root := os.Getenv("ARTEMIS_ARTIFACTS_DIR")
if root == "" {
return failure("task artifact directory unavailable", "not_started", "none", "missing_artifacts")
}
r, src, err := a.observe(observationArgs(args))
if err != nil || src == nil {
return failure("fresh window capture failed", "not_started", "none", "capture_failed")
}
var png []byte
for _, v := range items(r["content"]) {
b := object(v)
if b["type"] == "image" && b["mimeType"] == "image/png" {
png, _ = base64.StdEncoding.DecodeString(str(b["data"]))
break
}
}
if len(png) == 0 {
return failure("expected PNG capture", "not_started", "none", "capture_format")
}
if err = os.MkdirAll(root, 0700); err != nil {
return failure("artifact directory unavailable", "not_started", "none", "file_error")
}
path := filepath.Join(root, str(args["filename"]))
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
if err != nil {
return failure("file exists or cannot be created", "not_started", "none", "file_error")
}
n, writeErr := f.Write(png)
closeErr := f.Close()
actual, readErr := os.ReadFile(path)
if writeErr != nil || closeErr != nil || readErr != nil || n != len(png) || !bytes.Equal(actual, png) {
return failure("artifact write could not be verified", "partial", "possible", "file_error")
}
rec := receipt("completed", "confirmed", "")
rec["source"] = src
rec["attachments"] = []any{obj{"name": str(args["filename"]), "sha256": fmt.Sprintf("%x", sha256.Sum256(actual)), "media_type": "image/png"}}
object(r["structuredContent"])["artemis"] = rec
r["content"] = append(items(r["content"]), textBlock("Saved verified task attachment: "+str(args["filename"])))
return r
}
func serve(a *adapter, in io.Reader, out io.Writer) {
input := make(chan request)
go func() {
defer close(input)
defer a.peer.stop()
s := bufio.NewScanner(in)
s.Buffer(make([]byte, 65536), 2<<20)
for s.Scan() {
var q request
if json.Unmarshal(s.Bytes(), &q) == nil {
if q.Method == "notifications/cancelled" {
a.peer.stop()
continue
}
input <- q
}
}
}()
enc := json.NewEncoder(out)
for q := range input {
if len(q.ID) == 0 {
continue
}
var result any
var rpcErr any
switch q.Method {
case "initialize":
result = obj{"protocolVersion": "2024-11-05", "capabilities": obj{"tools": obj{}}, "serverInfo": obj{"name": "artemis-cua-driver", "version": adapterVersion}}
case "ping":
result = obj{}
case "tools/list":
result = obj{"tools": a.tools}
case "tools/call":
result = a.handle(str(q.Params["name"]), object(q.Params["arguments"]))
default:
rpcErr = obj{"code": -32601, "message": "method not found"}
}
response := obj{"jsonrpc": "2.0", "id": q.ID}
if rpcErr != nil {
response["error"] = rpcErr
} else {
response["result"] = result
}
if enc.Encode(response) != nil {
return
}
}
}
func main() {
exe, err := os.Executable()
if err != nil {
os.Exit(1)
}
p, err := startPeer(filepath.Join(filepath.Dir(exe), "cua-driver.exe"))
if err != nil {
fmt.Fprintln(os.Stderr, "Cua startup failed:", err)
os.Exit(1)
}
defer p.stop()
serve(newAdapter(p), os.Stdin, os.Stdout)
}