spb/zyquo-agent Public MIT
The autonomous agent that actually operates your Mac — plans, runs real commands, verifies its own work.
Swift 94.7%
Shell 4.1%
Python 0.7%
Makefile 0.5%
1//2// UISmoke.swift3// Zyquo Agent4//5// Author: Simon-Pierre Boucher6// Mail: contact@spboucher.ai7//8// Hidden `--run-ui-smoke` mode: drives the RunController (the UI's view9// model) with the scripted MockProviderClient — no network, no keys, no10// window — and asserts the published state transitions the command-center11// UI renders from: step entries appear and stream, the plan lands, tokens12// accumulate, the run completes, and the task record gains the persisted13// agent-run message with its embedded steps. Everything runs against14// temp-rooted persistence so the user's real tasks and policy rules are15// never touched.16//1718import Foundation1920enum UISmoke {21 /// Returns the process exit status (0 = all checks passed).22 @MainActor23 static func run() async -> Int32 {24 var failures: [String] = []25 func check(_ condition: Bool, _ label: String) {26 if condition {27 print(" ✔ \(label)")28 } else {29 failures.append(label)30 print(" ✘ \(label)")31 }32 }3334 print("Zyquo Agent — UI smoke (RunController + MockProviderClient)")3536 // Temp-rooted persistence: no real tasks/rules touched.37 let temporaryRoot = FileManager.default.temporaryDirectory38 .appendingPathComponent("ZyquoAgent-uismoke-\(UUID().uuidString.prefix(8))")39 let persistence = PersistenceService(rootDirectory: temporaryRoot)40 defer { try? FileManager.default.removeItem(at: temporaryRoot) }4142 let store = TaskStore(persistence: persistence)43 let task = store.newTask(model: MockProviderClient.model)44 let controller = RunController(taskID: task.id, store: store, policyPersistence: persistence)4546 var sawEntries = false47 var sawPlan = false48 var sawRunningStatus = false49 var sawApproval = false5051 controller.start(52 prompt: "Create a demo folder in the workspace with a shell command and verify it exists.",53 model: MockProviderClient.model,54 client: MockProviderClient(),55 apiKey: "mock"56 )57 check(controller.isRunning, "run starts (isRunning)")5859 // Poll the published state until the run finishes (mock finishes in60 // well under a second; 15s is a generous CI ceiling).61 let deadline = Date().addingTimeInterval(15)62 while controller.outcome == nil && Date() < deadline {63 sawEntries = sawEntries || !controller.entries.isEmpty64 sawPlan = sawPlan || controller.plan != nil65 sawRunningStatus = sawRunningStatus || (store.task(id: task.id)?.status.isActive ?? false)66 // Guarded mode holds the mock's `mkdir` at the gate — resolve the67 // approval card the way the UI's Approve button does.68 if controller.pendingApproval != nil {69 sawApproval = true70 controller.resolveApproval(.approve)71 }72 try? await Task.sleep(nanoseconds: 20_000_000)73 }7475 check(sawEntries, "live step entries were published during the run")76 check(sawPlan, "plan was published (update_plan intercepted)")77 check(sawRunningStatus, "task status went active while running")78 check(sawApproval, "approval card surfaced and resolved (guarded mode)")7980 guard let outcome = controller.outcome else {81 print(" ✘ run did not finish within 15s")82 return 183 }84 if case .completed(let answer) = outcome {85 check(!answer.isEmpty, "run completed with a final answer")86 } else {87 check(false, "run completed (got \(outcome))")88 }8990 check(!controller.isRunning, "isRunning cleared after the run")91 check(controller.entries.isEmpty, "live timeline folded into history after the run")92 check(controller.tokensUsed > 0, "token usage accumulated")93 check(controller.pendingApproval == nil, "no dangling approval")9495 let finished = store.task(id: task.id)96 check(finished?.status == .done, "task status is Done")97 check(finished?.messages.count == 2, "history has prompt + run (got \(finished?.messages.count ?? 0))")98 let runMessage = finished?.messages.last99 check(runMessage?.kind == .agentRun, "last history entry is the agent run")100 check((runMessage?.steps?.count ?? 0) >= 3, "run message embeds the steps (got \(runMessage?.steps?.count ?? 0))")101 check(runMessage?.plan?.doneCount == 2, "run message carries the finished plan")102 check(finished?.workspacePath != nil, "task bound to a workspace")103 check(!controller.terminalLines.isEmpty, "terminal feed captured output")104105 // Reopen path: a fresh controller re-attaches the workspace.106 if let workspacePath = finished?.workspacePath {107 let reopened = RunController(taskID: task.id, store: store, policyPersistence: persistence)108 check(reopened.workspaceRoot?.path == workspacePath, "reopened controller reattaches the workspace")109 check(reopened.plan != nil, "reopened controller restores the last plan")110 try? FileManager.default.removeItem(at: URL(fileURLWithPath: workspacePath))111 }112113 if failures.isEmpty {114 print("UI smoke: all checks passed.")115 return 0116 }117 print("UI smoke: \(failures.count) check(s) FAILED.")118 return 1119 }120}121