From 2d71966c801011b40efd95beadd2c74edce395af Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Wed, 19 Aug 2026 22:32:13 -0400 Subject: [PATCH 001/266] feat(remote): add authenticated mobile control service Add the versioned Aiden Remote API, secure local and Tailscale pairing, resumable chat streams, and workspace, file, Git, model, schedule, and attachment operations. Extract shared application services and cover authorization, idempotency, replay, revocation, opaque handles, and transport behavior. --- main/handlers/aiden-remote-parse.ts | 11 + main/handlers/aiden-remote.test.ts | 17 + main/handlers/aiden-remote.ts | 137 ++ main/handlers/chat-create-params.test.ts | 32 +- main/handlers/chats.append.contract.test.ts | 7 +- main/handlers/chats.ts | 190 +-- main/handlers/index.ts | 2 + main/handlers/providers.ts | 17 +- main/handlers/scheduled-tasks.ts | 68 +- main/handlers/workspaces.ts | 456 +------ main/index.ts | 19 + .../aiden-remote-approved-roots.test.ts | 73 ++ main/services/aiden-remote-approved-roots.ts | 79 ++ main/services/aiden-remote-attachments.ts | 373 ++++++ main/services/aiden-remote-chat-http.test.ts | 239 ++++ main/services/aiden-remote-chats.test.ts | 370 ++++++ main/services/aiden-remote-chats.ts | 631 ++++++++++ main/services/aiden-remote-errors.ts | 30 + main/services/aiden-remote-files.test.ts | 112 ++ main/services/aiden-remote-files.ts | 336 +++++ main/services/aiden-remote-git.test.ts | 285 +++++ main/services/aiden-remote-git.ts | 775 ++++++++++++ main/services/aiden-remote-models.test.ts | 56 + main/services/aiden-remote-models.ts | 94 ++ .../aiden-remote-opaque-handles.test.ts | 130 ++ main/services/aiden-remote-opaque-handles.ts | 188 +++ .../aiden-remote-operation-contract.test.ts | 867 +++++++++++++ .../aiden-remote-operation-contract.ts | 954 ++++++++++++++ main/services/aiden-remote-pairing.test.ts | 132 ++ main/services/aiden-remote-pairing.ts | 251 ++++ main/services/aiden-remote-protocol.test.ts | 674 ++++++++++ main/services/aiden-remote-protocol.ts | 971 +++++++++++++++ main/services/aiden-remote-router.test.ts | 755 +++++++++++ main/services/aiden-remote-router.ts | 1109 +++++++++++++++++ main/services/aiden-remote-schedules.test.ts | 171 +++ main/services/aiden-remote-schedules.ts | 422 +++++++ main/services/aiden-remote-service-main.ts | 346 +++++ main/services/aiden-remote-service.test.ts | 329 +++++ main/services/aiden-remote-service.ts | 501 ++++++++ main/services/aiden-remote-state.test.ts | 129 ++ main/services/aiden-remote-state.ts | 547 ++++++++ main/services/aiden-remote-streams.test.ts | 126 ++ main/services/aiden-remote-streams.ts | 724 +++++++++++ .../aiden-remote-tailscale-route.test.ts | 142 +++ main/services/aiden-remote-tailscale-route.ts | 146 +++ main/services/aiden-remote-tailscale.test.ts | 101 ++ main/services/aiden-remote-tailscale.ts | 188 +++ .../aiden-remote-tls-identity.test.ts | 69 + main/services/aiden-remote-tls-identity.ts | 319 +++++ .../aiden-remote-workspace-browser.test.ts | 144 +++ .../aiden-remote-workspace-browser.ts | 440 +++++++ .../aiden-remote-workspace-http.test.ts | 179 +++ .../services/aiden-remote-workspace-owners.ts | 42 + main/services/aiden-remote-workspaces.test.ts | 240 ++++ main/services/aiden-remote-workspaces.ts | 403 ++++++ .../services/chat-application-service-main.ts | 22 + .../services/chat-application-service.test.ts | 186 +++ main/services/chat-application-service.ts | 217 ++++ main/services/chat-generation-owner.test.ts | 52 + main/services/chat-generation-owner.ts | 94 +- main/services/chat-store-core.ts | 17 +- main/services/provider-list-main.ts | 19 + main/services/schedule-execution.ts | 3 +- main/services/schedule-service-core.ts | 25 +- ...scheduled-task-application-service-main.ts | 26 + ...scheduled-task-application-service.test.ts | 119 ++ .../scheduled-task-application-service.ts | 236 ++++ .../subagent-phase3-contract.test.ts | 319 +++-- .../workspace-application-service-main.ts | 29 + .../workspace-application-service.test.ts | 188 +++ .../services/workspace-application-service.ts | 268 ++++ ...ce-environment-application-service-main.ts | 16 + ...ce-environment-application-service.test.ts | 82 ++ ...rkspace-environment-application-service.ts | 151 +++ main/services/workspace-operation-registry.ts | 7 +- ...space-worktree-application-service-main.ts | 88 ++ ...space-worktree-application-service.test.ts | 121 ++ .../workspace-worktree-application-service.ts | 187 +++ package-lock.json | 255 +++- package.json | 9 +- protocol/aiden-appearance-v1.json | 29 + .../aiden-remote/v1/fixtures/contract.json | 204 +++ protocol/aiden-remote/v1/openapi.json | 305 +++++ renderer/shared/aiden-remote.ts | 51 + scripts/aiden-remote-lan-transport-spike.mjs | 230 ++++ .../aiden-remote-lan-transport-spike.test.mjs | 438 +++++++ ...en-remote-physical-device-spike-server.mjs | 890 +++++++++++++ 87 files changed, 20204 insertions(+), 827 deletions(-) create mode 100644 main/handlers/aiden-remote-parse.ts create mode 100644 main/handlers/aiden-remote.test.ts create mode 100644 main/handlers/aiden-remote.ts create mode 100644 main/services/aiden-remote-approved-roots.test.ts create mode 100644 main/services/aiden-remote-approved-roots.ts create mode 100644 main/services/aiden-remote-attachments.ts create mode 100644 main/services/aiden-remote-chat-http.test.ts create mode 100644 main/services/aiden-remote-chats.test.ts create mode 100644 main/services/aiden-remote-chats.ts create mode 100644 main/services/aiden-remote-errors.ts create mode 100644 main/services/aiden-remote-files.test.ts create mode 100644 main/services/aiden-remote-files.ts create mode 100644 main/services/aiden-remote-git.test.ts create mode 100644 main/services/aiden-remote-git.ts create mode 100644 main/services/aiden-remote-models.test.ts create mode 100644 main/services/aiden-remote-models.ts create mode 100644 main/services/aiden-remote-opaque-handles.test.ts create mode 100644 main/services/aiden-remote-opaque-handles.ts create mode 100644 main/services/aiden-remote-operation-contract.test.ts create mode 100644 main/services/aiden-remote-operation-contract.ts create mode 100644 main/services/aiden-remote-pairing.test.ts create mode 100644 main/services/aiden-remote-pairing.ts create mode 100644 main/services/aiden-remote-protocol.test.ts create mode 100644 main/services/aiden-remote-protocol.ts create mode 100644 main/services/aiden-remote-router.test.ts create mode 100644 main/services/aiden-remote-router.ts create mode 100644 main/services/aiden-remote-schedules.test.ts create mode 100644 main/services/aiden-remote-schedules.ts create mode 100644 main/services/aiden-remote-service-main.ts create mode 100644 main/services/aiden-remote-service.test.ts create mode 100644 main/services/aiden-remote-service.ts create mode 100644 main/services/aiden-remote-state.test.ts create mode 100644 main/services/aiden-remote-state.ts create mode 100644 main/services/aiden-remote-streams.test.ts create mode 100644 main/services/aiden-remote-streams.ts create mode 100644 main/services/aiden-remote-tailscale-route.test.ts create mode 100644 main/services/aiden-remote-tailscale-route.ts create mode 100644 main/services/aiden-remote-tailscale.test.ts create mode 100644 main/services/aiden-remote-tailscale.ts create mode 100644 main/services/aiden-remote-tls-identity.test.ts create mode 100644 main/services/aiden-remote-tls-identity.ts create mode 100644 main/services/aiden-remote-workspace-browser.test.ts create mode 100644 main/services/aiden-remote-workspace-browser.ts create mode 100644 main/services/aiden-remote-workspace-http.test.ts create mode 100644 main/services/aiden-remote-workspace-owners.ts create mode 100644 main/services/aiden-remote-workspaces.test.ts create mode 100644 main/services/aiden-remote-workspaces.ts create mode 100644 main/services/chat-application-service-main.ts create mode 100644 main/services/chat-application-service.test.ts create mode 100644 main/services/chat-application-service.ts create mode 100644 main/services/chat-generation-owner.test.ts create mode 100644 main/services/provider-list-main.ts create mode 100644 main/services/scheduled-task-application-service-main.ts create mode 100644 main/services/scheduled-task-application-service.test.ts create mode 100644 main/services/scheduled-task-application-service.ts create mode 100644 main/services/workspace-application-service-main.ts create mode 100644 main/services/workspace-application-service.test.ts create mode 100644 main/services/workspace-application-service.ts create mode 100644 main/services/workspace-environment-application-service-main.ts create mode 100644 main/services/workspace-environment-application-service.test.ts create mode 100644 main/services/workspace-environment-application-service.ts create mode 100644 main/services/workspace-worktree-application-service-main.ts create mode 100644 main/services/workspace-worktree-application-service.test.ts create mode 100644 main/services/workspace-worktree-application-service.ts create mode 100644 protocol/aiden-appearance-v1.json create mode 100644 protocol/aiden-remote/v1/fixtures/contract.json create mode 100644 protocol/aiden-remote/v1/openapi.json create mode 100644 renderer/shared/aiden-remote.ts create mode 100644 scripts/aiden-remote-lan-transport-spike.mjs create mode 100644 scripts/aiden-remote-lan-transport-spike.test.mjs create mode 100644 scripts/aiden-remote-physical-device-spike-server.mjs diff --git a/main/handlers/aiden-remote-parse.ts b/main/handlers/aiden-remote-parse.ts new file mode 100644 index 00000000..b6fef09a --- /dev/null +++ b/main/handlers/aiden-remote-parse.ts @@ -0,0 +1,11 @@ +import type { AidenRemoteConnectionMode } from "../services/aiden-remote-state.js"; + +export function parseAidenRemoteConnectionMode(value: unknown): AidenRemoteConnectionMode { + if (value === "lan" || value === "tailscale" || value === "both") return value; + throw new Error("Invalid Aiden Remote connection mode."); +} + +export function parseAidenRemoteTransport(value: unknown): "lan" | "tailscale" { + if (value === "lan" || value === "tailscale") return value; + throw new Error("Invalid Aiden Remote pairing transport."); +} diff --git a/main/handlers/aiden-remote.test.ts b/main/handlers/aiden-remote.test.ts new file mode 100644 index 00000000..33ecefe2 --- /dev/null +++ b/main/handlers/aiden-remote.test.ts @@ -0,0 +1,17 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { + parseAidenRemoteConnectionMode, + parseAidenRemoteTransport, +} from "./aiden-remote-parse.js"; + +test("Remote Access IPC parsers accept only exact transport values", () => { + assert.equal(parseAidenRemoteConnectionMode("lan"), "lan"); + assert.equal(parseAidenRemoteConnectionMode("tailscale"), "tailscale"); + assert.equal(parseAidenRemoteConnectionMode("both"), "both"); + assert.equal(parseAidenRemoteTransport("lan"), "lan"); + assert.equal(parseAidenRemoteTransport("tailscale"), "tailscale"); + for (const invalid of [undefined, null, true, "LAN", "both", ["lan"], { value: "lan" }]) { + assert.throws(() => parseAidenRemoteTransport(invalid), /invalid/iu); + } +}); diff --git a/main/handlers/aiden-remote.ts b/main/handlers/aiden-remote.ts new file mode 100644 index 00000000..f2605254 --- /dev/null +++ b/main/handlers/aiden-remote.ts @@ -0,0 +1,137 @@ +import { BrowserWindow, dialog, ipcMain } from "../platform.js"; +import { getAidenRemoteRuntime } from "../services/aiden-remote-service-main.js"; +import type { AidenRemoteSettingsSnapshot } from "../../renderer/shared/aiden-remote.js"; +import { + parseAidenRemoteConnectionMode, + parseAidenRemoteTransport, +} from "./aiden-remote-parse.js"; + +function parseIdentifier(value: unknown, prefix: "device_" | "root_"): string { + if ( + typeof value !== "string" || + !value.startsWith(prefix) || + value.length > 128 || + !/^[A-Za-z0-9_-]+$/u.test(value) + ) { + throw new Error("Invalid Aiden Remote identifier."); + } + return value; +} + +async function settingsSnapshot(): Promise { + const runtime = await getAidenRemoteRuntime(); + const state = await runtime.state.snapshot(); + return { + status: await runtime.service.status(), + devices: await runtime.state.listDevices(), + approvedRoots: state.approvedRoots.map((root) => ({ + id: root.id, + label: root.label, + folderPath: root.folderPath, + createdAt: root.createdAt, + })), + }; +} + +export function registerAidenRemoteHandlers(): void { + ipcMain.handle("remote:get", settingsSnapshot); + + ipcMain.handle("remote:setEnabled", async (_event, enabled: unknown) => { + if (typeof enabled !== "boolean") throw new Error("Invalid Aiden Remote enabled state."); + await (await getAidenRemoteRuntime()).service.setEnabled(enabled); + return settingsSnapshot(); + }); + + ipcMain.handle("remote:setConnectionMode", async (_event, mode: unknown) => { + await (await getAidenRemoteRuntime()).service.setConnectionMode( + parseAidenRemoteConnectionMode(mode), + ); + return settingsSnapshot(); + }); + + ipcMain.handle("remote:tailscaleConnect", async () => { + await (await getAidenRemoteRuntime()).service.connectTailscale(); + return settingsSnapshot(); + }); + + ipcMain.handle("remote:tailscaleDisconnect", async () => { + await (await getAidenRemoteRuntime()).service.disconnectTailscale(); + return settingsSnapshot(); + }); + + ipcMain.handle("remote:beginPairing", async (_event, transport: unknown) => { + const selectedTransport = parseAidenRemoteTransport(transport); + const service = (await getAidenRemoteRuntime()).service; + const bootstrap = await service.beginPairing(selectedTransport); + return { + ...bootstrap, + qrPayload: service.pairingQrPayload(bootstrap, selectedTransport), + }; + }); + + ipcMain.handle("remote:closePairing", async () => { + await (await getAidenRemoteRuntime()).service.closePairing(); + return { closed: true }; + }); + + ipcMain.handle("remote:revokeDevice", async (_event, deviceId: unknown) => { + const runtime = await getAidenRemoteRuntime(); + const revoked = await runtime.revokeDevice(parseIdentifier(deviceId, "device_")); + if (!revoked) throw new Error("This Aiden Remote device is already revoked or unavailable."); + return settingsSnapshot(); + }); + + ipcMain.handle("remote:addApprovedRoot", async (event) => { + const parent = BrowserWindow.fromWebContents(event.sender); + const selection = parent + ? await dialog.showOpenDialog(parent, { + title: "Approve a folder for Aiden On The Go", + buttonLabel: "Review Folder", + properties: ["openDirectory", "createDirectory"], + }) + : await dialog.showOpenDialog({ + title: "Approve a folder for Aiden On The Go", + buttonLabel: "Review Folder", + properties: ["openDirectory", "createDirectory"], + }); + const selectedPath = selection.filePaths[0]; + if (selection.canceled || !selectedPath) return settingsSnapshot(); + const runtime = await getAidenRemoteRuntime(); + try { + await runtime.approvedRoots.addLocalFolder(selectedPath); + } catch (error) { + if (!(error instanceof Error) || !error.message.includes("entire home directory")) throw error; + const warning = parent + ? await dialog.showMessageBox(parent, { + type: "warning", + title: "Approve your entire home folder?", + message: "A paired device could browse every non-hidden folder in your home directory.", + detail: "Approve a smaller project folder when possible. Provider credentials and hidden/system folders remain excluded by policy.", + buttons: ["Cancel", "Approve Home Folder"], + defaultId: 0, + cancelId: 0, + noLink: true, + }) + : await dialog.showMessageBox({ + type: "warning", + title: "Approve your entire home folder?", + message: "A paired device could browse every non-hidden folder in your home directory.", + detail: "Approve a smaller project folder when possible. Provider credentials and hidden/system folders remain excluded by policy.", + buttons: ["Cancel", "Approve Home Folder"], + defaultId: 0, + cancelId: 0, + noLink: true, + }); + if (warning.response !== 1) return settingsSnapshot(); + await runtime.approvedRoots.addLocalFolder(selectedPath, { confirmHomeDirectory: true }); + } + return settingsSnapshot(); + }); + + ipcMain.handle("remote:removeApprovedRoot", async (_event, rootId: unknown) => { + const runtime = await getAidenRemoteRuntime(); + const removed = await runtime.approvedRoots.removeLocalRoot(parseIdentifier(rootId, "root_")); + if (!removed) throw new Error("This approved root is no longer available."); + return settingsSnapshot(); + }); +} diff --git a/main/handlers/chat-create-params.test.ts b/main/handlers/chat-create-params.test.ts index 5bcba6ad..5d3fe66a 100644 --- a/main/handlers/chat-create-params.test.ts +++ b/main/handlers/chat-create-params.test.ts @@ -59,6 +59,10 @@ test("chat creation rejects excess keys lazily with constant errors", () => { test("the reserved Assistant workspace is minted only by the dedicated handler", () => { const handlers = readFileSync(new URL("./chats.ts", import.meta.url), "utf8"); + const applicationService = readFileSync( + new URL("../services/chat-application-service.ts", import.meta.url), + "utf8", + ); const renderer = readFileSync( new URL( "../../renderer/components/assistant/use-assistant-chat.ts", @@ -74,10 +78,14 @@ test("the reserved Assistant workspace is minted only by the dedicated handler", handlers.indexOf('"chats:createAssistant"'), handlers.indexOf('"chats:rename"'), ); - assert.match(publicCreate, /parsed\.workspaceId === ASSISTANT_WORKSPACE_ID/u); + assert.match(publicCreate, /chatApplicationService\.create\(parsed, owner\)/u); + assert.match( + applicationService, + /input\.workspaceId === ASSISTANT_WORKSPACE_ID/u, + ); assert.match( - publicCreate, - /configStore\.getWorkspace\(parsed\.workspaceId\)/u, + applicationService, + /configStore\.getWorkspace\(input\.workspaceId\)/u, ); assert.match(assistantCreate, /parseAssistantChatCreate\(input\)/u); assert.match(assistantCreate, /workspaceId: ASSISTANT_WORKSPACE_ID/u); @@ -117,16 +125,22 @@ test("public chat creation is aborted and drained across a workspace mutation", finishMutation(); const handlers = readFileSync(new URL("./chats.ts", import.meta.url), "utf8"); + const applicationService = readFileSync( + new URL("../services/chat-application-service.ts", import.meta.url), + "utf8", + ); const publicCreate = handlers.slice( handlers.indexOf('"chats:create"'), handlers.indexOf('"chats:createAssistant"'), ); + assert.match(publicCreate, /rendererDocumentOwner\(/u); + assert.match(publicCreate, /chatApplicationService\.create\(parsed, owner\)/u); assert.match( - publicCreate, - /workspaceMutationGate\.admit\(parsed\.workspaceId\)/u, + applicationService, + /workspaceMutationGate\.admit\(input\.workspaceId\)/u, ); - assert.match(publicCreate, /admitRendererOwnedWorkspaceOperation\(/u); - assert.match(publicCreate, /workspaceOperation\?\.signal\.aborted/u); - assert.match(publicCreate, /workspaceOperation\?\.release\(\)/u); - assert.match(publicCreate, /mutationAdmission\?\.release\(\)/u); + assert.match(applicationService, /admitOwnedWorkspaceOperation\(/u); + assert.match(applicationService, /workspaceOperation\?\.signal\.aborted/u); + assert.match(applicationService, /workspaceOperation\?\.release\(\)/u); + assert.match(applicationService, /mutationAdmission\.release\(\)/u); }); diff --git a/main/handlers/chats.append.contract.test.ts b/main/handlers/chats.append.contract.test.ts index 80f7ff24..00217264 100644 --- a/main/handlers/chats.append.contract.test.ts +++ b/main/handlers/chats.append.contract.test.ts @@ -9,6 +9,10 @@ import { } from "../../renderer/shared/chat-message-contract.js"; const source = fs.readFileSync(new URL("./chats.ts", import.meta.url), "utf8"); +const applicationServiceSource = fs.readFileSync( + new URL("../services/chat-application-service.ts", import.meta.url), + "utf8", +); const projectionSource = fs.readFileSync( new URL("../services/visible-chat-projection.ts", import.meta.url), "utf8", @@ -19,7 +23,8 @@ test("private canonical Pi protocol never crosses the renderer chat boundary", ( projectionSource, /const \{ pi: _privatePiProtocol, \.\.\.visible \} = message/u, ); - assert.match(source, /chat: chatForRenderer\(chat\)/u); + assert.match(source, /chatApplicationService\.get\(/u); + assert.match(applicationServiceSource, /chat: chatForRenderer\(chat\)/u); assert.match(source, /return chatForRenderer\(chat\)/u); assert.match(source, /return chatForRenderer\(copied\)/u); }); diff --git a/main/handlers/chats.ts b/main/handlers/chats.ts index 9b587265..fe8f8098 100644 --- a/main/handlers/chats.ts +++ b/main/handlers/chats.ts @@ -1,17 +1,15 @@ // Chat history CRUD IPC handlers. -import { BrowserWindow, dialog, ipcMain, logger } from "../platform.js"; +import { BrowserWindow, dialog, ipcMain } from "../platform.js"; import { chatStore } from "../services/chat-store.js"; +import { chatApplicationService } from "../services/chat-application-service-main.js"; import { chatTitleService } from "../services/chat-title.js"; import { configStore } from "../services/config-store.js"; import { computerUseStatus } from "../services/computer-use/status.js"; import { llmClient } from "../services/llm-client.js"; import { rendererDocumentOwner } from "../services/renderer-document-owner.js"; -import { subagentRunStore } from "../services/subagents/subagent-run-store.js"; import { persistedChatWorkspaceId } from "../../renderer/shared/chat-workspace.js"; import { isSafeSubagentIdentifier } from "../../renderer/shared/subagent-runs.js"; -import { piCompactionSessionStore } from "../services/pi-compaction-session-store.js"; -import { piRuntimeEffectStore } from "../services/pi-runtime-effect-store.js"; import { skillRegistry } from "../services/skill-registry-main.js"; import { commitSkillInvocationForAppend, @@ -58,35 +56,17 @@ export function registerChatHistoryHandlers(): void { let chatExportActive = false; ipcMain.handle("chats:activitySnapshot", () => chatActivityRegistry.snapshot()); ipcMain.handle("chats:list", async (_event, workspaceId?: unknown) => - chatStore.list( + chatApplicationService.list( typeof workspaceId === "string" && workspaceId ? workspaceId : undefined, ), ); - ipcMain.handle("chats:get", async (_event, id: unknown) => { - const chatId = asString(id, "id"); - let reconciliationRequired = false; - if (llmClient.isChatOwnedByInactiveRenderer(chatId)) { - reconciliationRequired = !(await llmClient.waitForChatIdle(chatId)); - } - const chat = await chatStore.get(chatId); - // The former renderer can be invalidated while this asynchronous read is - // already in flight. Mark that result provisional as well; the renderer - // retains a retry marker even if the one-shot settlement event was missed. - reconciliationRequired ||= llmClient.isChatOwnedByInactiveRenderer(chatId); - return { - chat: chatForRenderer(chat), - reconciliation: reconciliationRequired - ? { - chatId, - workspaceId: persistedChatWorkspaceId(chat?.workspaceId), - } - : null, - }; - }); + ipcMain.handle("chats:get", async (_event, id: unknown) => + chatApplicationService.get(asString(id, "id")), + ); ipcMain.handle("chats:waitUntilIdle", async (_event, id: unknown) => - llmClient.waitForChatIdle(asString(id, "id")), + chatApplicationService.waitUntilIdle(asString(id, "id")), ); ipcMain.handle("chats:create", async (event, input: unknown) => { @@ -94,65 +74,8 @@ export function registerChatHistoryHandlers(): void { event, () => new Error("Chats require the active application document."), ); - if (llmClient.requiresAppendReconciliation(owner.documentId)) { - throw new Error(appendReconciliationFailureMessage("blocked")); - } const parsed = parseChatCreate(input); - if (parsed.workspaceId === ASSISTANT_WORKSPACE_ID) { - throw new Error( - "Aiden Assistant chats require the Assistant chat creation path.", - ); - } - const mutationAdmission = parsed.workspaceId - ? workspaceMutationGate.admit(parsed.workspaceId) - : undefined; - let workspaceOperation: - ReturnType | undefined; - try { - workspaceOperation = parsed.workspaceId - ? admitRendererOwnedWorkspaceOperation( - workspaceOperationRegistry, - owner, - parsed.workspaceId, - ) - : undefined; - if ( - parsed.workspaceId && - !(await configStore.getWorkspace(parsed.workspaceId)) - ) { - throw new Error("The selected workspace is no longer available."); - } - const assertCurrent = () => { - if (owner.isDestroyed()) - throw new Error("The renderer document is no longer active."); - if ( - mutationAdmission?.signal.aborted || - workspaceOperation?.signal.aborted - ) { - throw new Error("The workspace changed before the chat was created."); - } - if (llmClient.requiresAppendReconciliation(owner.documentId)) { - throw new Error(appendReconciliationFailureMessage("blocked")); - } - }; - try { - return chatForRenderer( - await chatStore.create({ ...parsed, assertCurrent }), - ); - } catch (error) { - if (isChatCreateReconciliationRequiredError(error)) { - llmClient.markAppendReconciliationRequired(owner.documentId); - owner.onInvalidated(() => { - llmClient.clearAppendReconciliationRequired(owner.documentId); - }); - throw new Error(appendReconciliationFailureMessage("blocked")); - } - throw error; - } - } finally { - workspaceOperation?.release(); - mutationAdmission?.release(); - } + return chatApplicationService.create(parsed, owner); }); ipcMain.handle("chats:createAssistant", async (event, input: unknown) => { @@ -199,7 +122,7 @@ export function registerChatHistoryHandlers(): void { ipcMain.handle( "chats:rename", async (_event, id: unknown, title: unknown) => { - await chatStore.rename(asString(id, "id"), asString(title, "title")); + await chatApplicationService.rename(asString(id, "id"), asString(title, "title")); }, ); @@ -346,24 +269,10 @@ export function registerChatHistoryHandlers(): void { ipcMain.handle( "chats:moveEmptyToWorkspace", async (_event, id: unknown, workspaceId: unknown) => { - const chatId = asString(id, "id"); - const nextWorkspaceId = asString(workspaceId, "workspaceId"); - const finishMove = llmClient.beginChatWorkspaceChange(chatId); - if (!finishMove) { - throw new Error( - "Finish or stop the current response before changing workspaces.", - ); - } - try { - if (!(await configStore.getWorkspace(nextWorkspaceId))) { - throw new Error(`Workspace ${nextWorkspaceId} not found.`); - } - return chatForRenderer( - await chatStore.moveEmptyChatToWorkspace(chatId, nextWorkspaceId), - ); - } finally { - finishMove(); - } + return chatApplicationService.moveEmptyToWorkspace( + asString(id, "id"), + asString(workspaceId, "workspaceId"), + ); }, ); @@ -417,76 +326,9 @@ export function registerChatHistoryHandlers(): void { }, ); - ipcMain.handle("chats:remove", async (_event, id: unknown) => { - const chatId = asString(id, "id"); - const finishDeletion = llmClient.beginChatDeletion(chatId); - let releaseAdmission = false; - try { - await llmClient.cancelChat(chatId); - // Privacy data is removed first so a partial cross-store failure cannot - // leave orphaned inspector reports after the chat disappears from the UI. - try { - await subagentRunStore.deleteChat(chatId); - } catch (error) { - logger.error( - "subagents", - "Could not delete private subagent history.", - error, - ); - throw new Error("Aiden could not delete this chat's subagent history."); - } - try { - await piRuntimeEffectStore.deleteChat(chatId); - } catch (error) { - logger.error( - "pi", - "Could not delete private Pi effect history.", - error, - ); - throw new Error( - "Aiden could not delete this chat's tool-effect history.", - ); - } - try { - await piCompactionSessionStore.deleteChat(chatId); - } catch (error) { - logger.error( - "pi", - "Could not delete the private compaction journal.", - error, - ); - throw new Error( - "Aiden could not delete this chat's compaction history.", - ); - } - // remove() also reconciles an index entry whose payload is already - // missing or corrupt, while propagating real filesystem failures. - await chatStore.remove(chatId); - // Clear the crash-recovery intent only after both stores have crossed - // their durability barriers. - await subagentRunStore.completeChatDeletion(chatId); - releaseAdmission = true; - } finally { - if (!releaseAdmission) { - try { - releaseAdmission = !( - await subagentRunStore.pendingChatDeletions() - ).includes(chatId); - } catch (error) { - // An indeterminate durable state must keep generation admission - // closed until restart reconciliation can safely finish the delete. - logger.error( - "subagents", - "Could not inspect pending chat deletion state.", - error, - ); - } - } - // A durable but incomplete intent keeps admission closed for this - // process. Startup reconciliation finishes it before the next renderer. - if (releaseAdmission) finishDeletion(); - } - }); + ipcMain.handle("chats:remove", async (_event, id: unknown) => + chatApplicationService.remove(asString(id, "id")), + ); ipcMain.handle( "chats:appendMessage", diff --git a/main/handlers/index.ts b/main/handlers/index.ts index f02f01da..f97a3899 100644 --- a/main/handlers/index.ts +++ b/main/handlers/index.ts @@ -24,6 +24,7 @@ import { registerAssistantHandlers } from "./assistant.js"; import { registerShortcutHandlers } from "./shortcuts.js"; import { registerTelegramHandlers } from "./telegram.js"; import { registerSubagentHandlers } from "./subagents.js"; +import { registerAidenRemoteHandlers } from "./aiden-remote.js"; import { ipcMain, logger } from "../platform.js"; import { writeDevLog } from "../services/dev-log.js"; @@ -65,6 +66,7 @@ export function registerHandlers(): void { registerShortcutHandlers(); registerTelegramHandlers(); registerSubagentHandlers(); + registerAidenRemoteHandlers(); logger.info("handlers", "✓ IPC handlers registered"); diff --git a/main/handlers/providers.ts b/main/handlers/providers.ts index 10408721..b1530346 100644 --- a/main/handlers/providers.ts +++ b/main/handlers/providers.ts @@ -1,6 +1,6 @@ // Provider configuration + API key IPC handlers. Thin — logic lives in services. -import { ipcMain, logger } from "../platform.js"; +import { ipcMain } from "../platform.js"; import { configStore } from "../services/config-store.js"; import { canUseStoredProviderKey } from "../services/provider-key-policy.js"; import { secrets } from "../services/secrets.js"; @@ -23,7 +23,6 @@ import { parseAnthropicThinkingSelection } from "../services/anthropic-provider. import { assertMutableProviderId, forwardCodexProviderStatusChanges, - mergeCodexProvider, } from "../services/provider-list-core.js"; import { AppearancePreviewState } from "../services/appearance-preview-core.js"; import { @@ -35,7 +34,7 @@ import { normalizeProviderCredentialInput, providerConnectionSnapshot, } from "../services/provider-credential-rotation-core.js"; -import { listProvidersWithLegacyPiCredentialMigration } from "../services/legacy-pi-credential-migration.js"; +import { listConfiguredProviders } from "../services/provider-list-main.js"; import type { ProviderDeployment, ProviderKind, @@ -196,17 +195,7 @@ async function saveProvider( } async function listProviders() { - const customProviders = await listProvidersWithLegacyPiCredentialMigration(); - const providers = [ - ...(await providerRegistry.listBuiltinProviders()), - ...customProviders.filter((provider) => !providerRegistry.isBuiltinProvider(provider.id)), - ]; - try { - return mergeCodexProvider(providers, await providerRegistry.codex.snapshot()); - } catch { - logger.warn("providers", "ChatGPT / Codex status was unavailable while listing providers."); - return mergeCodexProvider(providers, null); - } + return listConfiguredProviders(); } export function registerProviderHandlers(): void { diff --git a/main/handlers/scheduled-tasks.ts b/main/handlers/scheduled-tasks.ts index 9b689f09..77269e1d 100644 --- a/main/handlers/scheduled-tasks.ts +++ b/main/handlers/scheduled-tasks.ts @@ -1,17 +1,7 @@ import { ipcMain } from "../platform.js"; -import { configStore } from "../services/config-store.js"; -import { scheduleService } from "../services/schedule-service.js"; -import { listScheduledScripts } from "../services/schedule-script.js"; -import { - nextScheduledRuns, - scheduleStore, - systemTimezone, - validateTimezone, -} from "../services/schedule-store.js"; import { parseScheduledTaskInput } from "./scheduled-tasks-parse.js"; -import type { ScheduledTaskSettings } from "../services/types.js"; -import { scheduledSettingsPatch } from "../services/scheduled-settings-core.js"; -import { selectedMcpServers } from "../services/mcp-selection.js"; +import { scheduledTaskApplicationService } from "../services/scheduled-task-application-service-main.js"; +import { systemTimezone } from "../services/schedule-store.js"; function requiredString(value: unknown, name: string): string { if (typeof value !== "string" || !value.trim()) { @@ -24,69 +14,41 @@ function optionalString(value: unknown): string | undefined { return typeof value === "string" && value.trim() ? value : undefined; } -function settingsDefaults( - input: Awaited>, -): ScheduledTaskSettings { - return { - enabled: input.scheduledTasksEnabled !== false, - defaultMode: input.scheduledDefaultMode === "script" ? "script" : "llm", - defaultPermission: input.scheduledDefaultPermission === "full" ? "full" : "read-only", - defaultMcpEnabled: input.scheduledDefaultMcpEnabled === true, - defaultNotify: input.scheduledDefaultNotify !== false, - defaultTimezone: validateTimezone(input.scheduledDefaultTimezone ?? systemTimezone()), - }; -} - export function registerScheduledTaskHandlers(): void { - ipcMain.handle("schedule:list", () => scheduleStore.list()); + ipcMain.handle("schedule:list", () => scheduledTaskApplicationService.list()); ipcMain.handle("schedule:save", async (_event, input: unknown) => { - const parsed = parseScheduledTaskInput(input); - if ((parsed.mcpServerIds?.length ?? 0) > 0) { - selectedMcpServers(await configStore.listMcpServers(), parsed.mcpServerIds); - } - return scheduleService.save(parsed); + return scheduledTaskApplicationService.save(parseScheduledTaskInput(input)); }); ipcMain.handle("schedule:remove", (_event, id: unknown) => - scheduleService.remove(requiredString(id, "id")), + scheduledTaskApplicationService.remove(requiredString(id, "id")), ); ipcMain.handle("schedule:pause", (_event, id: unknown) => - scheduleService.pause(requiredString(id, "id")), + scheduledTaskApplicationService.pause(requiredString(id, "id")), ); ipcMain.handle("schedule:resume", (_event, id: unknown) => - scheduleService.resume(requiredString(id, "id")), + scheduledTaskApplicationService.resume(requiredString(id, "id")), ); ipcMain.handle("schedule:runNow", (_event, id: unknown) => - scheduleService.runNow(requiredString(id, "id")), + scheduledTaskApplicationService.runNow(requiredString(id, "id")), ); ipcMain.handle("schedule:runs", (_event, id: unknown) => - scheduleStore.runs(requiredString(id, "id")), + scheduledTaskApplicationService.runs(requiredString(id, "id")), ); ipcMain.handle("schedule:preview", (_event, cron: unknown, timezone: unknown, count: unknown) => - nextScheduledRuns( + scheduledTaskApplicationService.preview( requiredString(cron, "cron"), optionalString(timezone) ?? systemTimezone(), typeof count === "number" ? count : 3, ), ); - ipcMain.handle("schedule:scripts", async (_event, workspaceId?: unknown) => { - const id = optionalString(workspaceId); - const workspace = id ? await configStore.getWorkspace(id) : undefined; - if (id && !workspace) throw new Error(`Workspace ${id} not found.`); - return listScheduledScripts({ workspaceRoot: workspace?.folderPath }); - }); + ipcMain.handle("schedule:scripts", (_event, workspaceId?: unknown) => + scheduledTaskApplicationService.scripts(optionalString(workspaceId))); ipcMain.handle("schedule:settings", async (_event, patch?: unknown) => { - const current = await configStore.getSettings(); - if (patch === undefined) return settingsDefaults(current); + const current = await scheduledTaskApplicationService.settings(); + if (patch === undefined) return current.value; const input = ( patch && typeof patch === "object" && !Array.isArray(patch) ? patch : {} ) as Record; - const saved = await configStore.setSettings(scheduledSettingsPatch(input, validateTimezone)); - if ( - typeof input.enabled === "boolean" && - input.enabled !== (current.scheduledTasksEnabled !== false) - ) { - await scheduleService.setGlobalEnabled(input.enabled); - } - return settingsDefaults(saved); + return (await scheduledTaskApplicationService.updateSettings(current.revision, input)).value; }); } diff --git a/main/handlers/workspaces.ts b/main/handlers/workspaces.ts index 4a60e433..6c3a5fce 100644 --- a/main/handlers/workspaces.ts +++ b/main/handlers/workspaces.ts @@ -1,11 +1,7 @@ // Workspace CRUD + folder helpers (git status, reveal in Finder). -import * as fs from "fs/promises"; -import * as path from "path"; import type { IpcMainInvokeEvent, OpenDialogOptions } from "electron"; -import { BrowserWindow, dialog, ipcMain, logger, shell } from "../platform.js"; -import { configStore } from "../services/config-store.js"; -import { ensureUserDataDir } from "../services/data-store.js"; +import { BrowserWindow, dialog, ipcMain, shell } from "../platform.js"; import { listExternalEditors, openFolderInExternalEditor } from "../services/external-editors.js"; import { gitBranches, @@ -14,53 +10,29 @@ import { gitCompare, gitComparisonDiff, gitCreateBranch, - gitCreateWorktree, - gitDeleteManagedWorktree, gitDiff, - gitFinalizeManagedWorktreeDeletion, gitInfo, - gitManagedWorktreeDeletionPending, - gitManagedWorktreeRegistered, - gitManagedWorktreeUsable, gitPush, gitPushCapability, gitReview, - gitRollbackWorktree, gitWorktrees, - GitManagedWorktreeDeleteError, type GitCommitInput, type GitComparisonDiffInput, type GitDiffInput, type GitPushInput, } from "../services/git.js"; -import { llmClient } from "../services/llm-client.js"; -import { scheduleService } from "../services/schedule-service.js"; -import { createScratchWorkspaceDirectory } from "../services/scratch-workspace.js"; -import { terminalService } from "../services/terminal.js"; -import type { Workspace, WorkspacePermission } from "../services/types.js"; +import { workspaceApplicationService } from "../services/workspace-application-service-main.js"; import { listWorkspaceFiles, readWorkspaceFile, WorkspaceFileError, writeWorkspaceFile, } from "../services/workspace-files.js"; -import { workspaceMutationGate } from "../services/workspace-mutation-gate.js"; -import { removeManagedWorkspace } from "../services/managed-worktree-removal-core.js"; -import { assertManagedWorktreeAdmission } from "../services/managed-worktree-admission.js"; -import { withWorkspaceScheduleRestoration } from "../services/workspace-schedule-restoration.js"; -import { - commitManagedWorktreeCreation, - ManagedWorktreeCreationError, -} from "../services/managed-worktree-creation-core.js"; -import { - admitRendererOwnedWorkspaceOperation, - workspaceOperationRegistry, -} from "../services/workspace-operation-registry.js"; import { rendererDocumentOwner } from "../services/renderer-document-owner.js"; -import { assertWorkspaceRecordRemovalAllowed } from "../services/workspace-record-removal.js"; import { parseWorktreeCreateParams } from "./worktree-create-params.js"; - -const PERMISSIONS: WorkspacePermission[] = ["full", "ask", "none"]; +import { workspaceEnvironmentApplicationService } from "../services/workspace-environment-application-service-main.js"; +import type { WorkspaceEnvironmentDirectory } from "../services/workspace-environment-application-service.js"; +import { workspaceWorktreeApplicationService } from "../services/workspace-worktree-application-service-main.js"; function asString(value: unknown, name: string): string { if (typeof value !== "string" || value.length === 0) { @@ -131,171 +103,51 @@ function asGitComparisonDiffInput(value: unknown): GitComparisonDiffInput { }; } -function newId(): string { - return `w-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; -} - -interface WorkspaceDirectory { - folderPath: string; - workspace: Workspace; -} - async function withWorkspaceOperation( event: IpcMainInvokeEvent, workspaceIdValue: unknown, - operation: (resolved: WorkspaceDirectory, signal: AbortSignal) => Promise, + operation: (resolved: WorkspaceEnvironmentDirectory, signal: AbortSignal) => Promise, allowNoAccess = false, ): Promise { - const workspaceId = asString(workspaceIdValue, "workspaceId"); - if (workspaceMutationGate.isChanging(workspaceId)) - throw new Error("The workspace is changing. Try again in a moment."); const owner = rendererDocumentOwner( event, () => new Error("Workspace access requires the active renderer document."), ); - const admission = admitRendererOwnedWorkspaceOperation( - workspaceOperationRegistry, + return workspaceEnvironmentApplicationService.run( owner, - workspaceId, + asString(workspaceIdValue, "workspaceId"), + operation, + { allowNoAccess }, ); - try { - const resolved = await workspaceDirectory(workspaceId, true, allowNoAccess); - if ( - !resolved || - owner.isDestroyed() || - admission.signal.aborted || - workspaceMutationGate.isChanging(workspaceId) - ) { - throw new Error("The workspace changed before the operation could start."); - } - return await operation(resolved, admission.signal); - } finally { - admission.release(); - } } -async function withWorkspaceRecordOperation( +async function withOptionalWorkspaceOperation( event: IpcMainInvokeEvent, workspaceIdValue: unknown, - operation: (workspace: Workspace, signal: AbortSignal) => Promise, + operation: (resolved: WorkspaceEnvironmentDirectory | undefined, signal: AbortSignal) => Promise, ): Promise { - const workspaceId = asString(workspaceIdValue, "workspaceId"); - if (workspaceMutationGate.isChanging(workspaceId)) { - throw new Error("The workspace is changing. Try again in a moment."); - } const owner = rendererDocumentOwner( event, () => new Error("Workspace access requires the active renderer document."), ); - const admission = admitRendererOwnedWorkspaceOperation( - workspaceOperationRegistry, + return workspaceEnvironmentApplicationService.runOptional( owner, - workspaceId, + asString(workspaceIdValue, "workspaceId"), + operation, ); - try { - const workspace = await configStore.getWorkspace(workspaceId); - if ( - !workspace || - owner.isDestroyed() || - admission.signal.aborted || - workspaceMutationGate.isChanging(workspaceId) - ) { - throw new Error("The workspace changed before the operation could start."); - } - return await operation(workspace, admission.signal); - } finally { - admission.release(); - } -} - -async function withOptionalWorkspaceOperation( - event: IpcMainInvokeEvent, - workspaceIdValue: unknown, - operation: (resolved: WorkspaceDirectory | undefined, signal: AbortSignal) => Promise, -): Promise { - const workspaceId = asString(workspaceIdValue, "workspaceId"); - return withWorkspaceRecordOperation(event, workspaceId, async (_workspace, signal) => { - const resolved = await workspaceDirectory(workspaceId, false); - if (signal.aborted || workspaceMutationGate.isChanging(workspaceId)) { - throw new Error("The workspace changed before the operation could start."); - } - return operation(resolved, signal); - }); -} - -async function saveWorkspaceForFolder( - folderPath: string, - permission: WorkspacePermission, -): Promise { - const canonicalPath = await fs.realpath(folderPath); - if (!(await fs.stat(canonicalPath)).isDirectory()) - throw new Error("Choose a folder for this workspace."); - const now = Date.now(); - return configStore.saveWorkspace({ - id: newId(), - name: path.basename(canonicalPath) || "Workspace", - folderPath: canonicalPath, - permission, - createdAt: now, - updatedAt: now, - }); -} - -/** Resolve Git paths from persisted workspace state, never from renderer input. */ -async function workspaceDirectory( - workspaceId: unknown, - required: boolean, - allowNoAccess = false, -): Promise { - const id = asString(workspaceId, "workspaceId"); - const workspace = await configStore.getWorkspace(id); - if (!workspace) throw new Error(`Workspace ${id} was not found.`); - if (workspace.permission === "none" && !allowNoAccess) { - if (!required) return undefined; - throw new Error(`${workspace.name} does not allow local file access.`); - } - if (!workspace.folderPath) { - if (!required) return undefined; - throw new Error(`${workspace.name} does not have a folder.`); - } - await assertManagedWorktreeAdmission(workspace); - try { - const folderPath = await fs.realpath(workspace.folderPath); - const stats = await fs.stat(folderPath); - if (!stats.isDirectory()) throw new Error("not a directory"); - return { folderPath, workspace }; - } catch { - if (!required) return undefined; - throw new Error(`${workspace.name}'s folder is no longer available.`); - } } export function registerWorkspaceHandlers(): void { - ipcMain.handle("workspaces:list", async () => configStore.listWorkspaces()); + ipcMain.handle("workspaces:list", async () => workspaceApplicationService.list()); ipcMain.handle( "workspaces:get", - async (_event, id: unknown) => (await configStore.getWorkspace(asString(id, "id"))) ?? null, + async (_event, id: unknown) => workspaceApplicationService.get(asString(id, "id")), ); - ipcMain.handle("workspaces:create", async (_event, input: unknown) => { - const i = (typeof input === "object" && input !== null ? input : {}) as Record; - if ("folderPath" in i) - throw new Error("Choose workspace folders through Aiden's folder picker."); - const name = (typeof i.name === "string" && i.name.trim()) || "Workspace"; - const permission = PERMISSIONS.includes(i.permission as WorkspacePermission) - ? (i.permission as WorkspacePermission) - : "ask"; - const now = Date.now(); - const workspace: Workspace = { - id: newId(), - name, - permission, - createdAt: now, - updatedAt: now, - }; - return configStore.saveWorkspace(workspace); - }); + ipcMain.handle("workspaces:create", async (_event, input: unknown) => + workspaceApplicationService.create(input), + ); ipcMain.handle("workspaces:createFromFolder", async (event) => { const parent = BrowserWindow.fromWebContents(event.sender); @@ -304,113 +156,20 @@ export function registerWorkspaceHandlers(): void { ? await dialog.showOpenDialog(parent, options) : await dialog.showOpenDialog(options); if (result.canceled || !result.filePaths[0]) return null; - return saveWorkspaceForFolder(result.filePaths[0], "ask"); + return workspaceApplicationService.createFromFolder(result.filePaths[0]); }); - ipcMain.handle("workspaces:createScratch", async () => { - const scratch = await createScratchWorkspaceDirectory(); - const now = Date.now(); - const workspace: Workspace = { - id: newId(), - name: scratch.name, - folderPath: scratch.folderPath, - permission: "ask", - createdAt: now, - updatedAt: now, - }; - try { - return await configStore.saveWorkspace(workspace); - } catch (error) { - // The directory is still empty here; avoid leaving an orphan if persistence fails. - await fs.rmdir(scratch.folderPath).catch(() => undefined); - throw error; - } - }); + ipcMain.handle("workspaces:createScratch", async () => + workspaceApplicationService.createScratch(), + ); - ipcMain.handle("workspaces:update", async (_event, id: unknown, patch: unknown) => { - const workspaceId = asString(id, "id"); - const finishMutation = workspaceMutationGate.begin(workspaceId); - try { - await workspaceOperationRegistry.cancelAndSettle(workspaceId); - const existing = await configStore.getWorkspace(workspaceId); - if (!existing) throw new Error(`Workspace ${String(id)} not found.`); - const p = (typeof patch === "object" && patch !== null ? patch : {}) as Record< - string, - unknown - >; - if ("folderPath" in p) - throw new Error("Workspace folders cannot be changed from renderer input."); - const next: Workspace = { - ...existing, - name: typeof p.name === "string" && p.name.trim() ? p.name.trim() : existing.name, - permission: PERMISSIONS.includes(p.permission as WorkspacePermission) - ? (p.permission as WorkspacePermission) - : existing.permission, - }; - if (next.permission !== existing.permission) { - return await withWorkspaceScheduleRestoration( - { - restoreOnExit: existing.permission !== "none", - resume: () => scheduleService.resumeWorkspace(existing.id), - onResumeError: (error) => { - logger.error( - "schedule", - "Could not restore scheduled tasks after workspace update failed.", - error, - ); - }, - }, - async ({ ensureResumedOnExit, keepPaused }) => { - terminalService.closeForWorkspace(existing.id); - await llmClient.cancelWorkspaceAndSettle(existing.id); - await scheduleService.cancelWorkspace(existing.id); - const saved = await configStore.saveWorkspace(next); - if (saved.permission !== "none") { - ensureResumedOnExit(); - await scheduleService.resumeWorkspace(saved.id); - } - keepPaused(); - return saved; - }, - ); - } - return await configStore.saveWorkspace(next); - } finally { - finishMutation(); - } - }); + ipcMain.handle("workspaces:update", async (_event, id: unknown, patch: unknown) => + workspaceApplicationService.update(asString(id, "id"), patch), + ); - ipcMain.handle("workspaces:remove", async (_event, id: unknown) => { - const workspaceId = asString(id, "id"); - const finishMutation = workspaceMutationGate.begin(workspaceId); - terminalService.closeForWorkspace(workspaceId); - try { - await workspaceOperationRegistry.cancelAndSettle(workspaceId); - const existing = await configStore.getWorkspace(workspaceId); - assertWorkspaceRecordRemovalAllowed(existing); - await withWorkspaceScheduleRestoration( - { - restoreOnExit: existing?.permission !== "none", - resume: () => scheduleService.resumeWorkspace(workspaceId), - onResumeError: (error) => { - logger.error( - "schedule", - "Could not restore scheduled tasks after workspace removal failed.", - error, - ); - }, - }, - async ({ keepPaused }) => { - await llmClient.cancelWorkspaceAndSettle(workspaceId); - await scheduleService.cancelWorkspace(workspaceId); - await configStore.removeWorkspace(workspaceId); - keepPaused(); - }, - ); - } finally { - finishMutation(); - } - }); + ipcMain.handle("workspaces:remove", async (_event, id: unknown) => + workspaceApplicationService.remove(asString(id, "id")), + ); ipcMain.handle("workspaces:gitInfo", async (event, workspaceId: unknown) => withOptionalWorkspaceOperation(event, workspaceId, async (resolved, signal) => @@ -532,155 +291,20 @@ export function registerWorkspaceHandlers(): void { workspaceId, name, ); - return withWorkspaceOperation(event, sourceWorkspaceId, async (resolved, signal) => { - const worktreeRoot = await ensureUserDataDir("worktrees"); - const worktree = await gitCreateWorktree(resolved.folderPath, worktreeRoot, branch, signal); - const now = Date.now(); - const workspace: Workspace = { - id: newId(), - name: `${path.basename(resolved.workspace.folderPath ?? resolved.workspace.name)} · ${branch}`, - folderPath: worktree.workspacePath, - permission: resolved.workspace.permission, - managedWorktree: { - repositoryPath: worktree.repositoryPath, - worktreePath: worktree.path, - branch, - worktreeGitDir: worktree.worktreeGitDir, - ownershipToken: worktree.ownershipToken, - worktreeDevice: worktree.worktreeDevice, - worktreeInode: worktree.worktreeInode, - createdFromHead: worktree.createdFromHead, - }, - createdAt: now, - updatedAt: now, - }; - try { - return await commitManagedWorktreeCreation({ - validateBeforeSave: async () => { - const latest = await workspaceDirectory(sourceWorkspaceId, true); - if ( - !latest || - signal.aborted || - latest.folderPath !== resolved.folderPath || - latest.workspace.permission !== resolved.workspace.permission - ) { - throw new Error( - "The source workspace changed while Aiden was creating the worktree.", - ); - } - }, - saveWorkspace: () => configStore.saveWorkspace(workspace), - validateAfterSave: () => { - if (signal.aborted || workspaceMutationGate.isChanging(sourceWorkspaceId)) { - throw new Error("The source workspace changed while Aiden was saving the worktree."); - } - }, - removeWorkspaceRecord: (saved) => configStore.removeWorkspace(saved.id), - rollbackWorktree: () => gitRollbackWorktree(resolved.folderPath, worktree), - }); - } catch (error) { - if (error instanceof ManagedWorktreeCreationError) { - logger.error("git", error.logMessage, error.errors); - } - throw error; - } - }); + const owner = rendererDocumentOwner( + event, + () => new Error("Workspace access requires the active renderer document."), + ); + return workspaceWorktreeApplicationService.create(owner, sourceWorkspaceId, branch); }); ipcMain.handle("git:deleteManagedWorktree", async (event, workspaceId: unknown) => { const id = asString(workspaceId, "workspaceId"); - return withWorkspaceRecordOperation(event, id, async (workspace, signal) => { - const managed = workspace.managedWorktree; - if (!managed) throw new Error("This workspace is not an Aiden-managed worktree."); - const finishMutation = workspaceMutationGate.begin(id); - try { - await workspaceOperationRegistry.cancelAndSettle(id, { - exceptSignal: signal, - }); - return await withWorkspaceScheduleRestoration( - { - restoreOnExit: workspace.permission !== "none", - resume: () => scheduleService.resumeWorkspace(id), - onResumeError: (error) => { - logger.error( - "schedule", - "Could not restore scheduled tasks after managed worktree deletion failed.", - error, - ); - }, - }, - async ({ keepPaused }) => { - terminalService.closeForWorkspace(id); - await llmClient.cancelWorkspaceAndSettle(id); - await scheduleService.cancelWorkspace(id); - const result = await removeManagedWorkspace({ - deleteWorktree: () => - gitDeleteManagedWorktree( - managed.repositoryPath, - managed.worktreePath, - managed.branch, - managed.createdFromHead, - signal, - managed.worktreeGitDir, - managed.ownershipToken, - managed.worktreeDevice, - managed.worktreeInode, - ), - destructiveMutationAttempted: (error) => - error instanceof GitManagedWorktreeDeleteError - ? error.destructiveMutationAttempted - : undefined, - deletionPending: () => - gitManagedWorktreeDeletionPending( - managed.worktreePath, - managed.worktreeGitDir!, - managed.ownershipToken!, - ), - workspacePathExists: async () => { - try { - await fs.stat(managed.worktreePath); - return true; - } catch (error) { - if ((error as NodeJS.ErrnoException).code === "ENOENT") return false; - throw error; - } - }, - worktreeRegistered: () => - gitManagedWorktreeRegistered( - managed.repositoryPath, - managed.worktreePath, - managed.branch, - managed.worktreeGitDir, - managed.ownershipToken, - ), - worktreeUsable: () => - gitManagedWorktreeUsable( - managed.repositoryPath, - managed.worktreePath, - managed.branch, - managed.worktreeGitDir, - managed.ownershipToken, - managed.worktreeDevice, - managed.worktreeInode, - ), - onDestructiveBoundary: keepPaused, - removeWorkspaceRecord: () => configStore.removeWorkspace(id), - reconciledResult: () => ({ branchDeleted: false }), - }); - if (managed.worktreeGitDir && managed.ownershipToken) { - await gitFinalizeManagedWorktreeDeletion( - managed.worktreePath, - managed.worktreeGitDir, - managed.ownershipToken, - ); - } - return result; - }, - ); - } finally { - finishMutation(); - } - }); + const owner = rendererDocumentOwner( + event, + () => new Error("Workspace access requires the active renderer document."), + ); + return workspaceWorktreeApplicationService.remove(owner, id); }); // Reveal the workspace folder in Finder. shell.openPath opens a directory itself. diff --git a/main/index.ts b/main/index.ts index 4b74c8fc..81d3bba5 100644 --- a/main/index.ts +++ b/main/index.ts @@ -101,6 +101,10 @@ import { reconcilePendingMcpCredentialCleanup, } from "./services/mcp-credential-cleanup.js"; import { resetOnboardingData } from "./services/onboarding-reset.js"; +import { + initializeAidenRemoteService, + stopAidenRemoteServiceAndSettle, +} from "./services/aiden-remote-service-main.js"; const ownsSingleInstanceLock = app.requestSingleInstanceLock(); @@ -340,6 +344,11 @@ async function shutdownAndQuit(settingsPrepared = false): Promise { app.exit(1); return; } + try { + await stopAidenRemoteServiceAndSettle(); + } catch (error) { + logger.error("aiden-remote", "Remote Access did not stop cleanly.", error); + } cleanupApplication(); try { await Promise.all([ @@ -1605,6 +1614,16 @@ if (!ownsSingleInstanceLock) { ); powerMonitor.on("resume", () => void portableConfigWatcher.refresh()); + try { + await initializeAidenRemoteService(); + } catch (error) { + logger.error( + "aiden-remote", + "Remote Access could not restore its saved listener state; the desktop app will remain available for repair.", + error, + ); + } + await createMainWindow(); if (packagedSubagentSoak) { await runPackagedSubagentSoak(packagedSubagentSoak); diff --git a/main/services/aiden-remote-approved-roots.test.ts b/main/services/aiden-remote-approved-roots.test.ts new file mode 100644 index 00000000..de54f436 --- /dev/null +++ b/main/services/aiden-remote-approved-roots.test.ts @@ -0,0 +1,73 @@ +import assert from "node:assert/strict"; +import * as fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { AidenRemoteApprovedRootService } from "./aiden-remote-approved-roots.js"; +import { AidenRemoteStateRegistry, createDefaultAidenRemoteState } from "./aiden-remote-state.js"; + +async function fixture() { + const directory = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-approved-root-")); + let document = createDefaultAidenRemoteState(); + const state = new AidenRemoteStateRegistry({ + load: async () => structuredClone(document), + save: async (next) => { document = structuredClone(next); }, + }); + const service = new AidenRemoteApprovedRootService(state, { + now: () => 12_345, + randomBytes: (size) => Buffer.alloc(size, 3), + homeDirectory: () => directory, + }); + return { directory, state, service }; +} + +test("approved roots canonicalize symlinks and persist filesystem identity without exposing it remotely", async () => { + const app = await fixture(); + try { + const folder = path.join(app.directory, "Workspace"); + const alias = path.join(app.directory, "Alias"); + await fs.mkdir(folder); + await fs.symlink(folder, alias); + const root = await app.service.addLocalFolder(alias); + assert.equal(root.folderPath, await fs.realpath(folder)); + assert.equal(root.label, "Workspace"); + assert.match(root.device, /^\d+$/u); + assert.match(root.inode, /^\d+$/u); + assert.equal(root.policyRevision, "remote-browser-v1:no-hidden-system"); + } finally { + await fs.rm(app.directory, { force: true, recursive: true }); + } +}); + +test("approved roots reject duplicates, nested overlap, files, and filesystem root", async () => { + const app = await fixture(); + try { + const parent = path.join(app.directory, "parent"); + const child = path.join(parent, "child"); + const file = path.join(app.directory, "file.txt"); + await fs.mkdir(child, { recursive: true }); + await fs.writeFile(file, "x"); + await app.service.addLocalFolder(parent); + await assert.rejects(app.service.addLocalFolder(parent), /already covered/u); + await assert.rejects(app.service.addLocalFolder(child), /already covered/u); + await assert.rejects(app.service.addLocalFolder(file), /must be a directory/u); + await assert.rejects(app.service.addLocalFolder(path.parse(app.directory).root), /filesystem root/u); + } finally { + await fs.rm(app.directory, { force: true, recursive: true }); + } +}); + +test("approving an entire home folder requires a separate local confirmation", async () => { + const app = await fixture(); + try { + await assert.rejects( + app.service.addLocalFolder(app.directory), + /requires local confirmation/u, + ); + const root = await app.service.addLocalFolder(app.directory, { confirmHomeDirectory: true }); + assert.equal(root.folderPath, await fs.realpath(app.directory)); + assert.equal(await app.service.removeLocalRoot(root.id), true); + } finally { + await fs.rm(app.directory, { force: true, recursive: true }); + } +}); diff --git a/main/services/aiden-remote-approved-roots.ts b/main/services/aiden-remote-approved-roots.ts new file mode 100644 index 00000000..193b63f9 --- /dev/null +++ b/main/services/aiden-remote-approved-roots.ts @@ -0,0 +1,79 @@ +import { randomBytes } from "node:crypto"; +import * as fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import type { + AidenRemoteApprovedRoot, + AidenRemoteStateRegistry, +} from "./aiden-remote-state.js"; + +export const AIDEN_REMOTE_ROOT_POLICY_REVISION = "remote-browser-v1:no-hidden-system"; + +export interface AidenRemoteApprovedRootDependencies { + now(): number; + randomBytes(size: number): Buffer; + homeDirectory(): string; +} + +function isWithin(parent: string, candidate: string): boolean { + const relative = path.relative(parent, candidate); + return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative)); +} + +function displayLabel(folderPath: string): string { + return path.basename(folderPath) || folderPath; +} + +export class AidenRemoteApprovedRootService { + constructor( + private readonly state: Pick, + private readonly dependencies: AidenRemoteApprovedRootDependencies = { + now: Date.now, + randomBytes, + homeDirectory: os.homedir, + }, + ) {} + + async addLocalFolder( + selectedPath: string, + options: { confirmHomeDirectory?: boolean } = {}, + ): Promise { + if (!path.isAbsolute(selectedPath) || Buffer.byteLength(selectedPath, "utf8") > 4_096) { + throw new Error("Select an absolute local folder."); + } + const canonicalPath = await fs.realpath(selectedPath); + const metadata = await fs.stat(canonicalPath, { bigint: true }); + if (!metadata.isDirectory()) throw new Error("The approved root must be a directory."); + if (canonicalPath === path.parse(canonicalPath).root) { + throw new Error("The filesystem root cannot be approved for remote browsing."); + } + const canonicalHome = await fs.realpath(this.dependencies.homeDirectory()); + if (canonicalPath === canonicalHome && options.confirmHomeDirectory !== true) { + throw new Error("Approving the entire home directory requires local confirmation."); + } + + const existing = (await this.state.snapshot()).approvedRoots; + if (existing.some((root) => isWithin(root.folderPath, canonicalPath))) { + throw new Error("This folder is already covered by an approved root."); + } + if (existing.some((root) => isWithin(canonicalPath, root.folderPath))) { + throw new Error("This folder would overlap an existing approved root."); + } + + const root: AidenRemoteApprovedRoot = { + id: `root_${this.dependencies.randomBytes(24).toString("base64url")}`, + label: displayLabel(canonicalPath), + folderPath: canonicalPath, + device: metadata.dev.toString(), + inode: metadata.ino.toString(), + policyRevision: AIDEN_REMOTE_ROOT_POLICY_REVISION, + createdAt: this.dependencies.now(), + }; + await this.state.addApprovedRoot(root); + return root; + } + + async removeLocalRoot(rootId: string): Promise { + return this.state.removeApprovedRoot(rootId); + } +} diff --git a/main/services/aiden-remote-attachments.ts b/main/services/aiden-remote-attachments.ts new file mode 100644 index 00000000..3c2671dc --- /dev/null +++ b/main/services/aiden-remote-attachments.ts @@ -0,0 +1,373 @@ +import { randomBytes } from "node:crypto"; +import { + attachmentInlineBytes, + attachmentRepresentationBytes, + parseAttachments, +} from "./attachment-contract.js"; +import { + MAX_IMAGE_BYTES, + MAX_TEXT_CHARS, +} from "./attachments.js"; +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; +import type { Attachment } from "./types.js"; +import { MAX_ATTACHMENT_INLINE_BYTES } from "../../renderer/shared/attachment-contract.js"; + +export const MAX_AIDEN_REMOTE_ATTACHMENTS_PER_TURN = 10; +export const MAX_AIDEN_REMOTE_ATTACHMENT_REQUEST_BYTES = 12 * 1_048_576; +export const AIDEN_REMOTE_ATTACHMENT_TTL_MS = 10 * 60 * 1_000; +const MAX_PENDING_ATTACHMENTS = 256; +const MAX_PENDING_ATTACHMENTS_PER_DEVICE = 40; +const MAX_PENDING_ATTACHMENTS_PER_CHAT = 20; +const MAX_PENDING_REPRESENTATION_BYTES = 64 * 1_048_576; +const MAX_REMOTE_IMAGE_DIMENSION = 16_384; +const MAX_REMOTE_IMAGE_PIXELS = 40_000_000; +const ATTACHMENT_ID = /^att_[A-Za-z0-9_-]{43}$/u; +const TEXT_MIME_TYPES = new Set([ + "text/plain", + "text/markdown", + "text/csv", + "application/json", + "application/xml", + "application/yaml", + "application/x-yaml", + "application/javascript", + "application/typescript", +]); + +export interface AidenRemoteAttachmentProjection { + id: string; + name: string; + mimeType: string; + kind: "image" | "text"; + size: number; + expiresAt: string; +} + +interface PendingAttachmentRecord { + id: string; + deviceId: string; + chatId: string; + attachment: Attachment; + expiresAt: number; + representationBytes: number; +} + +function ownRecord(value: unknown): Record | null { + return value !== null && typeof value === "object" && !Array.isArray(value) + ? (value as Record) + : null; +} + +function exactKeys(record: Record, expected: ReadonlySet): boolean { + let count = 0; + for (const key in record) { + if (!Object.prototype.hasOwnProperty.call(record, key)) continue; + count += 1; + if (count > expected.size || !expected.has(key)) return false; + } + return count === expected.size; +} + +function validDisplayName(value: unknown): value is string { + if (typeof value !== "string") return false; + const cleaned = value.trim(); + if (!cleaned || Array.from(cleaned).length > 255 || cleaned.includes("/") || cleaned.includes("\\")) { + return false; + } + for (let index = 0; index < cleaned.length; index += 1) { + const code = cleaned.charCodeAt(index); + if (code <= 0x1f || code === 0x7f) return false; + } + return true; +} + +function readUInt16BE(bytes: Buffer, offset: number): number | undefined { + return offset >= 0 && offset + 2 <= bytes.length ? bytes.readUInt16BE(offset) : undefined; +} + +function readUInt32BE(bytes: Buffer, offset: number): number | undefined { + return offset >= 0 && offset + 4 <= bytes.length ? bytes.readUInt32BE(offset) : undefined; +} + +function jpegDimensions(bytes: Buffer): [number, number] | undefined { + if (bytes.length < 4 || bytes[0] !== 0xff || bytes[1] !== 0xd8) return undefined; + let offset = 2; + while (offset + 4 <= bytes.length) { + while (offset < bytes.length && bytes[offset] === 0xff) offset += 1; + if (offset >= bytes.length) return undefined; + const marker = bytes[offset]!; + offset += 1; + if (marker === 0xd9 || marker === 0xda) return undefined; + if (marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) continue; + const segmentLength = readUInt16BE(bytes, offset); + if (segmentLength === undefined || segmentLength < 2 || offset + segmentLength > bytes.length) { + return undefined; + } + const isStartOfFrame = + (marker >= 0xc0 && marker <= 0xc3) || + (marker >= 0xc5 && marker <= 0xc7) || + (marker >= 0xc9 && marker <= 0xcb) || + (marker >= 0xcd && marker <= 0xcf); + if (isStartOfFrame) { + if (segmentLength < 7) return undefined; + const height = readUInt16BE(bytes, offset + 3); + const width = readUInt16BE(bytes, offset + 5); + return width && height ? [width, height] : undefined; + } + offset += segmentLength; + } + return undefined; +} + +function imageDimensions(bytes: Buffer, mimeType: string): [number, number] | undefined { + if (mimeType === "image/png") { + if ( + bytes.length < 24 || + bytes.subarray(0, 8).toString("hex") !== "89504e470d0a1a0a" || + bytes.subarray(12, 16).toString("ascii") !== "IHDR" + ) { + return undefined; + } + const width = readUInt32BE(bytes, 16); + const height = readUInt32BE(bytes, 20); + return width && height ? [width, height] : undefined; + } + if (mimeType === "image/jpeg") return jpegDimensions(bytes); + return undefined; +} + +function validateDimensions(bytes: Buffer, mimeType: string): void { + const dimensions = imageDimensions(bytes, mimeType); + if ( + !dimensions || + dimensions[0] > MAX_REMOTE_IMAGE_DIMENSION || + dimensions[1] > MAX_REMOTE_IMAGE_DIMENSION || + dimensions[0] * dimensions[1] > MAX_REMOTE_IMAGE_PIXELS + ) { + throw new AidenRemoteServiceError( + "invalid_request", + "The image dimensions are invalid or too large.", + 400, + ); + } +} + +function parseUpload(input: unknown, id: string): Attachment { + const record = ownRecord(input); + if (!record || !validDisplayName(record.name) || typeof record.kind !== "string") { + throw new AidenRemoteServiceError("invalid_request", "The attachment upload is invalid.", 400); + } + const name = record.name.trim(); + try { + if (record.kind === "image") { + if ( + !exactKeys(record, new Set(["name", "mimeType", "kind", "data"])) || + (record.mimeType !== "image/png" && record.mimeType !== "image/jpeg") || + typeof record.data !== "string" || + record.data.length === 0 || + record.data.length > Math.ceil(MAX_IMAGE_BYTES / 3) * 4 + ) { + throw new Error("invalid image envelope"); + } + const bytes = Buffer.from(record.data, "base64"); + const attachment = parseAttachments([{ + id, + name, + mimeType: record.mimeType, + kind: "image", + size: bytes.length, + data: record.data, + }])?.[0]; + if (!attachment) throw new Error("invalid image attachment"); + validateDimensions(bytes, attachment.mimeType); + return attachment; + } + + if ( + record.kind !== "text" || + !exactKeys(record, new Set(["name", "mimeType", "kind", "text"])) || + typeof record.mimeType !== "string" || + !TEXT_MIME_TYPES.has(record.mimeType.toLowerCase()) || + typeof record.text !== "string" || + Array.from(record.text).length > MAX_TEXT_CHARS || + Buffer.byteLength(record.text, "utf8") > MAX_TEXT_CHARS * 4 + ) { + throw new Error("invalid text envelope"); + } + const attachment = parseAttachments([{ + id, + name, + mimeType: record.mimeType.toLowerCase(), + kind: "text", + size: Buffer.byteLength(record.text, "utf8"), + text: record.text, + }])?.[0]; + if (!attachment) throw new Error("invalid text attachment"); + return attachment; + } catch (error) { + if (error instanceof AidenRemoteServiceError) throw error; + throw new AidenRemoteServiceError( + "invalid_request", + "The attachment data does not match its declared type or limits.", + 400, + ); + } +} + +function projection(record: PendingAttachmentRecord): AidenRemoteAttachmentProjection { + return { + id: record.id, + name: record.attachment.name, + mimeType: record.attachment.mimeType, + kind: record.attachment.kind, + size: record.attachment.size, + expiresAt: new Date(record.expiresAt).toISOString(), + }; +} + +export class AidenRemoteAttachmentStore { + private readonly records = new Map(); + private retainedRepresentationBytes = 0; + + constructor(private readonly options: { + now?: () => number; + randomId?: () => string; + maxEntries?: number; + maxRepresentationBytes?: number; + } = {}) {} + + upload(deviceId: string, chatId: string, input: unknown): AidenRemoteAttachmentProjection { + const now = this.now(); + this.prune(now); + const id = this.nextId(); + const attachment = parseUpload(input, id); + const representationBytes = attachmentRepresentationBytes([attachment]); + const maxEntries = this.options.maxEntries ?? MAX_PENDING_ATTACHMENTS; + const maxRepresentationBytes = + this.options.maxRepresentationBytes ?? MAX_PENDING_REPRESENTATION_BYTES; + let deviceCount = 0; + let chatCount = 0; + for (const record of this.records.values()) { + if (record.deviceId === deviceId) deviceCount += 1; + if (record.deviceId === deviceId && record.chatId === chatId) chatCount += 1; + } + if ( + this.records.size >= maxEntries || + deviceCount >= MAX_PENDING_ATTACHMENTS_PER_DEVICE || + chatCount >= MAX_PENDING_ATTACHMENTS_PER_CHAT || + representationBytes > maxRepresentationBytes - this.retainedRepresentationBytes + ) { + throw new AidenRemoteServiceError( + "handle_capacity", + "Aiden's temporary attachment capacity is full. Try again shortly.", + 429, + true, + ); + } + const record: PendingAttachmentRecord = { + id, + deviceId, + chatId, + attachment, + expiresAt: now + AIDEN_REMOTE_ATTACHMENT_TTL_MS, + representationBytes, + }; + this.records.set(id, record); + this.retainedRepresentationBytes += representationBytes; + return projection(record); + } + + consume(deviceId: string, chatId: string, input: unknown): Attachment[] | undefined { + if (input === undefined) return undefined; + if ( + !Array.isArray(input) || + input.length === 0 || + input.length > MAX_AIDEN_REMOTE_ATTACHMENTS_PER_TURN + ) { + throw new AidenRemoteServiceError("invalid_request", "The attachment references are invalid.", 400); + } + const ids: string[] = []; + const unique = new Set(); + for (const candidate of input) { + if (typeof candidate !== "string" || !ATTACHMENT_ID.test(candidate) || unique.has(candidate)) { + throw new AidenRemoteServiceError("invalid_request", "The attachment references are invalid.", 400); + } + unique.add(candidate); + ids.push(candidate); + } + + const now = this.now(); + const selected: PendingAttachmentRecord[] = []; + for (const id of ids) { + const record = this.records.get(id); + if (!record) { + throw new AidenRemoteServiceError("handle_invalid", "That attachment is no longer available.", 409); + } + if (record.expiresAt <= now) { + this.removeRecord(record); + throw new AidenRemoteServiceError("handle_expired", "That attachment expired. Attach it again.", 409); + } + if (record.deviceId !== deviceId) { + throw new AidenRemoteServiceError("handle_wrong_device", "That attachment belongs to another device.", 403); + } + if (record.chatId !== chatId) { + throw new AidenRemoteServiceError("handle_invalid", "That attachment belongs to another chat.", 409); + } + selected.push(record); + } + const attachments = selected.map((record) => structuredClone(record.attachment)); + if (attachmentInlineBytes(attachments) > MAX_ATTACHMENT_INLINE_BYTES) { + throw new AidenRemoteServiceError("payload_too_large", "The attachments exceed the turn limit.", 413); + } + for (const record of selected) this.removeRecord(record); + this.prune(now); + return attachments; + } + + remove(deviceId: string, chatId: string, id: string): void { + if (!ATTACHMENT_ID.test(id)) { + throw new AidenRemoteServiceError("invalid_request", "The attachment reference is invalid.", 400); + } + const record = this.records.get(id); + if (!record) return; + if (record.deviceId !== deviceId) { + throw new AidenRemoteServiceError("handle_wrong_device", "That attachment belongs to another device.", 403); + } + if (record.chatId !== chatId) { + throw new AidenRemoteServiceError("handle_invalid", "That attachment belongs to another chat.", 409); + } + this.removeRecord(record); + } + + revokeDevice(deviceId: string): void { + for (const record of [...this.records.values()]) { + if (record.deviceId === deviceId) this.removeRecord(record); + } + } + + private now(): number { + return this.options.now?.() ?? Date.now(); + } + + private nextId(): string { + const id = this.options.randomId?.() ?? `att_${randomBytes(32).toString("base64url")}`; + if (!ATTACHMENT_ID.test(id) || this.records.has(id)) { + throw new AidenRemoteServiceError("internal_error", "Aiden could not allocate an attachment reference.", 500); + } + return id; + } + + private prune(now: number): void { + for (const record of [...this.records.values()]) { + if (record.expiresAt <= now) this.removeRecord(record); + } + } + + private removeRecord(record: PendingAttachmentRecord): void { + if (!this.records.delete(record.id)) return; + this.retainedRepresentationBytes = Math.max( + 0, + this.retainedRepresentationBytes - record.representationBytes, + ); + } +} diff --git a/main/services/aiden-remote-chat-http.test.ts b/main/services/aiden-remote-chat-http.test.ts new file mode 100644 index 00000000..b0bd13b1 --- /dev/null +++ b/main/services/aiden-remote-chat-http.test.ts @@ -0,0 +1,239 @@ +import assert from "node:assert/strict"; +import { createServer } from "node:http"; +import test from "node:test"; +import type { Chat, ChatMessage } from "./types.js"; +import { AidenRemoteChatService } from "./aiden-remote-chats.js"; +import { createAidenRemoteRequestHandler } from "./aiden-remote-router.js"; +import { AidenRemoteStreamService } from "./aiden-remote-streams.js"; + +test("HTTP client resumes, approves, denies, and cancels device-owned mocked turns without duplicate messages", async () => { + let chat: Chat = { + id: "chat-1", + title: "Remote chat", + workspaceId: "workspace-1", + providerId: "provider-1", + model: "model-1", + createdAt: 1_000, + updatedAt: 2_000, + messages: [], + }; + let turnNumber = 0; + let appendCount = 0; + const ownerByStream = new Map(); + const approvalToStream = new Map(); + + const streams = new AidenRemoteStreamService({ + now: Date.now, + cancel: (streamId) => { + ownerByStream.get(streamId)?.send("chat:done", { streamId, content: "" }); + return true; + }, + approve: (approvalId, decision) => { + const streamId = approvalToStream.get(approvalId); + const owner = streamId ? ownerByStream.get(streamId) : undefined; + if (!streamId || !owner) return false; + const assistant: ChatMessage = { + id: `assistant-${turnNumber}`, + role: "assistant", + content: decision === "allow" ? "Allowed" : "Denied", + createdAt: Date.now(), + }; + chat.messages.push(assistant); + chat.updatedAt += 1; + owner.send("chat:delta", { streamId, delta: assistant.content }); + owner.send("chat:done", { streamId, chat: structuredClone(chat) }); + return true; + }, + }); + const chats = new AidenRemoteChatService({ + application: { + list: async () => [structuredClone(chat)], + get: async () => ({ chat: structuredClone(chat), reconciliation: null }), + create: async () => structuredClone(chat), + rename: async (_id, title, options) => { + await options?.assertCurrent?.(chat); + chat.title = title; + chat.updatedAt += 1; + return structuredClone(chat); + }, + moveEmptyToWorkspace: async () => structuredClone(chat), + remove: async () => undefined, + }, + chatStore: { + get: async () => structuredClone(chat), + appendMessage: async (_id, message, meta) => { + if (!meta?.isCurrent?.()) throw new Error("turn expired"); + appendCount += 1; + chat.messages.push({ + id: message.id!, + role: message.role, + content: message.content, + createdAt: Date.now(), + ...(message.attachments ? { attachments: structuredClone(message.attachments) } : {}), + }); + chat.updatedAt += 1; + return structuredClone(chat); + }, + }, + generation: { + beginChatTurn: () => ({ + isActive: () => true, + reserveAppendPayload: () => undefined, + settleAsyncWork: () => undefined, + onReleased: () => undefined, + release: () => undefined, + }), + start: async (streamId, params, owner, options) => { + turnNumber += 1; + ownerByStream.set(streamId, owner); + options.onTurnAccepted(); + owner.send("chat:delta", { streamId, delta: "Working" }); + if ( + !params.messages.length && + !chat.messages[chat.messages.length - 1]?.content.includes("Cancel") + ) { + const approvalId = `approval-${turnNumber}`; + approvalToStream.set(approvalId, streamId); + owner.send("chat:approval", { + streamId, + approvalId, + summary: "Change the workspace", + }); + } + return true; + }, + }, + streams, + models: { + resolve: async () => ({ providerId: "provider-1", modelId: "model-1", thinkingLevels: [] }), + }, + }); + + const handler = createAidenRemoteRequestHandler({ + instanceId: "instance-1", + appVersion: "0.30.0", + devices: { + authenticate: async (credential) => credential === "a".repeat(43) + ? { + id: "device-1", + revoked: false, + capabilities: new Set(["chat:read", "chat:write", "approval:respond"] as const), + } + : null, + }, + pairing: { exchange: async () => { throw new Error("unused"); } }, + chats, + streams, + models: { list: async () => ({ providers: [], defaults: {} }) }, + connectionMode: () => "lan", + now: Date.now, + log: () => undefined, + }); + const server = createServer(handler); + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", resolve); + }); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("server did not bind"); + const base = `http://127.0.0.1:${address.port}/api/aiden/v1`; + const headers = { + authorization: `Bearer ${"a".repeat(43)}`, + "aiden-protocol-version": "1", + }; + const start = async (text: string, key: string, attachmentIds?: string[]) => { + const response = await fetch(`${base}/chats/chat-1/turns`, { + method: "POST", + headers: { ...headers, "content-type": "application/json", "idempotency-key": key }, + body: JSON.stringify({ text, ...(attachmentIds ? { attachmentIds } : {}) }), + }); + assert.equal(response.status, 202); + return response.json() as Promise<{ + streamId: string; + message: { id: string; attachments?: { id: string; name: string }[] }; + }>; + }; + + try { + const uploadedResponse = await fetch(`${base}/chats/chat-1/attachments`, { + method: "POST", + headers: { ...headers, "content-type": "application/json" }, + body: JSON.stringify({ + name: "proof.txt", + mimeType: "text/plain", + kind: "text", + text: "HTTP attachment contents", + }), + }); + assert.equal(uploadedResponse.status, 201); + const uploaded = await uploadedResponse.json() as { id: string; name: string }; + assert.match(uploaded.id, /^att_[A-Za-z0-9_-]{43}$/u); + assert.equal(uploaded.name, "proof.txt"); + assert.equal(JSON.stringify(uploaded).includes("contents"), false); + + const discardedResponse = await fetch(`${base}/chats/chat-1/attachments`, { + method: "POST", + headers: { ...headers, "content-type": "application/json" }, + body: JSON.stringify({ + name: "discard.txt", + mimeType: "text/plain", + kind: "text", + text: "discard me", + }), + }); + assert.equal(discardedResponse.status, 201); + const discarded = await discardedResponse.json() as { id: string }; + const removed = await fetch(`${base}/chats/chat-1/attachments/${discarded.id}`, { + method: "DELETE", + headers, + }); + assert.equal(removed.status, 204); + + const first = await start("Approve this", "turn-http-key-000001", [uploaded.id]); + const replay = await start("Approve this", "turn-http-key-000001", [uploaded.id]); + assert.deepEqual(replay, first); + assert.deepEqual(first.message.attachments?.map(({ id, name }) => ({ id, name })), [ + { id: uploaded.id, name: "proof.txt" }, + ]); + assert.equal(appendCount, 1); + const waiting = await fetch(`${base}/streams/${first.streamId}`, { headers }); + assert.equal((await waiting.json()).state, "waiting_for_approval"); + + const allowed = await fetch(`${base}/approvals/approval-1/respond`, { + method: "POST", + headers: { ...headers, "content-type": "application/json", "idempotency-key": "allow-http-key-000001" }, + body: JSON.stringify({ decision: "allow" }), + }); + assert.equal(allowed.status, 200); + const firstEvents = await fetch(`${base}/streams/${first.streamId}/events?after=1`, { headers }); + const firstReplay = await firstEvents.text(); + assert.match(firstReplay, /event: approval_required/u); + assert.match(firstReplay, /event: done/u); + const eventIds = [...firstReplay.matchAll(/^id: (\d+)$/gmu)].map((match) => Number(match[1])); + const lastEventId = eventIds[eventIds.length - 1]!; + const terminalReplay = await fetch(`${base}/streams/${first.streamId}/events`, { + headers: { ...headers, "last-event-id": String(lastEventId - 1) }, + }); + assert.match(await terminalReplay.text(), /event: done/u); + + const second = await start("Deny this", "turn-http-key-000002"); + const denied = await fetch(`${base}/approvals/approval-2/respond`, { + method: "POST", + headers: { ...headers, "content-type": "application/json", "idempotency-key": "deny-http-key-000001" }, + body: JSON.stringify({ decision: "deny" }), + }); + assert.equal(denied.status, 200); + assert.equal(streams.status("device-1", second.streamId).state, "done"); + + const third = await start("Cancel this", "turn-http-key-000003"); + const cancelled = await fetch(`${base}/streams/${third.streamId}/cancel`, { + method: "POST", + headers: { ...headers, "idempotency-key": "cancel-http-key-0001" }, + }); + assert.equal(cancelled.status, 202); + assert.equal(streams.status("device-1", third.streamId).state, "cancelled"); + assert.equal(appendCount, 3); + } finally { + await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve())); + } +}); diff --git a/main/services/aiden-remote-chats.test.ts b/main/services/aiden-remote-chats.test.ts new file mode 100644 index 00000000..716dc2a4 --- /dev/null +++ b/main/services/aiden-remote-chats.test.ts @@ -0,0 +1,370 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; +import type { Chat, ChatMessage } from "./types.js"; +import { AidenRemoteChatService, projectAidenRemoteChat } from "./aiden-remote-chats.js"; +import { AidenRemoteStreamService } from "./aiden-remote-streams.js"; +import { + AIDEN_REMOTE_ATTACHMENT_TTL_MS, + AidenRemoteAttachmentStore, +} from "./aiden-remote-attachments.js"; + +const ONE_PIXEL_PNG = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIHWP4z8DwHwAFgAI/ScL2aQAAAABJRU5ErkJggg=="; + +function chat(overrides: Partial = {}): Chat { + return { + id: "chat-1", + title: "New chat", + workspaceId: "workspace-1", + providerId: "provider-1", + model: "model-1", + createdAt: 1_000, + updatedAt: 2_000, + messages: [], + ...overrides, + }; +} + +function fixture( + initial = chat(), + fixtureOptions: { startThrows?: boolean; attachments?: AidenRemoteAttachmentStore } = {}, +) { + let current: Chat | null = structuredClone(initial); + let creates = 0; + let appends = 0; + let notifications = 0; + const streams = new AidenRemoteStreamService({ + now: () => 10_000, + cancel: () => true, + approve: () => true, + }); + const service = new AidenRemoteChatService({ + application: { + list: async () => current ? [structuredClone(current)] : [], + get: async () => ({ chat: current ? structuredClone(current) : null, reconciliation: null }), + create: async (input) => { + creates += 1; + current = chat({ + id: `chat-${creates + 1}`, + workspaceId: input.workspaceId, + providerId: input.providerId, + model: input.model, + }); + return structuredClone(current); + }, + rename: async (_id, title, options) => { + if (!current) throw new Error("missing"); + await options?.assertCurrent?.(current); + current.title = title; + current.updatedAt += 1; + return structuredClone(current); + }, + moveEmptyToWorkspace: async (_id, workspaceId, options) => { + if (!current) throw new Error("missing"); + await options?.assertCurrent?.(current); + if (current.messages.length) throw new Error("not empty"); + current.workspaceId = workspaceId; + current.updatedAt += 1; + return structuredClone(current); + }, + remove: async (_id, options) => { + if (!current) throw new Error("missing"); + await options?.assertCurrent?.(current); + current = null; + }, + }, + chatStore: { + get: async () => current ? structuredClone(current) : null, + appendMessage: async (_id, message, meta) => { + if (!current || !meta?.isCurrent?.()) throw new Error("stale"); + appends += 1; + const stored: ChatMessage = { + id: message.id!, + role: message.role, + content: message.content, + createdAt: 3_000, + ...(message.attachments ? { attachments: structuredClone(message.attachments) } : {}), + }; + current.messages.push(stored); + current.providerId = meta.providerId; + current.model = meta.model; + current.updatedAt += 1; + return structuredClone(current); + }, + }, + generation: { + beginChatTurn: (_chatId, _turnId, _ownerId) => { + let active = true; + return { + isActive: () => active, + reserveAppendPayload: () => undefined, + settleAsyncWork: () => undefined, + onReleased: () => undefined, + release: () => { active = false; }, + }; + }, + start: async (streamId, _params, owner, generationOptions) => { + if (fixtureOptions.startThrows) throw new Error("provider setup failed"); + generationOptions.onTurnAccepted(); + owner.send("chat:delta", { streamId, delta: "Answer" }); + const assistant: ChatMessage = { + id: "assistant-1", + role: "assistant", + content: "Answer", + createdAt: 4_000, + reasoning: "private-safe-reasoning", + pi: { role: "assistant", content: [], api: "openai-responses", provider: "openai", model: "model-1", usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } }, stopReason: "stop", timestamp: 4_000 }, + }; + current?.messages.push(assistant); + owner.send("chat:done", { streamId, chat: current }); + return true; + }, + }, + streams, + models: { + resolve: async () => ({ providerId: "provider-1", modelId: "model-1", thinkingLevels: ["low", "high"] }), + }, + ...(fixtureOptions.attachments ? { attachments: fixtureOptions.attachments } : {}), + notifyChanged: () => { notifications += 1; }, + }); + return { + service, + streams, + creates: () => creates, + appends: () => appends, + notifications: () => notifications, + current: () => current ? structuredClone(current) : null, + }; +} + +test("chat projection is path-free and excludes private Pi protocol and reasoning", () => { + const projection = projectAidenRemoteChat(chat({ + messages: [{ + id: "user-1", + role: "user", + content: "Hello", + reasoning: "hidden", + createdAt: 2_000, + attachments: [{ + id: "/Users/private/attachment-id", + name: "/Users/private/notes.txt", + mimeType: "text/plain", + kind: "text", + size: 5, + text: "hello", + }], + }], + })); + assert.deepEqual(projection.messages[0], { + id: "user-1", + role: "user", + text: "Hello", + createdAt: new Date(2_000).toISOString(), + attachments: [{ + id: `legacy_${createHash("sha256").update("/Users/private/attachment-id").digest("base64url")}`, + name: "notes.txt", + mimeType: "text/plain", + kind: "text", + size: 5, + }], + }); + assert.equal(JSON.stringify(projection).includes("reasoning"), false); + assert.equal(JSON.stringify(projection).includes("/Users/private"), false); + assert.match(projection.revision, /^rev_[A-Za-z0-9_-]{43}$/u); +}); + +test("chat create is device-scoped idempotent and CRUD checks exact revisions", async () => { + const app = fixture(); + const key = "chat-create-key-00001"; + const created = await app.service.create("device-1", key, { workspaceId: "workspace-1" }); + assert.deepEqual(await app.service.create("device-1", key, { workspaceId: "workspace-1" }), created); + assert.equal(app.creates(), 1); + await assert.rejects( + app.service.rename(created.id, "rev_stale", { title: "Changed" }), + (error: unknown) => (error as { code?: string }).code === "revision_conflict", + ); + const renamed = await app.service.rename(created.id, created.revision, { title: "Changed" }); + assert.equal(renamed.title, "Changed"); + await app.service.remove(created.id, renamed.revision); + assert.equal(app.notifications(), 3); +}); + +test("remote turn atomically appends once, owns its stream, and replays the accepted response", async () => { + const app = fixture(); + const key = "turn-start-key-000001"; + const first = await app.service.startTurn("device-1", "chat-1", key, { text: "Hello" }); + const replay = await app.service.startTurn("device-1", "chat-1", key, { text: "Hello" }); + assert.deepEqual(replay, first); + assert.equal(app.appends(), 1); + assert.equal(first.message.text, "Hello"); + const status = app.streams.status("device-1", first.streamId); + assert.equal(status.state, "done"); + assert.throws( + () => app.streams.status("device-2", first.streamId), + (error: unknown) => (error as { code?: string }).code === "not_found", + ); +}); + +test("a provider setup failure after append returns the one accepted message with a terminal error stream", async () => { + const app = fixture(chat(), { startThrows: true }); + const accepted = await app.service.startTurn( + "device-1", + "chat-1", + "turn-failure-key-0001", + { text: "Keep this once" }, + ); + assert.equal(accepted.status, "accepted"); + assert.equal(accepted.message.text, "Keep this once"); + assert.equal(app.appends(), 1); + assert.equal(app.streams.status("device-1", accepted.streamId).state, "error"); + assert.deepEqual( + await app.service.startTurn("device-1", "chat-1", "turn-failure-key-0001", { text: "Keep this once" }), + accepted, + ); + assert.equal(app.appends(), 1); +}); + +test("remote attachments are one-use, bounded, and projected without inline contents", async () => { + let sequence = 0; + const attachments = new AidenRemoteAttachmentStore({ + now: () => 10_000, + randomId: () => `att_${String(sequence++).padStart(43, "A")}`, + }); + const app = fixture(chat(), { attachments }); + const image = await app.service.uploadAttachment("device-1", "chat-1", { + name: "diagram.png", + mimeType: "image/png", + kind: "image", + data: ONE_PIXEL_PNG, + }); + const text = await app.service.uploadAttachment("device-1", "chat-1", { + name: "notes.md", + mimeType: "text/markdown", + kind: "text", + text: "# Notes", + }); + + const accepted = await app.service.startTurn( + "device-1", + "chat-1", + "turn-attachments-0001", + { text: "", attachmentIds: [image.id, text.id] }, + ); + assert.deepEqual(accepted.message.attachments, [ + { id: image.id, name: "diagram.png", mimeType: "image/png", kind: "image", size: 70 }, + { id: text.id, name: "notes.md", mimeType: "text/markdown", kind: "text", size: 7 }, + ]); + const serialized = JSON.stringify(accepted.message); + assert.equal(serialized.includes(ONE_PIXEL_PNG), false); + assert.equal(serialized.includes("# Notes"), false); + assert.deepEqual(app.current()?.messages[0]?.attachments?.map(({ name, kind }) => ({ name, kind })), [ + { name: "diagram.png", kind: "image" }, + { name: "notes.md", kind: "text" }, + ]); + await assert.rejects( + app.service.startTurn("device-1", "chat-1", "turn-attachments-0002", { + text: "Do not reuse", + attachmentIds: [image.id], + }), + (error: unknown) => (error as { code?: string }).code === "handle_invalid", + ); +}); + +test("attachment references enforce device, chat, expiry, revocation, dimensions, and capacity", () => { + let now = 20_000; + let sequence = 0; + const store = new AidenRemoteAttachmentStore({ + now: () => now, + randomId: () => `att_${String(sequence++).padStart(43, "B")}`, + maxEntries: 2, + }); + const first = store.upload("device-1", "chat-1", { + name: "one.txt", + mimeType: "text/plain", + kind: "text", + text: "one", + }); + assert.throws( + () => store.consume("device-2", "chat-1", [first.id]), + (error: unknown) => (error as { code?: string }).code === "handle_wrong_device", + ); + assert.throws( + () => store.consume("device-1", "chat-2", [first.id]), + (error: unknown) => (error as { code?: string }).code === "handle_invalid", + ); + assert.throws( + () => store.consume("device-1", "chat-1", [first.id, first.id]), + (error: unknown) => (error as { code?: string }).code === "invalid_request", + ); + assert.throws( + () => store.consume( + "device-1", + "chat-1", + Array.from({ length: 11 }, (_, index) => `att_${String(index).padStart(43, "D")}`), + ), + (error: unknown) => (error as { code?: string }).code === "invalid_request", + ); + now += AIDEN_REMOTE_ATTACHMENT_TTL_MS; + assert.throws( + () => store.consume("device-1", "chat-1", [first.id]), + (error: unknown) => (error as { code?: string }).code === "handle_expired", + ); + + const revoked = store.upload("device-1", "chat-1", { + name: "revoked.txt", + mimeType: "text/plain", + kind: "text", + text: "private", + }); + store.revokeDevice("device-1"); + assert.throws( + () => store.consume("device-1", "chat-1", [revoked.id]), + (error: unknown) => (error as { code?: string }).code === "handle_invalid", + ); + + const oversizedDimensions = Buffer.alloc(24); + Buffer.from("89504e470d0a1a0a", "hex").copy(oversizedDimensions, 0); + Buffer.from("IHDR", "ascii").copy(oversizedDimensions, 12); + oversizedDimensions.writeUInt32BE(20_000, 16); + oversizedDimensions.writeUInt32BE(1, 20); + assert.throws( + () => store.upload("device-1", "chat-1", { + name: "huge.png", + mimeType: "image/png", + kind: "image", + data: oversizedDimensions.toString("base64"), + }), + (error: unknown) => (error as { code?: string }).code === "invalid_request", + ); + assert.throws( + () => store.upload("device-1", "chat-1", { + name: "../secret.txt", + mimeType: "text/plain", + kind: "text", + text: "secret", + }), + (error: unknown) => (error as { code?: string }).code === "invalid_request", + ); + + const capacity = new AidenRemoteAttachmentStore({ + now: () => now, + randomId: () => `att_${String(sequence++).padStart(43, "C")}`, + maxEntries: 1, + }); + capacity.upload("device-1", "chat-1", { + name: "first.txt", + mimeType: "text/plain", + kind: "text", + text: "first", + }); + assert.throws( + () => capacity.upload("device-1", "chat-1", { + name: "second.txt", + mimeType: "text/plain", + kind: "text", + text: "second", + }), + (error: unknown) => (error as { code?: string }).code === "handle_capacity", + ); +}); diff --git a/main/services/aiden-remote-chats.ts b/main/services/aiden-remote-chats.ts new file mode 100644 index 00000000..e00e02ea --- /dev/null +++ b/main/services/aiden-remote-chats.ts @@ -0,0 +1,631 @@ +import { createHash, randomUUID } from "node:crypto"; +import { persistedChatWorkspaceId } from "../../renderer/shared/chat-workspace.js"; +import { isGenerationThinkingLevel } from "../../renderer/shared/generation-thinking.js"; +import { + appendChatMessageWithReconciliation, + isAppendReconciliationRequiredError, +} from "./chat-append-commit.js"; +import type { createChatApplicationService } from "./chat-application-service.js"; +import type { ChatGenerationOwner } from "./chat-generation-owner.js"; +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; +import { + AidenIdempotencyLedger, + type AidenIdempotencySnapshot, + AidenOperationContractError, + AidenOperationUnknownOutcomeError, + assertRevision, +} from "./aiden-remote-operation-contract.js"; +import type { AidenRemoteModelService } from "./aiden-remote-models.js"; +import type { AidenRemoteStreamService } from "./aiden-remote-streams.js"; +import { + AidenRemoteAttachmentStore, + MAX_AIDEN_REMOTE_ATTACHMENTS_PER_TURN, + type AidenRemoteAttachmentProjection, +} from "./aiden-remote-attachments.js"; +import { + attachmentRepresentationBytes, + safeStoredAttachments, +} from "./attachment-contract.js"; +import type { Chat, ChatMessage, ChatStartParams } from "./types.js"; + +const SAFE_ID = /^[A-Za-z0-9._:-]{1,128}$/u; +const IDEMPOTENCY_KEY = /^[\x21-\x7e]{16,128}$/u; +type ChatApplicationService = ReturnType; + +export interface AidenRemoteMessageProjection { + id: string; + role: "user" | "assistant"; + text: string; + createdAt: string; + attachments?: AidenRemoteMessageAttachmentProjection[]; +} + +export interface AidenRemoteMessageAttachmentProjection { + id: string; + name: string; + mimeType: string; + kind: "image" | "text"; + size: number; +} + +export interface AidenRemoteChatProjection { + id: string; + workspaceId: string; + title: string; + providerId?: string; + modelId?: string; + messages: AidenRemoteMessageProjection[]; + createdAt: string; + updatedAt: string; + revision: string; +} + +function ownRecord(value: unknown): Record | null { + return value !== null && typeof value === "object" && !Array.isArray(value) + ? (value as Record) + : null; +} + +function exactKeys(record: Record, required: string[], optional: string[] = []): boolean { + const allowed = new Set([...required, ...optional]); + return required.every((key) => Object.prototype.hasOwnProperty.call(record, key)) + && Object.keys(record).every((key) => allowed.has(key)); +} + +function boundedString(value: unknown, maximum: number): value is string { + return typeof value === "string" && value.length > 0 && Array.from(value).length <= maximum; +} + +function safeId(value: string, label: string): string { + if (!SAFE_ID.test(value)) { + throw new AidenRemoteServiceError("invalid_request", `The ${label} identifier is invalid.`, 400); + } + return value; +} + +function safeAttachmentDisplayName(value: string): string { + const segments = value.replace(/\\/gu, "/").split("/"); + const leaf = segments[segments.length - 1] ?? ""; + const cleaned = Array.from(leaf) + .filter((character) => { + const code = character.charCodeAt(0); + return code > 0x1f && code !== 0x7f; + }) + .slice(0, 255) + .join("") + .trim(); + return cleaned || "Attachment"; +} + +function projectMessageAttachments(value: unknown): AidenRemoteMessageAttachmentProjection[] { + return (safeStoredAttachments(value) ?? []).map((attachment) => ({ + id: /^[A-Za-z0-9._:-]{1,256}$/u.test(attachment.id) + ? attachment.id + : `legacy_${createHash("sha256").update(attachment.id).digest("base64url")}`, + name: safeAttachmentDisplayName(attachment.name), + mimeType: /^[\x21-\x7e]{1,120}$/u.test(attachment.mimeType) + ? attachment.mimeType + : attachment.kind === "image" ? "image/unknown" : "text/plain", + kind: attachment.kind, + size: attachment.size, + })); +} + +function chatRevision(chat: Chat): string { + const visible = { + id: chat.id, + workspaceId: persistedChatWorkspaceId(chat.workspaceId), + title: chat.title, + providerId: chat.providerId ?? null, + model: chat.model ?? null, + createdAt: chat.createdAt, + updatedAt: chat.updatedAt, + messages: chat.messages + .filter((message) => message.role === "user" || message.role === "assistant") + .map((message) => ({ + id: message.id, + role: message.role, + content: message.content, + createdAt: message.createdAt, + attachments: projectMessageAttachments(message.attachments), + })), + }; + return `rev_${createHash("sha256").update(JSON.stringify(visible)).digest("base64url")}`; +} + +export function projectAidenRemoteChat(chat: Chat): AidenRemoteChatProjection { + return { + id: chat.id, + workspaceId: persistedChatWorkspaceId(chat.workspaceId), + title: chat.title.slice(0, 1_024), + ...(chat.providerId ? { providerId: chat.providerId } : {}), + ...(chat.model ? { modelId: chat.model } : {}), + messages: chat.messages.flatMap((message) => { + if (message.role !== "user" && message.role !== "assistant") return []; + const attachments = projectMessageAttachments(message.attachments); + return [{ + id: message.id, + role: message.role, + text: message.content.slice(0, 200_000), + createdAt: new Date(message.createdAt).toISOString(), + ...(attachments.length > 0 ? { attachments } : {}), + }]; + }), + createdAt: new Date(chat.createdAt).toISOString(), + updatedAt: new Date(chat.updatedAt).toISOString(), + revision: chatRevision(chat), + }; +} + +function parseCreate(input: unknown): { workspaceId: string; providerId?: string; model?: string } { + const record = ownRecord(input); + if ( + !record || + !exactKeys(record, ["workspaceId"], ["providerId", "modelId"]) || + !boundedString(record.workspaceId, 128) || + (record.providerId !== undefined && !boundedString(record.providerId, 256)) || + (record.modelId !== undefined && !boundedString(record.modelId, 256)) + ) { + throw new AidenRemoteServiceError("invalid_request", "The chat creation request is invalid.", 400); + } + return { + workspaceId: safeId(record.workspaceId, "workspace"), + ...(typeof record.providerId === "string" ? { providerId: record.providerId } : {}), + ...(typeof record.modelId === "string" ? { model: record.modelId } : {}), + }; +} + +function parseTitle(input: unknown): string { + const record = ownRecord(input); + if (!record || !exactKeys(record, ["title"]) || !boundedString(record.title, 200)) { + throw new AidenRemoteServiceError("invalid_request", "The chat title is invalid.", 400); + } + const title = record.title.trim(); + if (!title) { + throw new AidenRemoteServiceError("invalid_request", "The chat title is invalid.", 400); + } + return title; +} + +function parseMove(input: unknown): { workspaceId: string; confirmedForeground: true } { + const record = ownRecord(input); + if ( + !record || + !exactKeys(record, ["workspaceId", "confirmedForeground"]) || + !boundedString(record.workspaceId, 128) || + record.confirmedForeground !== true + ) { + throw new AidenRemoteServiceError("invalid_request", "The chat move request is invalid.", 400); + } + return { workspaceId: safeId(record.workspaceId, "workspace"), confirmedForeground: true }; +} + +function parseTurn(input: unknown): { + text: string; + providerId?: string; + modelId?: string; + thinkingLevel?: ChatStartParams["thinkingLevel"]; + attachmentIds?: string[]; +} { + const record = ownRecord(input); + const attachmentIds = record?.attachmentIds; + const structurallyValidAttachmentIds = + attachmentIds === undefined || + (Array.isArray(attachmentIds) && + attachmentIds.length > 0 && + attachmentIds.length <= MAX_AIDEN_REMOTE_ATTACHMENTS_PER_TURN && + attachmentIds.every((value) => typeof value === "string")); + if ( + !record || + !exactKeys(record, ["text"], ["providerId", "modelId", "thinkingLevel", "attachmentIds"]) || + typeof record.text !== "string" || + Array.from(record.text).length > 200_000 || + (!record.text.trim() && (!Array.isArray(attachmentIds) || attachmentIds.length === 0)) || + (record.providerId !== undefined && !boundedString(record.providerId, 256)) || + (record.modelId !== undefined && !boundedString(record.modelId, 256)) || + (record.thinkingLevel !== undefined && !isGenerationThinkingLevel(record.thinkingLevel)) || + !structurallyValidAttachmentIds + ) { + throw new AidenRemoteServiceError( + "invalid_request", + "The turn must contain bounded text or valid attachment references.", + 400, + ); + } + return { + text: record.text, + ...(typeof record.providerId === "string" ? { providerId: record.providerId } : {}), + ...(typeof record.modelId === "string" ? { modelId: record.modelId } : {}), + ...(isGenerationThinkingLevel(record.thinkingLevel) + ? { thinkingLevel: record.thinkingLevel } + : {}), + ...(Array.isArray(attachmentIds) ? { attachmentIds: [...attachmentIds] as string[] } : {}), + }; +} + +function ephemeralOwner(deviceId: string, operationId: string): ChatGenerationOwner { + let destroyed = false; + const listeners = new Set<() => void>(); + return { + id: 0, + documentId: `remote-chat:${createHash("sha256").update(`${deviceId}:${operationId}`).digest("base64url")}`, + isDestroyed: () => destroyed, + send: () => undefined, + onInvalidated: (listener) => { + if (destroyed) listener(); + else listeners.add(listener); + return () => listeners.delete(listener); + }, + }; +} + +function requireRevision(expected: string, chat: Chat): void { + try { + assertRevision(expected, chatRevision(chat)); + } catch { + throw new AidenRemoteServiceError( + "revision_conflict", + "The chat changed. Refresh it before trying again.", + 409, + false, + { currentRevision: chatRevision(chat) }, + ); + } +} + +export class AidenRemoteChatService { + private readonly idempotency: AidenIdempotencyLedger; + private readonly attachments: AidenRemoteAttachmentStore; + + constructor( + private readonly options: { + application: Pick; + chatStore: { + get(id: string): Promise; + appendMessage( + id: string, + message: Omit & { id?: string; createdAt?: number }, + meta?: { + providerId?: string; + model?: string; + expectedWorkspaceId?: string; + isCurrent?: () => boolean; + }, + ): Promise; + }; + generation: { + beginChatTurn(chatId: string, turnId: string, ownerId: string): { + isActive(): boolean; + reserveAppendPayload(bytes: number): void; + settleAsyncWork(): void; + onReleased(cleanup: () => void): void; + release(): void; + } | null; + start( + streamId: string, + params: ChatStartParams, + owner: ChatGenerationOwner, + options: { + allowSubagents: boolean; + allowComputerUse: false; + usageSource: "chat"; + turnId: string; + onTurnAccepted(): void; + }, + ): Promise; + }; + streams: AidenRemoteStreamService; + models: Pick; + attachments?: AidenRemoteAttachmentStore; + idempotency?: AidenIdempotencyLedger; + persistIdempotency?: (snapshot: AidenIdempotencySnapshot) => Promise; + notifyChanged?: (chatId?: string) => void; + }, + ) { + this.idempotency = options.idempotency ?? new AidenIdempotencyLedger(); + this.attachments = options.attachments ?? new AidenRemoteAttachmentStore(); + } + + private async executeIdempotent( + scope: { deviceId: string; route: string; resourceId: string; key: string }, + input: unknown, + action: () => Promise, + ): Promise { + if (!IDEMPOTENCY_KEY.test(scope.key)) { + throw new AidenRemoteServiceError("invalid_request", "Idempotency-Key is invalid.", 400); + } + if (!this.options.persistIdempotency) return this.idempotency.execute(scope, input, action); + let admit!: () => void; + let reject!: (error: unknown) => void; + const durable = new Promise((resolve, rejectPromise) => { + admit = resolve; + reject = rejectPromise; + }); + const pending = this.idempotency.execute(scope, input, async () => { + await durable; + return action(); + }); + try { + await this.options.persistIdempotency(this.idempotency.snapshot()); + admit(); + } catch (error) { + reject(error); + await pending.catch(() => undefined); + throw new AidenRemoteServiceError("internal_error", "Aiden could not prepare this chat request.", 500); + } + let result: T | undefined; + let failure: unknown; + try { + result = await pending; + } catch (error) { + failure = error; + } + try { + await this.options.persistIdempotency(this.idempotency.snapshot()); + } catch { + throw new AidenRemoteServiceError( + "idempotency_in_flight", + "The chat request may have completed, but its outcome could not be recorded.", + 409, + ); + } + if (failure) throw failure; + return result!; + } + + private async chat(chatId: string): Promise { + const result = await this.options.application.get(safeId(chatId, "chat")); + if (!result.chat) { + throw new AidenRemoteServiceError("not_found", "This Aiden chat no longer exists.", 404); + } + if (result.reconciliation) { + throw new AidenRemoteServiceError("operation_in_progress", "This chat is still reconciling.", 409, true); + } + return result.chat; + } + + async list(workspaceId?: string): Promise<{ chats: AidenRemoteChatProjection[] }> { + if (workspaceId) safeId(workspaceId, "workspace"); + const metadata = await this.options.application.list(workspaceId); + const chats = await Promise.all(metadata.map((entry) => this.chat(entry.id))); + return { chats: chats.map(projectAidenRemoteChat) }; + } + + async get(chatId: string): Promise { + return projectAidenRemoteChat(await this.chat(chatId)); + } + + async uploadAttachment( + deviceId: string, + chatId: string, + input: unknown, + ): Promise { + await this.chat(chatId); + return this.attachments.upload(deviceId, safeId(chatId, "chat"), input); + } + + async removeAttachment(deviceId: string, chatId: string, attachmentId: string): Promise { + await this.chat(chatId); + this.attachments.remove(deviceId, safeId(chatId, "chat"), attachmentId); + } + + revokeDevice(deviceId: string): void { + this.attachments.revokeDevice(deviceId); + } + + async create(deviceId: string, key: string, input: unknown): Promise { + const parsed = parseCreate(input); + try { + return await this.executeIdempotent( + { deviceId, route: "POST /chats", resourceId: "chat-registry", key }, + parsed, + async () => { + const owner = ephemeralOwner(deviceId, key); + const selection = parsed.providerId || parsed.model + ? await this.options.models.resolve(parsed.providerId, parsed.model) + : undefined; + const created = await this.options.application.create( + { + workspaceId: parsed.workspaceId, + ...(selection + ? { providerId: selection.providerId, model: selection.modelId } + : {}), + }, + owner, + ); + if (!created) throw new AidenRemoteServiceError("internal_error", "Aiden could not create the chat.", 500); + this.options.notifyChanged?.(created.id); + return projectAidenRemoteChat(created); + }, + ); + } catch (error) { + return this.mapOperationError(error); + } + } + + async rename(chatId: string, revision: string, input: unknown): Promise { + const title = parseTitle(input); + const updated = await this.options.application.rename(safeId(chatId, "chat"), title, { + assertCurrent: (chat) => requireRevision(revision, chat), + }); + this.options.notifyChanged?.(chatId); + return projectAidenRemoteChat(updated); + } + + async move( + deviceId: string, + chatId: string, + revision: string, + key: string, + input: unknown, + ): Promise { + const parsed = parseMove(input); + try { + return await this.executeIdempotent( + { deviceId, route: "POST /chats/{id}/move", resourceId: safeId(chatId, "chat"), key }, + { revision, ...parsed }, + async () => { + const moved = await this.options.application.moveEmptyToWorkspace(chatId, parsed.workspaceId, { + assertCurrent: (chat) => requireRevision(revision, chat), + }); + this.options.notifyChanged?.(chatId); + return projectAidenRemoteChat(moved!); + }, + ); + } catch (error) { + return this.mapOperationError(error); + } + } + + async remove(chatId: string, revision: string): Promise { + await this.options.application.remove(safeId(chatId, "chat"), { + assertCurrent: (chat) => requireRevision(revision, chat), + }); + this.options.notifyChanged?.(chatId); + } + + async startTurn( + deviceId: string, + chatId: string, + key: string, + input: unknown, + ): Promise<{ + turnId: string; + streamId: string; + status: "accepted"; + message: AidenRemoteMessageProjection; + }> { + const parsed = parseTurn(input); + try { + return await this.executeIdempotent( + { deviceId, route: "POST /chats/{id}/turns", resourceId: safeId(chatId, "chat"), key }, + parsed, + async () => { + const authoritative = await this.chat(chatId); + const selection = await this.options.models.resolve( + parsed.providerId ?? authoritative.providerId, + parsed.modelId ?? authoritative.model, + ); + if ( + parsed.thinkingLevel && + selection.thinkingLevels.length > 0 && + !selection.thinkingLevels.includes(parsed.thinkingLevel) + ) { + throw new AidenRemoteServiceError("invalid_request", "That thinking level is unavailable.", 400); + } + const turnId = `turn_${randomUUID()}`; + const streamId = `stream_${randomUUID()}`; + const owner = this.options.streams.create(deviceId, streamId, chatId, turnId); + const turn = this.options.generation.beginChatTurn(chatId, turnId, owner.owner.documentId); + if (!turn) { + owner.invalidate(); + this.options.streams.markStartError(deviceId, streamId, new Error("This chat already has a response in progress.")); + throw new AidenRemoteServiceError("turn_already_active", "This chat already has a response in progress.", 409); + } + const attachments = this.attachments.consume(deviceId, chatId, parsed.attachmentIds); + turn.onReleased(owner.owner.onInvalidated(turn.release)); + turn.reserveAppendPayload( + Buffer.byteLength(parsed.text, "utf8") + + attachmentRepresentationBytes(attachments) + + 1_024, + ); + const messageId = `message_${randomUUID()}`; + let appended = false; + let accepted = false; + let appendedChat: Chat | undefined; + try { + const workspaceId = persistedChatWorkspaceId(authoritative.workspaceId); + const chat = await appendChatMessageWithReconciliation({ + messageId, + append: () => this.options.chatStore.appendMessage( + chatId, + { + id: messageId, + role: "user", + content: parsed.text, + ...(attachments?.length ? { attachments } : {}), + }, + { + providerId: selection.providerId, + model: selection.modelId, + expectedWorkspaceId: workspaceId, + isCurrent: turn.isActive, + }, + ), + recover: () => this.options.chatStore.get(chatId), + }); + appended = true; + appendedChat = chat; + turn.settleAsyncWork(); + const started = await this.options.generation.start( + streamId, + { + chatId, + workspaceId, + providerId: selection.providerId, + model: selection.modelId, + ...(parsed.thinkingLevel ? { thinkingLevel: parsed.thinkingLevel } : {}), + messages: [], + }, + owner.owner, + { + allowSubagents: true, + allowComputerUse: false, + usageSource: "chat", + turnId, + onTurnAccepted: () => { + accepted = true; + this.options.streams.markRunning(deviceId, streamId); + }, + }, + ); + if (!started && !accepted) { + this.options.streams.markStartError( + deviceId, + streamId, + new Error("Generation stopped before it could begin."), + ); + } + this.options.notifyChanged?.(chatId); + const message = chat.messages.find((candidate) => candidate.id === messageId)!; + return { + turnId, + streamId, + status: "accepted" as const, + message: projectAidenRemoteChat({ ...chat, messages: [message] }).messages[0]!, + }; + } catch (error) { + if (!appended) turn.release(); + turn.settleAsyncWork(); + this.options.streams.markStartError(deviceId, streamId, error); + if (isAppendReconciliationRequiredError(error)) { + throw new AidenOperationUnknownOutcomeError(); + } + if (appended && appendedChat) { + this.options.notifyChanged?.(chatId); + const message = appendedChat.messages.find((candidate) => candidate.id === messageId)!; + return { + turnId, + streamId, + status: "accepted" as const, + message: projectAidenRemoteChat({ ...appendedChat, messages: [message] }).messages[0]!, + }; + } + throw error; + } + }, + ); + } catch (error) { + return this.mapOperationError(error); + } + } + + private mapOperationError(error: unknown): never { + if (error instanceof AidenRemoteServiceError) throw error; + if (error instanceof AidenOperationContractError) { + const status = error.code === "idempotency_capacity" ? 429 : 409; + throw new AidenRemoteServiceError(error.code, "This chat request cannot be safely repeated.", status); + } + throw error; + } +} diff --git a/main/services/aiden-remote-errors.ts b/main/services/aiden-remote-errors.ts new file mode 100644 index 00000000..73d40818 --- /dev/null +++ b/main/services/aiden-remote-errors.ts @@ -0,0 +1,30 @@ +import type { AidenRemoteErrorCode } from "./aiden-remote-protocol.js"; + +export class AidenRemoteServiceError extends Error { + constructor( + readonly code: AidenRemoteErrorCode, + message: string, + readonly status: number, + readonly retryable = false, + readonly details?: { + currentRevision?: string; + retryAfterSeconds?: number; + chatId?: string; + minimumClientVersion?: string; + limit?: number; + field?: string; + }, + ) { + super(message); + this.name = "AidenRemoteServiceError"; + } +} + +export function asAidenRemoteServiceError(error: unknown): AidenRemoteServiceError { + if (error instanceof AidenRemoteServiceError) return error; + return new AidenRemoteServiceError( + "internal_error", + "Aiden could not complete this remote request.", + 500, + ); +} diff --git a/main/services/aiden-remote-files.test.ts b/main/services/aiden-remote-files.test.ts new file mode 100644 index 00000000..933ac3b5 --- /dev/null +++ b/main/services/aiden-remote-files.test.ts @@ -0,0 +1,112 @@ +import assert from "node:assert/strict"; +import * as fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; +import { AidenRemoteFileService } from "./aiden-remote-files.js"; +import { AidenOpaqueHandleStore } from "./aiden-remote-opaque-handles.js"; +import { AidenRemoteWorkspaceOwnerRegistry } from "./aiden-remote-workspace-owners.js"; +import type { Workspace } from "./types.js"; +import { createWorkspaceEnvironmentApplicationService } from "./workspace-environment-application-service.js"; +import { WorkspaceMutationGate } from "./workspace-mutation-gate.js"; +import { WorkspaceOperationRegistry } from "./workspace-operation-registry.js"; + +test("remote Files uses device/workspace-bound opaque handles and version-safe writes", async () => { + const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-remote-files-")); + const root = path.join(temporary, "workspace"); + const outside = path.join(temporary, "outside.txt"); + await fs.mkdir(path.join(root, "Sources"), { recursive: true }); + await fs.writeFile(path.join(root, "Sources", "App.swift"), "let value = 1\n", "utf8"); + await fs.writeFile(outside, "private\n", "utf8"); + const workspace: Workspace = { + id: "workspace-1", + name: "Project", + folderPath: root, + permission: "ask", + createdAt: 1, + updatedAt: 2, + }; + const workspaces = new Map([[workspace.id, workspace]]); + const application = createWorkspaceEnvironmentApplicationService({ + configStore: { getWorkspace: async (id) => workspaces.get(id) }, + workspaceMutationGate: new WorkspaceMutationGate(), + workspaceOperationRegistry: new WorkspaceOperationRegistry(), + assertManagedWorktreeAdmission: async () => undefined, + realpath: fs.realpath, + stat: fs.stat, + }); + const handles = new AidenOpaqueHandleStore(); + const owners = new AidenRemoteWorkspaceOwnerRegistry(); + const service = new AidenRemoteFileService({ + instanceId: "instance-1", + application, + owners, + handles, + }); + + try { + const index = await service.list("device-1", workspace.id); + assert.equal(index.maxEntries, 4_000); + assert.equal(index.maxDepth, 20); + assert.equal(index.truncated, false); + const file = index.entries.find((entry) => entry.displayPath === "Sources/App.swift"); + assert.ok(file); + assert.match(file.id, /^file_[A-Za-z0-9_-]{43}$/u); + assert.equal(file.language, "Swift"); + assert.equal(JSON.stringify(index).includes(root), false); + assert.equal(handles.storedTokenMaterialForTesting().some((value) => value.includes("App.swift")), false); + + const first = await service.read("device-1", workspace.id, file.id); + assert.equal(first.content, "let value = 1\n"); + assert.equal(first.displayPath, "Sources/App.swift"); + + await assert.rejects( + () => service.read("device-2", workspace.id, file.id), + (error: unknown) => error instanceof AidenRemoteServiceError && error.code === "handle_wrong_device", + ); + workspaces.set("workspace-2", { ...workspace, id: "workspace-2" }); + await assert.rejects( + () => service.read("device-1", "workspace-2", file.id), + (error: unknown) => error instanceof AidenRemoteServiceError && error.code === "handle_wrong_device", + ); + + await fs.writeFile(path.join(root, "Sources", "App.swift"), "let value = 2\n", "utf8"); + await assert.rejects( + () => service.write("device-1", workspace.id, file.id, { + content: "let value = 3\n", + expectedVersion: first.version, + }), + (error: unknown) => error instanceof AidenRemoteServiceError && error.code === "revision_conflict", + ); + + const refreshed = await service.read("device-1", workspace.id, file.id); + const saved = await service.write("device-1", workspace.id, file.id, { + content: "let value = 3\n", + expectedVersion: refreshed.version, + }); + assert.equal(saved.content, "let value = 3\n"); + assert.equal(await fs.readFile(path.join(root, "Sources", "App.swift"), "utf8"), "let value = 3\n"); + + await fs.rm(path.join(root, "Sources", "App.swift")); + await fs.symlink(outside, path.join(root, "Sources", "App.swift")); + await assert.rejects( + () => service.read("device-1", workspace.id, file.id), + (error: unknown) => error instanceof AidenRemoteServiceError && error.code === "path_outside_root", + ); + } finally { + await fs.rm(temporary, { recursive: true, force: true }); + } +}); + +test("remote workspace owners survive disconnect-shaped reuse and revoke active ownership", () => { + const registry = new AidenRemoteWorkspaceOwnerRegistry(); + const first = registry.owner("device-1"); + assert.equal(first, registry.owner("device-1")); + let invalidations = 0; + first.onInvalidated(() => { invalidations += 1; }); + registry.revokeDevice("device-1"); + assert.equal(first.isDestroyed(), true); + assert.equal(invalidations, 1); + assert.notEqual(first, registry.owner("device-1")); +}); diff --git a/main/services/aiden-remote-files.ts b/main/services/aiden-remote-files.ts new file mode 100644 index 00000000..8e9de63e --- /dev/null +++ b/main/services/aiden-remote-files.ts @@ -0,0 +1,336 @@ +import { randomBytes } from "node:crypto"; +import path from "node:path"; +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; +import { + AidenOpaqueHandleError, + AidenOpaqueHandleStore, + inspectAidenFilesystemIdentity, + type AidenOpaqueHandleClaims, +} from "./aiden-remote-opaque-handles.js"; +import { projectAidenRemoteWorkspace } from "./aiden-remote-workspaces.js"; +import type { AidenRemoteWorkspaceOwnerRegistry } from "./aiden-remote-workspace-owners.js"; +import type { WorkspaceEnvironmentApplicationService } from "./workspace-environment-application-service.js"; +import { + listWorkspaceFiles, + readWorkspaceFile, + WorkspaceFileError, + writeWorkspaceFile, + type WorkspaceFileDocument, + type WorkspaceFileEntry, +} from "./workspace-files.js"; + +const FILE_HANDLE_TTL_MS = 10 * 60_000; +const MAX_DISPLAY_PATH_LENGTH = 4_096; +const MAX_WIRE_FILE_SIZE = 5 * 1_048_576; + +export interface AidenRemoteFileEntry { + id: string; + displayPath: string; + name: string; + kind: "file" | "directory" | "symlink"; + size?: number; + language?: string; +} + +export interface AidenRemoteFileIndex { + snapshotId: string; + entries: AidenRemoteFileEntry[]; + truncated: boolean; + maxEntries: 4_000; + maxDepth: 20; +} + +export interface AidenRemoteFileDocument { + id: string; + displayPath: string; + content: string; + version: string; + truncated: false; + warning?: string; +} + +function languageFor(entry: WorkspaceFileEntry): string | undefined { + if (entry.kind !== "file") return undefined; + const extension = path.extname(entry.name).slice(1).toLowerCase(); + const names: Record = { + c: "C", cc: "C++", cpp: "C++", css: "CSS", go: "Go", h: "C Header", + html: "HTML", java: "Java", js: "JavaScript", json: "JSON", jsx: "JSX", + kt: "Kotlin", md: "Markdown", mjs: "JavaScript", py: "Python", rb: "Ruby", + rs: "Rust", sh: "Shell", swift: "Swift", ts: "TypeScript", tsx: "TSX", + yaml: "YAML", yml: "YAML", + }; + return names[extension]; +} + +function safeDisplayPath(value: string): string { + if ( + !value || + value.length > MAX_DISPLAY_PATH_LENGTH || + path.isAbsolute(value) || + value.split("/").some((part) => part === "..") || + [...value].some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 0x1f || code === 0x7f; + }) + ) { + throw new AidenRemoteServiceError( + "workspace_unavailable", + "A workspace file could not be projected safely.", + 409, + ); + } + return value; +} + +function mapHandleError(error: unknown): never { + if (!(error instanceof AidenOpaqueHandleError)) throw error; + const status = error.code === "handle_wrong_device" + ? 403 + : error.code === "handle_expired" + ? 410 + : error.code === "handle_capacity" + ? 429 + : error.code === "root_policy_changed" || + error.code === "filesystem_identity_changed" || + error.code === "path_outside_root" + ? 409 + : 400; + throw new AidenRemoteServiceError( + error.code, + error.code === "handle_expired" + ? "This file link expired. Refresh Files and try again." + : error.code === "handle_capacity" + ? "Aiden's file-handle capacity is temporarily full." + : "This file link is no longer valid. Refresh Files and try again.", + status, + error.code === "handle_capacity", + ); +} + +function parseWrite(value: unknown): { content: string; expectedVersion: string } { + const record = value && typeof value === "object" && !Array.isArray(value) + ? value as Record + : null; + if ( + !record || + Object.keys(record).length !== 2 || + typeof record.content !== "string" || + Buffer.byteLength(record.content, "utf8") > 1_500_000 || + typeof record.expectedVersion !== "string" || + !/^[0-9a-f]{64}$/u.test(record.expectedVersion) + ) { + throw new AidenRemoteServiceError("invalid_request", "The file save request is invalid.", 400); + } + return { content: record.content, expectedVersion: record.expectedVersion }; +} + +function projectedDocument( + fileId: string, + displayPath: string, + document: WorkspaceFileDocument, +): AidenRemoteFileDocument { + return { + id: fileId, + displayPath, + content: document.content, + version: document.version, + truncated: false, + ...(document.warning ? { warning: [...document.warning].slice(0, 500).join("") } : {}), + }; +} + +export class AidenRemoteFileService { + private handleStore: AidenOpaqueHandleStore | undefined; + private readonly now = (): number => this.options.now?.() ?? Date.now(); + + constructor( + private readonly options: { + instanceId: string; + application: Pick; + owners: Pick; + handles?: AidenOpaqueHandleStore; + now?: () => number; + }, + ) {} + + private get handles(): AidenOpaqueHandleStore { + this.handleStore ??= this.options.handles ?? new AidenOpaqueHandleStore({ now: this.now }); + return this.handleStore; + } + + private async claims( + deviceId: string, + workspaceId: string, + folderPath: string, + workspaceRevision: string, + displayPath: string, + snapshotId: string, + ): Promise { + const identity = await inspectAidenFilesystemIdentity( + folderPath, + path.join(folderPath, displayPath), + ); + return { + instanceId: this.options.instanceId, + deviceId, + workspaceId, + rootId: workspaceId, + policyRevision: workspaceRevision, + ...identity, + displayPath, + snapshotId, + expiresAt: this.now() + FILE_HANDLE_TTL_MS, + }; + } + + async list(deviceId: string, workspaceId: string): Promise { + return this.options.application.run( + this.options.owners.owner(deviceId), + workspaceId, + async ({ folderPath, workspace }, signal) => { + const index = await listWorkspaceFiles(folderPath, signal); + const snapshotId = `files_${randomBytes(24).toString("base64url")}`; + const revision = projectAidenRemoteWorkspace(workspace).revision; + const entries: AidenRemoteFileEntry[] = []; + let omitted = false; + for (const entry of index.entries) { + if (signal.aborted) throw new Error("The workspace operation was cancelled."); + try { + const displayPath = safeDisplayPath(entry.path); + const claims = await this.claims( + deviceId, + workspaceId, + folderPath, + revision, + displayPath, + snapshotId, + ); + entries.push({ + id: this.handles.issue("file", claims), + displayPath, + name: [...entry.name].slice(0, 255).join(""), + kind: entry.kind, + ...(entry.size !== undefined && entry.size <= MAX_WIRE_FILE_SIZE + ? { size: entry.size } + : {}), + ...(languageFor(entry) ? { language: languageFor(entry)! } : {}), + }); + } catch (error) { + if (error instanceof AidenOpaqueHandleError && error.code === "handle_capacity") { + mapHandleError(error); + } + omitted = true; + } + } + const projection: AidenRemoteFileIndex = { + snapshotId, + entries, + truncated: index.truncated || omitted, + maxEntries: 4_000, + maxDepth: 20, + }; + return projection; + }, + ).catch((error: unknown) => { + if (error instanceof AidenRemoteServiceError) throw error; + throw new AidenRemoteServiceError( + "workspace_unavailable", + "This workspace's files are not currently available on the Mac.", + 409, + ); + }); + } + + private async withResolvedFile( + deviceId: string, + workspaceId: string, + fileId: string, + operation: ( + input: { folderPath: string; displayPath: string; signal: AbortSignal }, + ) => Promise, + ): Promise { + let stored: AidenOpaqueHandleClaims; + try { + stored = this.handles.claimsFor(fileId, "file"); + } catch (error) { + mapHandleError(error); + } + try { + return await this.options.application.run( + this.options.owners.owner(deviceId), + workspaceId, + async ({ folderPath, workspace }, signal) => { + if (!stored.displayPath || stored.workspaceId !== workspaceId) { + throw new AidenOpaqueHandleError("handle_wrong_device"); + } + const current = await this.claims( + deviceId, + workspaceId, + folderPath, + projectAidenRemoteWorkspace(workspace).revision, + stored.displayPath, + stored.snapshotId ?? "", + ); + current.expiresAt = stored.expiresAt; + this.handles.resolve(fileId, "file", current); + return operation({ folderPath, displayPath: stored.displayPath, signal }); + }, + ); + } catch (error) { + if (error instanceof AidenOpaqueHandleError) mapHandleError(error); + throw error; + } + } + + read(deviceId: string, workspaceId: string, fileId: string): Promise { + return this.withResolvedFile(deviceId, workspaceId, fileId, async (input) => { + try { + return projectedDocument( + fileId, + input.displayPath, + await readWorkspaceFile(input.folderPath, input.displayPath, input.signal), + ); + } catch { + throw new AidenRemoteServiceError( + "workspace_unavailable", + "This file cannot currently be read as bounded UTF-8 text.", + 409, + ); + } + }); + } + + write( + deviceId: string, + workspaceId: string, + fileId: string, + value: unknown, + ): Promise { + const input = parseWrite(value); + return this.withResolvedFile(deviceId, workspaceId, fileId, async (resolved) => { + try { + const document = await writeWorkspaceFile( + resolved.folderPath, + resolved.displayPath, + input.content, + input.expectedVersion, + resolved.signal, + ); + return projectedDocument(fileId, resolved.displayPath, document); + } catch (error) { + if (error instanceof WorkspaceFileError && error.code === "changed_on_disk") { + throw new AidenRemoteServiceError( + "revision_conflict", + "This file changed on the Mac. Reload it before saving.", + 409, + ); + } + throw new AidenRemoteServiceError( + "workspace_unavailable", + "Aiden could not safely save this file on the Mac.", + 409, + ); + } + }); + } +} diff --git a/main/services/aiden-remote-git.test.ts b/main/services/aiden-remote-git.test.ts new file mode 100644 index 00000000..bbcb8eda --- /dev/null +++ b/main/services/aiden-remote-git.test.ts @@ -0,0 +1,285 @@ +import assert from "node:assert/strict"; +import { execFile } from "node:child_process"; +import * as fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { promisify } from "node:util"; +import test from "node:test"; +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; +import { AidenRemoteGitService } from "./aiden-remote-git.js"; +import { AidenRemoteWorkspaceOwnerRegistry } from "./aiden-remote-workspace-owners.js"; +import { + gitBranches, + gitCheckout, + gitCommit, + gitCompare, + gitComparisonDiff, + gitCreateBranch, + gitDiff, + gitPush, + gitPushCapability, + gitReview, + gitWorktrees, +} from "./git.js"; +import type { Workspace } from "./types.js"; +import { createWorkspaceEnvironmentApplicationService } from "./workspace-environment-application-service.js"; +import { workspaceRevision } from "./aiden-remote-workspaces.js"; +import { WorkspaceMutationGate } from "./workspace-mutation-gate.js"; +import { WorkspaceOperationRegistry } from "./workspace-operation-registry.js"; + +const runFile = promisify(execFile); + +async function git(cwd: string, ...args: string[]): Promise { + const result = await runFile("/usr/bin/git", args, { cwd, encoding: "utf8" }); + return result.stdout.trim(); +} + +test("remote Git keeps paths and snapshot internals on the Mac and safely completes reviewed mutations", async () => { + const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-remote-git-")); + const repository = path.join(temporary, "repository"); + const bare = path.join(temporary, "remote.git"); + await fs.mkdir(repository); + await git(repository, "init", "-b", "main"); + await git(repository, "config", "user.email", "aiden@example.test"); + await git(repository, "config", "user.name", "Aiden Test"); + await fs.writeFile(path.join(repository, "App.swift"), "let value = 1\n", "utf8"); + await git(repository, "add", "App.swift"); + await git(repository, "commit", "-m", "Initial"); + await runFile("/usr/bin/git", ["init", "--bare", bare], { encoding: "utf8" }); + await git(repository, "remote", "add", "origin", bare); + await git(repository, "push", "-u", "origin", "main"); + + const workspace: Workspace = { + id: "workspace-1", + name: "Project", + folderPath: repository, + permission: "ask", + createdAt: 1, + updatedAt: 2, + }; + const application = createWorkspaceEnvironmentApplicationService({ + configStore: { getWorkspace: async (id) => id === workspace.id ? workspace : undefined }, + workspaceMutationGate: new WorkspaceMutationGate(), + workspaceOperationRegistry: new WorkspaceOperationRegistry(), + assertManagedWorktreeAdmission: async () => undefined, + realpath: fs.realpath, + stat: fs.stat, + }); + const managedWorkspace: Workspace = { + id: "workspace-managed", + name: "Mobile Worktree", + folderPath: path.join(temporary, "managed"), + permission: "ask", + managedWorktree: { + repositoryPath: repository, + worktreePath: path.join(temporary, "managed"), + branch: "feature/worktree", + worktreeGitDir: path.join(repository, ".git", "worktrees", "managed"), + ownershipToken: "a".repeat(64), + worktreeDevice: 1, + worktreeInode: 2, + createdFromHead: "b".repeat(40), + }, + createdAt: 3, + updatedAt: 4, + }; + let createWorktreeCount = 0; + let deleteWorktreeCount = 0; + const service = new AidenRemoteGitService({ + application, + owners: new AidenRemoteWorkspaceOwnerRegistry(), + git: { + review: gitReview, + diff: gitDiff, + branches: gitBranches, + checkout: gitCheckout, + createBranch: gitCreateBranch, + commit: gitCommit, + pushCapability: gitPushCapability, + push: gitPush, + compare: gitCompare, + comparisonDiff: gitComparisonDiff, + worktrees: gitWorktrees, + }, + worktrees: { + create: async (_owner, sourceId, branch, name) => { + assert.equal(sourceId, workspace.id); + assert.equal(branch, "feature/worktree"); + assert.equal(name, "Mobile Worktree"); + createWorktreeCount += 1; + return managedWorkspace; + }, + remove: async (_owner, id, validate) => { + assert.equal(id, managedWorkspace.id); + validate?.(managedWorkspace); + deleteWorktreeCount += 1; + return { branchDeleted: true }; + }, + }, + listWorkspaces: async () => [workspace], + persistIdempotency: async () => undefined, + }); + + try { + await fs.writeFile(path.join(repository, "App.swift"), "let value = 2\n", "utf8"); + const review = await service.review("device-1", workspace.id); + assert.equal(review.status, "snapshot"); + assert.equal(review.result?.kind, "review"); + if (review.result?.kind !== "review" || !review.snapshotId) throw new Error("missing review"); + const changed = review.result.files.find((file) => file.displayPath === "App.swift"); + assert.ok(changed); + assert.match(changed.id, /^file_[A-Za-z0-9_-]{43}$/u); + assert.equal(JSON.stringify(review).includes(repository), false); + + const diff = await service.diff("device-1", workspace.id, { + snapshotId: review.snapshotId, + fileId: changed.id, + }); + assert.equal(diff.result?.kind, "diff"); + if (diff.result?.kind !== "diff") throw new Error("missing diff"); + assert.match(diff.result.diff, /value = 2/u); + assert.equal(diff.result.diff.includes(repository), false); + + await assert.rejects( + () => service.diff("device-2", workspace.id, { + snapshotId: review.snapshotId, + fileId: changed.id, + }), + (error: unknown) => error instanceof AidenRemoteServiceError && error.code === "handle_wrong_device", + ); + await assert.rejects( + () => service.commit("device-1", workspace.id, "commit-key-remote-0001", { + snapshotId: review.snapshotId, + message: "Update value", + scope: "all-reviewed", + confirmedForeground: false, + }), + (error: unknown) => error instanceof AidenRemoteServiceError && error.code === "permission_confirmation_required", + ); + + const commitInput = { + snapshotId: review.snapshotId, + message: "Update value", + scope: "all-reviewed", + confirmedForeground: true, + }; + const committed = await service.commit( + "device-1", + workspace.id, + "commit-key-remote-0002", + commitInput, + ); + const replayed = await service.commit( + "device-1", + workspace.id, + "commit-key-remote-0002", + commitInput, + ); + assert.deepEqual(replayed, committed); + assert.equal(await git(repository, "rev-list", "--count", "HEAD"), "2"); + + const branchSnapshot = await service.branches("device-1", workspace.id); + assert.ok(branchSnapshot.snapshotId); + const created = await service.createBranch( + "device-1", + workspace.id, + "branch-key-remote-0001", + { name: "feature/mobile", startPoint: "main", confirmedForeground: true }, + ); + assert.equal(created.status, "succeeded"); + assert.equal(await git(repository, "branch", "--show-current"), "feature/mobile"); + await assert.rejects( + () => service.checkout( + "device-1", + workspace.id, + "checkout-key-remote-1", + { branch: "main", snapshotId: branchSnapshot.snapshotId, confirmedForeground: true }, + ), + (error: unknown) => error instanceof AidenRemoteServiceError && error.code === "operation_stale", + ); + const freshBranches = await service.branches("device-1", workspace.id); + await service.checkout( + "device-1", + workspace.id, + "checkout-key-remote-2", + { branch: "main", snapshotId: freshBranches.snapshotId, confirmedForeground: true }, + ); + assert.equal(await git(repository, "branch", "--show-current"), "main"); + + const pushCapability = await service.pushCapability("device-1", workspace.id); + assert.equal(pushCapability.result?.kind, "push-capability"); + if (pushCapability.result?.kind !== "push-capability" || !pushCapability.snapshotId) { + throw new Error("missing push capability"); + } + assert.equal(pushCapability.result.allowed, true); + const pushed = await service.push( + "device-1", + workspace.id, + "push-key-remote-000001", + { + snapshotId: pushCapability.snapshotId, + remote: pushCapability.result.remote, + branch: pushCapability.result.branch, + confirmedForeground: true, + }, + ); + assert.equal(pushed.status, "succeeded"); + assert.equal(await git(repository, "rev-parse", "HEAD"), await git(repository, "rev-parse", "origin/main")); + + await assert.rejects( + () => service.createWorktree("device-1", workspace.id, "worktree-key-remote-0001", { + branch: "feature/worktree", + name: "Mobile Worktree", + confirmedForeground: false, + }), + (error: unknown) => error instanceof AidenRemoteServiceError && error.code === "permission_confirmation_required", + ); + const createInput = { + branch: "feature/worktree", + name: "Mobile Worktree", + confirmedForeground: true, + }; + const worktreeCreated = await service.createWorktree( + "device-1", + workspace.id, + "worktree-key-remote-0002", + createInput, + ); + const worktreeReplay = await service.createWorktree( + "device-1", + workspace.id, + "worktree-key-remote-0002", + createInput, + ); + assert.deepEqual(worktreeReplay, worktreeCreated); + assert.equal(createWorktreeCount, 1); + + await assert.rejects( + () => service.deleteManagedWorktree( + "device-1", + managedWorkspace.id, + "rev_wrong", + "worktree-delete-key-0001", + { confirmedForeground: true }, + ), + (error: unknown) => error instanceof AidenRemoteServiceError && error.code === "revision_conflict", + ); + const deleted = await service.deleteManagedWorktree( + "device-1", + managedWorkspace.id, + workspaceRevision(managedWorkspace), + "worktree-delete-key-0002", + { confirmedForeground: true }, + ); + assert.equal(deleted.result?.kind, "mutation"); + assert.equal(deleteWorktreeCount, 1); + + const worktrees = await service.worktrees("device-1", workspace.id); + assert.equal(worktrees.result?.kind, "worktrees"); + if (worktrees.result?.kind !== "worktrees") throw new Error("missing worktrees"); + assert.deepEqual(worktrees.result.worktrees.map((entry) => entry.id), [workspace.id]); + assert.equal(JSON.stringify(worktrees).includes(repository), false); + } finally { + await fs.rm(temporary, { recursive: true, force: true }); + } +}); diff --git a/main/services/aiden-remote-git.ts b/main/services/aiden-remote-git.ts new file mode 100644 index 00000000..6d7f06fb --- /dev/null +++ b/main/services/aiden-remote-git.ts @@ -0,0 +1,775 @@ +import { createHash, randomBytes } from "node:crypto"; +import path from "node:path"; +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; +import { + AidenIdempotencyLedger, + AidenOperationContractError, + assertRevision, + type AidenIdempotencySnapshot, +} from "./aiden-remote-operation-contract.js"; +import type { AidenRemoteWorkspaceOwnerRegistry } from "./aiden-remote-workspace-owners.js"; +import { + GitServiceError, + type GitCommitInput, + type GitComparison, + type GitComparisonDiffInput, + type GitDiffInput, + type GitPushCapability, + type GitPushInput, + type GitReview, + type GitReviewFile, +} from "./git.js"; +import type { GitBranches, GitWorktree, Workspace } from "./types.js"; +import type { WorkspaceEnvironmentApplicationService } from "./workspace-environment-application-service.js"; +import type { WorkspaceWorktreeApplicationService } from "./workspace-worktree-application-service.js"; +import { workspaceRevision } from "./aiden-remote-workspaces.js"; + +const SNAPSHOT_TTL_MS = 10 * 60_000; +const MAX_SNAPSHOTS = 4_096; +const MAX_FILES = 4_000; +const MAX_DIFF_CHARACTERS = 2_000_000; +const IDEMPOTENCY_KEY_PATTERN = /^[\x21-\x7e]{16,128}$/u; + +type GitFileStatus = "added" | "modified" | "deleted" | "renamed" | "untracked" | "conflicted"; + +export interface AidenRemoteGitFile { + id: string; + displayPath: string; + status: GitFileStatus; + staged?: boolean; + additions?: number; + deletions?: number; +} + +export type AidenRemoteGitProjection = + | { kind: "review"; branch: string; uncommitted: number; files: AidenRemoteGitFile[] } + | { kind: "diff"; displayPath: string; diff: string; truncated: boolean } + | { kind: "branches"; current: string; branches: string[] } + | { kind: "comparison"; comparisonId: string; base: string; head: string; files: AidenRemoteGitFile[] } + | { kind: "push-capability"; allowed: boolean; reason?: string; remote?: string; branch?: string } + | { kind: "worktrees"; worktrees: Array<{ id: string; name: string; branch: string; managed: boolean }> } + | { kind: "mutation"; message: string; branch?: string; commitId?: string; workspaceId?: string; warning?: string }; + +export interface AidenRemoteGitResult { + operationId: string; + status: "snapshot" | "accepted" | "running" | "succeeded" | "failed" | "conflict"; + snapshotId?: string; + capability?: { allowed: boolean; reason?: string }; + result?: AidenRemoteGitProjection; +} + +interface SnapshotBase { + deviceId: string; + workspaceId: string; + expiresAt: number; + files: Map; +} + +type Snapshot = SnapshotBase & ( + | { kind: "review"; expectedSnapshot?: string } + | { kind: "branches"; digest: string } + | { kind: "comparison"; comparison: GitComparison } + | { kind: "push"; capability: GitPushCapability } +); + +type SnapshotInput = + | { kind: "review"; deviceId: string; workspaceId: string; expectedSnapshot?: string } + | { kind: "branches"; deviceId: string; workspaceId: string; digest: string } + | { kind: "comparison"; deviceId: string; workspaceId: string; comparison: GitComparison } + | { kind: "push"; deviceId: string; workspaceId: string; capability: GitPushCapability }; + +interface GitDependencies { + review(folderPath: string, signal?: AbortSignal): Promise; + diff(folderPath: string, input: GitDiffInput, signal?: AbortSignal): Promise<{ path: string; patch: string; truncated: boolean }>; + branches(folderPath: string, signal?: AbortSignal): Promise; + checkout(folderPath: string, name: string, signal?: AbortSignal): Promise; + createBranch(folderPath: string, name: string, signal?: AbortSignal): Promise; + commit(folderPath: string, input: GitCommitInput, signal?: AbortSignal): Promise<{ commit: string; branch: string; subject: string; warning?: string }>; + pushCapability(folderPath: string, signal?: AbortSignal): Promise; + push(folderPath: string, input: GitPushInput, signal?: AbortSignal): Promise<{ branch: string; commit: string; remote: string; warning?: string }>; + compare(folderPath: string, targetRef: string, signal?: AbortSignal): Promise; + comparisonDiff(folderPath: string, input: GitComparisonDiffInput, signal?: AbortSignal): Promise<{ path: string; patch: string; truncated: boolean }>; + worktrees(folderPath: string, signal?: AbortSignal): Promise; +} + +function digest(value: string): string { + return createHash("sha256").update(value).digest("base64url"); +} + +function operationId(): string { + return `op_${randomBytes(24).toString("base64url")}`; +} + +function safeDisplayPath(value: string): string { + if ( + !value || + value.length > 4_096 || + path.isAbsolute(value) || + value.split("/").some((part) => part === "..") || + [...value].some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 0x1f || code === 0x7f; + }) + ) { + throw new AidenRemoteServiceError("workspace_unavailable", "A Git path could not be projected safely.", 409); + } + return value; +} + +function safeString(value: string, maximum: number): string { + return [...value].slice(0, maximum).join(""); +} + +function status(value: GitReviewFile["status"]): GitFileStatus { + return value === "copied" ? "renamed" : value; +} + +function branchesDigest(value: GitBranches): string { + return digest(JSON.stringify({ + isRepo: value.isRepo, + current: value.current ?? null, + branches: value.branches, + remoteBranches: value.remoteBranches, + uncommitted: value.uncommitted, + detached: value.detached ?? false, + unborn: value.unborn ?? false, + })); +} + +function ownRecord(value: unknown): Record | null { + return value && typeof value === "object" && !Array.isArray(value) + ? value as Record + : null; +} + +function exactKeys(record: Record, required: string[]): boolean { + return Object.keys(record).length === required.length && required.every( + (key) => Object.prototype.hasOwnProperty.call(record, key), + ); +} + +function boundedString(value: unknown, maximum: number): value is string { + return typeof value === "string" && value.length > 0 && [...value].length <= maximum; +} + +function requireConfirmation(record: Record): void { + if (record.confirmedForeground !== true) { + throw new AidenRemoteServiceError( + "permission_confirmation_required", + "This Git change requires an explicit foreground confirmation.", + 409, + ); + } +} + +function mapGitError(error: unknown): never { + if (error instanceof AidenRemoteServiceError) throw error; + if (!(error instanceof GitServiceError)) { + throw new AidenRemoteServiceError("internal_error", "Aiden could not complete this Git operation.", 500); + } + if (error.code === "stale_snapshot" || error.code === "dirty_worktree" || error.code === "conflicted") { + throw new AidenRemoteServiceError("operation_stale", "The repository changed. Refresh Git and try again.", 409); + } + if (error.code === "invalid_input" || error.code === "invalid_ref") { + throw new AidenRemoteServiceError("invalid_request", "The Git request is invalid.", 400); + } + if (error.code === "aborted") { + throw new AidenRemoteServiceError("operation_stale", "The Git operation was cancelled.", 409, true); + } + throw new AidenRemoteServiceError( + "git_capability_denied", + error.code === "not_repo" + ? "This workspace is not a Git repository." + : "This Git operation is not available for the current workspace state.", + 409, + ); +} + +export class AidenRemoteGitService { + private readonly snapshots = new Map(); + private readonly idempotency: AidenIdempotencyLedger; + private readonly now = (): number => this.options.now?.() ?? Date.now(); + + constructor( + private readonly options: { + application: Pick; + owners: Pick; + git: GitDependencies; + worktrees?: Pick; + listWorkspaces(): Promise; + idempotency?: AidenIdempotencyLedger; + persistIdempotency?: (snapshot: AidenIdempotencySnapshot) => Promise; + now?: () => number; + }, + ) { + this.idempotency = options.idempotency ?? new AidenIdempotencyLedger(); + } + + private prune(): void { + const now = this.now(); + for (const [key, value] of this.snapshots) { + if (value.expiresAt <= now) this.snapshots.delete(key); + } + } + + private issueSnapshot(value: SnapshotInput, files: GitReviewFile[] = []): { + snapshotId: string; + projectedFiles: AidenRemoteGitFile[]; + } { + this.prune(); + if (this.snapshots.size >= MAX_SNAPSHOTS) { + throw new AidenRemoteServiceError("handle_capacity", "Aiden's Git snapshot capacity is temporarily full.", 429, true); + } + const snapshotId = `snap_${randomBytes(32).toString("base64url")}`; + const mapped = new Map(); + const projectedFiles = files.slice(0, MAX_FILES).map((file) => { + const token = `file_${randomBytes(32).toString("base64url")}`; + mapped.set(digest(token), file.path); + return { + id: token, + displayPath: safeDisplayPath(file.path), + status: status(file.status), + staged: file.staged, + ...(file.additions === undefined ? {} : { additions: Math.max(0, file.additions) }), + ...(file.deletions === undefined ? {} : { deletions: Math.max(0, file.deletions) }), + }; + }); + this.snapshots.set(digest(snapshotId), { + ...value, + expiresAt: this.now() + SNAPSHOT_TTL_MS, + files: mapped, + } as Snapshot); + return { snapshotId, projectedFiles }; + } + + private snapshot( + deviceId: string, + workspaceId: string, + snapshotId: string, + expectedKind: Snapshot["kind"], + ): Snapshot { + this.prune(); + if (!/^snap_[A-Za-z0-9_-]{43}$/u.test(snapshotId)) { + throw new AidenRemoteServiceError("handle_invalid", "This Git snapshot is invalid.", 400); + } + const snapshot = this.snapshots.get(digest(snapshotId)); + if (!snapshot) throw new AidenRemoteServiceError("handle_expired", "This Git snapshot expired. Refresh Git and try again.", 410); + if (snapshot.deviceId !== deviceId) throw new AidenRemoteServiceError("handle_wrong_device", "This Git snapshot belongs to another paired device.", 403); + if (snapshot.workspaceId !== workspaceId || snapshot.kind !== expectedKind) { + throw new AidenRemoteServiceError("operation_stale", "This Git snapshot does not match the workspace operation.", 409); + } + return snapshot; + } + + private filePath(snapshot: Snapshot, fileId: string): string { + if (!/^file_[A-Za-z0-9_-]{43}$/u.test(fileId)) { + throw new AidenRemoteServiceError("handle_invalid", "This Git file link is invalid.", 400); + } + const value = snapshot.files.get(digest(fileId)); + if (!value) throw new AidenRemoteServiceError("operation_stale", "This file is not part of the Git snapshot.", 409); + return value; + } + + private async executeIdempotent( + scope: { deviceId: string; route: string; resourceId: string; key: string }, + input: unknown, + action: () => Promise, + ): Promise { + if (!IDEMPOTENCY_KEY_PATTERN.test(scope.key)) { + throw new AidenRemoteServiceError("invalid_request", "Idempotency-Key is invalid.", 400); + } + if (!this.options.persistIdempotency) return this.idempotency.execute(scope, input, action); + let release!: () => void; + let reject!: (error: unknown) => void; + const admission = new Promise((resolve, rejectPromise) => { + release = resolve; + reject = rejectPromise; + }); + const pending = this.idempotency.execute(scope, input, async () => { + await admission; + return action(); + }); + try { + await this.options.persistIdempotency(this.idempotency.snapshot()); + release(); + } catch (error) { + reject(error); + await pending.catch(() => undefined); + throw new AidenRemoteServiceError("internal_error", "Aiden could not durably prepare this Git operation.", 500); + } + let result: T | undefined; + let failure: unknown; + try { result = await pending; } catch (error) { failure = error; } + try { + await this.options.persistIdempotency(this.idempotency.snapshot()); + } catch { + throw new AidenRemoteServiceError( + "idempotency_in_flight", + "The Git operation may have completed, but Aiden could not durably record its outcome.", + 409, + ); + } + if (failure) throw failure; + return result!; + } + + private run( + deviceId: string, + workspaceId: string, + operation: (folderPath: string, signal: AbortSignal) => Promise, + ): Promise { + return this.options.application.run( + this.options.owners.owner(deviceId), + workspaceId, + ({ folderPath }, signal) => operation(folderPath, signal), + ); + } + + async review(deviceId: string, workspaceId: string): Promise { + try { + const review = await this.run(deviceId, workspaceId, this.options.git.review); + const issued = this.issueSnapshot( + { kind: "review", deviceId, workspaceId, expectedSnapshot: review.commit.snapshot }, + review.files, + ); + return { + operationId: operationId(), + status: "snapshot", + snapshotId: issued.snapshotId, + capability: { + allowed: review.commit.allowed, + ...(review.commit.reason ? { reason: safeString(review.commit.reason, 500) } : {}), + }, + result: { + kind: "review", + branch: safeString(review.branch ?? "HEAD", 500), + uncommitted: review.summary.fileCount, + files: issued.projectedFiles, + }, + }; + } catch (error) { mapGitError(error); } + } + + async diff(deviceId: string, workspaceId: string, value: unknown): Promise { + const record = ownRecord(value); + if (!record || !exactKeys(record, ["snapshotId", "fileId"]) || !boundedString(record.snapshotId, 128) || !boundedString(record.fileId, 128)) { + throw new AidenRemoteServiceError("invalid_request", "The Git diff request is invalid.", 400); + } + const snapshot = this.snapshot(deviceId, workspaceId, record.snapshotId, "review"); + if (snapshot.kind !== "review" || !snapshot.expectedSnapshot) { + throw new AidenRemoteServiceError("git_capability_denied", "A diff is unavailable for this review.", 409); + } + const displayPath = this.filePath(snapshot, record.fileId); + try { + const diff = await this.run(deviceId, workspaceId, (folderPath, signal) => + this.options.git.diff(folderPath, { expectedSnapshot: snapshot.expectedSnapshot!, path: displayPath }, signal)); + return { + operationId: operationId(), + status: "snapshot", + snapshotId: record.snapshotId, + result: { + kind: "diff", + displayPath: safeDisplayPath(diff.path), + diff: [...diff.patch].slice(0, MAX_DIFF_CHARACTERS).join(""), + truncated: diff.truncated || [...diff.patch].length > MAX_DIFF_CHARACTERS, + }, + }; + } catch (error) { mapGitError(error); } + } + + async branches(deviceId: string, workspaceId: string): Promise { + try { + const branches = await this.run(deviceId, workspaceId, this.options.git.branches); + const issued = this.issueSnapshot({ + kind: "branches", + deviceId, + workspaceId, + digest: branchesDigest(branches), + }); + return { + operationId: operationId(), + status: "snapshot", + snapshotId: issued.snapshotId, + capability: { + allowed: branches.isRepo && !branches.detached, + ...(!branches.isRepo + ? { reason: "This workspace is not a Git repository." } + : branches.detached + ? { reason: "Checkout is unavailable while HEAD is detached." } + : {}), + }, + result: { + kind: "branches", + current: safeString(branches.current ?? "HEAD", 500), + branches: branches.branches.slice(0, 4_000).map((branch) => safeString(branch, 500)), + }, + }; + } catch (error) { mapGitError(error); } + } + + async checkout(deviceId: string, workspaceId: string, key: string, value: unknown): Promise { + const record = ownRecord(value); + if (!record || !exactKeys(record, ["branch", "snapshotId", "confirmedForeground"]) || !boundedString(record.branch, 500) || !boundedString(record.snapshotId, 128)) { + throw new AidenRemoteServiceError("invalid_request", "The branch checkout request is invalid.", 400); + } + requireConfirmation(record); + const snapshot = this.snapshot(deviceId, workspaceId, record.snapshotId, "branches"); + return this.executeIdempotent( + { deviceId, route: "POST /git/checkout", resourceId: workspaceId, key }, + record, + async () => { + try { + return await this.run(deviceId, workspaceId, async (folderPath, signal) => { + const current = await this.options.git.branches(folderPath, signal); + if (snapshot.kind !== "branches" || snapshot.digest !== branchesDigest(current)) { + throw new AidenRemoteServiceError("operation_stale", "The branch list changed. Refresh Git and try again.", 409); + } + await this.options.git.checkout(folderPath, record.branch as string, signal); + return { + operationId: operationId(), + status: "succeeded" as const, + result: { kind: "mutation" as const, message: "Checked out branch.", branch: record.branch as string }, + }; + }); + } catch (error) { mapGitError(error); } + }, + ); + } + + async createBranch(deviceId: string, workspaceId: string, key: string, value: unknown): Promise { + const record = ownRecord(value); + if (!record || !exactKeys(record, ["name", "startPoint", "confirmedForeground"]) || !boundedString(record.name, 200) || !boundedString(record.startPoint, 500)) { + throw new AidenRemoteServiceError("invalid_request", "The branch creation request is invalid.", 400); + } + requireConfirmation(record); + return this.executeIdempotent( + { deviceId, route: "POST /git/branches", resourceId: workspaceId, key }, + record, + async () => { + try { + return await this.run(deviceId, workspaceId, async (folderPath, signal) => { + const branches = await this.options.git.branches(folderPath, signal); + if (record.startPoint !== (branches.current ?? "HEAD")) { + throw new AidenRemoteServiceError("operation_stale", "The branch start point changed. Refresh Git and try again.", 409); + } + await this.options.git.createBranch(folderPath, record.name as string, signal); + return { + operationId: operationId(), + status: "succeeded" as const, + result: { kind: "mutation" as const, message: "Created and checked out branch.", branch: record.name as string }, + }; + }); + } catch (error) { mapGitError(error); } + }, + ); + } + + async commit(deviceId: string, workspaceId: string, key: string, value: unknown): Promise { + const record = ownRecord(value); + if ( + !record || + !exactKeys(record, ["snapshotId", "message", "scope", "confirmedForeground"]) || + !boundedString(record.snapshotId, 128) || + !boundedString(record.message, 20_000) || + (record.scope !== "all-reviewed" && record.scope !== "staged-reviewed") + ) { + throw new AidenRemoteServiceError("invalid_request", "The Git commit request is invalid.", 400); + } + requireConfirmation(record); + const snapshot = this.snapshot(deviceId, workspaceId, record.snapshotId, "review"); + if (snapshot.kind !== "review" || !snapshot.expectedSnapshot) { + throw new AidenRemoteServiceError("git_capability_denied", "Commit is unavailable for this review.", 409); + } + return this.executeIdempotent( + { deviceId, route: "POST /git/commit", resourceId: workspaceId, key }, + record, + async () => { + try { + const result = await this.run(deviceId, workspaceId, (folderPath, signal) => + this.options.git.commit(folderPath, { + expectedSnapshot: snapshot.expectedSnapshot!, + message: record.message as string, + mode: record.scope === "all-reviewed" ? "all" : "staged", + }, signal)); + return { + operationId: operationId(), + status: "succeeded" as const, + result: { + kind: "mutation" as const, + message: safeString(result.subject, 500), + branch: safeString(result.branch, 500), + commitId: safeString(result.commit, 128), + ...(result.warning ? { warning: safeString(result.warning, 500) } : {}), + }, + }; + } catch (error) { mapGitError(error); } + }, + ); + } + + async pushCapability(deviceId: string, workspaceId: string): Promise { + try { + const capability = await this.run(deviceId, workspaceId, this.options.git.pushCapability); + const issued = this.issueSnapshot({ kind: "push", deviceId, workspaceId, capability }); + const remote = capability.suggestedRemote; + return { + operationId: operationId(), + status: "snapshot", + snapshotId: issued.snapshotId, + capability: { + allowed: capability.allowed, + ...(capability.reason ? { reason: safeString(capability.reason, 500) } : {}), + }, + result: { + kind: "push-capability", + allowed: capability.allowed, + ...(capability.reason ? { reason: safeString(capability.reason, 500) } : {}), + ...(remote ? { remote: safeString(remote, 200) } : {}), + ...(capability.destinationBranch ? { branch: safeString(capability.destinationBranch, 500) } : {}), + }, + }; + } catch (error) { mapGitError(error); } + } + + async push(deviceId: string, workspaceId: string, key: string, value: unknown): Promise { + const record = ownRecord(value); + if (!record || !exactKeys(record, ["snapshotId", "remote", "branch", "confirmedForeground"]) || !boundedString(record.snapshotId, 128) || !boundedString(record.remote, 200) || !boundedString(record.branch, 500)) { + throw new AidenRemoteServiceError("invalid_request", "The Git push request is invalid.", 400); + } + requireConfirmation(record); + const snapshot = this.snapshot(deviceId, workspaceId, record.snapshotId, "push"); + if (snapshot.kind !== "push" || !snapshot.capability.allowed) { + throw new AidenRemoteServiceError("git_capability_denied", "Push is not available for this workspace state.", 409); + } + const capability = snapshot.capability; + if ( + record.remote !== capability.suggestedRemote || + record.branch !== capability.destinationBranch || + !capability.branch || + !capability.expectedHead || + !capability.suggestedRemote || + !capability.remoteIdentities[capability.suggestedRemote] + ) { + throw new AidenRemoteServiceError("operation_stale", "The reviewed push destination changed. Refresh Git and try again.", 409); + } + return this.executeIdempotent( + { deviceId, route: "POST /git/push", resourceId: workspaceId, key }, + record, + async () => { + try { + const input: GitPushInput = { + destinationBranch: capability.destinationBranch!, + expectedBranch: capability.branch!, + expectedHead: capability.expectedHead!, + expectedRemoteIdentity: capability.remoteIdentities[capability.suggestedRemote!]!, + remote: capability.suggestedRemote!, + setUpstream: !capability.upstream, + }; + const result = await this.run(deviceId, workspaceId, (folderPath, signal) => this.options.git.push(folderPath, input, signal)); + return { + operationId: operationId(), + status: "succeeded" as const, + result: { + kind: "mutation" as const, + message: "Pushed reviewed commits.", + branch: safeString(result.branch, 500), + commitId: safeString(result.commit, 128), + ...(result.warning ? { warning: safeString(result.warning, 500) } : {}), + }, + }; + } catch (error) { mapGitError(error); } + }, + ); + } + + async compare(deviceId: string, workspaceId: string, value: unknown): Promise { + const record = ownRecord(value); + if (!record || !exactKeys(record, ["baseRef"]) || !boundedString(record.baseRef, 500)) { + throw new AidenRemoteServiceError("invalid_request", "The Git comparison request is invalid.", 400); + } + try { + const comparison = await this.run(deviceId, workspaceId, (folderPath, signal) => this.options.git.compare(folderPath, record.baseRef as string, signal)); + const issued = this.issueSnapshot( + { kind: "comparison", deviceId, workspaceId, comparison }, + comparison.files, + ); + return { + operationId: operationId(), + status: "snapshot", + snapshotId: issued.snapshotId, + result: { + kind: "comparison", + comparisonId: issued.snapshotId, + base: safeString(comparison.targetLabel, 500), + head: safeString(comparison.currentBranch ?? "HEAD", 500), + files: issued.projectedFiles, + }, + }; + } catch (error) { mapGitError(error); } + } + + async comparisonDiff(deviceId: string, workspaceId: string, value: unknown): Promise { + const record = ownRecord(value); + if (!record || !exactKeys(record, ["comparisonId", "fileId"]) || !boundedString(record.comparisonId, 128) || !boundedString(record.fileId, 128)) { + throw new AidenRemoteServiceError("invalid_request", "The comparison diff request is invalid.", 400); + } + const snapshot = this.snapshot(deviceId, workspaceId, record.comparisonId, "comparison"); + if (snapshot.kind !== "comparison") throw new AidenRemoteServiceError("operation_stale", "This comparison is stale.", 409); + const displayPath = this.filePath(snapshot, record.fileId); + const comparison = snapshot.comparison; + try { + const diff = await this.run(deviceId, workspaceId, (folderPath, signal) => this.options.git.comparisonDiff(folderPath, { + expectedHead: comparison.expectedHead, + expectedTarget: comparison.expectedTarget, + mergeBase: comparison.mergeBase, + path: displayPath, + targetRef: comparison.targetRef, + }, signal)); + return { + operationId: operationId(), + status: "snapshot", + snapshotId: record.comparisonId, + result: { + kind: "diff", + displayPath: safeDisplayPath(diff.path), + diff: [...diff.patch].slice(0, MAX_DIFF_CHARACTERS).join(""), + truncated: diff.truncated || [...diff.patch].length > MAX_DIFF_CHARACTERS, + }, + }; + } catch (error) { mapGitError(error); } + } + + async worktrees(deviceId: string, workspaceId: string): Promise { + try { + const [worktrees, registered] = await Promise.all([ + this.run(deviceId, workspaceId, this.options.git.worktrees), + this.options.listWorkspaces(), + ]); + const resolvedWorkspaces = await Promise.all(registered.map(async (workspace) => { + try { + const resolved = await this.options.application.resolve(workspace.id, true, true); + return resolved ? { folderPath: resolved.folderPath, workspace } : undefined; + } catch { + return undefined; + } + })); + const projected = worktrees.flatMap((worktree) => { + const resolved = resolvedWorkspaces.find((candidate) => candidate?.folderPath === worktree.path); + if (!resolved) return []; + return [{ + id: resolved.workspace.id, + name: safeString(resolved.workspace.name, 120), + branch: safeString(worktree.branch ?? "detached", 500), + managed: Boolean(resolved.workspace.managedWorktree), + }]; + }); + return { + operationId: operationId(), + status: "snapshot", + result: { kind: "worktrees", worktrees: projected }, + }; + } catch (error) { mapGitError(error); } + } + + async createWorktree( + deviceId: string, + workspaceId: string, + key: string, + value: unknown, + ): Promise { + const record = ownRecord(value); + if ( + !record || + !exactKeys(record, ["branch", "name", "confirmedForeground"]) || + !boundedString(record.branch, 500) || + !boundedString(record.name, 120) + ) { + throw new AidenRemoteServiceError("invalid_request", "The managed-worktree request is invalid.", 400); + } + requireConfirmation(record); + if (!this.options.worktrees) { + throw new AidenRemoteServiceError("not_found", "Managed worktrees are unavailable.", 404); + } + return this.executeIdempotent( + { deviceId, route: "POST /git/worktrees", resourceId: workspaceId, key }, + record, + async () => { + try { + const workspace = await this.options.worktrees!.create( + this.options.owners.owner(deviceId), + workspaceId, + record.branch as string, + record.name as string, + ); + return { + operationId: operationId(), + status: "succeeded" as const, + result: { + kind: "mutation" as const, + message: "Created managed worktree.", + branch: safeString(workspace.managedWorktree?.branch ?? record.branch as string, 500), + workspaceId: workspace.id, + }, + }; + } catch (error) { mapGitError(error); } + }, + ); + } + + async deleteManagedWorktree( + deviceId: string, + workspaceId: string, + revision: string, + key: string, + value: unknown, + ): Promise { + const record = ownRecord(value); + if (!record || !exactKeys(record, ["confirmedForeground"])) { + throw new AidenRemoteServiceError("invalid_request", "The managed-worktree deletion request is invalid.", 400); + } + requireConfirmation(record); + if (!this.options.worktrees) { + throw new AidenRemoteServiceError("not_found", "Managed worktrees are unavailable.", 404); + } + return this.executeIdempotent( + { deviceId, route: "DELETE /git/managed-worktree", resourceId: workspaceId, key }, + { revision, ...record }, + async () => { + try { + const result = await this.options.worktrees!.remove( + this.options.owners.owner(deviceId), + workspaceId, + (workspace) => { + const currentRevision = workspaceRevision(workspace); + try { + assertRevision(revision, currentRevision); + } catch (error) { + if (error instanceof AidenOperationContractError) { + throw new AidenRemoteServiceError( + "revision_conflict", + "The workspace changed. Refresh it before trying again.", + 409, + false, + { currentRevision }, + ); + } + throw error; + } + }, + ); + return { + operationId: operationId(), + status: "succeeded" as const, + result: { + kind: "mutation" as const, + message: result.branchDeleted + ? "Removed managed worktree and branch." + : "Removed managed worktree.", + workspaceId, + }, + }; + } catch (error) { mapGitError(error); } + }, + ); + } +} diff --git a/main/services/aiden-remote-models.test.ts b/main/services/aiden-remote-models.test.ts new file mode 100644 index 00000000..eee48a06 --- /dev/null +++ b/main/services/aiden-remote-models.test.ts @@ -0,0 +1,56 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { AidenRemoteModelService } from "./aiden-remote-models.js"; +import type { Provider } from "./types.js"; + +function provider(overrides: Partial = {}): Provider { + return { + id: "provider-1", + kind: "openai", + label: "Provider", + baseUrl: "https://secret-endpoint.example/v1", + models: ["chat-model", "embedding-model"], + modelMetadata: { + "chat-model": { source: "provider", name: "Chat Model", type: "llm", thinkingLevels: ["low", "high"] }, + "embedding-model": { source: "provider", type: "embedding" }, + }, + needsKey: true, + hasKey: true, + authMethods: [{ type: "api_key", label: "Secret", canLogin: true }], + ...overrides, + }; +} + +test("model projection includes only configured chat models and no connection secrets", async () => { + const service = new AidenRemoteModelService({ + listProviders: async () => [provider(), provider({ id: "missing-key", hasKey: false })], + getSettings: async () => ({ lastProviderId: "provider-1", lastModel: "chat-model" }), + }); + const projection = await service.list(); + assert.deepEqual(projection.defaults, { providerId: "provider-1", modelId: "chat-model" }); + assert.equal(projection.providers.length, 1); + assert.deepEqual(projection.providers[0]?.models, [{ + id: "chat-model", + label: "Chat Model", + thinkingLevels: ["low", "high"], + }]); + const serialized = JSON.stringify(projection); + assert.equal(serialized.includes("secret-endpoint"), false); + assert.equal(serialized.includes("authMethods"), false); +}); + +test("model selection rejects missing providers and models", async () => { + const service = new AidenRemoteModelService({ + listProviders: async () => [provider()], + getSettings: async () => ({}), + }); + assert.deepEqual(await service.resolve(), { + providerId: "provider-1", + modelId: "chat-model", + thinkingLevels: ["low", "high"], + }); + await assert.rejects( + service.resolve("provider-1", "missing"), + (error: unknown) => (error as { code?: string }).code === "invalid_request", + ); +}); diff --git a/main/services/aiden-remote-models.ts b/main/services/aiden-remote-models.ts new file mode 100644 index 00000000..b6e369b1 --- /dev/null +++ b/main/services/aiden-remote-models.ts @@ -0,0 +1,94 @@ +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; +import type { AppSettings, Provider } from "./types.js"; + +export interface AidenRemoteModelProjection { + id: string; + label: string; + thinkingLevels?: string[]; +} + +export interface AidenRemoteProviderProjection { + id: string; + label: string; + models: AidenRemoteModelProjection[]; +} + +export interface AidenRemoteModelsProjection { + providers: AidenRemoteProviderProjection[]; + defaults: Record; +} + +function bounded(value: string, maximum: number): string { + return Array.from(value).slice(0, maximum).join(""); +} + +export class AidenRemoteModelService { + constructor( + private readonly options: { + listProviders(): Promise; + getSettings(): Promise; + }, + ) {} + + async list(): Promise { + const [configured, settings] = await Promise.all([ + this.options.listProviders(), + this.options.getSettings(), + ]); + const providers = configured + .filter((provider) => provider.hasKey || !provider.needsKey) + .map((provider) => ({ + id: bounded(provider.id, 256), + label: bounded(provider.label, 256), + models: provider.models + .filter((id) => provider.modelMetadata?.[id]?.type !== "embedding") + .map((id) => { + const metadata = provider.modelMetadata?.[id]; + return { + id: bounded(id, 256), + label: bounded(metadata?.name ?? id, 256), + ...(metadata?.thinkingLevels?.length + ? { thinkingLevels: [...metadata.thinkingLevels] } + : {}), + }; + }), + })) + .filter((provider) => provider.models.length > 0); + const selectedProvider = providers.find((provider) => provider.id === settings.lastProviderId) + ?? providers[0]; + const selectedModel = selectedProvider?.models.find((model) => model.id === settings.lastModel) + ?? selectedProvider?.models[0]; + return { + providers, + defaults: { + ...(selectedProvider ? { providerId: selectedProvider.id } : {}), + ...(selectedModel ? { modelId: selectedModel.id } : {}), + }, + }; + } + + async resolve( + providerId?: string, + modelId?: string, + ): Promise<{ providerId: string; modelId: string; thinkingLevels: readonly string[] }> { + const projection = await this.list(); + const provider = providerId + ? projection.providers.find((candidate) => candidate.id === providerId) + : projection.providers.find((candidate) => candidate.id === projection.defaults.providerId); + const model = provider && (modelId + ? provider.models.find((candidate) => candidate.id === modelId) + : provider.models.find((candidate) => candidate.id === projection.defaults.modelId)); + if (!provider || !model) { + throw new AidenRemoteServiceError( + "invalid_request", + "Choose a configured Aiden provider and model before starting this turn.", + 400, + ); + } + return { + providerId: provider.id, + modelId: model.id, + thinkingLevels: model.thinkingLevels ?? [], + }; + } +} diff --git a/main/services/aiden-remote-opaque-handles.test.ts b/main/services/aiden-remote-opaque-handles.test.ts new file mode 100644 index 00000000..056f3d63 --- /dev/null +++ b/main/services/aiden-remote-opaque-handles.test.ts @@ -0,0 +1,130 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { mkdtemp, mkdir, rm, symlink, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { + AidenOpaqueHandleError, + AidenOpaqueHandleStore, + inspectAidenFilesystemIdentity, + type AidenOpaqueHandleClaims, +} from "./aiden-remote-opaque-handles.js"; + +const claims = (overrides: Partial = {}): AidenOpaqueHandleClaims => ({ + instanceId: "instance-1", + deviceId: "device-1", + workspaceId: "workspace-1", + rootId: "root-1", + policyRevision: "policy-1", + canonicalRootPath: "/approved/root", + canonicalPath: "/approved/root/project/file.swift", + filesystemDevice: "device-volume-1", + filesystemInode: "inode-1", + expiresAt: 2_000, + depth: 2, + snapshotId: "snapshot-1", + kind: "file", + ...overrides, +}); + +function rejectsCode(action: () => unknown, code: string): void { + assert.throws(action, (error) => error instanceof AidenOpaqueHandleError && error.code === code); +} + +test("opaque handles store only a digest and bind identity, root policy, path identity, and snapshot", () => { + const store = new AidenOpaqueHandleStore({ now: () => 1_000 }); + const token = store.issue("file", claims()); + assert.match(token, /^file_[A-Za-z0-9_-]{43}$/); + assert.equal(store.storedTokenMaterialForTesting().includes(token), false); + assert.deepEqual(store.resolve(token, "file", claims(), { now: 1_000 }), claims()); + rejectsCode(() => store.resolve(token, "file", claims({ deviceId: "device-2" }), { now: 1_000 }), "handle_wrong_device"); + rejectsCode(() => store.resolve(token, "file", claims({ workspaceId: "workspace-2" }), { now: 1_000 }), "root_policy_changed"); + rejectsCode(() => store.resolve(token, "file", claims({ workspaceId: undefined }), { now: 1_000 }), "root_policy_changed"); + rejectsCode(() => store.resolve(token, "file", claims({ policyRevision: "policy-2" }), { now: 1_000 }), "root_policy_changed"); + rejectsCode(() => store.resolve(token, "file", claims({ filesystemInode: "inode-2" }), { now: 1_000 }), "filesystem_identity_changed"); + rejectsCode(() => store.resolve(token, "file", claims({ canonicalPath: "/outside/file.swift" }), { now: 1_000 }), "path_outside_root"); + rejectsCode(() => store.resolve(token, "file", claims(), { now: 2_000 }), "handle_expired"); + rejectsCode(() => store.issue("file", claims({ workspaceId: undefined, expiresAt: 3_000 })), "handle_invalid"); +}); + +test("filesystem inspection canonicalizes roots and rejects symlink escapes", async () => { + const directory = await mkdtemp(path.join(os.tmpdir(), "aiden-handle-root-")); + const root = path.join(directory, "root"); + const outside = path.join(directory, "outside"); + try { + await mkdir(root); + await mkdir(outside); + await writeFile(path.join(root, "safe.txt"), "safe"); + await writeFile(path.join(outside, "secret.txt"), "secret"); + const safe = await inspectAidenFilesystemIdentity(root, path.join(root, "safe.txt")); + assert.equal(safe.canonicalPath.startsWith(`${safe.canonicalRootPath}${path.sep}`), true); + await symlink(path.join(outside, "secret.txt"), path.join(root, "escape.txt")); + await assert.rejects(() => inspectAidenFilesystemIdentity(root, path.join(root, "escape.txt")), (error) => error instanceof AidenOpaqueHandleError && error.code === "path_outside_root"); + } finally { + await rm(directory, { recursive: true, force: true }); + } +}); + +test("selection handles are atomic single-use even under concurrent consumers", async () => { + const store = new AidenOpaqueHandleStore({ now: () => 1_000 }); + const selectionClaims = claims({ workspaceId: undefined, snapshotId: undefined, kind: "directory" }); + const token = store.issue("sel", selectionClaims); + const attempts = await Promise.allSettled([ + Promise.resolve().then(() => store.consumeSelection(token, selectionClaims, () => "workspace-1", 1_000)), + Promise.resolve().then(() => store.consumeSelection(token, selectionClaims, () => "workspace-2", 1_000)), + ]); + assert.equal(attempts.filter((entry) => entry.status === "fulfilled").length, 1); + assert.equal(attempts.filter((entry) => entry.status === "rejected").length, 1); + const retryStore = new AidenOpaqueHandleStore({ now: () => 1_000 }); + const retryToken = retryStore.issue("sel", selectionClaims); + assert.throws(() => retryStore.consumeSelection(retryToken, selectionClaims, () => { throw new Error("workspace write failed"); }, 1_000), /workspace write failed/); + rejectsCode(() => retryStore.consumeSelection(retryToken, selectionClaims, () => "workspace-retried", 1_000), "handle_invalid"); + const fileSelection = claims({ snapshotId: undefined, kind: "file" }); + const fileToken = retryStore.issue("sel", fileSelection); + rejectsCode(() => retryStore.consumeSelection(fileToken, fileSelection, () => "bad", 1_000), "handle_invalid"); +}); + +test("selection transactions reject async callbacks before invocation and consume promise escape attempts", async () => { + const selectionClaims = claims({ workspaceId: undefined, snapshotId: undefined, kind: "directory" }); + const asyncStore = new AidenOpaqueHandleStore({ now: () => 1_000 }); + const asyncToken = asyncStore.issue("sel", selectionClaims); + let invoked = false; + const asyncMutation = async () => { + invoked = true; + return "workspace-async"; + }; + rejectsCode( + () => asyncStore.consumeSelection(asyncToken, selectionClaims, asyncMutation as unknown as () => never, 1_000), + "handle_invalid", + ); + assert.equal(invoked, false); + assert.equal(asyncStore.consumeSelection(asyncToken, selectionClaims, () => "workspace-sync", 1_000), "workspace-sync"); + + const escapedStore = new AidenOpaqueHandleStore({ now: () => 1_000 }); + const escapedToken = escapedStore.issue("sel", selectionClaims); + const promiseReturningMutation = (() => Promise.resolve("workspace-escaped")) as unknown as () => never; + rejectsCode( + () => escapedStore.consumeSelection(escapedToken, selectionClaims, promiseReturningMutation, 1_000), + "handle_invalid", + ); + rejectsCode( + () => escapedStore.consumeSelection(escapedToken, selectionClaims, () => "must-not-retry", 1_000), + "handle_invalid", + ); +}); + +test("opaque handle storage prunes consumed and expired entries and fails closed at capacity", () => { + let now = 1_000; + const store = new AidenOpaqueHandleStore({ maxEntries: 2, now: () => now }); + const first = store.issue("file", claims({ filesystemInode: "inode-1" })); + const secondClaims = claims({ filesystemInode: "inode-2" }); + const second = store.issue("file", secondClaims); + rejectsCode(() => store.issue("file", claims({ filesystemInode: "inode-3" })), "handle_capacity"); + + store.resolve(first, "file", claims({ filesystemInode: "inode-1" }), { now, consume: true }); + assert.doesNotThrow(() => store.issue("file", claims({ filesystemInode: "inode-3" }))); + now = 2_000; + rejectsCode(() => store.resolve(second, "file", secondClaims, { now }), "handle_expired"); + assert.doesNotThrow(() => store.issue("file", claims({ filesystemInode: "inode-4", expiresAt: 3_000 }))); + assert.equal(store.storedTokenMaterialForTesting().length <= 2, true); +}); diff --git a/main/services/aiden-remote-opaque-handles.ts b/main/services/aiden-remote-opaque-handles.ts new file mode 100644 index 00000000..7656913a --- /dev/null +++ b/main/services/aiden-remote-opaque-handles.ts @@ -0,0 +1,188 @@ +import { createHash, randomBytes } from "node:crypto"; +import { realpath, stat } from "node:fs/promises"; +import path from "node:path"; + +export type AidenOpaqueHandleKind = "loc" | "cur" | "sel" | "file"; + +export interface AidenOpaqueHandleClaims { + instanceId: string; + deviceId: string; + workspaceId?: string; + rootId: string; + policyRevision: string; + canonicalRootPath: string; + canonicalPath: string; + filesystemDevice: string; + filesystemInode: string; + expiresAt: number; + depth?: number; + snapshotId?: string; + cursorOffset?: number; + parentHandleDigest?: string; + displayPath?: string; + kind?: "directory" | "file"; +} + +export type AidenOpaqueHandleErrorCode = + | "handle_invalid" + | "handle_expired" + | "handle_wrong_device" + | "root_policy_changed" + | "filesystem_identity_changed" + | "path_outside_root" + | "handle_capacity"; + +export class AidenOpaqueHandleError extends Error { + constructor(readonly code: AidenOpaqueHandleErrorCode) { + super(code); + } +} + +interface StoredHandle { + kind: AidenOpaqueHandleKind; + claims: AidenOpaqueHandleClaims; + consumed: boolean; +} + +function digest(token: string): string { + return createHash("sha256").update(token).digest("base64url"); +} + +export class AidenOpaqueHandleStore { + private readonly handles = new Map(); + + constructor(private readonly options: { maxEntries?: number; now?: () => number } = {}) {} + + private get maxEntries(): number { + return Math.max(1, this.options.maxEntries ?? 10_000); + } + + private now(): number { + return this.options.now?.() ?? Date.now(); + } + + private prune(now: number, exceptDigest?: string): void { + for (const [key, stored] of this.handles) { + if (key !== exceptDigest && (stored.consumed || stored.claims.expiresAt <= now)) { + this.handles.delete(key); + } + } + } + + issue(kind: AidenOpaqueHandleKind, claims: AidenOpaqueHandleClaims): string { + const now = this.now(); + this.prune(now); + if (claims.expiresAt <= now) throw new AidenOpaqueHandleError("handle_expired"); + if (kind === "file" && !claims.workspaceId) throw new AidenOpaqueHandleError("handle_invalid"); + if (this.handles.size >= this.maxEntries) throw new AidenOpaqueHandleError("handle_capacity"); + const token = `${kind}_${randomBytes(32).toString("base64url")}`; + this.handles.set(digest(token), { kind, claims: { ...claims }, consumed: false }); + return token; + } + + claimsFor( + token: string, + expectedKind: AidenOpaqueHandleKind, + ): AidenOpaqueHandleClaims { + if (!new RegExp(`^${expectedKind}_[A-Za-z0-9_-]{43}$`, "u").test(token)) { + throw new AidenOpaqueHandleError("handle_invalid"); + } + const stored = this.handles.get(digest(token)); + if (!stored || stored.kind !== expectedKind || stored.consumed) { + throw new AidenOpaqueHandleError("handle_invalid"); + } + if (stored.claims.expiresAt <= this.now()) { + this.handles.delete(digest(token)); + throw new AidenOpaqueHandleError("handle_expired"); + } + return { ...stored.claims }; + } + + resolve( + token: string, + expectedKind: AidenOpaqueHandleKind, + current: AidenOpaqueHandleClaims, + options: { consume?: boolean; now?: number } = {}, + ): AidenOpaqueHandleClaims { + if (!new RegExp(`^${expectedKind}_[A-Za-z0-9_-]{43}$`).test(token)) { + throw new AidenOpaqueHandleError("handle_invalid"); + } + const tokenDigest = digest(token); + const stored = this.handles.get(tokenDigest); + if (!stored || stored.kind !== expectedKind || stored.consumed) { + throw new AidenOpaqueHandleError("handle_invalid"); + } + const claims = stored.claims; + const now = options.now ?? this.now(); + if (now >= claims.expiresAt) { + this.handles.delete(tokenDigest); + throw new AidenOpaqueHandleError("handle_expired"); + } + this.prune(now, tokenDigest); + if (claims.instanceId !== current.instanceId || claims.deviceId !== current.deviceId) { + throw new AidenOpaqueHandleError("handle_wrong_device"); + } + if (expectedKind === "file" && (!claims.workspaceId || !current.workspaceId || claims.workspaceId !== current.workspaceId)) { + throw new AidenOpaqueHandleError("root_policy_changed"); + } + if (claims.rootId !== current.rootId || claims.policyRevision !== current.policyRevision) { + throw new AidenOpaqueHandleError("root_policy_changed"); + } + const relative = path.relative(current.canonicalRootPath, current.canonicalPath); + if (relative.startsWith("..") || path.isAbsolute(relative)) { + throw new AidenOpaqueHandleError("path_outside_root"); + } + if ( + claims.canonicalRootPath !== current.canonicalRootPath || + claims.canonicalPath !== current.canonicalPath || + claims.filesystemDevice !== current.filesystemDevice || + claims.filesystemInode !== current.filesystemInode || + claims.kind !== current.kind || + claims.depth !== current.depth || + claims.snapshotId !== current.snapshotId || + claims.cursorOffset !== current.cursorOffset || + claims.parentHandleDigest !== current.parentHandleDigest || + claims.displayPath !== current.displayPath + ) { + throw new AidenOpaqueHandleError("filesystem_identity_changed"); + } + if (options.consume) stored.consumed = true; + return { ...claims }; + } + + consumeSelection( + token: string, + current: AidenOpaqueHandleClaims, + createWorkspaceSynchronously: (claims: AidenOpaqueHandleClaims) => T extends PromiseLike ? never : T, + now = Date.now(), + ): T { + if (current.kind !== "directory") throw new AidenOpaqueHandleError("handle_invalid"); + if (createWorkspaceSynchronously.constructor.name === "AsyncFunction") { + throw new AidenOpaqueHandleError("handle_invalid"); + } + const claims = this.resolve(token, "sel", current, { now, consume: true }); + const result = createWorkspaceSynchronously(claims); + if (result && typeof (result as { then?: unknown }).then === "function") { + throw new AidenOpaqueHandleError("handle_invalid"); + } + return result; + } + + storedTokenMaterialForTesting(): string[] { + return [...this.handles.keys()]; + } +} + +export async function inspectAidenFilesystemIdentity(rootPath: string, candidatePath: string): Promise> { + const [canonicalRootPath, canonicalPath] = await Promise.all([realpath(rootPath), realpath(candidatePath)]); + const relative = path.relative(canonicalRootPath, canonicalPath); + if (relative.startsWith("..") || path.isAbsolute(relative)) throw new AidenOpaqueHandleError("path_outside_root"); + const identity = await stat(canonicalPath); + return { + canonicalRootPath, + canonicalPath, + filesystemDevice: String(identity.dev), + filesystemInode: String(identity.ino), + kind: identity.isDirectory() ? "directory" : "file", + }; +} diff --git a/main/services/aiden-remote-operation-contract.test.ts b/main/services/aiden-remote-operation-contract.test.ts new file mode 100644 index 00000000..a79f071b --- /dev/null +++ b/main/services/aiden-remote-operation-contract.test.ts @@ -0,0 +1,867 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { + AidenDurableOperationRegistry, + AidenIdempotencyLedger, + AidenOperationContractError, + AidenOperationUnknownOutcomeError, + assertRevision, + MAX_DURABLE_JSON_ARRAY_LENGTH, + MAX_DURABLE_JSON_DEPTH, + MAX_DURABLE_JSON_KEYS, + MAX_DURABLE_JSON_NODES, + MAX_DURABLE_JSON_RESULT_BYTES, + MAX_DURABLE_JSON_STRING_LENGTH, + MAX_DURABLE_LEDGER_SNAPSHOT_BYTES, + MAX_DURABLE_OPERATION_ENTRIES, + type AidenIdempotencySnapshotEntry, +} from "./aiden-remote-operation-contract.js"; + +test("idempotency is scoped and replays the original result while rejecting key reuse", async () => { + const ledger = new AidenIdempotencyLedger(); + const scope = { deviceId: "device-1", route: "/git/push", resourceId: "workspace-1", key: "key-0123456789abcdef" }; + let calls = 0; + const first = ledger.execute(scope, { branch: "main", nested: { a: 1, b: 2 } }, async () => ({ operationId: `op-${++calls}` })); + const replay = ledger.execute(scope, { nested: { b: 2, a: 1 }, branch: "main" }, async () => ({ operationId: `op-${++calls}` })); + assert.strictEqual(first, replay); + assert.deepEqual(await replay, { operationId: "op-1" }); + assert.throws(() => ledger.execute(scope, { branch: "other" }, async () => ({})), (error) => error instanceof AidenOperationContractError && error.code === "idempotency_conflict"); + assert.equal(calls, 1); +}); + +test("an explicitly unknown external outcome remains in-flight indefinitely", async () => { + let now = 1_000; + const scope = { + deviceId: "device-1", + route: "/chats/chat-1/turns", + resourceId: "chat-1", + key: "unknown-turn-key-001", + }; + const ledger = new AidenIdempotencyLedger(undefined, { now: () => now, ttlMs: 10 }); + await assert.rejects( + ledger.execute(scope, { text: "hello" }, async () => { + throw new AidenOperationUnknownOutcomeError(); + }), + (error: unknown) => error instanceof AidenOperationContractError && error.code === "idempotency_in_flight", + ); + assert.equal(ledger.snapshot().entries[0]?.state, "in_flight"); + now = 1_000_000; + assert.throws( + () => new AidenIdempotencyLedger(ledger.snapshot(), { now: () => now }).execute( + scope, + { text: "hello" }, + async () => ({ duplicated: true }), + ), + (error: unknown) => error instanceof AidenOperationContractError && error.code === "idempotency_in_flight", + ); +}); + +test("idempotency snapshots survive restart without retaining raw keys and stay TTL/capacity bounded", async () => { + let now = 1_000; + const options = { maxEntries: 2, ttlMs: 100, now: () => now }; + const scope = { deviceId: "device-1", route: "/scheduled-tasks/task-1/run", resourceId: "task-1", key: "secret-idempotency-key" }; + const ledger = new AidenIdempotencyLedger(undefined, options); + await ledger.execute(scope, { action: "run" }, async () => ({ runId: "run-1" })); + const snapshot = ledger.snapshot(); + assert.equal(JSON.stringify(snapshot).includes(scope.key), false); + + const restarted = new AidenIdempotencyLedger(snapshot, options); + let replayCalls = 0; + assert.deepEqual( + await restarted.execute(scope, { action: "run" }, async () => ({ runId: `run-${++replayCalls + 1}` })), + { runId: "run-1" }, + ); + assert.equal(replayCalls, 0); + + await restarted.execute({ ...scope, key: "key-2" }, { action: "run" }, async () => ({ runId: "run-2" })); + assert.throws( + () => restarted.execute({ ...scope, key: "key-3" }, { action: "run" }, async () => ({ runId: "run-3" })), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_capacity", + ); + assert.equal(restarted.sizeForTesting(), 2); + now = 1_101; + assert.equal(restarted.sizeForTesting(), 0); + assert.deepEqual( + await restarted.execute({ ...scope, key: "key-3" }, { action: "run" }, async () => ({ runId: "run-3" })), + { runId: "run-3" }, + ); +}); + +test("fulfilled idempotency results replay exactly after a JSON snapshot round trip", async () => { + let now = 1_000; + const options = { maxEntries: 1, ttlMs: 100, now: () => now }; + const scope = { deviceId: "device-1", route: "/git/push", resourceId: "workspace-1", key: "key-json-round-trip" }; + const original = { + operationId: "op-1", + accepted: true, + count: 3, + nested: { + message: "preserved", + values: [null, false, 0, "text", { key: "value" }], + }, + }; + const ledger = new AidenIdempotencyLedger(undefined, options); + assert.deepEqual(await ledger.execute(scope, { action: "push" }, async () => original), original); + + const snapshot = ledger.snapshot(); + const persisted = JSON.parse(JSON.stringify(snapshot)) as typeof snapshot; + assert.deepEqual(persisted, snapshot); + + const restarted = new AidenIdempotencyLedger(persisted, options); + assert.deepEqual( + await restarted.execute(scope, { action: "push" }, async () => ({ operationId: "must-not-run" })), + original, + ); + const replay = await restarted.execute(scope, { action: "push" }, async () => ({ operationId: "must-not-run" })); + (replay as typeof original).nested.values[4] = { key: "caller-mutated" }; + assert.deepEqual( + await restarted.execute(scope, { action: "push" }, async () => ({ operationId: "must-not-run" })), + original, + ); + assert.deepEqual(restarted.snapshot(), persisted); + now = 1_001; +}); + +test("non-durable fulfilled results retain an unknown operation instead of becoming retryable", async () => { + let getterCalls = 0; + const accessor: Record = {}; + Object.defineProperty(accessor, "value", { + enumerable: true, + get: () => { + getterCalls += 1; + return "must-not-be-read"; + }, + }); + const cycle: Record = {}; + cycle.self = cycle; + const shared = { value: 1 }; + const sparse: unknown[] = []; + sparse.length = 2; + sparse[1] = "present"; + const extraArrayProperty = ["value"] as unknown[] & { extra?: unknown }; + extraArrayProperty.extra = "omitted"; + const hiddenProperty: Record = { visible: true }; + Object.defineProperty(hiddenProperty, "hidden", { enumerable: false, value: true }); + const symbolKey = { visible: true }; + Object.defineProperty(symbolKey, Symbol("omitted"), { enumerable: true, value: true }); + class UnsupportedResult { + value = 1; + } + const unsafeResults: unknown[] = [ + undefined, + Number.NaN, + Number.POSITIVE_INFINITY, + Number.NEGATIVE_INFINITY, + -0, + 1n, + Symbol("unsupported"), + () => "unsupported", + { nested: undefined }, + { nested: Number.NaN }, + { nested: 1n }, + { nested: Symbol("unsupported") }, + accessor, + cycle, + new Date("2026-01-01T00:00:00.000Z"), + new Map([["key", "value"]]), + new Set(["value"]), + /unsupported/u, + new UnsupportedResult(), + Object.create(null), + sparse, + extraArrayProperty, + hiddenProperty, + symbolKey, + { left: shared, right: shared }, + new Proxy({ value: 1 }, {}), + ]; + + for (const [index, value] of unsafeResults.entries()) { + const ledger = new AidenIdempotencyLedger(undefined, { maxEntries: 1, now: () => 1_000 }); + const scope = { + deviceId: "device-1", + route: "/git/push", + resourceId: "workspace-1", + key: `key-unsafe-${index}`, + }; + await assert.rejects( + ledger.execute(scope, { action: "push" }, async () => value), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + const snapshot = ledger.snapshot(); + assert.equal(snapshot.entries[0]?.state, "in_flight"); + assert.equal("result" in (snapshot.entries[0] ?? {}), false); + assert.throws( + () => ledger.execute(scope, { action: "push" }, async () => "must-not-run"), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_in_flight", + ); + } + assert.equal(getterCalls, 0); +}); + +test("durable replay results enforce depth, node, key, array, and string bounds", async () => { + const deepResult: Record = { leaf: true }; + for (let index = 0; index <= MAX_DURABLE_JSON_DEPTH; index += 1) { + Object.assign(deepResult, { child: { ...deepResult } }); + } + const nodeResult: unknown[] = []; + const makeBinaryTree = (depth: number): unknown => { + if (depth === 0) return true; + return [makeBinaryTree(depth - 1), makeBinaryTree(depth - 1)]; + }; + nodeResult.push(makeBinaryTree(Math.ceil(Math.log2(MAX_DURABLE_JSON_NODES + 1)))); + const keyResult = Object.fromEntries( + Array.from({ length: MAX_DURABLE_JSON_KEYS + 1 }, (_, index) => [`key-${index}`, true]), + ); + const arrayResult = Array.from({ length: MAX_DURABLE_JSON_ARRAY_LENGTH + 1 }, () => true); + const stringResult = "x".repeat(MAX_DURABLE_JSON_STRING_LENGTH + 1); + const results: unknown[] = [deepResult, nodeResult, keyResult, arrayResult, stringResult]; + + for (const [index, value] of results.entries()) { + const ledger = new AidenIdempotencyLedger(undefined, { maxEntries: 1, now: () => 1_000 }); + const scope = { + deviceId: "device-1", + route: "/git/push", + resourceId: "workspace-1", + key: `key-bounded-result-${index}`, + }; + await assert.rejects( + ledger.execute(scope, { action: "push" }, async () => value), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + assert.deepEqual(ledger.snapshot().entries.map(({ state, errorCode }) => ({ state, errorCode })), [ + { state: "in_flight", errorCode: undefined }, + ]); + } +}); + +test("an oversized replay result stays unknown across TTL and restart until reconciled", async () => { + let now = 1_000; + const options = { maxEntries: 1, ttlMs: 10, now: () => now }; + const ledger = new AidenIdempotencyLedger(undefined, options); + const scope = { deviceId: "device-1", route: "/git/push", resourceId: "workspace-1", key: "key-10mb-result" }; + const oversized = "x".repeat(Math.max(10 * 1_048_576, MAX_DURABLE_JSON_RESULT_BYTES + 1)); + let calls = 0; + await assert.rejects( + ledger.execute(scope, { action: "push" }, async () => { + calls += 1; + return oversized; + }, { operationId: "operation-oversized-result" }), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + const snapshot = ledger.snapshot(); + assert.equal(snapshot.entries[0]?.state, "in_flight"); + now = 2_000; + const restarted = new AidenIdempotencyLedger(JSON.parse(JSON.stringify(snapshot)), options); + assert.throws( + () => restarted.execute(scope, { action: "push" }, async () => { + calls += 1; + return "must-not-run"; + }), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_in_flight", + ); + assert.equal(calls, 1); + assert.throws( + () => restarted.reconcile("operation-oversized-result", { + state: "fulfilled", + result: oversized, + }), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + assert.equal(restarted.snapshot().entries[0]?.state, "in_flight"); + restarted.reconcile("operation-oversized-result", { + state: "fulfilled", + result: { operationId: "operation-oversized-result", state: "done" }, + }); + assert.deepEqual( + await restarted.execute(scope, { action: "push" }, async () => "must-not-run"), + { operationId: "operation-oversized-result", state: "done" }, + ); + assert.equal(calls, 1); +}); + +test("an aggregate snapshot overflow retains an unknown operation for reconciliation", async () => { + let now = 1_000; + const options = { maxEntries: 3, maxSnapshotBytes: 1_500, now: () => now }; + const firstScope = { deviceId: "device-1", route: "/git/push", resourceId: "workspace-1", key: "key-aggregate-1" }; + const secondScope = { ...firstScope, key: "key-aggregate-2" }; + const result = "x".repeat(700); + const ledger = new AidenIdempotencyLedger(undefined, options); + assert.equal(await ledger.execute(firstScope, { action: "push" }, async () => result), result); + await assert.rejects( + ledger.execute(secondScope, { action: "push" }, async () => result), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + const snapshot = ledger.snapshot(); + assert.deepEqual(snapshot.entries.map(({ state, errorCode }) => ({ state, errorCode })), [ + { state: "fulfilled", errorCode: undefined }, + { state: "in_flight", errorCode: undefined }, + ]); + now = 1_001; + const restarted = new AidenIdempotencyLedger(JSON.parse(JSON.stringify(snapshot)), options); + assert.equal(await restarted.execute(firstScope, { action: "push" }, async () => "must-not-run"), result); + assert.throws( + () => restarted.execute(secondScope, { action: "push" }, async () => "must-not-run"), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_in_flight", + ); +}); + +test("bounded replay snapshots survive an exact JSON round trip", async () => { + assert.ok(MAX_DURABLE_LEDGER_SNAPSHOT_BYTES > MAX_DURABLE_JSON_RESULT_BYTES); + const ledger = new AidenIdempotencyLedger(undefined, { maxEntries: 1, now: () => 1_000 }); + const scope = { deviceId: "device-1", route: "/git/push", resourceId: "workspace-1", key: "key-bounded-round-trip" }; + const result = { unicode: "😀é", values: [null, false, 0, "text"], nested: { accepted: true } }; + assert.deepEqual(await ledger.execute(scope, { action: "push" }, async () => result), result); + const snapshot = ledger.snapshot(); + const persisted = JSON.parse(JSON.stringify(snapshot)); + assert.deepEqual(persisted, snapshot); + const restarted = new AidenIdempotencyLedger(persisted, { maxEntries: 1, now: () => 1_000 }); + assert.deepEqual(await restarted.execute(scope, { action: "push" }, async () => ({ changed: true })), result); +}); + +test("persisted high-water time prevents a pruned key from reopening after clock rollback", async () => { + let now = 1_000; + const options = { maxEntries: 1, ttlMs: 100, now: () => now }; + const scope = { deviceId: "device-1", route: "/scheduled-tasks/task-1/run", resourceId: "task-1", key: "key-clock-rollback" }; + let calls = 0; + const ledger = new AidenIdempotencyLedger(undefined, options); + + assert.deepEqual( + await ledger.execute(scope, { action: "run" }, async () => ({ runId: `run-${++calls}` })), + { runId: "run-1" }, + ); + + now = 1_101; + const prunedSnapshot = ledger.snapshot(); + assert.deepEqual(prunedSnapshot.entries, []); + assert.equal(prunedSnapshot.lastObservedAt, 1_101); + const persistedSnapshot = JSON.parse(JSON.stringify(prunedSnapshot)) as typeof prunedSnapshot; + + now = 1_050; + const restarted = new AidenIdempotencyLedger(persistedSnapshot, options); + assert.equal(restarted.sizeForTesting(), 0); + assert.throws( + () => restarted.execute(scope, { action: "run" }, async () => ({ runId: `duplicate-${++calls}` })), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_in_flight", + ); + assert.equal(calls, 1); + + now = 1_101; + assert.throws( + () => restarted.execute(scope, { action: "run" }, async () => ({ runId: `duplicate-${++calls}` })), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_in_flight", + ); + now = 1_102; + assert.deepEqual( + await restarted.execute(scope, { action: "run" }, async () => ({ runId: `run-${++calls}` })), + { runId: "run-2" }, + ); + assert.equal(calls, 2); +}); + +test("legacy array snapshots are rejected while an omitted snapshot remains fresh initialization", async () => { + let now = 1_000; + const options = { maxEntries: 1, ttlMs: 100, now: () => now }; + const scope = { deviceId: "device-1", route: "/scheduled-tasks/task-1/run", resourceId: "task-1", key: "key-legacy-array" }; + const ledger = new AidenIdempotencyLedger(undefined, options); + await ledger.execute(scope, { action: "run" }, async () => ({ runId: "run-1" })); + + now = 1_101; + const legacyArray = ledger.snapshot().entries; + assert.deepEqual(legacyArray, []); + assert.throws( + () => new AidenIdempotencyLedger(legacyArray as never, options), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + + const fresh = new AidenIdempotencyLedger(undefined, options); + assert.deepEqual( + await fresh.execute(scope, { action: "run" }, async () => ({ runId: "fresh-run" })), + { runId: "fresh-run" }, + ); +}); + +test("rejected idempotent actions persist a bounded safe failure instead of remaining in flight", async () => { + let now = 1_000; + const options = { maxEntries: 1, ttlMs: 100, now: () => now }; + const scope = { deviceId: "device-1", route: "/workspaces", resourceId: "registry", key: "key-rejected" }; + const ledger = new AidenIdempotencyLedger(undefined, options); + await assert.rejects( + ledger.execute(scope, { mode: "scratch" }, async () => { throw new AidenOperationContractError("revision_conflict"); }), + (error) => error instanceof AidenOperationContractError && error.code === "revision_conflict", + ); + const snapshot = ledger.snapshot(); + assert.deepEqual(snapshot.entries.map(({ state, errorCode }) => ({ state, errorCode })), [ + { state: "rejected", errorCode: "revision_conflict" }, + ]); + const restarted = new AidenIdempotencyLedger(snapshot, options); + assert.throws( + () => restarted.execute(scope, { mode: "scratch" }, async () => ({ workspaceId: "duplicate" })), + (error) => error instanceof AidenOperationContractError && error.code === "revision_conflict", + ); + assert.throws( + () => restarted.execute({ ...scope, key: "other-key" }, { mode: "scratch" }, async () => ({ workspaceId: "other" })), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_capacity", + ); + now = 1_101; + assert.deepEqual( + await restarted.execute({ ...scope, key: "other-key" }, { mode: "scratch" }, async () => ({ workspaceId: "other" })), + { workspaceId: "other" }, + ); +}); + +test("a rejection that cannot fit the snapshot budget remains durably in flight", async () => { + const scope = { + deviceId: "device-1", + route: "/workspaces", + resourceId: "registry", + key: "key-tight-rejection-budget", + }; + const input = { mode: "scratch" }; + let releaseProbe: (() => void) | undefined; + const probe = new AidenIdempotencyLedger(undefined, { maxEntries: 1, now: () => 1_000 }); + const pendingProbe = probe.execute( + scope, + input, + () => new Promise((resolve) => { releaseProbe = () => resolve(true); }), + { operationId: "operation-tight-budget" }, + ); + await Promise.resolve(); + const inFlightBytes = Buffer.byteLength(JSON.stringify(probe.snapshot()), "utf8"); + releaseProbe?.(); + await pendingProbe; + + const ledger = new AidenIdempotencyLedger(undefined, { + maxEntries: 1, + maxSnapshotBytes: inFlightBytes, + now: () => 1_000, + }); + await assert.rejects( + ledger.execute( + scope, + input, + async () => { throw new AidenOperationContractError("revision_conflict"); }, + { operationId: "operation-tight-budget" }, + ), + (error) => error instanceof AidenOperationContractError && error.code === "revision_conflict", + ); + const snapshot = ledger.snapshot(); + assert.equal(Buffer.byteLength(JSON.stringify(snapshot), "utf8"), inFlightBytes); + assert.deepEqual(snapshot.entries.map(({ state, errorCode }) => ({ state, errorCode })), [ + { state: "in_flight", errorCode: undefined }, + ]); + assert.throws( + () => ledger.reconcile("operation-tight-budget", { + state: "rejected", + errorCode: "revision_conflict", + }), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + assert.equal(ledger.snapshot().entries[0]?.state, "in_flight"); + assert.throws( + () => ledger.execute(scope, input, async () => "must-not-run"), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_in_flight", + ); +}); + +test("invalid runtime error codes are canonicalized before durable rejection storage", async () => { + const ledger = new AidenIdempotencyLedger(undefined, { maxEntries: 1, now: () => 1_000 }); + const scope = { + deviceId: "device-1", + route: "/workspaces", + resourceId: "registry", + key: "key-invalid-error-code", + }; + const malformed = new AidenOperationContractError("internal_error"); + Object.defineProperty(malformed, "code", { value: "not_a_contract_code" }); + await assert.rejects( + ledger.execute(scope, { mode: "scratch" }, async () => { throw malformed; }), + (error) => error === malformed, + ); + assert.deepEqual(ledger.snapshot().entries.map(({ state, errorCode }) => ({ state, errorCode })), [ + { state: "rejected", errorCode: "internal_error" }, + ]); +}); + +test("idempotency rejects overflow while every bounded entry is still in flight", async () => { + let now = 1_000; + const ledger = new AidenIdempotencyLedger(undefined, { maxEntries: 1, ttlMs: 10, now: () => now }); + let resolveFirst: ((value: string) => void) | undefined; + const firstScope = { deviceId: "device-1", route: "/git/push", resourceId: "workspace-1", key: "key-1" }; + const first = ledger.execute( + firstScope, + { branch: "main" }, + () => new Promise((resolve) => { resolveFirst = resolve; }), + ); + await Promise.resolve(); + now = 1_100; + assert.strictEqual(ledger.execute(firstScope, { branch: "main" }, async () => "duplicate"), first); + const restarted = new AidenIdempotencyLedger(ledger.snapshot(), { maxEntries: 1, ttlMs: 10, now: () => now }); + assert.throws( + () => restarted.execute(firstScope, { branch: "main" }, async () => "duplicate-after-restart"), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_in_flight", + ); + assert.throws( + () => restarted.execute(firstScope, { branch: "other" }, async () => "conflict-after-restart"), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_conflict", + ); + assert.throws( + () => ledger.execute( + { deviceId: "device-1", route: "/git/push", resourceId: "workspace-1", key: "key-2" }, + { branch: "main" }, + async () => "second", + ), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_capacity", + ); + resolveFirst?.("first"); + assert.equal(await first, "first"); + assert.equal(ledger.sizeForTesting(), 1); + now = 1_111; + assert.equal(ledger.sizeForTesting(), 0); +}); + +test("restarted in-flight entries retain a safe operation reference until authoritative reconciliation", async () => { + let now = 1_000; + const options = { maxEntries: 1, ttlMs: 100, now: () => now }; + const scope = { deviceId: "device-1", route: "/scheduled-tasks/task-1/run", resourceId: "task-1", key: "raw-idempotency-secret" }; + let release: ((value: { runId: string }) => void) | undefined; + const ledger = new AidenIdempotencyLedger(undefined, options); + const pending = ledger.execute( + scope, + { action: "run" }, + () => new Promise<{ runId: string }>((resolve) => { release = resolve; }), + { operationId: "op_run_authoritative_1" }, + ); + await Promise.resolve(); + const snapshot = ledger.snapshot(); + assert.equal(snapshot.entries[0]?.operationId, "op_run_authoritative_1"); + assert.equal(snapshot.entries[0]?.expiresAt, null); + assert.equal(JSON.stringify(snapshot).includes(scope.key), false); + assert.throws( + () => ledger.reconcile("op_run_authoritative_1", { state: "fulfilled", result: { runId: "must-not-race-live-promise" } }), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_in_flight", + ); + + now = 10_000; + const restarted = new AidenIdempotencyLedger(snapshot, options); + assert.equal(restarted.sizeForTesting(), 1); + assert.throws( + () => restarted.execute(scope, { action: "run" }, async () => ({ runId: "duplicate" })), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_in_flight", + ); + assert.throws( + () => restarted.execute({ ...scope, key: "other-key" }, { action: "run" }, async () => ({ runId: "duplicate" })), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_capacity", + ); + + restarted.finalize("op_run_authoritative_1", { state: "fulfilled", result: { runId: "run-authoritative" } }); + assert.deepEqual( + await restarted.execute(scope, { action: "run" }, async () => ({ runId: "duplicate-after-reconcile" })), + { runId: "run-authoritative" }, + ); + assert.doesNotThrow(() => restarted.reconcile("op_run_authoritative_1", { state: "fulfilled", result: { runId: "run-authoritative" } })); + assert.throws( + () => restarted.reconcile("op_run_authoritative_1", { state: "fulfilled", result: { runId: "different" } }), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_conflict", + ); + + release?.({ runId: "run-original" }); + assert.deepEqual(await pending, { runId: "run-original" }); +}); + +test("restarted in-flight entries survive a clock rollback when created in the future", async () => { + let now = 1_000; + const options = { maxEntries: 1, ttlMs: 100, now: () => now }; + const scope = { deviceId: "device-1", route: "/scheduled-tasks/task-1/run", resourceId: "task-1", key: "raw-idempotency-secret" }; + let release: ((value: { runId: string }) => void) | undefined; + const ledger = new AidenIdempotencyLedger(undefined, options); + const pending = ledger.execute( + scope, + { action: "run" }, + () => new Promise<{ runId: string }>((resolve) => { release = resolve; }), + { operationId: "op_run_clock_rollback_1" }, + ); + await Promise.resolve(); + const snapshot = ledger.snapshot(); + + now = 500; + const restarted = new AidenIdempotencyLedger(snapshot, options); + assert.equal(restarted.sizeForTesting(), 1); + assert.throws( + () => restarted.execute(scope, { action: "run" }, async () => ({ runId: "duplicate" })), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_in_flight", + ); + restarted.reconcile("op_run_clock_rollback_1", { state: "fulfilled", result: { runId: "authoritative" } }); + assert.deepEqual( + await restarted.execute(scope, { action: "run" }, async () => ({ runId: "duplicate-after-reconcile" })), + { runId: "authoritative" }, + ); + + release?.({ runId: "run-original" }); + assert.deepEqual(await pending, { runId: "run-original" }); +}); + +test("restart rejects malformed timestamps instead of silently reopening idempotency scopes", () => { + const common = { scopeDigest: "scope-1", requestDigest: "request-1", operationId: "op-1" }; + const snapshots: AidenIdempotencySnapshotEntry[] = [ + { ...common, state: "in_flight", createdAt: Number.NaN, expiresAt: null }, + { ...common, state: "in_flight", createdAt: Number.POSITIVE_INFINITY, expiresAt: null }, + { ...common, state: "fulfilled", createdAt: 2, expiresAt: Number.NaN, result: "result" }, + { ...common, state: "fulfilled", createdAt: 2, expiresAt: 1, result: "result" }, + ]; + for (const entry of snapshots) { + assert.throws( + () => new AidenIdempotencyLedger({ version: 1, lastObservedAt: 1, entries: [entry] }, { now: () => 1 }), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + } + + const malformedSnapshots = [ + { version: 1, lastObservedAt: Number.NaN, entries: [] }, + { version: 1, lastObservedAt: 1, entries: {} }, + { version: 2, lastObservedAt: 1, entries: [] }, + { version: 1, lastObservedAt: 1, entries: [{ ...common, state: "invalid", createdAt: 1, expiresAt: null }] }, + ]; + for (const snapshot of malformedSnapshots) { + assert.throws( + () => new AidenIdempotencyLedger(snapshot as never, { now: () => 1 }), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + } +}); + +test("restart rejects idempotency entries with state-incompatible or non-durable terminal fields", () => { + const common = { scopeDigest: "scope-1", requestDigest: "request-1", operationId: "op-1", createdAt: 1, expiresAt: 2 }; + const malformedEntries = [ + { ...common, state: "fulfilled" as const }, + { ...common, state: "fulfilled" as const, result: undefined }, + { ...common, state: "fulfilled" as const, result: "result", errorCode: "internal_error" as const }, + { ...common, state: "rejected" as const }, + { ...common, state: "rejected" as const, errorCode: "internal_error" as const, result: "incompatible" }, + { ...common, state: "rejected" as const, errorCode: undefined }, + { ...common, state: "in_flight" as const, expiresAt: null, result: "incompatible" }, + { ...common, state: "in_flight" as const, expiresAt: null, errorCode: "internal_error" as const }, + { ...common, state: "in_flight" as const, expiresAt: 2 }, + ]; + for (const entry of malformedEntries) { + assert.throws( + () => new AidenIdempotencyLedger({ version: 1, lastObservedAt: 1, entries: [entry] }, { now: () => 1 }), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + } + + const validResultValues = [null, false, 0, ""]; + for (const result of validResultValues) { + assert.doesNotThrow(() => new AidenIdempotencyLedger({ + version: 1, + lastObservedAt: 1, + entries: [{ ...common, state: "fulfilled" as const, result }], + }, { now: () => 1 })); + } +}); + +test("restart enforces exact snapshot and state-specific entry allowlists", () => { + const common = { scopeDigest: "scope-1", requestDigest: "request-1", operationId: "op-1", createdAt: 1, expiresAt: null }; + const validEntry = { ...common, state: "in_flight" as const }; + const snapshotFor = (entry: unknown): unknown => ({ version: 1, lastObservedAt: 1, entries: [entry] }); + let envelopeGetterCalls = 0; + let entryGetterCalls = 0; + + const envelopeExtra = { version: 1, lastObservedAt: 1, entries: [], extra: true }; + const envelopeSymbol = { version: 1, lastObservedAt: 1, entries: [] }; + Object.defineProperty(envelopeSymbol, Symbol("extra"), { enumerable: true, value: true }); + const envelopeHidden = { version: 1, lastObservedAt: 1, entries: [] }; + Object.defineProperty(envelopeHidden, "extra", { enumerable: false, value: true }); + const envelopeAccessor = { version: 1, lastObservedAt: 1, entries: [] }; + Object.defineProperty(envelopeAccessor, "extra", { + enumerable: true, + get: () => { + envelopeGetterCalls += 1; + return true; + }, + }); + + const entryExtra = { ...validEntry, extra: true }; + const entrySymbol = { ...validEntry }; + Object.defineProperty(entrySymbol, Symbol("extra"), { enumerable: true, value: true }); + const entryHidden = { ...validEntry }; + Object.defineProperty(entryHidden, "extra", { enumerable: false, value: true }); + const entryAccessor = { ...validEntry }; + Object.defineProperty(entryAccessor, "extra", { + enumerable: true, + get: () => { + entryGetterCalls += 1; + return true; + }, + }); + + const malformedSnapshots = [ + envelopeExtra, + envelopeSymbol, + envelopeHidden, + envelopeAccessor, + snapshotFor(entryExtra), + snapshotFor(entrySymbol), + snapshotFor(entryHidden), + snapshotFor(entryAccessor), + ]; + for (const snapshot of malformedSnapshots) { + assert.throws( + () => new AidenIdempotencyLedger(snapshot as never, { now: () => 1 }), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + } + assert.equal(envelopeGetterCalls, 0); + assert.equal(entryGetterCalls, 0); + + assert.doesNotThrow(() => new AidenIdempotencyLedger({ + version: 1, + lastObservedAt: 1, + entries: [{ ...common, state: "in_flight" as const }], + }, { now: () => 1 })); + assert.doesNotThrow(() => new AidenIdempotencyLedger({ + version: 1, + lastObservedAt: 1, + entries: [{ ...common, state: "fulfilled" as const, expiresAt: 2, result: null }], + }, { now: () => 1 })); + assert.doesNotThrow(() => new AidenIdempotencyLedger({ + version: 1, + lastObservedAt: 1, + entries: [{ ...common, state: "rejected" as const, expiresAt: 2, errorCode: "internal_error" as const }], + }, { now: () => 1 })); +}); + +test("restart refuses to evict active idempotency entries when a snapshot exceeds capacity", () => { + const snapshot = { + version: 1 as const, + lastObservedAt: 1_000, + entries: [ + { scopeDigest: "scope-1", requestDigest: "request-1", operationId: "op-1", state: "in_flight" as const, createdAt: 1, expiresAt: null }, + { scopeDigest: "scope-2", requestDigest: "request-2", operationId: "op-2", state: "in_flight" as const, createdAt: 1, expiresAt: null }, + ], + }; + assert.throws( + () => new AidenIdempotencyLedger(snapshot, { maxEntries: 1, now: () => 1_000 }), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_capacity", + ); +}); + +test("revision checks and durable operation ownership fail closed", () => { + assert.doesNotThrow(() => assertRevision("revision-2", "revision-2")); + assert.throws(() => assertRevision("revision-1", "revision-2"), (error) => error instanceof AidenOperationContractError && error.code === "revision_conflict"); + const operations = new AidenDurableOperationRegistry(); + operations.start("operation-1", "device-1"); + operations.start("operation-1", "device-1"); + assert.throws(() => operations.start("operation-1", "device-2"), (error) => error instanceof AidenOperationContractError && error.code === "capability_denied"); + operations.assertOwner("operation-1", "device-1"); + assert.throws(() => operations.assertOwner("operation-1", "device-2"), (error) => error instanceof AidenOperationContractError && error.code === "capability_denied"); + operations.assertOwner("operation-1", "device-1"); + const restarted = new AidenDurableOperationRegistry(operations.snapshot()); + restarted.assertOwner("operation-1", "device-1"); + assert.throws(() => restarted.assertOwner("operation-1", "device-2"), (error) => error instanceof AidenOperationContractError && error.code === "capability_denied"); +}); + +test("durable operation starts enforce the live 10,000-entry bound and owner completion frees capacity", () => { + const operations = new AidenDurableOperationRegistry(); + for (let index = 0; index < MAX_DURABLE_OPERATION_ENTRIES; index += 1) { + operations.start(`operation-${index}`, `device-${index}`); + } + operations.start("operation-0", "device-0"); + assert.throws( + () => operations.start("operation-over-capacity", "device-over-capacity"), + (error) => error instanceof AidenOperationContractError && error.code === "idempotency_capacity", + ); + + assert.throws( + () => operations.complete("operation-0", "wrong-device"), + (error) => error instanceof AidenOperationContractError && error.code === "capability_denied", + ); + operations.assertOwner("operation-0", "device-0"); + operations.complete("operation-0", "device-0"); + assert.throws( + () => operations.assertOwner("operation-0", "device-0"), + (error) => error instanceof AidenOperationContractError && error.code === "capability_denied", + ); + operations.start("operation-after-completion", "device-after-completion"); + assert.equal(operations.snapshot().length, MAX_DURABLE_OPERATION_ENTRIES); +}); + +test("a full durable operation snapshot remains JSON-round-trip restorable", () => { + const entries = Array.from({ length: MAX_DURABLE_OPERATION_ENTRIES }, (_, index) => ({ + operationId: `operation-${index}`, + deviceId: `device-${index}`, + })); + const operations = new AidenDurableOperationRegistry(entries); + const persisted = JSON.parse(JSON.stringify(operations.snapshot())) as typeof entries; + const restarted = new AidenDurableOperationRegistry(persisted); + restarted.assertOwner("operation-0", "device-0"); + restarted.assertOwner(`operation-${MAX_DURABLE_OPERATION_ENTRIES - 1}`, `device-${MAX_DURABLE_OPERATION_ENTRIES - 1}`); + assert.equal(restarted.snapshot().length, MAX_DURABLE_OPERATION_ENTRIES); +}); + +test("durable operation registry restores only exact bounded identities and rejects duplicate IDs", () => { + const accessorEntry: Record = { deviceId: "device-1" }; + Object.defineProperty(accessorEntry, "operationId", { + enumerable: true, + get: () => "operation-1", + }); + const sparseEntries: unknown[] = []; + sparseEntries.length = 1; + const outOfRangeNumericEntries = [{ operationId: "operation-1", deviceId: "device-1" }] as unknown[]; + Object.defineProperty(outOfRangeNumericEntries, "4294967295", { enumerable: true, value: true }); + const extraEntry = { operationId: "operation-1", deviceId: "device-1", extra: true }; + const symbolEntries = [{ operationId: "operation-1", deviceId: "device-1" }]; + Object.defineProperty(symbolEntries, Symbol("extra"), { enumerable: true, value: true }); + const malformedEntries: unknown[] = [ + null, + {}, + sparseEntries, + outOfRangeNumericEntries, + [{ operationId: "", deviceId: "device-1" }], + [{ operationId: "operation-1", deviceId: "" }], + [{ operationId: "o".repeat(129), deviceId: "device-1" }], + [{ operationId: "operation-1", deviceId: "d".repeat(129) }], + [extraEntry], + [accessorEntry], + [Object.create(null)], + symbolEntries, + ]; + for (const entries of malformedEntries) { + assert.throws( + () => new AidenDurableOperationRegistry(entries as never), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + } + + const duplicateEntries = [ + [ + { operationId: "operation-1", deviceId: "device-1" }, + { operationId: "operation-1", deviceId: "device-1" }, + ], + [ + { operationId: "operation-1", deviceId: "device-1" }, + { operationId: "operation-1", deviceId: "device-2" }, + ], + ]; + for (const entries of duplicateEntries) { + assert.throws( + () => new AidenDurableOperationRegistry(entries), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + } + + const valid = new AidenDurableOperationRegistry([ + { operationId: "operation-1", deviceId: "device-1" }, + { operationId: "operation-2", deviceId: "device-1" }, + ]); + valid.assertOwner("operation-1", "device-1"); + valid.assertOwner("operation-2", "device-1"); + assert.throws( + () => valid.start("", "device-1"), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); + assert.throws( + () => valid.start("operation-3", ""), + (error) => error instanceof AidenOperationContractError && error.code === "internal_error", + ); +}); diff --git a/main/services/aiden-remote-operation-contract.ts b/main/services/aiden-remote-operation-contract.ts new file mode 100644 index 00000000..15338932 --- /dev/null +++ b/main/services/aiden-remote-operation-contract.ts @@ -0,0 +1,954 @@ +import { createHash, randomBytes } from "node:crypto"; +import { types as utilTypes } from "node:util"; + +export const MAX_DURABLE_OPERATION_ENTRIES = 10_000; +export const MAX_DURABLE_JSON_DEPTH = 128; +export const MAX_DURABLE_JSON_NODES = 100_000; +export const MAX_DURABLE_JSON_KEYS = 16_384; +export const MAX_DURABLE_JSON_ARRAY_LENGTH = 16_384; +export const MAX_DURABLE_JSON_STRING_LENGTH = 1_048_576; +export const MAX_DURABLE_JSON_RESULT_BYTES = 1_048_576; +export const MAX_DURABLE_LEDGER_SNAPSHOT_BYTES = 16 * 1_048_576; + +function canonical(value: unknown): string { + if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`; + if (value && typeof value === "object") { + return `{${Object.entries(value as Record) + .sort(([left], [right]) => left.localeCompare(right)) + .map(([key, child]) => `${JSON.stringify(key)}:${canonical(child)}`) + .join(",")}}`; + } + return JSON.stringify(value); +} + +export type AidenOperationContractErrorCode = + | "idempotency_conflict" + | "idempotency_capacity" + | "idempotency_in_flight" + | "revision_conflict" + | "capability_denied" + | "internal_error"; + +export class AidenOperationContractError extends Error { + constructor(readonly code: AidenOperationContractErrorCode) { + super(code); + } +} + +/** The external mutation may have committed, so its idempotency admission must never expire into a retry. */ +export class AidenOperationUnknownOutcomeError extends Error { + constructor() { + super("The operation outcome requires authoritative reconciliation."); + this.name = "AidenOperationUnknownOutcomeError"; + } +} + +export interface AidenIdempotencySnapshotEntry { + scopeDigest: string; + requestDigest: string; + operationId: string; + state: "in_flight" | "fulfilled" | "rejected"; + result?: unknown; + errorCode?: AidenOperationContractErrorCode; + createdAt: number; + expiresAt: number | null; +} + +export interface AidenIdempotencySnapshot { + version: 1; + lastObservedAt: number; + entries: AidenIdempotencySnapshotEntry[]; +} + +// A fresh ledger has no snapshot. The versioned envelope is the only format +// that carries the wall-clock high-water mark required for safe restoration. +type AidenIdempotencySnapshotInput = AidenIdempotencySnapshot | undefined; + +export interface AidenIdempotencyOperationReference { + operationId: string; +} + +export type AidenIdempotencyOutcome = + | { state: "fulfilled"; result: T } + | { state: "rejected"; errorCode: AidenOperationContractErrorCode }; + +interface AidenIdempotencyEntry { + requestDigest: string; + result?: Promise; + operationId: string; + createdAt: number; + expiresAt: number | null; + settled: boolean; + settledResult?: unknown; + rejectionCode?: AidenOperationContractErrorCode; +} + +function operationId(): string { + return `op_${randomBytes(24).toString("base64url")}`; +} + +function assertOperationId(value: unknown): asserts value is string { + if (typeof value !== "string" || value.length === 0 || value.length > 128) { + throw new AidenOperationContractError("internal_error"); + } +} + +function boundedPositiveIntegerOption(value: number | undefined, fallback: number, maximum: number): number { + if (value === undefined) return fallback; + if (!Number.isFinite(value)) throw new AidenOperationContractError("internal_error"); + return Math.min(maximum, Math.max(1, Math.floor(value))); +} + +type DurableJsonValue = null | boolean | number | string | DurableJsonValue[] | { [key: string]: DurableJsonValue }; + +interface DurableJsonLimits { + maxDepth: number; + maxNodes: number; + maxKeys: number; + maxArrayLength: number; + maxStringLength: number; +} + +const DURABLE_JSON_LIMITS: DurableJsonLimits = { + maxDepth: MAX_DURABLE_JSON_DEPTH, + maxNodes: MAX_DURABLE_JSON_NODES, + maxKeys: MAX_DURABLE_JSON_KEYS, + maxArrayLength: MAX_DURABLE_JSON_ARRAY_LENGTH, + maxStringLength: MAX_DURABLE_JSON_STRING_LENGTH, +}; + +// A snapshot envelope contains one small object per entry. Its structural +// limits are intentionally larger than a replay result's limits so that the +// hard 10,000-entry ledger bound remains usable; the aggregate UTF-8 budget +// below is the final memory/disk bound for the complete envelope. +const DURABLE_SNAPSHOT_JSON_LIMITS: DurableJsonLimits = { + maxDepth: MAX_DURABLE_JSON_DEPTH, + maxNodes: MAX_DURABLE_JSON_NODES + MAX_DURABLE_OPERATION_ENTRIES, + maxKeys: MAX_DURABLE_JSON_KEYS + MAX_DURABLE_OPERATION_ENTRIES * 8, + maxArrayLength: MAX_DURABLE_OPERATION_ENTRIES, + maxStringLength: MAX_DURABLE_JSON_STRING_LENGTH, +}; + +interface DurableJsonValidationState { + readonly seen: Set; + nodes: number; + keys: number; +} + +function isDenseArrayIndexKey(key: string, length: number): boolean { + if (!/^(?:0|[1-9]\d*)$/u.test(key)) return false; + const index = Number(key); + return Number.isSafeInteger(index) && index >= 0 && index < length && String(index) === key; +} + +function assertExactOwnEnumerableDataKeys(value: object, expectedKeys: readonly string[]): void { + const descriptors = Object.getOwnPropertyDescriptors(value); + const keys = Reflect.ownKeys(descriptors); + if ( + keys.length !== expectedKeys.length || + keys.some((key) => typeof key !== "string" || !expectedKeys.includes(key)) || + expectedKeys.some((key) => !Object.prototype.hasOwnProperty.call(descriptors, key)) + ) { + throw new AidenOperationContractError("internal_error"); + } + for (const key of expectedKeys) { + const descriptor = descriptors[key]; + if ( + !descriptor || + descriptor.enumerable !== true || + !("value" in descriptor) || + "get" in descriptor || + "set" in descriptor + ) { + throw new AidenOperationContractError("internal_error"); + } + } +} + +function durableStringLength(value: string): number { + let length = 0; + for (let index = 0; index < value.length; index += 1) { + const codeUnit = value.charCodeAt(index); + if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) { + const nextCodeUnit = value.charCodeAt(index + 1); + if (Number.isNaN(nextCodeUnit) || nextCodeUnit < 0xdc00 || nextCodeUnit > 0xdfff) { + throw new AidenOperationContractError("internal_error"); + } + index += 1; + } else if (codeUnit >= 0xdc00 && codeUnit <= 0xdfff) { + throw new AidenOperationContractError("internal_error"); + } + length += 1; + } + return length; +} + +function assertDurableString(value: string, maxLength: number): void { + if (durableStringLength(value) > maxLength) throw new AidenOperationContractError("internal_error"); +} + +/** + * Clone only values whose shape and values survive JSON.stringify/parse + * unchanged. A structured clone is deliberately not sufficient here: it + * accepts values (for example Date, undefined, and non-finite numbers) that + * either disappear or change when the idempotency snapshot is persisted. + */ +function cloneDurableJson(value: unknown, limits: DurableJsonLimits = DURABLE_JSON_LIMITS): DurableJsonValue { + const state: DurableJsonValidationState = { seen: new Set(), nodes: 0, keys: 0 }; + + const visit = (candidate: unknown, depth: number): DurableJsonValue => { + if (depth > limits.maxDepth) throw new AidenOperationContractError("internal_error"); + state.nodes += 1; + if (state.nodes > limits.maxNodes) throw new AidenOperationContractError("internal_error"); + if (candidate === null) return null; + if (typeof candidate === "boolean") return candidate; + if (typeof candidate === "string") { + assertDurableString(candidate, limits.maxStringLength); + return candidate; + } + if (typeof candidate === "number") { + if (!Number.isFinite(candidate) || Object.is(candidate, -0)) { + throw new AidenOperationContractError("internal_error"); + } + return candidate; + } + if (typeof candidate !== "object" || utilTypes.isProxy(candidate)) { + throw new AidenOperationContractError("internal_error"); + } + if (state.seen.has(candidate)) throw new AidenOperationContractError("internal_error"); + state.seen.add(candidate); + + if (Array.isArray(candidate)) { + if (candidate.length > limits.maxArrayLength) throw new AidenOperationContractError("internal_error"); + const descriptors = Object.getOwnPropertyDescriptors(candidate); + const keys = Reflect.ownKeys(descriptors); + const lengthDescriptor = Object.getOwnPropertyDescriptor(candidate, "length"); + const length = lengthDescriptor?.value; + if ( + keys.some( + (key) => + key !== "length" && + (typeof key !== "string" || !isDenseArrayIndexKey(key, length as number)), + ) || Object.getPrototypeOf(candidate) !== Array.prototype + ) { + throw new AidenOperationContractError("internal_error"); + } + if ( + !lengthDescriptor || + !Number.isSafeInteger(length) || + length < 0 || + lengthDescriptor.enumerable || + !("value" in lengthDescriptor) || + "get" in lengthDescriptor || + "set" in lengthDescriptor + ) { + throw new AidenOperationContractError("internal_error"); + } + const result: DurableJsonValue[] = []; + result.length = length; + for (let index = 0; index < length; index += 1) { + const descriptor = descriptors[String(index)]; + if ( + !descriptor || + descriptor.enumerable !== true || + !("value" in descriptor) || + "get" in descriptor || + "set" in descriptor + ) { + throw new AidenOperationContractError("internal_error"); + } + result[index] = visit(descriptor.value, depth + 1); + } + return result; + } + + if (Object.getPrototypeOf(candidate) !== Object.prototype) { + throw new AidenOperationContractError("internal_error"); + } + const descriptors = Object.getOwnPropertyDescriptors(candidate); + const result: { [key: string]: DurableJsonValue } = {}; + for (const key of Reflect.ownKeys(descriptors)) { + if (typeof key !== "string") throw new AidenOperationContractError("internal_error"); + const descriptor = descriptors[key]; + if ( + !descriptor || + descriptor.enumerable !== true || + !("value" in descriptor) || + "get" in descriptor || + "set" in descriptor + ) { + throw new AidenOperationContractError("internal_error"); + } + assertDurableString(key, limits.maxStringLength); + state.keys += 1; + if (state.keys > limits.maxKeys) throw new AidenOperationContractError("internal_error"); + Object.defineProperty(result, key, { + value: visit(descriptor.value, depth + 1), + enumerable: true, + configurable: true, + writable: true, + }); + } + return result; + }; + + try { + return visit(value, 0); + } catch (error) { + if (error instanceof AidenOperationContractError) throw error; + throw new AidenOperationContractError("internal_error"); + } +} + +function durableJsonUtf8Bytes(value: DurableJsonValue): number { + let serialized: string; + try { + serialized = JSON.stringify(value); + } catch { + throw new AidenOperationContractError("internal_error"); + } + if (typeof serialized !== "string") throw new AidenOperationContractError("internal_error"); + const bytes = Buffer.byteLength(serialized, "utf8"); + if (bytes > MAX_DURABLE_JSON_RESULT_BYTES) throw new AidenOperationContractError("internal_error"); + return bytes; +} + +function cloneDurableResult(value: unknown): DurableJsonValue { + const clone = cloneDurableJson(value); + durableJsonUtf8Bytes(clone); + return clone; +} + +function serializedSnapshotBytes(snapshot: AidenIdempotencySnapshot): number { + let serialized: string; + try { + serialized = JSON.stringify(snapshot); + } catch { + throw new AidenOperationContractError("internal_error"); + } + if (typeof serialized !== "string") throw new AidenOperationContractError("internal_error"); + return Buffer.byteLength(serialized, "utf8"); +} + +const operationContractErrorCodes = new Set([ + "idempotency_conflict", + "idempotency_capacity", + "idempotency_in_flight", + "revision_conflict", + "capability_denied", + "internal_error", +]); + +function assertSnapshotString(value: unknown): asserts value is string { + if (typeof value !== "string" || value.length === 0) { + throw new AidenOperationContractError("internal_error"); + } + assertDurableString(value, MAX_DURABLE_JSON_STRING_LENGTH); +} + +function assertFiniteTimestamp(value: unknown): asserts value is number { + if (typeof value !== "number" || !Number.isFinite(value)) { + throw new AidenOperationContractError("internal_error"); + } +} + +function assertSnapshotErrorCode(value: unknown): asserts value is AidenOperationContractErrorCode | undefined { + if (value !== undefined && (typeof value !== "string" || !operationContractErrorCodes.has(value as AidenOperationContractErrorCode))) { + throw new AidenOperationContractError("internal_error"); + } +} + +function assertSnapshotEntry(value: unknown): asserts value is AidenIdempotencySnapshotEntry { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new AidenOperationContractError("internal_error"); + } + cloneDurableJson(value); + const entry = value as Partial; + const state = entry.state; + const expectedKeys = state === "in_flight" + ? ["scopeDigest", "requestDigest", "operationId", "state", "createdAt", "expiresAt"] + : state === "fulfilled" + ? ["scopeDigest", "requestDigest", "operationId", "state", "result", "createdAt", "expiresAt"] + : state === "rejected" + ? ["scopeDigest", "requestDigest", "operationId", "state", "errorCode", "createdAt", "expiresAt"] + : []; + if (expectedKeys.length === 0) throw new AidenOperationContractError("internal_error"); + assertExactOwnEnumerableDataKeys(value, expectedKeys); + const hasResult = Object.prototype.hasOwnProperty.call(entry, "result"); + const hasErrorCode = Object.prototype.hasOwnProperty.call(entry, "errorCode"); + assertSnapshotString(entry.scopeDigest); + assertSnapshotString(entry.requestDigest); + assertOperationId(entry.operationId); + if (entry.state !== "in_flight" && entry.state !== "fulfilled" && entry.state !== "rejected") { + throw new AidenOperationContractError("internal_error"); + } + assertFiniteTimestamp(entry.createdAt); + if (entry.expiresAt !== null) assertFiniteTimestamp(entry.expiresAt); + if (entry.state === "in_flight") { + if (entry.expiresAt !== null || hasResult || hasErrorCode) { + throw new AidenOperationContractError("internal_error"); + } + return; + } + if (typeof entry.expiresAt !== "number" || entry.createdAt > entry.expiresAt) { + throw new AidenOperationContractError("internal_error"); + } + if (entry.state === "fulfilled") { + // Undefined is not a durable result: JSON.stringify omits it, so an + // explicitly undefined result would become indistinguishable from a + // missing result after restart. + if (!hasResult || entry.result === undefined || hasErrorCode) { + throw new AidenOperationContractError("internal_error"); + } + cloneDurableResult(entry.result); + return; + } + if (!hasErrorCode || hasResult) { + throw new AidenOperationContractError("internal_error"); + } + assertSnapshotErrorCode(entry.errorCode); + if (entry.errorCode === undefined) { + throw new AidenOperationContractError("internal_error"); + } +} + +function assertSnapshot(value: unknown): asserts value is AidenIdempotencySnapshot { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new AidenOperationContractError("internal_error"); + } + cloneDurableJson(value, DURABLE_SNAPSHOT_JSON_LIMITS); + assertExactOwnEnumerableDataKeys(value, ["version", "lastObservedAt", "entries"]); + const snapshot = value as Partial; + if ( + snapshot.version !== 1 || + !Array.isArray(snapshot.entries) || + snapshot.entries.length > MAX_DURABLE_OPERATION_ENTRIES + ) { + throw new AidenOperationContractError("internal_error"); + } + assertFiniteTimestamp(snapshot.lastObservedAt); + for (const entry of snapshot.entries) assertSnapshotEntry(entry); + if (serializedSnapshotBytes(snapshot as AidenIdempotencySnapshot) > MAX_DURABLE_LEDGER_SNAPSHOT_BYTES) { + throw new AidenOperationContractError("internal_error"); + } +} + +export class AidenIdempotencyLedger { + private readonly entries = new Map(); + private lastObservedAt: number; + private clockRollbackDetected = false; + + constructor( + snapshot: AidenIdempotencySnapshotInput = undefined, + private readonly options: { + maxEntries?: number; + ttlMs?: number; + now?: () => number; + maxSnapshotBytes?: number; + } = {}, + ) { + const now = this.readWallClock(); + // Do not accept the pre-envelope array shape here. A legacy array has no + // persisted lastObservedAt value, so restoring it could reopen a key after + // a forward clock jump followed by a rollback. Fresh initialization is + // represented by an omitted/undefined snapshot instead. + const restoredSnapshot = snapshot === undefined ? undefined : (assertSnapshot(snapshot), snapshot); + if ( + restoredSnapshot && + serializedSnapshotBytes(restoredSnapshot) > this.maxSnapshotBytes + ) { + throw new AidenOperationContractError("internal_error"); + } + this.lastObservedAt = restoredSnapshot?.lastObservedAt ?? now; + if (now < this.lastObservedAt) this.clockRollbackDetected = true; + else this.lastObservedAt = now; + const snapshotEntries: readonly AidenIdempotencySnapshotEntry[] = restoredSnapshot?.entries ?? []; + const retained: AidenIdempotencySnapshotEntry[] = []; + for (const candidate of snapshotEntries) { + // In-flight work has no local expiration. A clock rollback or a writer + // whose clock is ahead must never turn an unresolved operation into a + // fresh execution opportunity. + if ( + candidate.state === "in_flight" + || this.clockRollbackDetected + || (typeof candidate.expiresAt === "number" && candidate.expiresAt > now) + ) retained.push(candidate); + } + if (retained.length > this.maxEntries) { + throw new AidenOperationContractError("idempotency_capacity"); + } + for (const entry of retained) { + const settled = entry.state !== "in_flight"; + const result = entry.state === "fulfilled" ? cloneDurableResult(entry.result) : undefined; + const restoredOperationId = entry.operationId; + assertOperationId(restoredOperationId); + if ([...this.entries.values()].some((existing) => existing.operationId === restoredOperationId)) { + throw new AidenOperationContractError("idempotency_conflict"); + } + if (this.entries.has(entry.scopeDigest)) { + throw new AidenOperationContractError("idempotency_conflict"); + } + this.entries.set(entry.scopeDigest, { + requestDigest: entry.requestDigest, + operationId: restoredOperationId, + result: entry.state === "fulfilled" ? Promise.resolve().then(() => cloneDurableResult(result)) : undefined, + createdAt: entry.createdAt, + expiresAt: settled ? entry.expiresAt : null, + settled, + settledResult: entry.state === "fulfilled" ? result : undefined, + rejectionCode: entry.state === "rejected" ? entry.errorCode ?? "internal_error" : undefined, + }); + } + this.assertSnapshotBudget(this.buildSnapshot()); + } + + private get maxEntries(): number { + return boundedPositiveIntegerOption( + this.options.maxEntries, + MAX_DURABLE_OPERATION_ENTRIES, + MAX_DURABLE_OPERATION_ENTRIES, + ); + } + + private get maxSnapshotBytes(): number { + return boundedPositiveIntegerOption( + this.options.maxSnapshotBytes, + MAX_DURABLE_LEDGER_SNAPSHOT_BYTES, + MAX_DURABLE_LEDGER_SNAPSHOT_BYTES, + ); + } + + private get ttlMs(): number { + return Math.max(1, this.options.ttlMs ?? 24 * 60 * 60 * 1_000); + } + + private now(): number { + const now = this.readWallClock(); + if (now < this.lastObservedAt) this.clockRollbackDetected = true; + else if (now > this.lastObservedAt) { + this.lastObservedAt = now; + this.clockRollbackDetected = false; + } + return now; + } + + private readWallClock(): number { + const now = this.options.now?.() ?? Date.now(); + assertFiniteTimestamp(now); + return now; + } + + private effectiveNow(now: number): number { + return Math.max(now, this.lastObservedAt); + } + + private prune(now: number): void { + if (this.clockRollbackDetected) return; + for (const [key, entry] of this.entries) { + if (entry.settled && entry.expiresAt !== null && entry.expiresAt <= now) this.entries.delete(key); + } + } + + private snapshotEntry(scopeDigest: string, entry: AidenIdempotencyEntry): AidenIdempotencySnapshotEntry { + return { + scopeDigest, + requestDigest: entry.requestDigest, + operationId: entry.operationId, + state: !entry.settled + ? "in_flight" + : entry.rejectionCode + ? "rejected" + : "fulfilled", + ...(entry.rejectionCode + ? { errorCode: entry.rejectionCode } + : entry.settled + ? { result: cloneDurableResult(entry.settledResult) } + : {}), + createdAt: entry.createdAt, + expiresAt: entry.settled ? entry.expiresAt : null, + }; + } + + private buildSnapshot(candidate?: { scopeDigest: string; entry: AidenIdempotencyEntry }): AidenIdempotencySnapshot { + let candidateIncluded = false; + const entries = [...this.entries].map(([scopeDigest, entry]) => { + if (candidate && scopeDigest === candidate.scopeDigest) { + candidateIncluded = true; + return this.snapshotEntry(scopeDigest, candidate.entry); + } + return this.snapshotEntry(scopeDigest, entry); + }); + if (candidate && !candidateIncluded) { + entries.push(this.snapshotEntry(candidate.scopeDigest, candidate.entry)); + } + return { + version: 1, + lastObservedAt: this.lastObservedAt, + entries, + }; + } + + private assertSnapshotBudget(snapshot: AidenIdempotencySnapshot): void { + if (serializedSnapshotBytes(snapshot) > this.maxSnapshotBytes) { + throw new AidenOperationContractError("internal_error"); + } + } + + private assertPersistableSnapshot(snapshot: AidenIdempotencySnapshot): void { + assertSnapshot(snapshot); + this.assertSnapshotBudget(snapshot); + } + + private retainUnknownOutcome(entry: AidenIdempotencyEntry): void { + entry.result = undefined; + entry.settledResult = undefined; + entry.rejectionCode = undefined; + entry.settled = false; + entry.expiresAt = null; + } + + private rejectionCode(error: unknown): AidenOperationContractErrorCode { + try { + if (error instanceof AidenOperationContractError) { + const code = error.code; + if (operationContractErrorCodes.has(code)) return code; + } + } catch { + // Hostile or malformed error objects never gain durable code authority. + } + return "internal_error"; + } + + private settleRejected( + scopeDigest: string, + entry: AidenIdempotencyEntry, + error: unknown, + ): boolean { + if (entry.settled) return true; + const rejectedEntry: AidenIdempotencyEntry = { + ...entry, + result: undefined, + settledResult: undefined, + rejectionCode: this.rejectionCode(error), + settled: true, + expiresAt: this.effectiveNow(this.now()) + this.ttlMs, + }; + try { + this.assertPersistableSnapshot( + this.buildSnapshot({ scopeDigest, entry: rejectedEntry }), + ); + } catch { + this.retainUnknownOutcome(entry); + return false; + } + entry.result = undefined; + entry.settledResult = undefined; + entry.rejectionCode = rejectedEntry.rejectionCode; + entry.settled = true; + entry.expiresAt = rejectedEntry.expiresAt; + return true; + } + + private admit(): void { + if (this.entries.size < this.maxEntries) return; + throw new AidenOperationContractError("idempotency_capacity"); + } + + execute( + scope: { deviceId: string; route: string; resourceId: string; key: string }, + input: unknown, + action: () => Promise, + reference?: AidenIdempotencyOperationReference, + ): Promise { + const now = this.now(); + this.prune(now); + const ledgerKey = createHash("sha256").update(canonical(scope)).digest("base64url"); + const requestDigest = createHash("sha256").update(canonical(input)).digest("base64url"); + const existing = this.entries.get(ledgerKey); + if (existing) { + if (existing.requestDigest !== requestDigest) throw new AidenOperationContractError("idempotency_conflict"); + if (existing.rejectionCode) throw new AidenOperationContractError(existing.rejectionCode); + if (!existing.result) throw new AidenOperationContractError("idempotency_in_flight"); + if (existing.settled) { + return Promise.resolve().then(() => cloneDurableResult(existing.settledResult)) as Promise; + } + return existing.result as Promise; + } + if (this.clockRollbackDetected) throw new AidenOperationContractError("idempotency_in_flight"); + this.admit(); + const restoredOperationId = reference?.operationId ?? operationId(); + assertOperationId(restoredOperationId); + if ([...this.entries.values()].some((entry) => entry.operationId === restoredOperationId)) { + throw new AidenOperationContractError("idempotency_conflict"); + } + const entry: AidenIdempotencyEntry = { + requestDigest, + operationId: restoredOperationId, + createdAt: this.effectiveNow(now), + expiresAt: null, + settled: false, + }; + this.assertSnapshotBudget(this.buildSnapshot({ scopeDigest: ledgerKey, entry })); + this.entries.set(ledgerKey, entry); + const result = Promise.resolve() + .then(action) + .then( + (value) => { + let durableValue: DurableJsonValue; + let settledAt: number; + try { + durableValue = cloneDurableResult(value); + settledAt = this.effectiveNow(this.now()) + this.ttlMs; + const settledEntry: AidenIdempotencyEntry = { + ...entry, + expiresAt: settledAt, + settled: true, + settledResult: durableValue, + rejectionCode: undefined, + }; + this.assertPersistableSnapshot( + this.buildSnapshot({ scopeDigest: ledgerKey, entry: settledEntry }), + ); + } catch (error) { + // The action may already have committed an external mutation. If + // its result cannot be durably represented, retain the stable + // operation reference as an indefinite unknown outcome. Expiring + // it into a retry would risk executing the mutation twice. + this.retainUnknownOutcome(entry); + throw error instanceof AidenOperationContractError + ? error + : new AidenOperationContractError("internal_error"); + } + entry.settledResult = durableValue; + entry.settled = true; + entry.expiresAt = settledAt; + entry.rejectionCode = undefined; + // Keep the stored value isolated from callers that mutate a result + // after fulfillment. The snapshot owns its separate durable clone. + return cloneDurableResult(durableValue); + }, + (error) => { + if (error instanceof AidenOperationUnknownOutcomeError) { + this.retainUnknownOutcome(entry); + throw new AidenOperationContractError("idempotency_in_flight"); + } + this.settleRejected(ledgerKey, entry, error); + throw error; + }, + ); + entry.result = result; + return result as Promise; + } + + reconcile(operationIdToFinalize: string, outcome: AidenIdempotencyOutcome): void { + assertOperationId(operationIdToFinalize); + this.prune(this.now()); + const entryRecord = [...this.entries].find(([, candidate]) => candidate.operationId === operationIdToFinalize); + if (!entryRecord) throw new AidenOperationContractError("idempotency_in_flight"); + const [ledgerKey, entry] = entryRecord; + if (entry.settled) { + const durableOutcomeResult = outcome.state === "fulfilled" ? cloneDurableResult(outcome.result) : undefined; + if (outcome.state === "rejected") assertSnapshotErrorCode(outcome.errorCode); + const sameOutcome = outcome.state === "fulfilled" + ? !entry.rejectionCode && canonical(entry.settledResult) === canonical(durableOutcomeResult) + : entry.rejectionCode === outcome.errorCode; + if (!sameOutcome) throw new AidenOperationContractError("idempotency_conflict"); + return; + } + if (entry.result) throw new AidenOperationContractError("idempotency_in_flight"); + if (outcome.state === "fulfilled") { + let durableResult: DurableJsonValue; + let settledAt: number; + try { + durableResult = cloneDurableResult(outcome.result); + settledAt = this.effectiveNow(this.now()) + this.ttlMs; + const settledEntry: AidenIdempotencyEntry = { + ...entry, + expiresAt: settledAt, + settled: true, + settledResult: durableResult, + rejectionCode: undefined, + }; + this.assertPersistableSnapshot( + this.buildSnapshot({ scopeDigest: ledgerKey, entry: settledEntry }), + ); + } catch (error) { + this.retainUnknownOutcome(entry); + throw error instanceof AidenOperationContractError + ? error + : new AidenOperationContractError("internal_error"); + } + entry.settledResult = durableResult; + entry.result = Promise.resolve().then(() => cloneDurableResult(entry.settledResult)); + entry.rejectionCode = undefined; + entry.settled = true; + entry.expiresAt = settledAt; + } else { + assertSnapshotErrorCode(outcome.errorCode); + if (outcome.errorCode === undefined) { + throw new AidenOperationContractError("internal_error"); + } + if (!this.settleRejected( + ledgerKey, + entry, + new AidenOperationContractError(outcome.errorCode), + )) { + throw new AidenOperationContractError("internal_error"); + } + } + } + + finalize(operationIdToFinalize: string, outcome: AidenIdempotencyOutcome): void { + this.reconcile(operationIdToFinalize, outcome); + } + + snapshot(): AidenIdempotencySnapshot { + this.prune(this.now()); + const snapshot = this.buildSnapshot(); + this.assertSnapshotBudget(snapshot); + return snapshot; + } + + sizeForTesting(): number { + this.prune(this.now()); + return this.entries.size; + } +} + +export function assertRevision(expected: string, current: string): void { + if (expected !== current) throw new AidenOperationContractError("revision_conflict"); +} + +export class AidenDurableOperationRegistry { + private readonly owners = new Map(); + + constructor(entries: readonly { operationId: string; deviceId: string }[] = []) { + assertDurableOperationEntries(entries); + for (const entry of entries) { + if (this.owners.has(entry.operationId)) { + throw new AidenOperationContractError("internal_error"); + } + this.owners.set(entry.operationId, entry.deviceId); + } + } + + start(operationId: string, deviceId: string): void { + assertOperationId(operationId); + assertOperationId(deviceId); + const existing = this.owners.get(operationId); + if (existing && existing !== deviceId) throw new AidenOperationContractError("capability_denied"); + if (existing) return; + if (this.owners.size >= MAX_DURABLE_OPERATION_ENTRIES) { + throw new AidenOperationContractError("idempotency_capacity"); + } + this.owners.set(operationId, deviceId); + } + + complete(operationId: string, deviceId: string): void { + assertOperationId(operationId); + assertOperationId(deviceId); + if (this.owners.get(operationId) !== deviceId) { + throw new AidenOperationContractError("capability_denied"); + } + this.owners.delete(operationId); + } + + remove(operationId: string, deviceId: string): void { + this.complete(operationId, deviceId); + } + + assertOwner(operationId: string, deviceId: string): void { + assertOperationId(operationId); + assertOperationId(deviceId); + if (this.owners.get(operationId) !== deviceId) { + throw new AidenOperationContractError("capability_denied"); + } + } + + snapshot(): { operationId: string; deviceId: string }[] { + return [...this.owners].map(([operationId, deviceId]) => ({ operationId, deviceId })); + } +} + +function assertDurableOperationEntries( + value: unknown, +): asserts value is readonly { operationId: string; deviceId: string }[] { + try { + if ( + !Array.isArray(value) || + utilTypes.isProxy(value) || + Object.getPrototypeOf(value) !== Array.prototype || + value.length > MAX_DURABLE_OPERATION_ENTRIES + ) { + throw new AidenOperationContractError("internal_error"); + } + const descriptors = Object.getOwnPropertyDescriptors(value); + const keys = Reflect.ownKeys(descriptors); + const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length"); + if ( + !lengthDescriptor || + lengthDescriptor.enumerable || + !("value" in lengthDescriptor) || + "get" in lengthDescriptor || + "set" in lengthDescriptor || + !Number.isSafeInteger(lengthDescriptor.value) || + lengthDescriptor.value < 0 || + keys.some( + (key) => + key !== "length" && + (typeof key !== "string" || !isDenseArrayIndexKey(key, value.length)), + ) + ) { + throw new AidenOperationContractError("internal_error"); + } + for (let index = 0; index < value.length; index += 1) { + const descriptor = descriptors[String(index)]; + if ( + !descriptor || + descriptor.enumerable !== true || + !("value" in descriptor) || + "get" in descriptor || + "set" in descriptor + ) { + throw new AidenOperationContractError("internal_error"); + } + assertDurableOperationEntry(descriptor.value); + } + } catch (error) { + if (error instanceof AidenOperationContractError) throw error; + throw new AidenOperationContractError("internal_error"); + } +} + +function assertDurableOperationEntry(value: unknown): asserts value is { operationId: string; deviceId: string } { + if ( + typeof value !== "object" || + value === null || + Array.isArray(value) || + utilTypes.isProxy(value) || + Object.getPrototypeOf(value) !== Object.prototype + ) { + throw new AidenOperationContractError("internal_error"); + } + const descriptors = Object.getOwnPropertyDescriptors(value); + const keys = Reflect.ownKeys(descriptors); + if ( + keys.length !== 2 || + !keys.every((key) => key === "operationId" || key === "deviceId") + ) { + throw new AidenOperationContractError("internal_error"); + } + for (const key of ["operationId", "deviceId"] as const) { + const descriptor = descriptors[key]; + if ( + !descriptor || + descriptor.enumerable !== true || + !("value" in descriptor) || + "get" in descriptor || + "set" in descriptor + ) { + throw new AidenOperationContractError("internal_error"); + } + assertOperationId(descriptor.value); + } +} diff --git a/main/services/aiden-remote-pairing.test.ts b/main/services/aiden-remote-pairing.test.ts new file mode 100644 index 00000000..440e77f6 --- /dev/null +++ b/main/services/aiden-remote-pairing.test.ts @@ -0,0 +1,132 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { + AidenRemotePairingService, + parseAidenRemotePairingExchangeInput, +} from "./aiden-remote-pairing.js"; +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; + +const endpoint = "https://aiden.example.test/api/aiden/v1"; +const fingerprint = `sha256/${Buffer.alloc(32, 4).toString("base64")}`; + +function fixture(options: { issueFails?: boolean } = {}) { + let now = 1_000; + let issued = 0; + const service = new AidenRemotePairingService( + "instance-1", + { + issueDevice: async (input) => { + issued += 1; + if (options.issueFails) throw new Error("disk failed"); + return { + credential: Buffer.alloc(32, 8).toString("base64url"), + device: { + id: "device-1", + name: input.name, + type: input.type, + clientVersion: input.clientVersion, + capabilities: [...(input.capabilities ?? [])], + createdAt: now, + lastSeenAt: now, + }, + }; + }, + }, + { + now: () => now, + randomBytes: (size) => Buffer.alloc(size, 7), + }, + ); + return { + service, + issued: () => issued, + advance: (milliseconds: number) => { + now += milliseconds; + }, + }; +} + +function exchange(secret: string) { + return { + secret, + deviceName: "Sambit’s iPhone", + deviceType: "iphone" as const, + clientVersion: "1.0", + }; +} + +test("pairing opens for exactly five minutes and consumes its 256-bit secret once", async () => { + const pairing = fixture(); + const bootstrap = pairing.service.begin(endpoint, fingerprint); + assert.equal(bootstrap.expiresAt, new Date(301_000).toISOString()); + assert.equal(bootstrap.secret.length, 43); + const result = await pairing.service.exchange(exchange(bootstrap.secret), "lan:phone"); + assert.equal(result.deviceId, "device-1"); + assert.equal(result.endpoint, endpoint); + assert.equal(result.capabilities.includes("workspace:manage"), true); + await assert.rejects( + pairing.service.exchange(exchange(bootstrap.secret), "lan:phone"), + (error: unknown) => + error instanceof AidenRemoteServiceError && + error.code === "pairing_already_used", + ); + assert.equal(pairing.issued(), 1); +}); + +test("an expired, closed, or invalid pairing window fails with stable safe codes", async () => { + const pairing = fixture(); + await assert.rejects( + pairing.service.exchange(exchange("x".repeat(43)), "source"), + (error: unknown) => + error instanceof AidenRemoteServiceError && error.code === "pairing_closed", + ); + const bootstrap = pairing.service.begin(endpoint, fingerprint); + await assert.rejects( + pairing.service.exchange(exchange("x".repeat(43)), "source"), + (error: unknown) => + error instanceof AidenRemoteServiceError && + error.code === "authentication_required", + ); + pairing.advance(5 * 60_000); + await assert.rejects( + pairing.service.exchange(exchange(bootstrap.secret), "source"), + (error: unknown) => + error instanceof AidenRemoteServiceError && error.code === "pairing_expired", + ); +}); + +test("pairing persistence failure still burns the one-time secret", async () => { + const pairing = fixture({ issueFails: true }); + const bootstrap = pairing.service.begin(endpoint, fingerprint); + await assert.rejects( + pairing.service.exchange(exchange(bootstrap.secret), "source"), + /disk failed/u, + ); + await assert.rejects( + pairing.service.exchange(exchange(bootstrap.secret), "source"), + (error: unknown) => + error instanceof AidenRemoteServiceError && + error.code === "pairing_already_used", + ); + assert.equal(pairing.issued(), 1); +}); + +test("pairing DTOs are exact and per-source attempts are rate limited", async () => { + assert.throws( + () => parseAidenRemotePairingExchangeInput({ ...exchange("x".repeat(43)), extra: true }), + (error: unknown) => + error instanceof AidenRemoteServiceError && error.code === "invalid_request", + ); + const pairing = fixture(); + pairing.service.begin(endpoint, fingerprint); + for (let index = 0; index < 10; index += 1) { + await assert.rejects( + pairing.service.exchange(exchange("x".repeat(43)), "attacker"), + ); + } + await assert.rejects( + pairing.service.exchange(exchange("x".repeat(43)), "attacker"), + (error: unknown) => + error instanceof AidenRemoteServiceError && error.code === "rate_limited", + ); +}); diff --git a/main/services/aiden-remote-pairing.ts b/main/services/aiden-remote-pairing.ts new file mode 100644 index 00000000..916c2941 --- /dev/null +++ b/main/services/aiden-remote-pairing.ts @@ -0,0 +1,251 @@ +import { createHash, randomBytes, timingSafeEqual } from "node:crypto"; +import { + AIDEN_REMOTE_CAPABILITIES, + AIDEN_REMOTE_PROTOCOL_VERSION, + assertAidenRemoteEndpoint, + type AidenRemoteCapability, +} from "./aiden-remote-protocol.js"; +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; +import type { + AidenRemoteDeviceType, + AidenRemoteStateRegistry, +} from "./aiden-remote-state.js"; + +const PAIRING_WINDOW_MS = 5 * 60_000; +const PAIRING_ATTEMPTS_PER_SOURCE = 10; +const PAIRING_RATE_WINDOW_MS = 60_000; +const MAX_RATE_LIMIT_SOURCES = 1_024; + +export interface AidenRemotePairingBootstrap { + protocolVersion: typeof AIDEN_REMOTE_PROTOCOL_VERSION; + instanceId: string; + endpoint: string; + serverSpkiSha256: string; + secret: string; + expiresAt: string; +} + +export interface AidenRemotePairingExchangeInput { + secret: string; + deviceName: string; + deviceType: AidenRemoteDeviceType; + clientVersion: string; +} + +export interface AidenRemotePairingExchangeResponse { + protocolVersion: typeof AIDEN_REMOTE_PROTOCOL_VERSION; + instanceId: string; + deviceId: string; + credential: string; + capabilities: AidenRemoteCapability[]; + endpoint: string; + serverSpkiSha256: string; +} + +interface PairingWindow { + secretDigest: Buffer; + endpoint: string; + serverSpkiSha256: string; + expiresAt: number; + consumed: boolean; +} + +export interface AidenRemotePairingDependencies { + now(): number; + randomBytes(size: number): Buffer; +} + +function characterLength(value: string): number { + let length = 0; + for (const _character of value) length += 1; + return length; +} + +function bounded(value: unknown, maximum: number): value is string { + return ( + typeof value === "string" && + value.length > 0 && + characterLength(value) <= maximum + ); +} + +function digestSecret(secret: string): Buffer { + return createHash("sha256").update(secret).digest(); +} + +function sameDigest(left: Buffer, right: Buffer): boolean { + return left.length === right.length && timingSafeEqual(left, right); +} + +export function parseAidenRemotePairingExchangeInput( + value: unknown, +): AidenRemotePairingExchangeInput { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new AidenRemoteServiceError( + "invalid_request", + "Pairing details are invalid.", + 400, + ); + } + const record = value as Record; + const allowed = new Set([ + "secret", + "deviceName", + "deviceType", + "clientVersion", + ]); + if ( + Object.keys(record).length !== allowed.size || + Object.keys(record).some((key) => !allowed.has(key)) || + typeof record.secret !== "string" || + !/^[A-Za-z0-9_-]{43}$/u.test(record.secret) || + !bounded(record.deviceName, 80) || + (record.deviceType !== "iphone" && record.deviceType !== "ipad") || + !bounded(record.clientVersion, 40) + ) { + throw new AidenRemoteServiceError( + "invalid_request", + "Pairing details are invalid.", + 400, + ); + } + return { + secret: record.secret, + deviceName: record.deviceName, + deviceType: record.deviceType, + clientVersion: record.clientVersion, + }; +} + +export class AidenRemotePairingService { + private window: PairingWindow | null = null; + private attempts = new Map(); + + constructor( + private readonly instanceId: string, + private readonly devices: Pick, + private readonly dependencies: AidenRemotePairingDependencies = { + now: Date.now, + randomBytes, + }, + ) {} + + begin( + endpoint: string, + serverSpkiSha256: string, + ): AidenRemotePairingBootstrap { + assertAidenRemoteEndpoint(endpoint); + if (!/^sha256\/[A-Za-z0-9+/]{43}=$/u.test(serverSpkiSha256)) { + throw new Error("Aiden Remote server fingerprint is invalid."); + } + const secret = this.dependencies.randomBytes(32).toString("base64url"); + const expiresAt = this.dependencies.now() + PAIRING_WINDOW_MS; + this.window = { + secretDigest: digestSecret(secret), + endpoint, + serverSpkiSha256, + expiresAt, + consumed: false, + }; + return { + protocolVersion: AIDEN_REMOTE_PROTOCOL_VERSION, + instanceId: this.instanceId, + endpoint, + serverSpkiSha256, + secret, + expiresAt: new Date(expiresAt).toISOString(), + }; + } + + close(): void { + this.window = null; + } + + private admitAttempt(source: string): void { + const now = this.dependencies.now(); + const cutoff = now - PAIRING_RATE_WINDOW_MS; + for (const [key, timestamps] of this.attempts) { + const retained = timestamps.filter((timestamp) => timestamp > cutoff); + if (retained.length === 0) this.attempts.delete(key); + else if (retained.length !== timestamps.length) this.attempts.set(key, retained); + } + if (!this.attempts.has(source) && this.attempts.size >= MAX_RATE_LIMIT_SOURCES) { + throw new AidenRemoteServiceError( + "rate_limited", + "Too many pairing attempts. Try again shortly.", + 429, + true, + { retryAfterSeconds: 60 }, + ); + } + const timestamps = this.attempts.get(source) ?? []; + if (timestamps.length >= PAIRING_ATTEMPTS_PER_SOURCE) { + throw new AidenRemoteServiceError( + "rate_limited", + "Too many pairing attempts. Try again shortly.", + 429, + true, + { retryAfterSeconds: 60 }, + ); + } + timestamps.push(now); + this.attempts.set(source, timestamps); + } + + async exchange( + value: unknown, + source: string, + ): Promise { + this.admitAttempt(source); + const input = parseAidenRemotePairingExchangeInput(value); + const current = this.window; + if (!current) { + throw new AidenRemoteServiceError( + "pairing_closed", + "Open a new pairing window in Aiden Settings.", + 403, + ); + } + if (this.dependencies.now() >= current.expiresAt) { + this.window = null; + throw new AidenRemoteServiceError( + "pairing_expired", + "This pairing code expired. Open a new pairing window.", + 403, + ); + } + if (current.consumed) { + throw new AidenRemoteServiceError( + "pairing_already_used", + "This pairing code was already used.", + 409, + ); + } + if (!sameDigest(current.secretDigest, digestSecret(input.secret))) { + throw new AidenRemoteServiceError( + "authentication_required", + "The pairing code is not valid.", + 401, + ); + } + + // Consume synchronously before any durable work. Concurrent exchanges and + // persistence failures can never turn this high-authority secret reusable. + current.consumed = true; + const issued = await this.devices.issueDevice({ + name: input.deviceName, + type: input.deviceType, + clientVersion: input.clientVersion, + capabilities: AIDEN_REMOTE_CAPABILITIES, + }); + return { + protocolVersion: AIDEN_REMOTE_PROTOCOL_VERSION, + instanceId: this.instanceId, + deviceId: issued.device.id, + credential: issued.credential, + capabilities: [...issued.device.capabilities], + endpoint: current.endpoint, + serverSpkiSha256: current.serverSpkiSha256, + }; + } +} diff --git a/main/services/aiden-remote-protocol.test.ts b/main/services/aiden-remote-protocol.test.ts new file mode 100644 index 00000000..e2398d97 --- /dev/null +++ b/main/services/aiden-remote-protocol.test.ts @@ -0,0 +1,674 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import path from "node:path"; +import test from "node:test"; +import { + AIDEN_REMOTE_BASE_PATH, + AIDEN_REMOTE_CAPABILITIES, + AIDEN_REMOTE_ERROR_CODES, + AIDEN_REMOTE_EVENT_TYPES, + AIDEN_REMOTE_MAX_SSE_FRAME_BYTES, + AIDEN_REMOTE_PROTOCOL_VERSION, + parseAidenRemoteStreamEvent, + parseAidenSseFrames, + reconcileAidenSseFrames, + parseAidenRemoteContractFixture, +} from "./aiden-remote-protocol.js"; + +const protocolRoot = path.resolve(process.cwd(), "protocol/aiden-remote/v1"); + +async function json(relativePath: string): Promise { + return JSON.parse(await readFile(path.join(protocolRoot, relativePath), "utf8")); +} + +function record(value: unknown, label: string): Record { + assert(value && typeof value === "object" && !Array.isArray(value), `${label} must be an object`); + return value as Record; +} + +// Keep this authority vector byte-for-byte aligned with +// AidenRemotePhase0Tests.testEndpointAuthorityGrammarMatchesDesktopVectors. +const endpointAuthorityVectors: readonly [string, boolean][] = [ + ["aiden.example.test", true], + ["localhost", true], + ["aiden-lan.local", true], + ["192.168.1.42", true], + ["192.0.2.1:443", true], + ["aiden.0", false], + ["aiden.123", false], + ["aiden.example.test:1", true], + ["aiden.example.test:65535", true], + ["[::]", true], + ["[::1]", true], + ["[2001:db8::1]:443", true], + ["[::ffff:192.0.2.1]", true], + ["aiden.example.test:0443", false], + ["aiden.example.test:00001", false], + ["aiden.example.test:0", false], + ["aiden.example.test:65536", false], + ["aiden.example.test:abc", false], + ["aiden.example.test:", false], + [":443", false], + ["aiden.example.test:1:2", false], + ["aiden.example.test%2eexample.test", false], + ["aiden.example.test%25", false], + ["aiden.example.test", false], + ["aiden\u{0301}.example.test", false], + ["aiden.example.test\u{0009}", false], + ["aiden.example.test\u{001f}", false], + ["aiden.example.test\u{007f}", false], + ["aiden..example.test", false], + ["-aiden.example.test", false], + ["aiden-.example.test", false], + ["aiden_example.test", false], + ["123", false], + ["192.168.001.1", false], + ["256.1.1.1", false], + ["[fe80::1%25en0]", false], + ["[v1.fe]", false], + ["[::1", false], + ["[::1]x", false], + ["::1", false], + ["[::1]:00001", false], + ["[::1]:65536", false], + ["[2001:db8::1::2]", false], + ["[192.0.2.1::]", false], + ["[::ffff:192.000.2.1]", false], + ["[2001:db8:0:0:0:0:0]", false], +]; + +test("shared Aiden Remote v1 fixture is complete, ordered, and contains no unsafe wire keys", async () => { + const fixture = parseAidenRemoteContractFixture(await json("fixtures/contract.json")); + assert.equal(fixture.protocolVersion, AIDEN_REMOTE_PROTOCOL_VERSION); + assert.deepEqual(fixture.capabilities, AIDEN_REMOTE_CAPABILITIES); + assert.deepEqual( + new Set(fixture.events.map((event) => event.type)), + new Set(AIDEN_REMOTE_EVENT_TYPES), + ); + assert.equal(JSON.stringify(fixture).includes("/Users/"), false); + assert.equal(JSON.stringify(fixture).includes("BEGIN PRIVATE KEY"), false); +}); + +test("OpenAPI freezes every planned route under authenticated Aiden v1 semantics", async () => { + const document = record(await json("openapi.json"), "OpenAPI"); + assert.equal(document.openapi, "3.1.0"); + const info = record(document.info, "OpenAPI info"); + assert.equal(info.version, "1.0.0"); + const paths = record(document.paths, "OpenAPI paths"); + const requiredPaths = [ + "/health", + "/pairing/exchange", + "/server", + "/workspaces", + "/workspaces/{workspaceId}", + "/workspace-browser/roots", + "/workspace-browser/children", + "/workspace-browser/selections", + "/chats", + "/chats/{chatId}", + "/chats/{chatId}/move", + "/chats/{chatId}/turns", + "/chats/{chatId}/attachments", + "/chats/{chatId}/attachments/{attachmentId}", + "/streams/{streamId}", + "/streams/{streamId}/events", + "/streams/{streamId}/cancel", + "/approvals/{approvalId}/respond", + "/models", + "/usage", + "/workspaces/{workspaceId}/files", + "/workspaces/{workspaceId}/files/{fileId}", + "/workspaces/{workspaceId}/git/review", + "/workspaces/{workspaceId}/git/diff", + "/workspaces/{workspaceId}/git/branches", + "/workspaces/{workspaceId}/git/checkout", + "/workspaces/{workspaceId}/git/commit", + "/workspaces/{workspaceId}/git/push-capability", + "/workspaces/{workspaceId}/git/push", + "/workspaces/{workspaceId}/git/compare", + "/workspaces/{workspaceId}/git/comparison-diff", + "/workspaces/{workspaceId}/git/worktrees", + "/workspaces/{workspaceId}/git/managed-worktree", + "/scheduled-tasks", + "/scheduled-tasks/{taskId}", + "/scheduled-tasks/{taskId}/pause", + "/scheduled-tasks/{taskId}/resume", + "/scheduled-tasks/{taskId}/run", + "/scheduled-tasks/{taskId}/runs", + "/scheduled-tasks/preview", + "/scheduled-tasks/scripts", + "/scheduled-tasks/mcp-servers", + "/scheduled-tasks/settings", + ]; + assert.deepEqual(Object.keys(paths), requiredPaths); + assert.deepEqual(document.security, [{ deviceBearer: [], protocolVersion: [] }]); + const securitySchemes = record(record(document.components, "components").securitySchemes, "security schemes"); + assert.deepEqual(securitySchemes.protocolVersion, { + type: "apiKey", + in: "header", + name: "Aiden-Protocol-Version", + description: "Must be exactly 1.", + }); + const healthGet = record(record(paths["/health"], "health").get, "health get"); + assert.deepEqual(healthGet.security, []); + const pairingPost = record(record(paths["/pairing/exchange"], "pairing").post, "pairing post"); + assert.deepEqual(pairingPost.security, []); + + const methods = new Set(["get", "post", "put", "patch", "delete"]); + for (const [route, pathValue] of Object.entries(paths)) { + if (route !== "/health" && route !== "/pairing/exchange") { + const inherited = (record(pathValue, route).parameters as Array> | undefined) ?? []; + assert(inherited.some((parameter) => parameter.$ref === "#/components/parameters/ProtocolVersion"), `${route} must require the exact protocol-version header`); + } + for (const [method, operationValue] of Object.entries(record(pathValue, route))) { + if (!methods.has(method)) continue; + const operationRecord = record(operationValue, `${method} ${route}`); + if (route === "/health" || route === "/pairing/exchange") continue; + assert( + AIDEN_REMOTE_CAPABILITIES.includes( + operationRecord["x-aiden-capability"] as (typeof AIDEN_REMOTE_CAPABILITIES)[number], + ), + `${method} ${route} must name a known capability`, + ); + } + } + + const resolveLocalReference = (reference: string): unknown => { + assert.match(reference, /^#\//, `Only local OpenAPI references are allowed: ${reference}`); + return reference + .slice(2) + .split("/") + .map((segment) => segment.split("~1").join("/").split("~0").join("~")) + .reduce((current, segment) => record(current, reference)[segment], document); + }; + const inspectReferences = (value: unknown): void => { + if (Array.isArray(value)) { + value.forEach(inspectReferences); + return; + } + if (!value || typeof value !== "object") return; + for (const [key, child] of Object.entries(value as Record)) { + if (key === "$ref") { + if (typeof child !== "string") throw new Error("OpenAPI $ref must be a string."); + assert.notEqual(resolveLocalReference(child), undefined, `Unresolved OpenAPI reference ${child}`); + } else { + inspectReferences(child); + } + } + }; + inspectReferences(document); +}); + +test("mutation contracts require idempotency or revision preconditions", async () => { + const document = record(await json("openapi.json"), "OpenAPI"); + const paths = record(document.paths, "paths"); + const operation = (route: string, method: string) => + record(record(paths[route], route)[method], `${method} ${route}`); + const parameterRefs = (route: string, method: string) => + ((operation(route, method).parameters as Array> | undefined) ?? []).map( + (parameter) => parameter.$ref, + ); + + for (const [route, method] of [ + ["/workspaces", "post"], + ["/chats", "post"], + ["/chats/{chatId}/move", "post"], + ["/chats/{chatId}/turns", "post"], + ["/streams/{streamId}/cancel", "post"], + ["/approvals/{approvalId}/respond", "post"], + ["/workspaces/{workspaceId}/git/branches", "post"], + ["/workspaces/{workspaceId}/git/checkout", "post"], + ["/workspaces/{workspaceId}/git/commit", "post"], + ["/workspaces/{workspaceId}/git/push", "post"], + ["/workspaces/{workspaceId}/git/worktrees", "post"], + ["/workspaces/{workspaceId}/git/managed-worktree", "delete"], + ["/scheduled-tasks", "post"], + ["/scheduled-tasks/{taskId}/pause", "post"], + ["/scheduled-tasks/{taskId}/resume", "post"], + ["/scheduled-tasks/{taskId}/run", "post"], + ] as const) { + assert(parameterRefs(route, method).includes("#/components/parameters/IdempotencyKey")); + } + + for (const [route, method] of [ + ["/workspaces/{workspaceId}", "patch"], + ["/workspaces/{workspaceId}", "delete"], + ["/chats/{chatId}", "patch"], + ["/chats/{chatId}", "delete"], + ["/scheduled-tasks/{taskId}", "patch"], + ["/scheduled-tasks/{taskId}", "delete"], + ["/scheduled-tasks/settings", "patch"], + ["/scheduled-tasks/{taskId}/pause", "post"], + ["/scheduled-tasks/{taskId}/resume", "post"], + ] as const) { + assert(parameterRefs(route, method).includes("#/components/parameters/IfMatch")); + } +}); + +test("wire schemas are allowlists and pairing requires pinned HTTPS identity", async () => { + const document = record(await json("openapi.json"), "OpenAPI"); + const components = record(document.components, "components"); + const schemas = record(components.schemas, "schemas"); + for (const name of [ + "Server", + "Workspace", + "Chat", + "MessageAttachment", + "FileIndex", + "FileDocument", + "GitResult", + "ScheduledTask", + "ErrorEnvelope", + ]) { + assert.equal(record(schemas[name], name).additionalProperties, false, `${name} must be an allowlist`); + } + for (const name of [ + "PairingBootstrap", + "GitDiffRequest", + "GitCreateBranchRequest", + "GitCheckoutRequest", + "GitCommitRequest", + "GitPushRequest", + "GitCompareRequest", + "GitComparisonDiffRequest", + "GitCreateWorktreeRequest", + ]) { + assert.equal(record(schemas[name], name).additionalProperties, false, `${name} must be an allowlist`); + } + const attachmentUploadVariants = record(schemas.AttachmentUpload, "AttachmentUpload").oneOf; + assert(Array.isArray(attachmentUploadVariants)); + assert.equal(attachmentUploadVariants.length, 2); + for (const [index, variant] of attachmentUploadVariants.entries()) { + assert.equal(record(variant, `AttachmentUpload.oneOf[${index}]`).additionalProperties, false); + } + const gitProjectionVariants = record(schemas.GitProjection, "GitProjection").oneOf; + assert(Array.isArray(gitProjectionVariants)); + assert(gitProjectionVariants.length > 0); + for (const [index, variant] of gitProjectionVariants.entries()) { + assert.equal(record(variant, `GitProjection.oneOf[${index}]`).additionalProperties, false); + } + const fixture = parseAidenRemoteContractFixture(await json("fixtures/contract.json")); + const bootstrap = fixture.pairingBootstrap; + const pairing = record(fixture.pairingExchange, "pairing exchange fixture"); + assert.match(bootstrap.endpoint, /^https:\/\//); + assert.match(bootstrap.serverSpkiSha256, /^sha256\/[A-Za-z0-9+/]{43}=$/); + assert.equal(bootstrap.secret.length >= 32, true); + assert.equal(pairing.endpoint, bootstrap.endpoint); + assert.equal(pairing.serverSpkiSha256, bootstrap.serverSpkiSha256); + assert.equal(record(schemas.PairingBootstrap, "PairingBootstrap").additionalProperties, false); + const pairingBootstrapEndpointPattern = record( + record(record(schemas.PairingBootstrap, "PairingBootstrap").properties, "PairingBootstrap properties").endpoint, + "PairingBootstrap endpoint", + ).pattern; + const pairingExchangeEndpointPattern = record( + record(record(schemas.PairingExchangeResponse, "PairingExchangeResponse").properties, "PairingExchangeResponse properties").endpoint, + "PairingExchangeResponse endpoint", + ).pattern; + for (const pattern of [pairingBootstrapEndpointPattern, pairingExchangeEndpointPattern]) { + assert.equal(typeof pattern, "string"); + const endpoint = new RegExp(pattern as string); + for (const [authority, valid] of endpointAuthorityVectors) { + assert.equal( + endpoint.test(`https://${authority}${AIDEN_REMOTE_BASE_PATH}`), + valid, + `OpenAPI endpoint pattern disagrees for ${authority}`, + ); + } + assert.equal(endpoint.test("https://user:secret@aiden.example.test/api/aiden/v1"), false); + } + assert.deepEqual(record(schemas.WorkspacePatch, "WorkspacePatch").required, ["confirmedForeground"]); + assert.deepEqual(record(schemas.ScheduleSettingsMutation, "ScheduleSettingsMutation").required, ["confirmedForeground"]); + const messageRoles = record(record(record(schemas.Message, "Message").properties, "Message properties").role, "Message role").enum; + assert(Array.isArray(messageRoles)); + assert.equal(messageRoles.includes("system"), false); + assert.equal(record(schemas.ErrorDetails, "ErrorDetails").additionalProperties, false); + const streamEvent = record(schemas.StreamEvent, "StreamEvent"); + assert.equal(streamEvent.additionalProperties, true, "SSE envelopes must remain additively extensible"); + const streamEventVariants = streamEvent.allOf; + assert(Array.isArray(streamEventVariants)); + const conditionalForType = (type: string) => streamEventVariants.find((variant) => { + const condition = record(record(record(variant, "StreamEvent conditional").if, "if").properties, "if properties"); + const typeCondition = record(condition.type, "type condition"); + return typeCondition.const === type; + }); + for (const [type, terminal] of [ + ["heartbeat", false], + ["done", true], + ["error", true], + ["cancelled", true], + ] as const) { + const conditional = record(conditionalForType(type), `${type} conditional`); + const thenProperties = record(record(conditional.then, `${type} then`).properties, `${type} then properties`); + assert.equal(record(thenProperties.terminal, `${type} terminal`).const, terminal); + } + const unknownConditional = streamEventVariants.find((variant) => { + const condition = record(record(record(variant, "unknown conditional").if, "if").properties, "if properties"); + const typeCondition = record(condition.type, "unknown type condition"); + return "not" in typeCondition; + }); + const unknownThen = record(record(record(unknownConditional, "unknown conditional").then, "unknown then").properties, "unknown then properties"); + assert.equal(record(unknownThen.terminal, "unknown terminal").const, false); + const streamStates = record(record(record(schemas.StreamStatus, "StreamStatus").properties, "StreamStatus properties").state, "StreamStatus state").enum; + assert(Array.isArray(streamStates)); + assert(streamStates.includes("reconciling")); + const statusConditional = record(conditionalForType("status"), "status conditional"); + const statusThenProperties = record(record(statusConditional.then, "status then").properties, "status then properties"); + const statusPayloadStates = record(record(record(statusThenProperties.payload, "status payload").properties, "status payload properties").state, "status payload state").enum; + assert.deepEqual(statusPayloadStates, ["queued", "running", "waiting_for_approval", "reconciling"]); + assert.deepEqual(record(schemas.ErrorCode, "ErrorCode").enum, AIDEN_REMOTE_ERROR_CODES); + const remotePattern = record(record(record(schemas.GitPushRequest, "GitPushRequest").properties, "GitPushRequest properties").remote, "Git remote").pattern; + assert.equal(typeof remotePattern, "string"); + const safeRemote = new RegExp(remotePattern as string); + assert.equal(safeRemote.test("origin"), true); + assert.equal(safeRemote.test("https://user:secret@example.test/repo.git"), false); +}); + +test("pairing, typed SSE payloads, and error details fail closed", async () => { + const source = record(await json("fixtures/contract.json"), "fixture"); + const clone = () => structuredClone(source); + const bootstrapWithExtraField = clone(); + record(bootstrapWithExtraField.pairingBootstrap, "bootstrap").unexpected = true; + assert.throws(() => parseAidenRemoteContractFixture(bootstrapWithExtraField), /unsupported field/); + const exchangeWithExtraField = clone(); + record(exchangeWithExtraField.pairingExchange, "exchange").unexpected = true; + assert.throws(() => parseAidenRemoteContractFixture(exchangeWithExtraField), /unsupported field/); + const errorEnvelopeWithExtraField = clone(); + record(errorEnvelopeWithExtraField.error, "error envelope").unexpected = true; + assert.throws(() => parseAidenRemoteContractFixture(errorEnvelopeWithExtraField), /unsupported field/); + const errorBodyWithExtraField = clone(); + record(record(errorBodyWithExtraField.error, "error envelope").error, "error").unexpected = true; + assert.throws(() => parseAidenRemoteContractFixture(errorBodyWithExtraField), /unsupported field/); + const errorDetailsWithExtraField = clone(); + record(record(errorDetailsWithExtraField.error, "error envelope").error, "error").details = { unexpected: true }; + assert.throws(() => parseAidenRemoteContractFixture(errorDetailsWithExtraField), /unsupported field/); + + const oversizedBootstrapInstance = clone(); + record(oversizedBootstrapInstance.pairingBootstrap, "bootstrap").instanceId = "i".repeat(129); + assert.throws(() => parseAidenRemoteContractFixture(oversizedBootstrapInstance), /instanceId.*128/); + const oversizedExchangeInstance = clone(); + record(oversizedExchangeInstance.pairingExchange, "exchange").instanceId = "i".repeat(129); + assert.throws(() => parseAidenRemoteContractFixture(oversizedExchangeInstance), /instanceId.*128/); + const oversizedDeviceId = clone(); + record(oversizedDeviceId.pairingExchange, "exchange").deviceId = "d".repeat(129); + assert.throws(() => parseAidenRemoteContractFixture(oversizedDeviceId), /deviceId.*128/); + const oversizedUtf8Endpoint = clone(); + record(oversizedUtf8Endpoint.pairingBootstrap, "bootstrap").endpoint = + `https://${"é".repeat(1_020)}.test/api/aiden/v1`; + assert.throws(() => parseAidenRemoteContractFixture(oversizedUtf8Endpoint), /UTF-8 bytes/); + + const weakSecret = clone(); + record(weakSecret.pairingBootstrap, "bootstrap").secret = "predictable-secret-that-is-long-enough"; + assert.throws(() => parseAidenRemoteContractFixture(weakSecret), /32 random bytes/); + const wrongExchange = clone(); + record(wrongExchange.pairingExchange, "exchange").endpoint = "https://other.example.test/api/aiden/v1"; + assert.throws(() => parseAidenRemoteContractFixture(wrongExchange), /does not match bootstrap/); + const userInfoEndpoint = clone(); + record(userInfoEndpoint.pairingBootstrap, "bootstrap").endpoint = "https://user:secret@aiden-fixture.example.test/api/aiden/v1"; + assert.throws(() => parseAidenRemoteContractFixture(userInfoEndpoint), /canonical HTTPS Aiden v1 URL/); + for (const endpoint of [ + "https://:443/api/aiden/v1", + "https://aiden-fixture.example.test:0/api/aiden/v1", + "https://aiden-fixture.example.test:65536/api/aiden/v1", + "https://aiden-fixture.example.test:abc/api/aiden/v1", + "https://aiden-fixture.example.test/api/aiden/v1?", + "https://aiden-fixture.example.test/api/aiden/v1#", + "https://aiden-fixture.example.test/api/./aiden/v1", + "https://aiden-fixture.example.test/api/aiden/../aiden/v1", + "https://aiden-fixture.example.test/api/aiden/v1/../v1", + "https://aiden-fixture.example.test/api/aiden/v1/%2e%2e/v1", + "https://aiden-fixture.example.test/api/aiden/v1/%2E", + "https://aiden-fixture.example.test/%61pi/aiden/v1", + "https://aiden-fixture.example.test/api/aiden/%76%31", + ]) { + const nonCanonicalPath = clone(); + record(nonCanonicalPath.pairingBootstrap, "bootstrap").endpoint = endpoint; + assert.throws(() => parseAidenRemoteContractFixture(nonCanonicalPath), /canonical HTTPS Aiden v1 URL/); + } + const longLived = clone(); + record(longLived.pairingBootstrap, "bootstrap").expiresAt = "2026-08-18T19:06:00.000Z"; + assert.throws(() => parseAidenRemoteContractFixture(longLived), /five minutes/); + const permissiveExpiry = clone(); + record(permissiveExpiry.pairingBootstrap, "bootstrap").expiresAt = "August 18, 2026 19:05:00 GMT"; + assert.throws(() => parseAidenRemoteContractFixture(permissiveExpiry), /strict RFC 3339/); + const malformedServerTime = clone(); + record(malformedServerTime.server, "server").serverTime = "not-a-date"; + assert.throws(() => parseAidenRemoteContractFixture(malformedServerTime), /serverTime.*RFC 3339/); + const missingServerTime = clone(); + delete record(missingServerTime.server, "server").serverTime; + assert.throws(() => parseAidenRemoteContractFixture(missingServerTime), /serverTime.*RFC 3339/); + const unknownCapability = clone(); + record(unknownCapability.pairingExchange, "exchange").capabilities = ["admin:everything"]; + assert.throws(() => parseAidenRemoteContractFixture(unknownCapability), /Unknown pairing capability/); + const unsafeDetails = clone(); + record(record(unsafeDetails.error, "error envelope").error, "error").details = { absolutePath: "/private/secret" }; + assert.throws(() => parseAidenRemoteContractFixture(unsafeDetails), /unsupported field/); + for (const requiredField of ["message", "requestId", "retryable"]) { + const malformed = clone(); + delete record(record(malformed.error, "error envelope").error, "error")[requiredField]; + assert.throws(() => parseAidenRemoteContractFixture(malformed)); + } + for (const [field, value] of [ + ["retryAfterSeconds", 86_401], + ["limit", 1_000_001], + ["minimumClientVersion", "v".repeat(41)], + ["field", "f".repeat(121)], + ] as const) { + const malformed = clone(); + record(record(malformed.error, "error envelope").error, "error").details = { [field]: value }; + assert.throws(() => parseAidenRemoteContractFixture(malformed), new RegExp(`Error detail ${field} is invalid`)); + } + + const event = record((source.events as unknown[])[0], "event"); + assert.throws(() => parseAidenRemoteStreamEvent({ ...event, payload: { ...record(event.payload, "payload"), hiddenPrompt: "secret" } }), /unsupported field/); + assert.throws(() => parseAidenRemoteStreamEvent({ ...event, payload: { chatId: "chat_fixture_01" } }), /missing required field/); + assert.throws(() => parseAidenRemoteStreamEvent({ ...event, payload: { chatId: "chat_fixture_01", turnId: "turn_fixture_01", nextSequence: "2" } }), /must be positive/); + assert.throws(() => parseAidenRemoteStreamEvent({ ...event, protocolVersion: 2 }), /unsupported protocolVersion/); + assert.throws(() => parseAidenRemoteStreamEvent({ ...event, streamId: "" }), /non-empty string/); + assert.throws(() => parseAidenRemoteStreamEvent({ ...event, sequence: 0 }), /positive/); + assert.throws( + () => parseAidenRemoteStreamEvent({ ...event, timestamp: "August 18, 2026 19:01:01 GMT" }), + /strict RFC 3339/, + ); + assert.doesNotThrow(() => parseAidenRemoteStreamEvent({ ...event, futureEnvelopeMetadata: { ignored: true } })); + assert.doesNotThrow(() => parseAidenRemoteStreamEvent({ ...event, type: "status", payload: { state: "reconciling" } })); + assert.throws(() => parseAidenRemoteStreamEvent({ ...event, type: "status", payload: { state: "done" } }), /status state is invalid/); + assert.doesNotThrow(() => parseAidenRemoteStreamEvent({ ...event, type: "error", terminal: true, payload: { code: "idempotency_capacity", message: "Retry later." } })); + assert.doesNotThrow(() => parseAidenRemoteStreamEvent({ ...event, type: "error", terminal: true, payload: { code: "idempotency_in_flight", message: "Still reconciling." } })); + assert.doesNotThrow(() => parseAidenRemoteStreamEvent({ ...event, type: "error", terminal: true, payload: { code: "handle_capacity", message: "Browse again later." } })); + assert.equal(parseAidenRemoteStreamEvent({ ...event, type: "future_progress", terminal: false, payload: {}, futureEnvelopeMetadata: true }), null); + assert.throws(() => parseAidenRemoteStreamEvent({ ...event, type: "future_progress", terminal: false, payload: undefined }), /payload must be an object/); + assert.throws( + () => parseAidenRemoteStreamEvent({ ...event, type: "future_progress", terminal: false, payload: { absolutePath: "/private/secret" } }), + /Forbidden Aiden Remote wire key/, + ); + assert.throws(() => parseAidenRemoteStreamEvent({ ...event, type: "future_terminal", terminal: true, payload: {} }), /Unknown terminal/); +}); + +test("endpoint authority grammar stays exact across LAN and tailnet host forms", async () => { + const source = record(await json("fixtures/contract.json"), "fixture"); + for (const [authority, valid] of endpointAuthorityVectors) { + const candidate = structuredClone(source); + const endpoint = `https://${authority}${AIDEN_REMOTE_BASE_PATH}`; + record(candidate.pairingBootstrap, "bootstrap").endpoint = endpoint; + if (valid) { + record(candidate.pairingExchange, "exchange").endpoint = endpoint; + assert.doesNotThrow(() => parseAidenRemoteContractFixture(candidate), authority); + } else { + assert.throws( + () => parseAidenRemoteContractFixture(candidate), + /canonical HTTPS Aiden v1 URL/, + authority, + ); + } + } +}); + +test("OpenAPI and runtime stream sequence bounds stay at the JSON safe-integer maximum", async () => { + const document = record(await json("openapi.json"), "OpenAPI"); + const paths = record(document.paths, "paths"); + const streamEvents = record(paths["/streams/{streamId}/events"], "stream events"); + const get = record(streamEvents.get, "stream events get"); + const parameters = get.parameters as Array>; + const parameterSchema = (name: string): Record => { + const parameter = parameters.find((candidate) => candidate.name === name); + assert(parameter, `missing stream sequence parameter ${name}`); + return record(parameter.schema, `${name} schema`); + }; + for (const name of ["Last-Event-ID", "after"]) { + assert.equal(parameterSchema(name).maximum, Number.MAX_SAFE_INTEGER, `${name} maximum`); + } + + const schemas = record(record(document.components, "components").schemas, "schemas"); + const streamStatusProperties = record(record(schemas.StreamStatus, "StreamStatus").properties, "StreamStatus properties"); + assert.equal(record(streamStatusProperties.lastSequence, "lastSequence").maximum, Number.MAX_SAFE_INTEGER); + const streamEvent = record(schemas.StreamEvent, "StreamEvent"); + const streamEventVariants = streamEvent.allOf as Array>; + const streamEventBaseProperties = record( + streamEvent.properties, + "StreamEvent base properties", + ); + assert.equal(record(streamEventBaseProperties.sequence, "sequence").maximum, Number.MAX_SAFE_INTEGER); + const snapshotConditional = streamEventVariants.find((variant) => { + const condition = record(record(variant.if, "snapshot if").properties, "snapshot if properties"); + return record(condition.type, "snapshot type").const === "snapshot"; + }); + assert(snapshotConditional, "missing snapshot conditional"); + const snapshotPayload = record( + record(record(snapshotConditional.then, "snapshot then").properties, "snapshot then properties").payload, + "snapshot payload", + ); + const snapshotProperties = record( + snapshotPayload.properties, + "snapshot payload properties", + ); + assert.equal(record(snapshotProperties.nextSequence, "nextSequence").maximum, Number.MAX_SAFE_INTEGER); + + const fixture = parseAidenRemoteContractFixture(await json("fixtures/contract.json")); + const event = record(fixture.events[0], "event"); + assert.doesNotThrow(() => parseAidenRemoteStreamEvent({ + ...event, + sequence: Number.MAX_SAFE_INTEGER, + })); + assert.throws( + () => parseAidenRemoteStreamEvent({ + ...event, + sequence: 9007199254740992, + }), + /safe integer/, + ); + assert.doesNotThrow(() => parseAidenRemoteStreamEvent({ + ...event, + payload: { + chatId: "chat_fixture_01", + turnId: "turn_fixture_01", + nextSequence: Number.MAX_SAFE_INTEGER, + }, + })); + assert.throws( + () => parseAidenRemoteStreamEvent({ + ...event, + payload: { + chatId: "chat_fixture_01", + turnId: "turn_fixture_01", + nextSequence: 9007199254740992, + }, + }), + /positive/, + ); +}); + +test("JSON entry points reject non-finite values, invalid UTF-16, and unsupported object graphs", async () => { + const fixture = parseAidenRemoteContractFixture(await json("fixtures/contract.json")); + const event = record(fixture.events[0], "event"); + + for (const value of [Infinity, Number.NaN]) { + assert.throws( + () => parseAidenRemoteStreamEvent({ ...event, futureEnvelopeMetadata: { value } }), + /numbers must be finite/, + ); + } + for (const token of ["Infinity", "NaN", "1e400"]) { + assert.throws( + () => parseAidenSseFrames(`id: 1\ndata: ${token}\n\n`), + /Malformed Aiden SSE JSON data/, + ); + } + + let deepMetadata: Record = {}; + for (let index = 0; index < 129; index += 1) { + deepMetadata = { nested: deepMetadata }; + } + assert.throws( + () => parseAidenRemoteStreamEvent({ ...event, futureEnvelopeMetadata: deepMetadata }), + /maximum nesting depth/, + ); + + const tooManyKeys = Object.fromEntries( + Array.from({ length: 16_385 }, (_, index) => [`key${index}`, index]), + ); + assert.throws( + () => parseAidenRemoteStreamEvent({ ...event, futureEnvelopeMetadata: tooManyKeys }), + /maximum object-key count/, + ); + + const cyclicMetadata: Record = {}; + cyclicMetadata.self = cyclicMetadata; + assert.throws( + () => parseAidenRemoteStreamEvent({ ...event, futureEnvelopeMetadata: cyclicMetadata }), + /cycles are not supported/, + ); + + for (const escape of ["\\ud800", "\\udc00"]) { + assert.throws( + () => parseAidenSseFrames(`id: 1\ndata: {"value":"${escape}"}\n\n`), + /Malformed Aiden SSE JSON data/, + ); + } + const validPair = parseAidenSseFrames('id: 1\ndata: {"value":"\\ud83d\\ude00"}\n\n'); + assert.deepEqual(validPair[0]?.data, { value: "😀" }); + assert.throws( + () => parseAidenSseFrames('id: 1\ndata: {"😀":1,"\\ud83d\\ude00":2}\n\n'), + /Malformed Aiden SSE JSON data/, + ); +}); + +test("SSE framing resumes by id, ignores duplicates and unknown nonterminal events, and reconciles gaps", async () => { + const fixture = parseAidenRemoteContractFixture(await json("fixtures/contract.json")); + const [first, second] = fixture.events; + const wire = `id: ${first.sequence}\ndata: ${JSON.stringify(first)}\n\nid: ${second.sequence}\ndata: ${JSON.stringify(second)}\n\n`; + const frames = parseAidenSseFrames(wire); + assert.throws( + () => parseAidenSseFrames('id: 1\ndata: {"a":1,"a":2}\n\n'), + /Malformed Aiden SSE JSON data/, + ); + assert.throws( + () => parseAidenSseFrames('id: 1\ndata: {"outer":{"a":1,"\\u0061":2}}\n\n'), + /Malformed Aiden SSE JSON data/, + ); + assert.throws( + () => parseAidenSseFrames(`id: 1\ndata: ${"x".repeat(AIDEN_REMOTE_MAX_SSE_FRAME_BYTES)}\n\n`), + /exceeds the byte limit/, + ); + assert.deepEqual(reconcileAidenSseFrames(frames, 0, first.streamId), { events: [first, second], reconcileRequired: false }); + assert.deepEqual(reconcileAidenSseFrames(frames, 1, first.streamId), { events: [second], reconcileRequired: false }); + assert.equal(reconcileAidenSseFrames([{ ...frames[1], id: "3" }], 1, first.streamId).reconcileRequired, true); + assert.equal(reconcileAidenSseFrames([{ ...frames[1], data: { ...record(frames[1].data, "data"), sequence: 3 } }], 1, first.streamId).reconcileRequired, true); + const future = { ...record(frames[0].data, "data"), type: "future_progress", terminal: false, payload: {} }; + assert.deepEqual(reconcileAidenSseFrames([{ id: "1", data: future }], 0, first.streamId), { events: [], reconcileRequired: false }); + const futureBetween = { ...future, sequence: 2 }; + const third = { ...second, sequence: 3 }; + assert.deepEqual(reconcileAidenSseFrames([{ id: "1", data: first }, { id: "2", data: futureBetween }, { id: "3", data: third }], 0, first.streamId), { events: [first, third], reconcileRequired: false }); + const terminal = { ...first, sequence: 1, type: "done", terminal: true, payload: { messageId: "message-terminal" } }; + const afterTerminal = { ...second, sequence: 2, type: "heartbeat", terminal: false, payload: {} }; + const terminalResult = reconcileAidenSseFrames([{ id: "1", data: terminal }, { id: "2", data: afterTerminal }], 0, first.streamId); + assert.equal(terminalResult.reconcileRequired, true); + assert.deepEqual(terminalResult.events.map((event) => event.type), ["done"]); + assert.equal( + reconcileAidenSseFrames([{ id: "1", data: { ...first, streamId: "stream_other" } }], 0, first.streamId).reconcileRequired, + true, + ); + assert.equal( + reconcileAidenSseFrames([{ id: "1", data: { ...future, streamId: "stream_other" } }], 0, first.streamId).reconcileRequired, + true, + ); +}); diff --git a/main/services/aiden-remote-protocol.ts b/main/services/aiden-remote-protocol.ts new file mode 100644 index 00000000..9dcf356f --- /dev/null +++ b/main/services/aiden-remote-protocol.ts @@ -0,0 +1,971 @@ +export const AIDEN_REMOTE_PROTOCOL_VERSION = 1 as const; +export const AIDEN_REMOTE_BASE_PATH = "/api/aiden/v1" as const; +export const AIDEN_REMOTE_MAX_SSE_FRAME_BYTES = 1_048_576; + +const AIDEN_REMOTE_MAX_IDENTIFIER_LENGTH = 128; +const AIDEN_REMOTE_MAX_ENDPOINT_UTF8_BYTES = 2_048; +const AIDEN_REMOTE_MAX_ENDPOINT_PORT = 65_535; +const AIDEN_REMOTE_MAX_JSON_NESTING_DEPTH = 128; +const AIDEN_REMOTE_MAX_JSON_OBJECT_KEYS = 16_384; + +export const AIDEN_REMOTE_CAPABILITIES = [ + "server:read", + "chat:read", + "chat:write", + "approval:respond", + "workspace:read", + "workspace:browse", + "workspace:manage", + "files:read", + "files:write", + "git:read", + "git:write", + "schedule:read", + "schedule:write", +] as const; + +export type AidenRemoteCapability = (typeof AIDEN_REMOTE_CAPABILITIES)[number]; + +export const AIDEN_REMOTE_EVENT_TYPES = [ + "snapshot", + "status", + "text_delta", + "reasoning_delta", + "tool_started", + "tool_finished", + "timeline", + "approval_required", + "done", + "error", + "cancelled", + "heartbeat", +] as const; + +export type AidenRemoteEventType = (typeof AIDEN_REMOTE_EVENT_TYPES)[number]; +export type AidenRemoteTerminalEventType = "done" | "error" | "cancelled"; + +export const AIDEN_REMOTE_ERROR_CODES = [ + "invalid_request", + "payload_too_large", + "rate_limited", + "authentication_required", + "credential_revoked", + "capability_denied", + "pairing_closed", + "pairing_expired", + "pairing_already_used", + "server_identity_changed", + "not_found", + "already_exists", + "revision_conflict", + "idempotency_conflict", + "idempotency_capacity", + "idempotency_in_flight", + "workspace_unavailable", + "workspace_changing", + "permission_confirmation_required", + "handle_invalid", + "handle_expired", + "handle_wrong_device", + "root_policy_changed", + "filesystem_identity_changed", + "path_outside_root", + "handle_capacity", + "turn_already_active", + "stream_gone", + "approval_already_resolved", + "approval_expired", + "operation_in_progress", + "operation_stale", + "git_capability_denied", + "schedule_disabled", + "schedule_run_in_progress", + "server_interrupted", + "internal_error", +] as const; + +export type AidenRemoteErrorCode = (typeof AIDEN_REMOTE_ERROR_CODES)[number]; + +export interface AidenRemoteErrorEnvelope { + error: { + code: AidenRemoteErrorCode; + message: string; + requestId: string; + retryable: boolean; + details?: { + currentRevision?: string; + retryAfterSeconds?: number; + chatId?: string; + minimumClientVersion?: string; + limit?: number; + field?: string; + }; + }; +} + +export interface AidenRemoteStreamEvent { + protocolVersion: typeof AIDEN_REMOTE_PROTOCOL_VERSION; + streamId: string; + sequence: number; + timestamp: string; + type: AidenRemoteEventType; + terminal: boolean; + payload: Record; +} + +export interface AidenRemoteContractFixture { + contractRevision: number; + protocolVersion: typeof AIDEN_REMOTE_PROTOCOL_VERSION; + capabilities: AidenRemoteCapability[]; + health: { ok: true; protocolVersion: typeof AIDEN_REMOTE_PROTOCOL_VERSION }; + pairingBootstrap: { + protocolVersion: typeof AIDEN_REMOTE_PROTOCOL_VERSION; + instanceId: string; + endpoint: string; + serverSpkiSha256: string; + secret: string; + expiresAt: string; + }; + pairingExchange: { + protocolVersion: typeof AIDEN_REMOTE_PROTOCOL_VERSION; + instanceId: string; + deviceId: string; + credential: string; + capabilities: AidenRemoteCapability[]; + endpoint: string; + serverSpkiSha256: string; + }; + events: AidenRemoteStreamEvent[]; + error: AidenRemoteErrorEnvelope; + [key: string]: unknown; +} + +export const AIDEN_REMOTE_FORBIDDEN_WIRE_KEYS = new Set([ + "authorization", + "credentialDigest", + "providerFingerprint", + "mcpServerBindings", + "folderPath", + "repositoryPath", + "worktreePath", + "worktreeGitDir", + "ownershipToken", + "worktreeDevice", + "worktreeInode", + "createdFromHead", + "canonicalPath", + "absolutePath", + "scriptPath", + "environment", + "stdout", + "stderr", +]); + +function isRecord(value: unknown): value is Record { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} + +function isValidJsonString(value: string): boolean { + for (let index = 0; index < value.length; index += 1) { + const codeUnit = value.charCodeAt(index); + if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) { + const nextCodeUnit = value.charCodeAt(index + 1); + if ( + Number.isNaN(nextCodeUnit) || + nextCodeUnit < 0xdc00 || + nextCodeUnit > 0xdfff + ) return false; + index += 1; + } else if (codeUnit >= 0xdc00 && codeUnit <= 0xdfff) { + return false; + } + } + return true; +} + +function assertAidenRemoteJsonValue(value: unknown, location: string): void { + let objectKeyCount = 0; + const activeObjects = new WeakSet(); + + function fail(message: string): never { + throw new Error(`Invalid Aiden Remote JSON value at ${location}: ${message}`); + } + + function visit(current: unknown, depth: number, currentLocation: string): void { + if (depth > AIDEN_REMOTE_MAX_JSON_NESTING_DEPTH) { + fail(`maximum nesting depth is ${AIDEN_REMOTE_MAX_JSON_NESTING_DEPTH}.`); + } + if (current === null) return; + if (typeof current === "string") { + if (!isValidJsonString(current)) fail("strings must not contain unpaired UTF-16 surrogates."); + return; + } + if (typeof current === "boolean") return; + if (typeof current === "number") { + if (!Number.isFinite(current)) fail("numbers must be finite."); + return; + } + if (typeof current !== "object") { + fail("value must be a JSON-compatible primitive, array, or plain object."); + } + + if (activeObjects.has(current)) fail("cycles are not supported."); + activeObjects.add(current); + + try { + if (Array.isArray(current)) { + const ownKeys = Reflect.ownKeys(current); + const arrayIndexes = new Set(); + for (const ownKey of ownKeys) { + if (ownKey === "length") { + const descriptor = Object.getOwnPropertyDescriptor(current, ownKey); + if ( + !descriptor || + descriptor.enumerable || + !Object.prototype.hasOwnProperty.call(descriptor, "value") || + descriptor.value !== current.length + ) { + fail("arrays must have the standard length property."); + } + continue; + } + if (typeof ownKey !== "string") fail("symbol array properties are not supported."); + const descriptor = Object.getOwnPropertyDescriptor(current, ownKey); + const index = Number(ownKey); + if ( + !descriptor || + !descriptor.enumerable || + !Object.prototype.hasOwnProperty.call(descriptor, "value") || + !Number.isSafeInteger(index) || + index < 0 || + index >= current.length || + String(index) !== ownKey || + arrayIndexes.has(index) + ) { + fail("arrays must contain only dense numeric JSON elements."); + } + arrayIndexes.add(index); + } + if (arrayIndexes.size !== current.length) { + fail("arrays must contain only dense numeric JSON elements."); + } + for (let index = 0; index < current.length; index += 1) { + visit(current[index], depth + 1, `${currentLocation}[${index}]`); + } + return; + } + + const prototype = Object.getPrototypeOf(current); + if (prototype !== Object.prototype && prototype !== null) { + fail("objects must use the plain JSON object prototype."); + } + const ownKeys = Reflect.ownKeys(current); + const objectEntries: Array<[string, unknown]> = []; + for (const ownKey of ownKeys) { + if (typeof ownKey !== "string") fail("symbol object properties are not supported."); + const descriptor = Object.getOwnPropertyDescriptor(current, ownKey); + if ( + !descriptor || + !descriptor.enumerable || + !Object.prototype.hasOwnProperty.call(descriptor, "value") + ) { + fail(`object property ${ownKey} must be an enumerable data property.`); + } + if (!isValidJsonString(ownKey)) fail("object keys must not contain unpaired UTF-16 surrogates."); + if (AIDEN_REMOTE_FORBIDDEN_WIRE_KEYS.has(ownKey)) { + throw new Error(`Forbidden Aiden Remote wire key ${ownKey} at ${currentLocation}.`); + } + objectKeyCount += 1; + if (objectKeyCount > AIDEN_REMOTE_MAX_JSON_OBJECT_KEYS) { + fail(`maximum object-key count is ${AIDEN_REMOTE_MAX_JSON_OBJECT_KEYS}.`); + } + objectEntries.push([ownKey, descriptor.value]); + } + for (const [key, child] of objectEntries) { + visit(child, depth + 1, `${currentLocation}.${key}`); + } + } finally { + activeObjects.delete(current); + } + } + + visit(value, 0, location); +} + +function requiredString(record: Record, key: string): string { + const value = record[key]; + if (typeof value !== "string" || value.length === 0) { + throw new Error(`Aiden Remote fixture field ${key} must be a non-empty string.`); + } + return value; +} + +function requiredInteger(record: Record, key: string): number { + const value = record[key]; + if (!Number.isSafeInteger(value)) { + throw new Error(`Aiden Remote fixture field ${key} must be a safe integer.`); + } + return value as number; +} + +function characterLength(value: string): number { + let length = 0; + for (const _character of value) length += 1; + return length; +} + +export function assertNoForbiddenWireKeys(value: unknown, location = "fixture"): void { + assertAidenRemoteJsonValue(value, location); +} + +const EVENT_PAYLOAD_KEYS: Record = { + snapshot: ["chatId", "turnId", "nextSequence"], + status: ["state"], + text_delta: ["text"], + reasoning_delta: ["text"], + tool_started: ["toolId", "name"], + tool_finished: ["toolId", "status"], + timeline: ["label"], + approval_required: ["approvalId", "summary", "expiresAt"], + done: ["messageId"], + error: ["code", "message"], + cancelled: ["source"], + heartbeat: [], +}; + +const TERMINAL_EVENT_TYPES = new Set(["done", "error", "cancelled"]); + +function assertExactKeys(record: Record, allowed: readonly string[], label: string): void { + for (const key of Object.keys(record)) { + if (!allowed.includes(key)) throw new Error(`${label} contains unsupported field ${key}.`); + } +} + +function assertBoundedString(record: Record, key: string, maxLength: number): string { + const value = requiredString(record, key); + if (characterLength(value) > maxLength) throw new Error(`Aiden Remote field ${key} exceeds ${maxLength} characters.`); + return value; +} + +function parseStrictRfc3339(value: string, label: string): number { + const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|[+-]\d{2}:\d{2})$/u.exec(value); + if (!match) throw new Error(`${label} must be a strict RFC 3339 date-time.`); + + const year = Number(match[1]); + const month = Number(match[2]); + const day = Number(match[3]); + const hour = Number(match[4]); + const minute = Number(match[5]); + const second = Number(match[6]); + const fraction = match[7] ?? ""; + const offset = match[8]!; + const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); + const daysInMonth = [31, leapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month - 1]; + if ( + !Number.isInteger(daysInMonth) || + day < 1 || day > daysInMonth || + hour > 23 || minute > 59 || second > 59 + ) { + throw new Error(`${label} must be a valid strict RFC 3339 date-time.`); + } + + const offsetHours = offset === "Z" ? 0 : Number(offset.slice(1, 3)); + const offsetMinutes = offset === "Z" ? 0 : Number(offset.slice(4, 6)); + if (offsetHours > 23 || offsetMinutes > 59) { + throw new Error(`${label} must have a valid RFC 3339 UTC offset.`); + } + const milliseconds = Number(fraction.slice(1).padEnd(3, "0").slice(0, 3) || "0"); + const date = new Date(Date.UTC(2000, month - 1, day, hour, minute, second, milliseconds)); + date.setUTCFullYear(year); + const signedOffsetMinutes = offset === "Z" ? 0 : (offset[0] === "+" ? 1 : -1) * (offsetHours * 60 + offsetMinutes); + return date.getTime() - signedOffsetMinutes * 60_000; +} + +function validateEventPayload(type: AidenRemoteEventType, payload: Record): void { + const keys = EVENT_PAYLOAD_KEYS[type]; + assertExactKeys(payload, keys, `${type} payload`); + for (const key of keys) { + if (!(key in payload)) throw new Error(`${type} payload is missing required field ${key}.`); + } + if (type === "snapshot") { + assertBoundedString(payload, "chatId", 128); + assertBoundedString(payload, "turnId", 128); + if (!Number.isSafeInteger(payload.nextSequence) || (payload.nextSequence as number) < 1) throw new Error("snapshot nextSequence must be positive."); + } else if (type === "status") { + const state = requiredString(payload, "state"); + if (!["queued", "running", "waiting_for_approval", "reconciling"].includes(state)) throw new Error("status state is invalid."); + } else if (type === "text_delta" || type === "reasoning_delta") { + assertBoundedString(payload, "text", 200_000); + } else if (type === "tool_started") { + assertBoundedString(payload, "toolId", 128); + assertBoundedString(payload, "name", 120); + } else if (type === "tool_finished") { + assertBoundedString(payload, "toolId", 128); + if (!["succeeded", "failed", "cancelled"].includes(requiredString(payload, "status"))) throw new Error("tool status is invalid."); + } else if (type === "timeline") { + assertBoundedString(payload, "label", 500); + } else if (type === "approval_required") { + assertBoundedString(payload, "approvalId", 128); + assertBoundedString(payload, "summary", 2_000); + parseStrictRfc3339(requiredString(payload, "expiresAt"), "Approval expiry"); + } else if (type === "done") { + assertBoundedString(payload, "messageId", 128); + } else if (type === "error") { + const code = assertBoundedString(payload, "code", 80); + if (!(AIDEN_REMOTE_ERROR_CODES as readonly string[]).includes(code)) throw new Error("stream error code is invalid."); + assertBoundedString(payload, "message", 2_000); + } else if (type === "cancelled") { + if (!["device", "server"].includes(requiredString(payload, "source"))) throw new Error("cancellation source is invalid."); + } +} + +function assertBase64Url32(value: string, label: string): void { + if (!/^[A-Za-z0-9_-]{43}$/.test(value) || Buffer.from(value, "base64url").length !== 32) { + throw new Error(`${label} must encode exactly 32 random bytes as unpadded base64url.`); + } +} + +function isCanonicalAidenIPv4(value: string): boolean { + const octets = value.split("."); + return octets.length === 4 && octets.every((octet) => { + if (!/^(?:0|[1-9][0-9]{0,2})$/u.test(octet)) return false; + const number = Number(octet); + return number >= 0 && number <= 255; + }); +} + +function parseAidenIPv6Side(value: string): number | null { + if (!value) return 0; + const groups = value.split(":"); + if (groups.some((group) => group.length === 0)) return null; + let count = 0; + for (const [index, group] of groups.entries()) { + if (group.includes(".")) { + if (index !== groups.length - 1 || !isCanonicalAidenIPv4(group)) return null; + count += 2; + } else { + if (!/^[0-9A-Fa-f]{1,4}$/u.test(group)) return null; + count += 1; + } + } + return count; +} + +function isCanonicalAidenIPv6(value: string): boolean { + if (!value || !/^[0-9A-Fa-f:.]+$/u.test(value)) return false; + const sides = value.split("::"); + if (sides.length > 2) return false; + if (sides.length === 2) { + if (sides[0]!.includes(".")) return false; + const left = parseAidenIPv6Side(sides[0]!); + const right = parseAidenIPv6Side(sides[1]!); + return left !== null && right !== null && left + right < 8; + } + return parseAidenIPv6Side(value) === 8; +} + +function isCanonicalAidenDnsHost(value: string): boolean { + if (!value || value.length > 253) return false; + const labels = value.split("."); + if (labels.some((label) => label.length === 0 || label.length > 63)) return false; + if (labels.every((label) => /^[0-9]+$/u.test(label))) { + // Numeric-only authorities are ambiguous under WHATWG URL parsing. Keep + // only canonical dotted-decimal IPv4 rather than letting `123` become + // `0.0.0.123` on one platform and a DNS name on another. + return isCanonicalAidenIPv4(value); + } + // A DNS authority must not end in a numeric-only label. This keeps + // `aiden.123` distinct from canonical IPv4 while retaining numeric labels + // in non-terminal positions. + if (/^[0-9]+$/u.test(labels[labels.length - 1]!)) return false; + return labels.every((label) => /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/u.test(label)); +} + +function isCanonicalAidenPort(value: string): boolean { + if (!/^[1-9][0-9]{0,4}$/u.test(value)) return false; + const port = Number(value); + return Number.isInteger(port) && port >= 1 && port <= AIDEN_REMOTE_MAX_ENDPOINT_PORT; +} + +function isCanonicalAidenAuthority(value: string): boolean { + // Keep the raw grammar ASCII-only. This rejects C0/DEL, all Unicode + // whitespace and normalization-sensitive host spellings before either URL + // implementation can decode or normalize them. + for (const character of value) { + const codePoint = character.codePointAt(0)!; + if (codePoint > 0x7f || codePoint <= 0x20 || codePoint === 0x7f) return false; + } + + let host: string; + let rawPort: string | undefined; + if (value.startsWith("[")) { + const closingBracket = value.indexOf("]"); + if (closingBracket <= 1 || value.indexOf("[", 1) >= 0 || value.indexOf("]", closingBracket + 1) >= 0) return false; + host = value.slice(1, closingBracket); + const suffix = value.slice(closingBracket + 1); + if (suffix) { + if (!suffix.startsWith(":")) return false; + rawPort = suffix.slice(1); + } + if (!isCanonicalAidenIPv6(host)) return false; + } else { + if (value.includes("[") || value.includes("]")) return false; + const firstColon = value.indexOf(":"); + if (firstColon >= 0) { + if (firstColon !== value.lastIndexOf(":")) return false; + host = value.slice(0, firstColon); + rawPort = value.slice(firstColon + 1); + } else { + host = value; + } + if (!isCanonicalAidenDnsHost(host)) return false; + } + + return rawPort === undefined || isCanonicalAidenPort(rawPort); +} + +function hasCanonicalRawEndpointSyntax(value: string): boolean { + // WHATWG URL parsing removes raw and percent-encoded dot segments and + // normalizes empty trailing query/fragment markers. Match the complete wire + // syntax first so only one exact HTTPS endpoint spelling reaches URL parsing. + const match = /^https:\/\/([^/?#]+)\/api\/aiden\/v1$/u.exec(value); + if (!match) return false; + + const authority = match[1]!; + return isCanonicalAidenAuthority(authority); +} + +export function assertAidenRemoteEndpoint(value: string): void { + if (Buffer.byteLength(value, "utf8") > AIDEN_REMOTE_MAX_ENDPOINT_UTF8_BYTES) { + throw new Error(`Pairing endpoint exceeds ${AIDEN_REMOTE_MAX_ENDPOINT_UTF8_BYTES} UTF-8 bytes.`); + } + if (!hasCanonicalRawEndpointSyntax(value)) { + throw new Error("Pairing endpoint must be the canonical HTTPS Aiden v1 URL."); + } + let endpoint: URL; + try { + endpoint = new URL(value); + } catch { + throw new Error("Pairing endpoint must be the canonical HTTPS Aiden v1 URL."); + } + if ( + endpoint.protocol !== "https:" || endpoint.username || endpoint.password || endpoint.search || + endpoint.hash || endpoint.pathname !== AIDEN_REMOTE_BASE_PATH + ) throw new Error("Pairing endpoint must be the canonical HTTPS Aiden v1 URL."); +} + +export function parseAidenRemoteStreamEvent(value: unknown): AidenRemoteStreamEvent | null { + if (!isRecord(value)) throw new Error("Aiden Remote event must be an object."); + if ("payload" in value && !isRecord(value.payload)) { + throw new Error("Aiden Remote event payload must be an object."); + } + if (value.protocolVersion !== AIDEN_REMOTE_PROTOCOL_VERSION) { + throw new Error("Aiden Remote event has an unsupported protocolVersion."); + } + const streamId = assertBoundedString(value, "streamId", 128); + const sequence = requiredInteger(value, "sequence"); + if (sequence < 1) throw new Error("Aiden Remote event sequence must be positive."); + const timestamp = requiredString(value, "timestamp"); + parseStrictRfc3339(timestamp, "Aiden Remote event timestamp"); + const type = assertBoundedString(value, "type", 80); + if (typeof value.terminal !== "boolean") throw new Error("Aiden Remote event terminal must be boolean."); + if (!isRecord(value.payload)) throw new Error("Aiden Remote event payload must be an object."); + if (Object.keys(value.payload).length > 32) throw new Error("Aiden Remote event payload has too many properties."); + if (!(AIDEN_REMOTE_EVENT_TYPES as readonly string[]).includes(type)) { + if (value.terminal) throw new Error(`Unknown terminal Aiden Remote event type ${type}.`); + assertNoForbiddenWireKeys(value, "event"); + return null; + } + const knownType = type as AidenRemoteEventType; + if (value.terminal !== TERMINAL_EVENT_TYPES.has(knownType)) { + throw new Error(`Aiden Remote event ${type} has an invalid terminal classification.`); + } + validateEventPayload(knownType, value.payload); + assertNoForbiddenWireKeys(value, "event"); + return { + protocolVersion: AIDEN_REMOTE_PROTOCOL_VERSION, + streamId, + sequence, + timestamp, + type: knownType, + terminal: value.terminal, + payload: value.payload, + }; +} + +export function assertOrderedAidenRemoteEvents(events: readonly AidenRemoteStreamEvent[]): void { + const lastByStream = new Map(); + const terminalStreams = new Set(); + for (const event of events) { + if (terminalStreams.has(event.streamId)) { + throw new Error(`Aiden Remote stream ${event.streamId} contains an event after terminal state.`); + } + const previous = lastByStream.get(event.streamId) ?? 0; + if (event.sequence !== previous + 1) { + throw new Error( + `Aiden Remote stream ${event.streamId} expected sequence ${previous + 1}, received ${event.sequence}.`, + ); + } + lastByStream.set(event.streamId, event.sequence); + if (event.terminal) { + terminalStreams.add(event.streamId); + } + } +} + +export function parseAidenRemoteContractFixture(value: unknown): AidenRemoteContractFixture { + if (!isRecord(value)) throw new Error("Aiden Remote contract fixture must be an object."); + if (value.protocolVersion !== AIDEN_REMOTE_PROTOCOL_VERSION) { + throw new Error("Aiden Remote contract fixture protocolVersion must be 1."); + } + const contractRevision = requiredInteger(value, "contractRevision"); + if (contractRevision < 1) throw new Error("Aiden Remote contractRevision must be positive."); + if (!Array.isArray(value.capabilities)) throw new Error("Fixture capabilities must be an array."); + const capabilities = value.capabilities.map((entry) => { + if ( + typeof entry !== "string" || + !(AIDEN_REMOTE_CAPABILITIES as readonly string[]).includes(entry) + ) { + throw new Error(`Unknown Aiden Remote capability ${String(entry)}.`); + } + return entry as AidenRemoteCapability; + }); + if (!isRecord(value.health) || value.health.ok !== true || value.health.protocolVersion !== 1) { + throw new Error("Fixture health response is invalid."); + } + assertExactKeys(value.health, ["ok", "protocolVersion"], "Fixture health response"); + if (!isRecord(value.pairingBootstrap)) { + throw new Error("Fixture pairing bootstrap is invalid."); + } + const pairingBootstrap = value.pairingBootstrap; + assertExactKeys( + pairingBootstrap, + ["protocolVersion", "instanceId", "endpoint", "serverSpkiSha256", "secret", "expiresAt"], + "Fixture pairing bootstrap", + ); + if (pairingBootstrap.protocolVersion !== AIDEN_REMOTE_PROTOCOL_VERSION) { + throw new Error("Fixture pairing bootstrap protocolVersion must be 1."); + } + const instanceId = assertBoundedString(pairingBootstrap, "instanceId", AIDEN_REMOTE_MAX_IDENTIFIER_LENGTH); + const endpoint = requiredString(pairingBootstrap, "endpoint"); + const fingerprint = requiredString(pairingBootstrap, "serverSpkiSha256"); + const secret = requiredString(pairingBootstrap, "secret"); + assertAidenRemoteEndpoint(endpoint); + if (!/^sha256\/[A-Za-z0-9+/]{43}=$/.test(fingerprint)) { + throw new Error("Pairing bootstrap fingerprint must be a SHA-256 SPKI digest."); + } + assertBase64Url32(secret, "Pairing bootstrap secret"); + const expiresAt = requiredString(pairingBootstrap, "expiresAt"); + const expiryTime = parseStrictRfc3339(expiresAt, "Pairing bootstrap expiry"); + if (!isRecord(value.server) || typeof value.server.serverTime !== "string") { + throw new Error("Fixture serverTime must be a strict RFC 3339 date-time."); + } + const serverTime = parseStrictRfc3339(value.server.serverTime, "Fixture serverTime"); + if (expiryTime <= serverTime) throw new Error("Pairing bootstrap must not be expired."); + if (expiryTime - serverTime > 5 * 60_000) throw new Error("Pairing bootstrap TTL must not exceed five minutes."); + if (!isRecord(value.pairingExchange)) throw new Error("Fixture pairing exchange is invalid."); + const pairingExchange = value.pairingExchange; + assertExactKeys( + pairingExchange, + ["protocolVersion", "instanceId", "deviceId", "credential", "capabilities", "endpoint", "serverSpkiSha256"], + "Fixture pairing exchange", + ); + if (pairingExchange.protocolVersion !== 1) throw new Error("Pairing exchange protocolVersion must be 1."); + if (assertBoundedString(pairingExchange, "instanceId", AIDEN_REMOTE_MAX_IDENTIFIER_LENGTH) !== instanceId) throw new Error("Pairing exchange instance does not match bootstrap."); + assertBoundedString(pairingExchange, "deviceId", AIDEN_REMOTE_MAX_IDENTIFIER_LENGTH); + assertBase64Url32(requiredString(pairingExchange, "credential"), "Pairing device credential"); + if (!Array.isArray(pairingExchange.capabilities)) throw new Error("Pairing exchange capabilities must be an array."); + const exchangeCapabilities = pairingExchange.capabilities.map((entry) => { + if (typeof entry !== "string" || !(AIDEN_REMOTE_CAPABILITIES as readonly string[]).includes(entry)) throw new Error(`Unknown pairing capability ${String(entry)}.`); + return entry as AidenRemoteCapability; + }); + if (new Set(exchangeCapabilities).size !== exchangeCapabilities.length) throw new Error("Pairing exchange capabilities must be unique."); + if (requiredString(pairingExchange, "endpoint") !== endpoint || requiredString(pairingExchange, "serverSpkiSha256") !== fingerprint) throw new Error("Pairing exchange identity does not match bootstrap."); + if (!Array.isArray(value.events)) throw new Error("Fixture events must be an array."); + const events = value.events.map(parseAidenRemoteStreamEvent).filter((event): event is AidenRemoteStreamEvent => event !== null); + assertOrderedAidenRemoteEvents(events); + if (!isRecord(value.error) || !isRecord(value.error.error)) { + throw new Error("Fixture error envelope is invalid."); + } + assertExactKeys(value.error, ["error"], "Error envelope"); + const errorBody = value.error.error; + assertExactKeys(errorBody, ["code", "message", "requestId", "retryable", "details"], "Error envelope error"); + const code = requiredString(errorBody, "code"); + if (!(AIDEN_REMOTE_ERROR_CODES as readonly string[]).includes(code)) { + throw new Error(`Unknown Aiden Remote error code ${code}.`); + } + if (characterLength(requiredString(errorBody, "message")) > 2_000) throw new Error("Error message is too long."); + if (characterLength(requiredString(errorBody, "requestId")) > 128) throw new Error("Error requestId is too long."); + if (typeof errorBody.retryable !== "boolean") throw new Error("Error retryable must be boolean."); + if (errorBody.details !== undefined) { + if (!isRecord(errorBody.details)) throw new Error("Error details must be an object."); + assertExactKeys(errorBody.details, ["currentRevision", "retryAfterSeconds", "chatId", "minimumClientVersion", "limit", "field"], "Error details"); + const stringDetailMaxima = { + currentRevision: 128, + chatId: 128, + minimumClientVersion: 40, + field: 120, + } as const; + for (const [key, maximum] of Object.entries(stringDetailMaxima)) { + const detail = errorBody.details[key]; + if (detail !== undefined && (typeof detail !== "string" || detail.length === 0 || characterLength(detail) > maximum)) throw new Error(`Error detail ${key} is invalid.`); + } + const boundedNumericDetails = { + retryAfterSeconds: 86_400, + limit: 1_000_000, + } as const; + for (const [key, maximum] of Object.entries(boundedNumericDetails)) { + const detail = errorBody.details[key]; + if (detail !== undefined && (!Number.isSafeInteger(detail) || (detail as number) < 0 || (detail as number) > maximum)) throw new Error(`Error detail ${key} is invalid.`); + } + } + assertNoForbiddenWireKeys(value); + return { + ...value, + contractRevision, + protocolVersion: AIDEN_REMOTE_PROTOCOL_VERSION, + capabilities, + health: { ok: true, protocolVersion: AIDEN_REMOTE_PROTOCOL_VERSION }, + pairingBootstrap: pairingBootstrap as unknown as AidenRemoteContractFixture["pairingBootstrap"], + pairingExchange: { ...(pairingExchange as unknown as AidenRemoteContractFixture["pairingExchange"]), capabilities: exchangeCapabilities }, + events, + error: value.error as unknown as AidenRemoteErrorEnvelope, + }; +} + +export interface AidenSseFrame { id: string; data: unknown } + +/** + * JSON.parse keeps only the last occurrence of a duplicate object key. Scan + * the raw JSON first so that no duplicate can hide an SSE envelope field. + * Keys are decoded while scanning, which also catches escaped-equivalent + * spellings such as "a" and "\\u0061" at every nesting level. + */ +export function parseAidenRemoteJson( + serialized: string, + label = "JSON data", +): unknown { + let offset = 0; + let objectKeys = 0; + + function fail(): never { + throw new Error(`Malformed Aiden ${label}.`); + } + + function skipWhitespace(): void { + while (serialized[offset] === " " || serialized[offset] === "\t" || serialized[offset] === "\n" || serialized[offset] === "\r") { + offset += 1; + } + } + + function readString(decode: boolean): string | undefined { + if (serialized[offset] !== '"') fail(); + offset += 1; + let segmentStart = offset; + let decoded = ""; + while (offset < serialized.length) { + const character = serialized[offset]!; + if (character === '"') { + decoded += serialized.slice(segmentStart, offset); + if (!isValidJsonString(decoded)) fail(); + offset += 1; + return decode ? decoded : undefined; + } + if (character === "\\") { + decoded += serialized.slice(segmentStart, offset); + offset += 1; + const escaped = serialized[offset]; + if (escaped === undefined) fail(); + if (escaped === "u") { + const hexadecimal = serialized.slice(offset + 1, offset + 5); + if (!/^[0-9a-fA-F]{4}$/u.test(hexadecimal)) fail(); + decoded += String.fromCharCode(Number.parseInt(hexadecimal, 16)); + offset += 5; + segmentStart = offset; + continue; + } + const replacement = escaped === '"' + ? '"' + : escaped === "\\" + ? "\\" + : escaped === "/" + ? "/" + : escaped === "b" + ? "\b" + : escaped === "f" + ? "\f" + : escaped === "n" + ? "\n" + : escaped === "r" + ? "\r" + : escaped === "t" + ? "\t" + : undefined; + if (replacement === undefined) fail(); + decoded += replacement; + offset += 1; + segmentStart = offset; + continue; + } + if (character.charCodeAt(0) < 0x20) fail(); + offset += 1; + } + fail(); + } + + function readNumber(): void { + const numberStart = offset; + if (serialized[offset] === "-") offset += 1; + if (serialized[offset] === "0") { + offset += 1; + } else { + const first = serialized.charCodeAt(offset); + if (first < 0x31 || first > 0x39) fail(); + do { + offset += 1; + } while (serialized.charCodeAt(offset) >= 0x30 && serialized.charCodeAt(offset) <= 0x39); + } + if (serialized[offset] === ".") { + offset += 1; + const firstFraction = serialized.charCodeAt(offset); + if (firstFraction < 0x30 || firstFraction > 0x39) fail(); + do { + offset += 1; + } while (serialized.charCodeAt(offset) >= 0x30 && serialized.charCodeAt(offset) <= 0x39); + } + if (serialized[offset] === "e" || serialized[offset] === "E") { + offset += 1; + if (serialized[offset] === "+" || serialized[offset] === "-") offset += 1; + const firstExponent = serialized.charCodeAt(offset); + if (firstExponent < 0x30 || firstExponent > 0x39) fail(); + do { + offset += 1; + } while (serialized.charCodeAt(offset) >= 0x30 && serialized.charCodeAt(offset) <= 0x39); + } + if (!Number.isFinite(Number(serialized.slice(numberStart, offset)))) fail(); + } + + function readValue(depth: number): void { + if (depth > AIDEN_REMOTE_MAX_JSON_NESTING_DEPTH) fail(); + skipWhitespace(); + const character = serialized[offset]; + if (character === "{") { + offset += 1; + skipWhitespace(); + const keys = new Set(); + if (serialized[offset] === "}") { + offset += 1; + return; + } + for (;;) { + const key = readString(true); + objectKeys += 1; + if (objectKeys > AIDEN_REMOTE_MAX_JSON_OBJECT_KEYS || key === undefined || keys.has(key)) fail(); + keys.add(key); + skipWhitespace(); + if (serialized[offset] !== ":") fail(); + offset += 1; + readValue(depth + 1); + skipWhitespace(); + if (serialized[offset] === "}") { + offset += 1; + return; + } + if (serialized[offset] !== ",") fail(); + offset += 1; + skipWhitespace(); + } + } + if (character === "[") { + offset += 1; + skipWhitespace(); + if (serialized[offset] === "]") { + offset += 1; + return; + } + for (;;) { + readValue(depth + 1); + skipWhitespace(); + if (serialized[offset] === "]") { + offset += 1; + return; + } + if (serialized[offset] !== ",") fail(); + offset += 1; + skipWhitespace(); + } + } + if (character === '"') { + readString(false); + return; + } + if (character === "-" || (character !== undefined && character >= "0" && character <= "9")) { + readNumber(); + return; + } + for (const literal of ["true", "false", "null"]) { + if (serialized.startsWith(literal, offset)) { + offset += literal.length; + return; + } + } + fail(); + } + + readValue(0); + skipWhitespace(); + if (offset !== serialized.length) fail(); + let parsed: unknown; + try { + parsed = JSON.parse(serialized) as unknown; + } catch { + throw new Error(`Malformed Aiden Remote ${label}.`); + } + assertNoForbiddenWireKeys(parsed, label); + return parsed; +} + +export function parseAidenSseFrames(input: string): AidenSseFrame[] { + return input.split(/\r?\n\r?\n/).filter(Boolean).map((frame) => { + if (Buffer.byteLength(frame, "utf8") > AIDEN_REMOTE_MAX_SSE_FRAME_BYTES) { + throw new Error("Aiden SSE frame exceeds the byte limit."); + } + let id: string | undefined; + const data: string[] = []; + for (const line of frame.split(/\r?\n/)) { + if (line.startsWith("id:")) id = line.slice(3).trim(); + if (line.startsWith("data:")) data.push(line.slice(5).trimStart()); + } + if (!id || data.length === 0) throw new Error("Malformed Aiden SSE frame."); + return { id, data: parseAidenRemoteJson(data.join("\n"), "SSE JSON data") }; + }); +} + +export function reconcileAidenSseFrames( + frames: readonly AidenSseFrame[], + lastEventId: number, + expectedStreamId: string, +): { events: AidenRemoteStreamEvent[]; reconcileRequired: boolean } { + if (expectedStreamId.length === 0 || expectedStreamId.length > 128) { + throw new Error("Aiden SSE expected streamId must be a bounded non-empty string."); + } + const events: AidenRemoteStreamEvent[] = []; + let expected = lastEventId + 1; + let terminalSeen = false; + for (const frame of frames) { + const event = parseAidenRemoteStreamEvent(frame.data); + const streamId = event?.streamId ?? (isRecord(frame.data) && typeof frame.data.streamId === "string" ? frame.data.streamId : undefined); + if (streamId !== expectedStreamId) return { events, reconcileRequired: true }; + const sequence = event?.sequence ?? (isRecord(frame.data) ? frame.data.sequence : undefined); + if (!Number.isSafeInteger(sequence) || frame.id !== String(sequence)) return { events, reconcileRequired: true }; + if ((sequence as number) <= lastEventId) continue; + if (sequence !== expected) return { events, reconcileRequired: true }; + if (terminalSeen) return { events, reconcileRequired: true }; + if (event) { + events.push(event); + terminalSeen = event.terminal; + } + expected += 1; + } + return { events, reconcileRequired: false }; +} diff --git a/main/services/aiden-remote-router.test.ts b/main/services/aiden-remote-router.test.ts new file mode 100644 index 00000000..411f232c --- /dev/null +++ b/main/services/aiden-remote-router.test.ts @@ -0,0 +1,755 @@ +import assert from "node:assert/strict"; +import { createServer } from "node:http"; +import test from "node:test"; +import { createAidenRemoteRequestHandler } from "./aiden-remote-router.js"; +import type { AidenRemoteCapability } from "./aiden-remote-protocol.js"; + +async function fixture(options: { + authenticate?: "valid" | "revoked" | "denied" | "invalid"; + capabilities?: AidenRemoteCapability[]; +} = {}) { + const logs: unknown[] = []; + const calls: string[] = []; + const workspace = { + id: "workspace-1", + name: "Project", + permission: "ask" as const, + hasFolder: false, + isManagedWorktree: false, + createdAt: new Date(1_000).toISOString(), + updatedAt: new Date(2_000).toISOString(), + revision: `rev_${"r".repeat(43)}`, + }; + const chat = { + id: "chat-1", + workspaceId: "workspace-1", + title: "Chat", + providerId: "provider-1", + modelId: "model-1", + messages: [], + createdAt: new Date(1_000).toISOString(), + updatedAt: new Date(2_000).toISOString(), + revision: `rev_${"c".repeat(43)}`, + }; + const handler = createAidenRemoteRequestHandler({ + instanceId: "instance-1", + appVersion: "0.30.0", + devices: { + authenticate: async (credential) => { + if (credential !== "a".repeat(43) || options.authenticate === "invalid") { + return null; + } + return { + id: "device-authorized-12345678", + revoked: options.authenticate === "revoked", + capabilities: new Set( + options.authenticate === "denied" + ? [] + : (options.capabilities ?? ["server:read" as const]), + ), + }; + }, + }, + workspaces: { + list: async () => ({ workspaces: [workspace] }), + get: async (id) => ({ ...workspace, id }), + create: async (deviceId, key) => { + calls.push(`create:${deviceId}:${key}`); + return workspace; + }, + update: async (id, revision) => { + calls.push(`update:${id}:${revision}`); + return { ...workspace, id }; + }, + remove: async (id, revision) => { + calls.push(`remove:${id}:${revision}`); + }, + }, + workspaceBrowser: { + listRoots: async (deviceId) => ({ + roots: [{ + id: "root-1", + label: "Projects", + location: `loc_${"l".repeat(43)}`, + policyRevision: "policy-1", + }], + deviceId, + }) as never, + listChildren: async (deviceId, location, cursor) => { + calls.push(`children:${deviceId}:${location}:${cursor ?? ""}`); + return { rootId: "root-1", label: "Projects", breadcrumbs: [], entries: [] }; + }, + createSelection: async (deviceId, location) => { + calls.push(`selection:${deviceId}:${location}`); + return { + selection: `sel_${"s".repeat(43)}`, + displayName: "Projects", + expiresAt: new Date(60_000).toISOString(), + }; + }, + }, + chats: { + list: async (workspaceId) => { + calls.push(`chat-list:${workspaceId ?? ""}`); + return { chats: [chat] }; + }, + get: async (id) => ({ ...chat, id }), + create: async (deviceId, key) => { + calls.push(`chat-create:${deviceId}:${key}`); + return chat; + }, + rename: async (id, revision) => { + calls.push(`chat-rename:${id}:${revision}`); + return { ...chat, id, title: "Renamed" }; + }, + move: async (deviceId, id, revision, key) => { + calls.push(`chat-move:${deviceId}:${id}:${revision}:${key}`); + return { ...chat, id, workspaceId: "workspace-2" }; + }, + remove: async (id, revision) => { + calls.push(`chat-remove:${id}:${revision}`); + }, + startTurn: async (deviceId, id, key) => { + calls.push(`turn:${deviceId}:${id}:${key}`); + return { + turnId: "turn-1", + streamId: "stream-1", + status: "accepted" as const, + message: { + id: "message-1", + role: "user" as const, + text: "Hello", + createdAt: new Date(3_000).toISOString(), + }, + }; + }, + }, + models: { + list: async () => ({ + providers: [{ id: "provider-1", label: "Provider", models: [{ id: "model-1", label: "Model" }] }], + defaults: { providerId: "provider-1", modelId: "model-1" }, + }), + }, + usage: { + summary: async (range) => ({ + range, + startDate: "2026-07-21", + endDate: "2026-08-19", + totals: { + requests: 12, completedRequests: 11, failedRequests: 1, cancelledRequests: 0, + reportedTokenRequests: 10, unmeteredRequests: 2, localRequests: 3, + costedRequests: 8, unpricedHostedRequests: 1, hostedCostUsd: 1.25, + activeDays: 4, currentStreak: 2, longestStreak: 3, + tokens: { input: 100, output: 50, cacheRead: 10, cacheWrite: 2, reasoning: 8, total: 170 }, + }, + days: [], + models: [], + }), + }, + streams: { + status: (_deviceId, streamId) => ({ + streamId, + chatId: "chat-1", + turnId: "turn-1", + state: "running" as const, + lastSequence: 2, + updatedAt: new Date(4_000).toISOString(), + }), + cancel: async (deviceId, streamId, _key) => { + calls.push(`cancel:${deviceId}:${streamId}`); + return { + streamId, + chatId: "chat-1", + turnId: "turn-1", + state: "running" as const, + lastSequence: 2, + updatedAt: new Date(4_000).toISOString(), + }; + }, + respondApproval: async (deviceId, approvalId, decision, _key) => { + calls.push(`approval:${deviceId}:${approvalId}:${decision}`); + return { approvalId, decision, resolvedAt: new Date(5_000).toISOString() }; + }, + openEvents: (_deviceId, streamId, after, response) => { + calls.push(`events:${streamId}:${after}`); + const data = JSON.stringify({ streamId, sequence: after + 1 }); + response.writeHead(200, { "content-type": "text/event-stream" }); + response.end(`id: ${after + 1}\ndata: ${data}\n\n`); + }, + }, + files: { + list: async (deviceId, workspaceId) => { + calls.push(`files:${deviceId}:${workspaceId}`); + return { + snapshotId: "files-snapshot-1", + entries: [{ + id: `file_${"f".repeat(43)}`, + displayPath: "Sources/App.swift", + name: "App.swift", + kind: "file" as const, + size: 12, + language: "Swift", + }], + truncated: false, + maxEntries: 4_000 as const, + maxDepth: 20 as const, + }; + }, + read: async (deviceId, workspaceId, fileId) => { + calls.push(`file-read:${deviceId}:${workspaceId}:${fileId}`); + return { + id: fileId, + displayPath: "Sources/App.swift", + content: "let value = 1\n", + version: "a".repeat(64), + truncated: false as const, + }; + }, + write: async (deviceId, workspaceId, fileId) => { + calls.push(`file-write:${deviceId}:${workspaceId}:${fileId}`); + return { + id: fileId, + displayPath: "Sources/App.swift", + content: "let value = 2\n", + version: "b".repeat(64), + truncated: false as const, + }; + }, + }, + git: { + review: async (deviceId, workspaceId) => { + calls.push(`git-review:${deviceId}:${workspaceId}`); + return { + operationId: "op-review", + status: "snapshot", + snapshotId: `snap_${"s".repeat(43)}`, + result: { kind: "review", branch: "main", uncommitted: 0, files: [] }, + } as never; + }, + diff: async () => ({}) as never, + branches: async () => ({}) as never, + checkout: async () => ({}) as never, + createBranch: async () => ({}) as never, + commit: async () => ({}) as never, + pushCapability: async () => ({}) as never, + push: async () => ({}) as never, + compare: async () => ({}) as never, + comparisonDiff: async () => ({}) as never, + worktrees: async (deviceId, workspaceId) => { + calls.push(`git-worktrees:${deviceId}:${workspaceId}`); + return { + operationId: "op-worktrees", + status: "snapshot", + result: { kind: "worktrees", worktrees: [] }, + } as never; + }, + createWorktree: async (deviceId, workspaceId, key) => { + calls.push(`git-worktree-create:${deviceId}:${workspaceId}:${key}`); + return { + operationId: "op-worktree-create", + status: "succeeded", + result: { kind: "mutation", message: "Created managed worktree.", workspaceId: "workspace-2" }, + } as never; + }, + deleteManagedWorktree: async (deviceId, workspaceId, revision, key) => { + calls.push(`git-worktree-delete:${deviceId}:${workspaceId}:${revision}:${key}`); + return { + operationId: "op-worktree-delete", + status: "succeeded", + result: { kind: "mutation", message: "Removed managed worktree.", workspaceId }, + } as never; + }, + }, + schedules: { + list: async (deviceId) => { + calls.push(`schedule-list:${deviceId}`); + return { tasks: [] }; + }, + get: async () => ({}) as never, + create: async (deviceId, key) => { + calls.push(`schedule-create:${deviceId}:${key}`); + return { id: "task-1", revision: "rev-task-1" } as never; + }, + update: async () => ({}) as never, + remove: async (taskId, revision) => { + calls.push(`schedule-remove:${taskId}:${revision}`); + }, + pause: async (deviceId, taskId, revision, key) => { + calls.push(`schedule-pause:${deviceId}:${taskId}:${revision}:${key}`); + return { id: taskId, revision: "rev-task-2" } as never; + }, + resume: async () => ({}) as never, + run: async (deviceId, taskId, key) => { + calls.push(`schedule-run:${deviceId}:${taskId}:${key}`); + return { taskId, runId: "run-1", status: "accepted" as const, acceptedAt: new Date(1_000).toISOString() }; + }, + runs: async (taskId) => { + calls.push(`schedule-runs:${taskId}`); + return { runs: [] }; + }, + preview: () => ({ dates: [new Date(2_000).toISOString()] }), + scripts: async (deviceId, workspaceId) => { + calls.push(`schedule-scripts:${deviceId}:${workspaceId ?? ""}`); + return { scripts: [{ id: `script_${"s".repeat(43)}`, name: "daily.sh" }] }; + }, + mcpServers: async () => ({ servers: [{ id: "mcp-1", name: "GitHub" }] }), + settings: async () => ({ + revision: "rev-settings", enabled: true, defaultMode: "llm" as const, + defaultPermission: "read-only" as const, defaultMcpEnabled: false, + defaultNotify: true, defaultTimezone: "UTC", + }), + updateSettings: async () => ({}) as never, + }, + pairing: { + exchange: async () => ({ + protocolVersion: 1, + instanceId: "instance-1", + deviceId: "device-1", + credential: "b".repeat(43), + capabilities: ["server:read" as const], + endpoint: "https://aiden.example.test/api/aiden/v1", + serverSpkiSha256: `sha256/${Buffer.alloc(32).toString("base64")}`, + }), + }, + connectionMode: () => "lan", + now: () => 1_000, + log: (entry) => logs.push(entry), + }); + const server = createServer(handler); + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", resolve); + }); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("test server did not bind"); + return { + base: `http://127.0.0.1:${address.port}/api/aiden/v1`, + logs, + calls, + close: () => new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve())), + }; +} + +test("health is the only unauthenticated read and server projection requires both headers", async () => { + const app = await fixture(); + try { + const health = await fetch(`${app.base}/health`); + assert.equal(health.status, 200); + assert.deepEqual(await health.json(), { ok: true, protocolVersion: 1 }); + + const unauthenticated = await fetch(`${app.base}/server`); + assert.equal(unauthenticated.status, 400); + assert.equal((await unauthenticated.json()).error.code, "invalid_request"); + + const authenticated = await fetch(`${app.base}/server`, { + headers: { + authorization: `Bearer ${"a".repeat(43)}`, + "aiden-protocol-version": "1", + }, + }); + assert.equal(authenticated.status, 200); + const server = await authenticated.json(); + assert.equal(server.instanceId, "instance-1"); + assert.equal(server.connectionMode, "lan"); + assert.equal(JSON.stringify(server).includes("credential"), false); + } finally { + await app.close(); + } +}); + +test("authenticated workspace CRUD and browser routes preserve the frozen HTTP contract", async () => { + const app = await fixture({ + capabilities: ["workspace:read", "workspace:manage", "workspace:browse"], + }); + const headers = { + authorization: `Bearer ${"a".repeat(43)}`, + "aiden-protocol-version": "1", + }; + try { + const listed = await fetch(`${app.base}/workspaces`, { headers }); + assert.equal(listed.status, 200); + assert.equal((await listed.json()).workspaces[0].id, "workspace-1"); + + const created = await fetch(`${app.base}/workspaces`, { + method: "POST", + headers: { + ...headers, + "content-type": "application/json", + "idempotency-key": "workspace-create-key-0001", + }, + body: JSON.stringify({ mode: "folderless", name: "Project" }), + }); + assert.equal(created.status, 201); + + const revision = `rev_${"r".repeat(43)}`; + const updated = await fetch(`${app.base}/workspaces/workspace-1`, { + method: "PATCH", + headers: { ...headers, "content-type": "application/json", "if-match": revision }, + body: JSON.stringify({ confirmedForeground: true, name: "Renamed" }), + }); + assert.equal(updated.status, 200); + + const location = `loc_${"l".repeat(43)}`; + const cursor = `cur_${"c".repeat(43)}`; + const children = await fetch( + `${app.base}/workspace-browser/children?location=${location}&cursor=${cursor}`, + { headers }, + ); + assert.equal(children.status, 200); + const selection = await fetch(`${app.base}/workspace-browser/selections`, { + method: "POST", + headers: { ...headers, "content-type": "application/json" }, + body: JSON.stringify({ location }), + }); + assert.equal(selection.status, 201); + + const removed = await fetch(`${app.base}/workspaces/workspace-1`, { + method: "DELETE", + headers: { ...headers, "if-match": revision }, + }); + assert.equal(removed.status, 204); + assert.deepEqual(app.calls, [ + "create:device-authorized-12345678:workspace-create-key-0001", + `update:workspace-1:${revision}`, + `children:device-authorized-12345678:${location}:${cursor}`, + `selection:device-authorized-12345678:${location}`, + `remove:workspace-1:${revision}`, + ]); + } finally { + await app.close(); + } +}); + +test("authenticated chat, model, turn, stream, cancel, and approval routes preserve the contract", async () => { + const app = await fixture({ + capabilities: ["chat:read", "chat:write", "approval:respond"], + }); + const headers = { + authorization: `Bearer ${"a".repeat(43)}`, + "aiden-protocol-version": "1", + }; + const revision = `rev_${"c".repeat(43)}`; + try { + const models = await fetch(`${app.base}/models`, { headers }); + assert.equal(models.status, 200); + assert.equal((await models.json()).defaults.modelId, "model-1"); + + const chats = await fetch(`${app.base}/chats?workspaceId=workspace-1`, { headers }); + assert.equal(chats.status, 200); + assert.equal((await chats.json()).chats[0].id, "chat-1"); + + const created = await fetch(`${app.base}/chats`, { + method: "POST", + headers: { ...headers, "content-type": "application/json", "idempotency-key": "chat-create-key-00001" }, + body: JSON.stringify({ workspaceId: "workspace-1" }), + }); + assert.equal(created.status, 201); + + const renamed = await fetch(`${app.base}/chats/chat-1`, { + method: "PATCH", + headers: { ...headers, "content-type": "application/json", "if-match": revision }, + body: JSON.stringify({ title: "Renamed" }), + }); + assert.equal(renamed.status, 200); + + const turn = await fetch(`${app.base}/chats/chat-1/turns`, { + method: "POST", + headers: { ...headers, "content-type": "application/json", "idempotency-key": "turn-start-key-000001" }, + body: JSON.stringify({ text: "Hello" }), + }); + assert.equal(turn.status, 202); + assert.equal((await turn.json()).streamId, "stream-1"); + + const status = await fetch(`${app.base}/streams/stream-1`, { headers }); + assert.equal(status.status, 200); + assert.equal((await status.json()).state, "running"); + + const events = await fetch(`${app.base}/streams/stream-1/events?after=1`, { headers }); + assert.equal(events.status, 200); + assert.match(await events.text(), /id: 2/u); + + const cancelled = await fetch(`${app.base}/streams/stream-1/cancel`, { + method: "POST", + headers: { ...headers, "idempotency-key": "cancel-stream-key-0001" }, + }); + assert.equal(cancelled.status, 202); + + const approval = await fetch(`${app.base}/approvals/approval-1/respond`, { + method: "POST", + headers: { ...headers, "content-type": "application/json", "idempotency-key": "approval-key-0000001" }, + body: JSON.stringify({ decision: "deny" }), + }); + assert.equal(approval.status, 200); + assert.equal((await approval.json()).decision, "deny"); + } finally { + await app.close(); + } +}); + +test("authenticated usage returns privacy-safe Mac aggregates with a bounded range", async () => { + const app = await fixture({ capabilities: ["server:read"] }); + const headers = { + authorization: `Bearer ${"a".repeat(43)}`, + "aiden-protocol-version": "1", + }; + try { + const response = await fetch(`${app.base}/usage?range=30d`, { headers }); + assert.equal(response.status, 200); + const summary = await response.json(); + assert.equal(summary.range, "30d"); + assert.equal(summary.totals.requests, 12); + assert.equal(JSON.stringify(summary).includes("chatId"), false); + + const invalid = await fetch(`${app.base}/usage?range=forever`, { headers }); + assert.equal(invalid.status, 400); + } finally { + await app.close(); + } +}); + +test("authenticated file index, read, and versioned write routes preserve opaque identifiers", async () => { + const app = await fixture({ capabilities: ["files:read", "files:write"] }); + const headers = { + authorization: `Bearer ${"a".repeat(43)}`, + "aiden-protocol-version": "1", + }; + const fileId = `file_${"f".repeat(43)}`; + try { + const index = await fetch(`${app.base}/workspaces/workspace-1/files`, { headers }); + assert.equal(index.status, 200); + assert.equal((await index.json()).entries[0].id, fileId); + + const document = await fetch(`${app.base}/workspaces/workspace-1/files/${fileId}`, { headers }); + assert.equal(document.status, 200); + assert.equal((await document.json()).displayPath, "Sources/App.swift"); + + const saved = await fetch(`${app.base}/workspaces/workspace-1/files/${fileId}`, { + method: "PUT", + headers: { ...headers, "content-type": "application/json" }, + body: JSON.stringify({ content: "let value = 2\n", expectedVersion: "a".repeat(64) }), + }); + assert.equal(saved.status, 200); + assert.equal((await saved.json()).version, "b".repeat(64)); + assert.deepEqual(app.calls, [ + "files:device-authorized-12345678:workspace-1", + `file-read:device-authorized-12345678:workspace-1:${fileId}`, + `file-write:device-authorized-12345678:workspace-1:${fileId}`, + ]); + } finally { + await app.close(); + } +}); + +test("authenticated Git review and confirmed managed-worktree routes preserve mutation preconditions", async () => { + const app = await fixture({ capabilities: ["git:read", "git:write"] }); + const headers = { + authorization: `Bearer ${"a".repeat(43)}`, + "aiden-protocol-version": "1", + }; + const createKey = "git-worktree-create-key-0001"; + const deleteKey = "git-worktree-delete-key-0001"; + const revision = `rev_${"r".repeat(43)}`; + try { + const review = await fetch(`${app.base}/workspaces/workspace-1/git/review`, { headers }); + assert.equal(review.status, 200); + assert.equal((await review.json()).result.kind, "review"); + + const created = await fetch(`${app.base}/workspaces/workspace-1/git/worktrees`, { + method: "POST", + headers: { ...headers, "content-type": "application/json", "idempotency-key": createKey }, + body: JSON.stringify({ branch: "feature/mobile", name: "Mobile", confirmedForeground: true }), + }); + assert.equal(created.status, 202); + + const missingRevision = await fetch(`${app.base}/workspaces/workspace-2/git/managed-worktree`, { + method: "DELETE", + headers: { ...headers, "content-type": "application/json", "idempotency-key": deleteKey }, + body: JSON.stringify({ confirmedForeground: true }), + }); + assert.equal(missingRevision.status, 400); + + const removed = await fetch(`${app.base}/workspaces/workspace-2/git/managed-worktree`, { + method: "DELETE", + headers: { + ...headers, + "content-type": "application/json", + "idempotency-key": deleteKey, + "if-match": revision, + }, + body: JSON.stringify({ confirmedForeground: true }), + }); + assert.equal(removed.status, 202); + assert.deepEqual(app.calls, [ + "git-review:device-authorized-12345678:workspace-1", + `git-worktree-create:device-authorized-12345678:workspace-1:${createKey}`, + `git-worktree-delete:device-authorized-12345678:workspace-2:${revision}:${deleteKey}`, + ]); + } finally { + await app.close(); + } +}); + +test("authenticated scheduled-task routes enforce capability and mutation preconditions", async () => { + const app = await fixture({ capabilities: ["schedule:read", "schedule:write"] }); + const headers = { + authorization: `Bearer ${"a".repeat(43)}`, + "aiden-protocol-version": "1", + }; + const key = "schedule-action-key-0001"; + try { + const listed = await fetch(`${app.base}/scheduled-tasks`, { headers }); + assert.equal(listed.status, 200); + + const scripts = await fetch(`${app.base}/scheduled-tasks/scripts?workspaceId=workspace-1`, { headers }); + assert.equal(scripts.status, 200); + assert.match((await scripts.json()).scripts[0].id, /^script_/u); + + const mcpServers = await fetch(`${app.base}/scheduled-tasks/mcp-servers`, { headers }); + assert.equal(mcpServers.status, 200); + assert.deepEqual(await mcpServers.json(), { servers: [{ id: "mcp-1", name: "GitHub" }] }); + + const created = await fetch(`${app.base}/scheduled-tasks`, { + method: "POST", + headers: { ...headers, "content-type": "application/json", "idempotency-key": key }, + body: JSON.stringify({ + name: "Daily", schedule: "0 8 * * *", timezone: "UTC", mode: "llm", + permission: "read-only", prompt: "Summarize", confirmedForeground: true, + }), + }); + assert.equal(created.status, 201); + + const missingRevision = await fetch(`${app.base}/scheduled-tasks/task-1/pause`, { + method: "POST", + headers: { ...headers, "idempotency-key": key }, + }); + assert.equal(missingRevision.status, 400); + + const paused = await fetch(`${app.base}/scheduled-tasks/task-1/pause`, { + method: "POST", + headers: { ...headers, "idempotency-key": key, "if-match": "rev-task-1" }, + }); + assert.equal(paused.status, 202); + + const run = await fetch(`${app.base}/scheduled-tasks/task-1/run`, { + method: "POST", + headers: { ...headers, "idempotency-key": key }, + }); + assert.equal(run.status, 202); + assert.equal((await run.json()).runId, "run-1"); + + const history = await fetch(`${app.base}/scheduled-tasks/task-1/runs`, { headers }); + assert.equal(history.status, 200); + assert.deepEqual(app.calls, [ + "schedule-list:device-authorized-12345678", + "schedule-scripts:device-authorized-12345678:workspace-1", + `schedule-create:device-authorized-12345678:${key}`, + `schedule-pause:device-authorized-12345678:task-1:rev-task-1:${key}`, + `schedule-run:device-authorized-12345678:task-1:${key}`, + "schedule-runs:task-1", + ]); + } finally { + await app.close(); + } +}); + +test("workspace routes reject query aliases, duplicate query keys, and missing mutation preconditions", async () => { + const app = await fixture({ + capabilities: ["workspace:read", "workspace:manage", "workspace:browse"], + }); + const headers = { + authorization: `Bearer ${"a".repeat(43)}`, + "aiden-protocol-version": "1", + }; + try { + const aliased = await fetch(`${app.base}/workspaces?workspaceId=secret`, { headers }); + assert.equal(aliased.status, 400); + assert.equal((await aliased.json()).error.code, "invalid_request"); + + const location = `loc_${"l".repeat(43)}`; + const duplicate = await fetch( + `${app.base}/workspace-browser/children?location=${location}&location=${location}`, + { headers }, + ); + assert.equal(duplicate.status, 400); + + const missingRevision = await fetch(`${app.base}/workspaces/workspace-1`, { + method: "DELETE", + headers, + }); + assert.equal(missingRevision.status, 400); + + const encodedAlias = await fetch( + `${app.base}/workspace-browser/children?location=loc_%61${"a".repeat(42)}`, + { headers }, + ); + assert.equal(encodedAlias.status, 400); + } finally { + await app.close(); + } +}); + +test("revoked and capability-limited credentials fail with stable classifications", async () => { + for (const [mode, code] of [ + ["revoked", "credential_revoked"], + ["denied", "capability_denied"], + ["invalid", "authentication_required"], + ] as const) { + const app = await fixture({ authenticate: mode }); + try { + const response = await fetch(`${app.base}/server`, { + headers: { + authorization: `Bearer ${"a".repeat(43)}`, + "aiden-protocol-version": "1", + }, + }); + assert.equal((await response.json()).error.code, code); + } finally { + await app.close(); + } + } +}); + +test("pairing rejects duplicate JSON fields, browser origins, and oversized bodies", async () => { + const app = await fixture(); + try { + const duplicate = await fetch(`${app.base}/pairing/exchange`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: `{"secret":"${"x".repeat(43)}","secret":"${"y".repeat(43)}"}`, + }); + assert.equal(duplicate.status, 400); + assert.equal((await duplicate.json()).error.code, "invalid_request"); + + const browser = await fetch(`${app.base}/health`, { + headers: { origin: "https://attacker.example" }, + }); + assert.equal(browser.status, 403); + + const oversized = await fetch(`${app.base}/pairing/exchange`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ value: "x".repeat(1_048_576) }), + }); + assert.equal(oversized.status, 413); + assert.equal((await oversized.json()).error.code, "payload_too_large"); + await new Promise((resolve) => setImmediate(resolve)); + assert.equal(app.logs.length, 3); + } finally { + await app.close(); + } +}); + +test("unknown routes and query aliases fail without reflecting untrusted input", async () => { + const app = await fixture(); + try { + const canary = "do-not-reflect-this-secret"; + const response = await fetch(`${app.base}/health?value=${canary}`); + const body = await response.text(); + assert.equal(response.status, 400); + assert.equal(body.includes(canary), false); + const missing = await fetch(`${app.base}/missing`); + assert.equal((await missing.json()).error.code, "not_found"); + } finally { + await app.close(); + } +}); diff --git a/main/services/aiden-remote-router.ts b/main/services/aiden-remote-router.ts new file mode 100644 index 00000000..8aced43c --- /dev/null +++ b/main/services/aiden-remote-router.ts @@ -0,0 +1,1109 @@ +import { randomBytes } from "node:crypto"; +import type { IncomingMessage, ServerResponse } from "node:http"; +import { + AIDEN_REMOTE_BASE_PATH, + AIDEN_REMOTE_CAPABILITIES, + AIDEN_REMOTE_PROTOCOL_VERSION, + parseAidenRemoteJson, + type AidenRemoteCapability, + type AidenRemoteErrorEnvelope, +} from "./aiden-remote-protocol.js"; +import { + AidenRemoteServiceError, + asAidenRemoteServiceError, +} from "./aiden-remote-errors.js"; +import type { AidenRemotePairingService } from "./aiden-remote-pairing.js"; +import type { + AidenRemoteAuthenticatedDevice, + AidenRemoteConnectionMode, + AidenRemoteStateRegistry, +} from "./aiden-remote-state.js"; +import type { AidenRemoteWorkspaceBrowserService } from "./aiden-remote-workspace-browser.js"; +import type { AidenRemoteWorkspaceService } from "./aiden-remote-workspaces.js"; +import type { AidenRemoteChatService } from "./aiden-remote-chats.js"; +import type { AidenRemoteModelService } from "./aiden-remote-models.js"; +import type { AidenRemoteStreamService } from "./aiden-remote-streams.js"; +import type { AidenRemoteFileService } from "./aiden-remote-files.js"; +import type { AidenRemoteGitService } from "./aiden-remote-git.js"; +import type { AidenRemoteScheduleService } from "./aiden-remote-schedules.js"; +import type { UsageDateRange, UsageSummary } from "./types.js"; +import { MAX_AIDEN_REMOTE_ATTACHMENT_REQUEST_BYTES } from "./aiden-remote-attachments.js"; + +const MAX_REQUEST_BODY_BYTES = 1_048_576; +const MAX_FILE_REQUEST_BODY_BYTES = 6 * 1_048_576; +const MAX_REQUEST_URL_LENGTH = 2_048; +const SERVER_NAME = "Aiden Agent"; + +export interface AidenRemoteServerProjection { + protocolVersion: typeof AIDEN_REMOTE_PROTOCOL_VERSION; + instanceId: string; + name: string; + appVersion: string; + capabilities: AidenRemoteCapability[]; + connectionMode: AidenRemoteConnectionMode; + minimumClientVersion?: string; + serverTime: string; +} + +export interface AidenRemoteRouterDependencies { + instanceId: string; + appVersion: string; + devices: Pick; + pairing: Pick; + workspaces?: Pick; + workspaceBrowser?: Pick< + AidenRemoteWorkspaceBrowserService, + "listRoots" | "listChildren" | "createSelection" + >; + chats?: Pick< + AidenRemoteChatService, + "list" | "get" | "create" | "rename" | "move" | "remove" | "startTurn" + > & Partial>; + models?: Pick; + streams?: Pick< + AidenRemoteStreamService, + "status" | "cancel" | "respondApproval" | "openEvents" + >; + files?: Pick; + git?: Pick; + schedules?: Pick; + usage?: { summary(range: UsageDateRange): Promise }; + connectionMode(): AidenRemoteConnectionMode; + now(): number; + /** Tailscale Serve strips the public API prefix before loopback proxying. */ + acceptStrippedBasePath?: boolean; + log(entry: { + requestId: string; + route: + | "health" + | "pairingExchange" + | "server" + | "workspaces" + | "workspace" + | "workspaceBrowserRoots" + | "workspaceBrowserChildren" + | "workspaceBrowserSelection" + | "workspaceFiles" + | "workspaceFile" + | "workspaceGit" + | "scheduledTasks" + | "usage" + | "chats" + | "chat" + | "chatMove" + | "chatAttachment" + | "turns" + | "models" + | "stream" + | "streamEvents" + | "streamCancel" + | "approvalRespond" + | "unknown"; + status: number; + latencyMs: number; + deviceIdSuffix?: string; + errorCode?: string; + }): void; +} + +function requestId(): string { + return `req_${randomBytes(18).toString("base64url")}`; +} + +function hasAsciiControl(value: string): boolean { + for (let index = 0; index < value.length; index += 1) { + const code = value.charCodeAt(index); + if (code <= 0x1f || code === 0x7f) return true; + } + return false; +} + +function responseHeaders(contentType = "application/json; charset=utf-8") { + return { + "aiden-protocol-version": String(AIDEN_REMOTE_PROTOCOL_VERSION), + "cache-control": "no-store", + "content-type": contentType, + "cross-origin-resource-policy": "same-site", + "referrer-policy": "no-referrer", + "x-content-type-options": "nosniff", + }; +} + +function writeJson(response: ServerResponse, status: number, value: unknown): void { + const body = JSON.stringify(value); + response.writeHead(status, { + ...responseHeaders(), + "content-length": String(Buffer.byteLength(body, "utf8")), + }); + response.end(body); +} + +function writeError( + response: ServerResponse, + id: string, + error: AidenRemoteServiceError, +): void { + const envelope: AidenRemoteErrorEnvelope = { + error: { + code: error.code, + message: error.message, + requestId: id, + retryable: error.retryable, + ...(error.details ? { details: error.details } : {}), + }, + }; + writeJson(response, error.status, envelope); +} + +async function readJsonBody( + request: IncomingMessage, + maximumBytes = MAX_REQUEST_BODY_BYTES, +): Promise { + const contentType = request.headers["content-type"]; + if ( + typeof contentType !== "string" || + contentType.toLowerCase().split(";", 1)[0]?.trim() !== "application/json" + ) { + throw new AidenRemoteServiceError( + "invalid_request", + "This endpoint requires an application/json request body.", + 415, + ); + } + const declaredLength = request.headers["content-length"]; + if ( + typeof declaredLength === "string" && + (!/^\d+$/u.test(declaredLength) || Number(declaredLength) > maximumBytes) + ) { + throw new AidenRemoteServiceError( + "payload_too_large", + "The request body is too large.", + 413, + ); + } + const chunks: Buffer[] = []; + let bytes = 0; + for await (const chunk of request) { + const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); + bytes += buffer.length; + if (bytes > maximumBytes) { + throw new AidenRemoteServiceError( + "payload_too_large", + "The request body is too large.", + 413, + ); + } + chunks.push(buffer); + } + if (bytes === 0) { + throw new AidenRemoteServiceError( + "invalid_request", + "The request body is required.", + 400, + ); + } + let serialized: string; + try { + serialized = new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks)); + } catch { + throw new AidenRemoteServiceError( + "invalid_request", + "The request body must be valid UTF-8 JSON.", + 400, + ); + } + try { + return parseAidenRemoteJson(serialized, "request JSON data"); + } catch { + throw new AidenRemoteServiceError( + "invalid_request", + "The request body must be valid JSON with unique safe fields.", + 400, + ); + } +} + +function bearerCredential(request: IncomingMessage): string | null { + const authorization = request.headers.authorization; + if (typeof authorization !== "string") return null; + const match = /^Bearer ([A-Za-z0-9_-]{43})$/u.exec(authorization); + return match?.[1] ?? null; +} + +async function authenticate( + request: IncomingMessage, + devices: Pick, + capability: AidenRemoteCapability, +): Promise { + if (request.headers["aiden-protocol-version"] !== "1") { + throw new AidenRemoteServiceError( + "invalid_request", + "Aiden-Protocol-Version must be 1.", + 400, + false, + { minimumClientVersion: "1" }, + ); + } + const credential = bearerCredential(request); + if (!credential) { + throw new AidenRemoteServiceError( + "authentication_required", + "Pair this device in Aiden Settings before connecting.", + 401, + ); + } + const device = await devices.authenticate(credential); + if (!device) { + throw new AidenRemoteServiceError( + "authentication_required", + "This device credential is not valid for this Aiden installation.", + 401, + ); + } + if (device.revoked) { + throw new AidenRemoteServiceError( + "credential_revoked", + "This device was revoked in Aiden Settings.", + 403, + ); + } + if (!device.capabilities.has(capability)) { + throw new AidenRemoteServiceError( + "capability_denied", + "This device does not have access to that Aiden capability.", + 403, + ); + } + return device; +} + +function requestTarget( + request: IncomingMessage, + acceptStrippedBasePath: boolean, +): { path: string; query: string } { + const raw = request.url; + if ( + !raw || + raw.length > MAX_REQUEST_URL_LENGTH || + raw.includes("#") || + raw.includes("%") || + raw.includes("\\") || + hasAsciiControl(raw) + ) { + throw new AidenRemoteServiceError( + "invalid_request", + "The request URL is invalid.", + 400, + ); + } + const query = raw.indexOf("?"); + const path = query < 0 ? raw : raw.slice(0, query); + if ( + !path.startsWith("/") || + path.includes("//") || + path.split("/").some((segment) => segment === "." || segment === "..") + ) { + throw new AidenRemoteServiceError( + "invalid_request", + "The request URL is invalid.", + 400, + ); + } + const queryString = query < 0 ? "" : raw.slice(query + 1); + if (path.startsWith(`${AIDEN_REMOTE_BASE_PATH}/`)) { + return { path: path.slice(AIDEN_REMOTE_BASE_PATH.length), query: queryString }; + } + if (acceptStrippedBasePath) { + return { path, query: queryString }; + } + return { path: "", query: queryString }; +} + +function requireNoQuery(query: string): void { + if (query) { + throw new AidenRemoteServiceError( + "invalid_request", + "This endpoint does not accept query parameters.", + 400, + ); + } +} + +function browserQuery(query: string): { location: string; cursor?: string } { + const values = new Map(); + for (const component of query.split("&")) { + const separator = component.indexOf("="); + if (separator <= 0) { + throw new AidenRemoteServiceError("invalid_request", "The folder-browser query is invalid.", 400); + } + const key = component.slice(0, separator); + const value = component.slice(separator + 1); + if (values.has(key) || (key !== "location" && key !== "cursor")) { + throw new AidenRemoteServiceError("invalid_request", "The folder-browser query is invalid.", 400); + } + values.set(key, value); + } + const location = values.get("location"); + const cursor = values.get("cursor"); + if ( + !location || + !/^loc_[A-Za-z0-9_-]{43}$/u.test(location) || + (cursor !== undefined && !/^cur_[A-Za-z0-9_-]{43}$/u.test(cursor)) + ) { + throw new AidenRemoteServiceError("invalid_request", "The folder-browser query is invalid.", 400); + } + return { location, ...(cursor ? { cursor } : {}) }; +} + +function chatsQuery(query: string): { workspaceId?: string } { + if (!query) return {}; + const separator = query.indexOf("="); + if ( + separator <= 0 || + query.slice(0, separator) !== "workspaceId" || + query.indexOf("&") >= 0 || + !/^[A-Za-z0-9._:-]{1,128}$/u.test(query.slice(separator + 1)) + ) { + throw new AidenRemoteServiceError("invalid_request", "The chats query is invalid.", 400); + } + return { workspaceId: query.slice(separator + 1) }; +} + +function usageQuery(query: string): UsageDateRange { + const params = new URLSearchParams(query); + const range = params.get("range") ?? "30d"; + if (params.size !== 1 || !["7d", "30d", "90d", "1y", "all"].includes(range)) { + throw new AidenRemoteServiceError("invalid_request", "The usage range is invalid.", 400); + } + return range as UsageDateRange; +} + +function scheduledScriptsQuery(query: string): { workspaceId?: string } { + if (!query) return {}; + const separator = query.indexOf("="); + if ( + separator <= 0 || + query.slice(0, separator) !== "workspaceId" || + query.indexOf("&") >= 0 || + !/^[A-Za-z0-9_-]{1,128}$/u.test(query.slice(separator + 1)) + ) { + throw new AidenRemoteServiceError("invalid_request", "The scheduled-script query is invalid.", 400); + } + return { workspaceId: query.slice(separator + 1) }; +} + +function streamAfter(request: IncomingMessage, query: string): number { + let after: string | undefined; + if (query) { + const separator = query.indexOf("="); + if ( + separator <= 0 || + query.slice(0, separator) !== "after" || + query.indexOf("&") >= 0 + ) { + throw new AidenRemoteServiceError("invalid_request", "The stream cursor is invalid.", 400); + } + after = query.slice(separator + 1); + } + const lastEventId = request.headers["last-event-id"]; + if (Array.isArray(lastEventId) || (lastEventId !== undefined && typeof lastEventId !== "string")) { + throw new AidenRemoteServiceError("invalid_request", "Last-Event-ID is invalid.", 400); + } + if (after !== undefined && lastEventId !== undefined && after !== lastEventId) { + throw new AidenRemoteServiceError("invalid_request", "Stream cursors disagree.", 400); + } + const value = after ?? lastEventId ?? "0"; + if (!/^(?:0|[1-9]\d{0,15})$/u.test(value)) { + throw new AidenRemoteServiceError("invalid_request", "The stream cursor is invalid.", 400); + } + const parsed = Number(value); + if (!Number.isSafeInteger(parsed)) { + throw new AidenRemoteServiceError("invalid_request", "The stream cursor is invalid.", 400); + } + return parsed; +} + +function approvalDecision(value: unknown): "allow" | "deny" { + const record = value && typeof value === "object" && !Array.isArray(value) + ? value as Record + : null; + if ( + !record || + Object.keys(record).length !== 1 || + (record.decision !== "allow" && record.decision !== "deny") + ) { + throw new AidenRemoteServiceError("invalid_request", "The approval response is invalid.", 400); + } + return record.decision; +} + +function requiredHeader( + request: IncomingMessage, + name: "if-match" | "idempotency-key", + pattern: RegExp, +): string { + const value = request.headers[name]; + if (typeof value !== "string" || !pattern.test(value)) { + throw new AidenRemoteServiceError( + "invalid_request", + `${name === "if-match" ? "If-Match" : "Idempotency-Key"} is required and invalid.`, + 400, + ); + } + return value; +} + +function selectionLocation(value: unknown): string { + if ( + !value || + typeof value !== "object" || + Array.isArray(value) || + Object.keys(value).length !== 1 || + typeof (value as { location?: unknown }).location !== "string" || + !/^loc_[A-Za-z0-9_-]{43}$/u.test((value as { location: string }).location) + ) { + throw new AidenRemoteServiceError("invalid_request", "The folder selection request is invalid.", 400); + } + return (value as { location: string }).location; +} + +function sourceIdentity(request: IncomingMessage): string { + const address = request.socket.remoteAddress ?? "unknown"; + return address.length <= 128 ? address : "unknown"; +} + +export function createAidenRemoteRequestHandler( + dependencies: AidenRemoteRouterDependencies, +): (request: IncomingMessage, response: ServerResponse) => void { + return (request, response) => { + const id = requestId(); + const startedAt = dependencies.now(); + let route: Parameters[0]["route"] = "unknown"; + let deviceIdSuffix: string | undefined; + void (async () => { + if (request.headers.origin !== undefined) { + throw new AidenRemoteServiceError( + "invalid_request", + "Browser-origin requests are not accepted by Aiden Remote.", + 403, + ); + } + const target = requestTarget( + request, + dependencies.acceptStrippedBasePath === true, + ); + const { path, query } = target; + if (request.method === "GET" && path === "/health") { + requireNoQuery(query); + route = "health"; + writeJson(response, 200, { + ok: true, + protocolVersion: AIDEN_REMOTE_PROTOCOL_VERSION, + }); + return; + } + if (request.method === "POST" && path === "/pairing/exchange") { + requireNoQuery(query); + route = "pairingExchange"; + const result = await dependencies.pairing.exchange( + await readJsonBody(request), + sourceIdentity(request), + ); + writeJson(response, 200, result); + return; + } + if (request.method === "GET" && path === "/server") { + requireNoQuery(query); + route = "server"; + const device = await authenticate(request, dependencies.devices, "server:read"); + deviceIdSuffix = device.id.slice(-8); + const projection: AidenRemoteServerProjection = { + protocolVersion: AIDEN_REMOTE_PROTOCOL_VERSION, + instanceId: dependencies.instanceId, + name: SERVER_NAME, + appVersion: dependencies.appVersion, + capabilities: [...AIDEN_REMOTE_CAPABILITIES], + connectionMode: dependencies.connectionMode(), + serverTime: new Date(dependencies.now()).toISOString(), + }; + writeJson(response, 200, projection); + return; + } + if (path === "/workspaces" && request.method === "GET") { + requireNoQuery(query); + route = "workspaces"; + const device = await authenticate(request, dependencies.devices, "workspace:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.workspaces) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.workspaces.list()); + return; + } + if (path === "/workspaces" && request.method === "POST") { + requireNoQuery(query); + route = "workspaces"; + const device = await authenticate(request, dependencies.devices, "workspace:manage"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.workspaces) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + writeJson( + response, + 201, + await dependencies.workspaces.create(device.id, key, await readJsonBody(request)), + ); + return; + } + const workspaceMatch = /^\/workspaces\/([A-Za-z0-9_-]{1,128})$/u.exec(path); + if (workspaceMatch && request.method === "GET") { + requireNoQuery(query); + route = "workspace"; + const device = await authenticate(request, dependencies.devices, "workspace:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.workspaces) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.workspaces.get(workspaceMatch[1]!)); + return; + } + if (workspaceMatch && request.method === "PATCH") { + requireNoQuery(query); + route = "workspace"; + const device = await authenticate(request, dependencies.devices, "workspace:manage"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.workspaces) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const revision = requiredHeader(request, "if-match", /^[\x21-\x7e]{1,128}$/u); + writeJson( + response, + 200, + await dependencies.workspaces.update( + workspaceMatch[1]!, + revision, + await readJsonBody(request), + ), + ); + return; + } + if (workspaceMatch && request.method === "DELETE") { + requireNoQuery(query); + route = "workspace"; + const device = await authenticate(request, dependencies.devices, "workspace:manage"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.workspaces) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const revision = requiredHeader(request, "if-match", /^[\x21-\x7e]{1,128}$/u); + await dependencies.workspaces.remove(workspaceMatch[1]!, revision); + response.writeHead(204, responseHeaders()); + response.end(); + return; + } + const workspaceFilesMatch = /^\/workspaces\/([A-Za-z0-9_-]{1,128})\/files$/u.exec(path); + if (workspaceFilesMatch && request.method === "GET") { + requireNoQuery(query); + route = "workspaceFiles"; + const device = await authenticate(request, dependencies.devices, "files:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.files) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.files.list(device.id, workspaceFilesMatch[1]!)); + return; + } + const workspaceFileMatch = /^\/workspaces\/([A-Za-z0-9_-]{1,128})\/files\/(file_[A-Za-z0-9_-]{43})$/u.exec(path); + if (workspaceFileMatch && request.method === "GET") { + requireNoQuery(query); + route = "workspaceFile"; + const device = await authenticate(request, dependencies.devices, "files:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.files) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson( + response, + 200, + await dependencies.files.read(device.id, workspaceFileMatch[1]!, workspaceFileMatch[2]!), + ); + return; + } + if (workspaceFileMatch && request.method === "PUT") { + requireNoQuery(query); + route = "workspaceFile"; + const device = await authenticate(request, dependencies.devices, "files:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.files) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson( + response, + 200, + await dependencies.files.write( + device.id, + workspaceFileMatch[1]!, + workspaceFileMatch[2]!, + await readJsonBody(request, MAX_FILE_REQUEST_BODY_BYTES), + ), + ); + return; + } + const gitBaseMatch = /^\/workspaces\/([A-Za-z0-9_-]{1,128})\/git\/(review|diff|branches|checkout|commit|push-capability|push|compare|comparison-diff|worktrees)$/u.exec(path); + if (gitBaseMatch) { + requireNoQuery(query); + route = "workspaceGit"; + const workspaceId = gitBaseMatch[1]!; + const action = gitBaseMatch[2]!; + const readRoute = + (action === "review" && request.method === "GET") || + (action === "diff" && request.method === "POST") || + (action === "branches" && request.method === "GET") || + (action === "push-capability" && request.method === "GET") || + (action === "compare" && request.method === "POST") || + (action === "comparison-diff" && request.method === "POST") || + (action === "worktrees" && request.method === "GET"); + const writeRoute = + (action === "branches" && request.method === "POST") || + (action === "checkout" && request.method === "POST") || + (action === "commit" && request.method === "POST") || + (action === "push" && request.method === "POST") || + (action === "worktrees" && request.method === "POST"); + if (readRoute || writeRoute) { + const device = await authenticate( + request, + dependencies.devices, + writeRoute ? "git:write" : "git:read", + ); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.git) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + if (action === "review" && request.method === "GET") { + writeJson(response, 200, await dependencies.git.review(device.id, workspaceId)); + return; + } + if (action === "diff" && request.method === "POST") { + writeJson(response, 200, await dependencies.git.diff(device.id, workspaceId, await readJsonBody(request))); + return; + } + if (action === "branches" && request.method === "GET") { + writeJson(response, 200, await dependencies.git.branches(device.id, workspaceId)); + return; + } + if (action === "branches" && request.method === "POST") { + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + writeJson(response, 202, await dependencies.git.createBranch(device.id, workspaceId, key, await readJsonBody(request))); + return; + } + if (action === "checkout" && request.method === "POST") { + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + writeJson(response, 202, await dependencies.git.checkout(device.id, workspaceId, key, await readJsonBody(request))); + return; + } + if (action === "commit" && request.method === "POST") { + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + writeJson(response, 202, await dependencies.git.commit(device.id, workspaceId, key, await readJsonBody(request))); + return; + } + if (action === "push-capability" && request.method === "GET") { + writeJson(response, 200, await dependencies.git.pushCapability(device.id, workspaceId)); + return; + } + if (action === "push" && request.method === "POST") { + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + writeJson(response, 202, await dependencies.git.push(device.id, workspaceId, key, await readJsonBody(request))); + return; + } + if (action === "compare" && request.method === "POST") { + writeJson(response, 200, await dependencies.git.compare(device.id, workspaceId, await readJsonBody(request))); + return; + } + if (action === "comparison-diff" && request.method === "POST") { + writeJson(response, 200, await dependencies.git.comparisonDiff(device.id, workspaceId, await readJsonBody(request))); + return; + } + if (action === "worktrees" && request.method === "GET") { + writeJson(response, 200, await dependencies.git.worktrees(device.id, workspaceId)); + return; + } + if (action === "worktrees" && request.method === "POST") { + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + writeJson(response, 202, await dependencies.git.createWorktree(device.id, workspaceId, key, await readJsonBody(request))); + return; + } + } + } + const managedWorktreeMatch = /^\/workspaces\/([A-Za-z0-9_-]{1,128})\/git\/managed-worktree$/u.exec(path); + if (managedWorktreeMatch && request.method === "DELETE") { + requireNoQuery(query); + route = "workspaceGit"; + const device = await authenticate(request, dependencies.devices, "git:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.git) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const revision = requiredHeader(request, "if-match", /^[\x21-\x7e]{1,128}$/u); + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + writeJson( + response, + 202, + await dependencies.git.deleteManagedWorktree( + device.id, + managedWorktreeMatch[1]!, + revision, + key, + await readJsonBody(request), + ), + ); + return; + } + if (path === "/scheduled-tasks" && request.method === "GET") { + requireNoQuery(query); + route = "scheduledTasks"; + const device = await authenticate(request, dependencies.devices, "schedule:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.schedules) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.schedules.list(device.id)); + return; + } + if (path === "/scheduled-tasks" && request.method === "POST") { + requireNoQuery(query); + route = "scheduledTasks"; + const device = await authenticate(request, dependencies.devices, "schedule:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.schedules) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + writeJson(response, 201, await dependencies.schedules.create(device.id, key, await readJsonBody(request))); + return; + } + if (path === "/scheduled-tasks/preview" && request.method === "POST") { + requireNoQuery(query); + route = "scheduledTasks"; + const device = await authenticate(request, dependencies.devices, "schedule:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.schedules) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, dependencies.schedules.preview(await readJsonBody(request))); + return; + } + if (path === "/scheduled-tasks/scripts" && request.method === "GET") { + route = "scheduledTasks"; + const device = await authenticate(request, dependencies.devices, "schedule:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.schedules) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.schedules.scripts(device.id, scheduledScriptsQuery(query).workspaceId)); + return; + } + if (path === "/scheduled-tasks/mcp-servers" && request.method === "GET") { + requireNoQuery(query); + route = "scheduledTasks"; + const device = await authenticate(request, dependencies.devices, "schedule:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.schedules) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.schedules.mcpServers()); + return; + } + if (path === "/scheduled-tasks/settings" && request.method === "GET") { + requireNoQuery(query); + route = "scheduledTasks"; + const device = await authenticate(request, dependencies.devices, "schedule:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.schedules) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.schedules.settings()); + return; + } + if (path === "/scheduled-tasks/settings" && request.method === "PATCH") { + requireNoQuery(query); + route = "scheduledTasks"; + const device = await authenticate(request, dependencies.devices, "schedule:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.schedules) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const revision = requiredHeader(request, "if-match", /^[\x21-\x7e]{1,128}$/u); + writeJson(response, 200, await dependencies.schedules.updateSettings(revision, await readJsonBody(request))); + return; + } + const scheduledRunsMatch = /^\/scheduled-tasks\/([A-Za-z0-9._:-]{1,160})\/runs$/u.exec(path); + if (scheduledRunsMatch && request.method === "GET") { + requireNoQuery(query); + route = "scheduledTasks"; + const device = await authenticate(request, dependencies.devices, "schedule:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.schedules) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.schedules.runs(scheduledRunsMatch[1]!)); + return; + } + const scheduledActionMatch = /^\/scheduled-tasks\/([A-Za-z0-9._:-]{1,160})\/(pause|resume|run)$/u.exec(path); + if (scheduledActionMatch && request.method === "POST") { + requireNoQuery(query); + route = "scheduledTasks"; + const device = await authenticate(request, dependencies.devices, "schedule:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.schedules) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const taskId = scheduledActionMatch[1]!; + const action = scheduledActionMatch[2]!; + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + if (action === "run") { + writeJson(response, 202, await dependencies.schedules.run(device.id, taskId, key)); + return; + } + const revision = requiredHeader(request, "if-match", /^[\x21-\x7e]{1,128}$/u); + writeJson(response, 202, action === "pause" + ? await dependencies.schedules.pause(device.id, taskId, revision, key) + : await dependencies.schedules.resume(device.id, taskId, revision, key)); + return; + } + const scheduledTaskMatch = /^\/scheduled-tasks\/([A-Za-z0-9._:-]{1,160})$/u.exec(path); + if (scheduledTaskMatch && request.method === "GET") { + requireNoQuery(query); + route = "scheduledTasks"; + const device = await authenticate(request, dependencies.devices, "schedule:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.schedules) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.schedules.get(device.id, scheduledTaskMatch[1]!)); + return; + } + if (scheduledTaskMatch && request.method === "PATCH") { + requireNoQuery(query); + route = "scheduledTasks"; + const device = await authenticate(request, dependencies.devices, "schedule:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.schedules) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const revision = requiredHeader(request, "if-match", /^[\x21-\x7e]{1,128}$/u); + writeJson(response, 200, await dependencies.schedules.update(device.id, scheduledTaskMatch[1]!, revision, await readJsonBody(request))); + return; + } + if (scheduledTaskMatch && request.method === "DELETE") { + requireNoQuery(query); + route = "scheduledTasks"; + const device = await authenticate(request, dependencies.devices, "schedule:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.schedules) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const revision = requiredHeader(request, "if-match", /^[\x21-\x7e]{1,128}$/u); + await dependencies.schedules.remove(scheduledTaskMatch[1]!, revision); + response.writeHead(204, responseHeaders()); + response.end(); + return; + } + if (path === "/workspace-browser/roots" && request.method === "GET") { + requireNoQuery(query); + route = "workspaceBrowserRoots"; + const device = await authenticate(request, dependencies.devices, "workspace:browse"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.workspaceBrowser) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.workspaceBrowser.listRoots(device.id)); + return; + } + if (path === "/workspace-browser/children" && request.method === "GET") { + route = "workspaceBrowserChildren"; + const device = await authenticate(request, dependencies.devices, "workspace:browse"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.workspaceBrowser) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const parsed = browserQuery(query); + writeJson( + response, + 200, + await dependencies.workspaceBrowser.listChildren( + device.id, + parsed.location, + parsed.cursor, + ), + ); + return; + } + if (path === "/workspace-browser/selections" && request.method === "POST") { + requireNoQuery(query); + route = "workspaceBrowserSelection"; + const device = await authenticate(request, dependencies.devices, "workspace:browse"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.workspaceBrowser) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson( + response, + 201, + await dependencies.workspaceBrowser.createSelection( + device.id, + selectionLocation(await readJsonBody(request)), + ), + ); + return; + } + if (path === "/models" && request.method === "GET") { + requireNoQuery(query); + route = "models"; + const device = await authenticate(request, dependencies.devices, "chat:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.models) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.models.list()); + return; + } + if (path === "/usage" && request.method === "GET") { + route = "usage"; + const device = await authenticate(request, dependencies.devices, "server:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.usage) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.usage.summary(usageQuery(query))); + return; + } + if (path === "/chats" && request.method === "GET") { + route = "chats"; + const device = await authenticate(request, dependencies.devices, "chat:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.chats) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.chats.list(chatsQuery(query).workspaceId)); + return; + } + if (path === "/chats" && request.method === "POST") { + requireNoQuery(query); + route = "chats"; + const device = await authenticate(request, dependencies.devices, "chat:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.chats) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + writeJson(response, 201, await dependencies.chats.create(device.id, key, await readJsonBody(request))); + return; + } + const chatMatch = /^\/chats\/([A-Za-z0-9._:-]{1,128})$/u.exec(path); + if (chatMatch && request.method === "GET") { + requireNoQuery(query); + route = "chat"; + const device = await authenticate(request, dependencies.devices, "chat:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.chats) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, await dependencies.chats.get(chatMatch[1]!)); + return; + } + if (chatMatch && request.method === "PATCH") { + requireNoQuery(query); + route = "chat"; + const device = await authenticate(request, dependencies.devices, "chat:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.chats) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const revision = requiredHeader(request, "if-match", /^[\x21-\x7e]{1,128}$/u); + writeJson(response, 200, await dependencies.chats.rename(chatMatch[1]!, revision, await readJsonBody(request))); + return; + } + if (chatMatch && request.method === "DELETE") { + requireNoQuery(query); + route = "chat"; + const device = await authenticate(request, dependencies.devices, "chat:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.chats) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const revision = requiredHeader(request, "if-match", /^[\x21-\x7e]{1,128}$/u); + await dependencies.chats.remove(chatMatch[1]!, revision); + response.writeHead(204, responseHeaders()); + response.end(); + return; + } + const moveMatch = /^\/chats\/([A-Za-z0-9._:-]{1,128})\/move$/u.exec(path); + if (moveMatch && request.method === "POST") { + requireNoQuery(query); + route = "chatMove"; + const device = await authenticate(request, dependencies.devices, "chat:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.chats) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const revision = requiredHeader(request, "if-match", /^[\x21-\x7e]{1,128}$/u); + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + writeJson(response, 200, await dependencies.chats.move(device.id, moveMatch[1]!, revision, key, await readJsonBody(request))); + return; + } + const turnsMatch = /^\/chats\/([A-Za-z0-9._:-]{1,128})\/turns$/u.exec(path); + if (turnsMatch && request.method === "POST") { + requireNoQuery(query); + route = "turns"; + const device = await authenticate(request, dependencies.devices, "chat:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.chats) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + writeJson(response, 202, await dependencies.chats.startTurn(device.id, turnsMatch[1]!, key, await readJsonBody(request))); + return; + } + const attachmentCollectionMatch = /^\/chats\/([A-Za-z0-9._:-]{1,128})\/attachments$/u.exec(path); + if (attachmentCollectionMatch && request.method === "POST") { + requireNoQuery(query); + route = "chatAttachment"; + const device = await authenticate(request, dependencies.devices, "chat:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.chats?.uploadAttachment) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson( + response, + 201, + await dependencies.chats.uploadAttachment( + device.id, + attachmentCollectionMatch[1]!, + await readJsonBody(request, MAX_AIDEN_REMOTE_ATTACHMENT_REQUEST_BYTES), + ), + ); + return; + } + const attachmentMatch = /^\/chats\/([A-Za-z0-9._:-]{1,128})\/attachments\/(att_[A-Za-z0-9_-]{43})$/u.exec(path); + if (attachmentMatch && request.method === "DELETE") { + requireNoQuery(query); + route = "chatAttachment"; + const device = await authenticate(request, dependencies.devices, "chat:write"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.chats?.removeAttachment) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + await dependencies.chats.removeAttachment(device.id, attachmentMatch[1]!, attachmentMatch[2]!); + response.writeHead(204, responseHeaders()); + response.end(); + return; + } + const streamMatch = /^\/streams\/([A-Za-z0-9._:-]{1,128})$/u.exec(path); + if (streamMatch && request.method === "GET") { + requireNoQuery(query); + route = "stream"; + const device = await authenticate(request, dependencies.devices, "chat:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.streams) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 200, dependencies.streams.status(device.id, streamMatch[1]!)); + return; + } + const eventsMatch = /^\/streams\/([A-Za-z0-9._:-]{1,128})\/events$/u.exec(path); + if (eventsMatch && request.method === "GET") { + route = "streamEvents"; + const device = await authenticate(request, dependencies.devices, "chat:read"); + deviceIdSuffix = device.id.slice(-8); + if (!dependencies.streams) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + dependencies.streams.openEvents(device.id, eventsMatch[1]!, streamAfter(request, query), response); + return; + } + const cancelMatch = /^\/streams\/([A-Za-z0-9._:-]{1,128})\/cancel$/u.exec(path); + if (cancelMatch && request.method === "POST") { + requireNoQuery(query); + route = "streamCancel"; + const device = await authenticate(request, dependencies.devices, "chat:write"); + deviceIdSuffix = device.id.slice(-8); + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + if (!dependencies.streams) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson(response, 202, await dependencies.streams.cancel(device.id, cancelMatch[1]!, key)); + return; + } + const approvalMatch = /^\/approvals\/([A-Za-z0-9._:-]{1,128})\/respond$/u.exec(path); + if (approvalMatch && request.method === "POST") { + requireNoQuery(query); + route = "approvalRespond"; + const device = await authenticate(request, dependencies.devices, "approval:respond"); + deviceIdSuffix = device.id.slice(-8); + const key = requiredHeader(request, "idempotency-key", /^[\x21-\x7e]{16,128}$/u); + if (!dependencies.streams) throw new AidenRemoteServiceError("not_found", "This endpoint is unavailable.", 404); + writeJson( + response, + 200, + await dependencies.streams.respondApproval( + device.id, + approvalMatch[1]!, + approvalDecision(await readJsonBody(request)), + key, + ), + ); + return; + } + throw new AidenRemoteServiceError( + "not_found", + "This Aiden Remote endpoint does not exist.", + 404, + ); + })() + .then(() => { + dependencies.log({ + requestId: id, + route, + status: response.statusCode, + latencyMs: Math.max(0, dependencies.now() - startedAt), + ...(deviceIdSuffix ? { deviceIdSuffix } : {}), + }); + }) + .catch((error: unknown) => { + const safe = asAidenRemoteServiceError(error); + if (!response.headersSent) writeError(response, id, safe); + else response.destroy(); + dependencies.log({ + requestId: id, + route, + status: safe.status, + latencyMs: Math.max(0, dependencies.now() - startedAt), + ...(deviceIdSuffix ? { deviceIdSuffix } : {}), + errorCode: safe.code, + }); + }); + }; +} diff --git a/main/services/aiden-remote-schedules.test.ts b/main/services/aiden-remote-schedules.test.ts new file mode 100644 index 00000000..149dc8e3 --- /dev/null +++ b/main/services/aiden-remote-schedules.test.ts @@ -0,0 +1,171 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { AidenRemoteScheduleService } from "./aiden-remote-schedules.js"; +import type { ScheduledRun, ScheduledTask, ScheduledTaskInput } from "./types.js"; + +function fixture() { + let clock = 100; + let runStarts = 0; + const tasks = new Map(); + const histories = new Map(); + let settingsValue = { + revision: "rev_settings", + value: { + enabled: true, defaultMode: "llm" as const, defaultPermission: "read-only" as const, + defaultMcpEnabled: false, defaultNotify: true, defaultTimezone: "UTC", + }, + }; + const application = { + list: async () => [...tasks.values()], + get: async (id: string) => { + const task = tasks.get(id); + if (!task) throw new Error(`Scheduled task ${id} not found.`); + return task; + }, + save: async (input: ScheduledTaskInput, options: { expectedRevision?: string } = {}) => { + const existing = input.id ? tasks.get(input.id) : undefined; + if (options.expectedRevision && options.expectedRevision !== `revision:${existing?.updatedAt}`) { + throw new Error("This automation changed."); + } + const updatedAt = ++clock; + const task: ScheduledTask = { + id: existing?.id ?? `task-${tasks.size + 1}`, name: input.name, enabled: true, + mode: input.mode, cron: input.cron, timezone: input.timezone ?? "UTC", + workspaceId: input.workspaceId, providerId: input.providerId, model: input.model, + prompt: input.prompt, script: input.script, permission: input.permission ?? "read-only", + mcpServerIds: input.mcpServerIds, notify: input.notify ?? true, + providerFingerprint: "private-provider-fingerprint", chatId: "private-chat", + mcpServerBindings: [{ id: "mcp-1", fingerprint: "private-mcp-fingerprint" }], + createdAt: existing?.createdAt ?? updatedAt, updatedAt, + }; + tasks.set(task.id, task); + return task; + }, + remove: async (id: string) => { tasks.delete(id); }, + pause: async (id: string) => { + const next = { ...tasks.get(id)!, enabled: false, updatedAt: ++clock }; + tasks.set(id, next); + return next; + }, + resume: async (id: string) => { + const next = { ...tasks.get(id)!, enabled: true, updatedAt: ++clock }; + tasks.set(id, next); + return next; + }, + runNow: async (id: string, runId?: string) => { + runStarts += 1; + const run: ScheduledRun = { + id: runId ?? "run-local", taskId: id, startedAt: 1, finishedAt: 2, + result: "success", output: "read /Users/private/project and token_secretvalue123456", + }; + histories.set(id, [run]); + return undefined; + }, + runs: async (id: string) => histories.get(id) ?? [], + preview: (_cron: string, _timezone: string, count = 3) => Array.from({ length: count }, (_, index) => 1_000 + index), + scripts: async () => ["safe.sh"], + mcpServers: async () => [{ id: "mcp-1", name: "GitHub" }], + settings: async () => settingsValue, + updateSettings: async (_revision: string, patch: Record) => { + settingsValue = { ...settingsValue, revision: "rev_settings_2", value: { ...settingsValue.value, ...patch } } as typeof settingsValue; + return settingsValue; + }, + isRunning: () => false, + }; + const service = new AidenRemoteScheduleService({ + application, + models: { resolve: async () => ({ providerId: "provider-1", modelId: "model-1", thinkingLevels: [] }) }, + }); + return { service, tasks, histories, application, runStarts: () => runStarts }; +} + +const llmMutation = { + name: "Morning review", schedule: "0 8 * * *", timezone: "UTC", mode: "llm", + permission: "read-only", prompt: "Summarize the workspace", confirmedForeground: true, +}; + +test("scheduled-task projections omit runtime authority and script selections are opaque and device bound", async () => { + const value = fixture(); + const created = await value.service.create("device-1", "create-key-123456", llmMutation); + const serialized = JSON.stringify(created); + assert.doesNotMatch(serialized, /fingerprint|private-chat|\/Users\//u); + assert.equal(created.providerId, "provider-1"); + assert.equal(created.prompt, "Summarize the workspace"); + + const inventory = await value.service.scripts("device-1", "workspace-1"); + assert.match(inventory.scripts[0]!.id, /^script_[A-Za-z0-9_-]{43}$/u); + await assert.rejects( + value.service.create("device-2", "script-key-123456", { + name: "Script", schedule: "0 9 * * *", timezone: "UTC", mode: "script", + permission: "full", scriptId: inventory.scripts[0]!.id, workspaceId: "workspace-1", + confirmedForeground: true, + }), + /another device/u, + ); + const script = await value.service.create("device-1", "script-key-654321", { + name: "Script", schedule: "0 9 * * *", timezone: "UTC", mode: "script", + permission: "full", scriptId: inventory.scripts[0]!.id, workspaceId: "workspace-1", + confirmedForeground: true, + }); + assert.ok(script.scriptId); + assert.doesNotMatch(JSON.stringify(script), /safe\.sh|\/Users\//u); + assert.deepEqual(await value.service.mcpServers(), { + servers: [{ id: "mcp-1", name: "GitHub" }], + }); +}); + +test("scheduled task run retries reuse one accepted run and history is bounded and redacted", async () => { + const value = fixture(); + const created = await value.service.create("device-1", "create-key-123456", llmMutation); + const first = await value.service.run("device-1", created.id, "run-key-12345678"); + const replay = await value.service.run("device-1", created.id, "run-key-12345678"); + assert.deepEqual(replay, first); + assert.equal(value.runStarts(), 1); + const history = await value.service.runs(created.id); + assert.equal(history.runs[0]?.id, first.runId); + assert.match(history.runs[0]?.summary ?? "", /\[local path\]/u); + assert.doesNotMatch(history.runs[0]?.summary ?? "", /secretvalue|\/Users\//u); +}); + +test("accepted execution remains scheduler-owned after the remote caller disconnects", async () => { + const value = fixture(); + const created = await value.service.create("device-1", "create-key-123456", llmMutation); + let finish!: () => void; + const completion = new Promise((resolve) => { finish = resolve; }); + value.application.runNow = async (id: string, runId?: string) => { + void completion.then(() => { + value.histories.set(id, [{ + id: runId ?? "run-local", taskId: id, startedAt: 1, finishedAt: 2, + result: "success", output: "completed without the caller", + }]); + }); + return undefined; + }; + + const accepted = await value.service.run("device-1", created.id, "disconnect-key-1234"); + assert.deepEqual((await value.service.runs(created.id)).runs, []); + finish(); + await completion; + await new Promise((resolve) => setImmediate(resolve)); + assert.equal((await value.service.runs(created.id)).runs[0]?.id, accepted.runId); +}); + +test("scheduled task mutations require foreground confirmation and stale edits fail explicitly", async () => { + const value = fixture(); + await assert.rejects( + value.service.create("device-1", "create-key-123456", { ...llmMutation, confirmedForeground: false }), + /foreground review/u, + ); + const created = await value.service.create("device-1", "create-key-654321", llmMutation); + await assert.rejects( + value.service.update("device-1", created.id, "rev_stale", llmMutation), + /changed/u, + ); + await assert.rejects( + value.service.updateSettings("rev_settings", { + confirmedForeground: true, + defaultMode: "garbage", + }), + /settings are invalid/u, + ); +}); diff --git a/main/services/aiden-remote-schedules.ts b/main/services/aiden-remote-schedules.ts new file mode 100644 index 00000000..94ebabee --- /dev/null +++ b/main/services/aiden-remote-schedules.ts @@ -0,0 +1,422 @@ +import { createHash, randomBytes } from "node:crypto"; +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; +import type { AidenRemoteModelService } from "./aiden-remote-models.js"; +import { + AidenIdempotencyLedger, + type AidenIdempotencySnapshot, +} from "./aiden-remote-operation-contract.js"; +import type { ScheduledTaskApplicationService } from "./scheduled-task-application-service.js"; +import { scheduledTaskRevision } from "./scheduled-task-application-service.js"; +import type { + ScheduledRun, + ScheduledTask, + ScheduledTaskInput, + ScheduledTaskMode, + ScheduledTaskPermission, +} from "./types.js"; + +const IDEMPOTENCY_KEY_PATTERN = /^[\x21-\x7e]{16,128}$/u; +const TASK_ID_PATTERN = /^[A-Za-z0-9._:-]{1,160}$/u; +const SCRIPT_ID_PATTERN = /^script_[A-Za-z0-9_-]{43}$/u; +const SCRIPT_CLAIM_TTL_MS = 10 * 60_000; +const MAX_SCRIPT_CLAIMS = 4_096; + +interface ScriptClaim { + deviceId: string; + workspaceId?: string; + name: string; + expiresAt: number; +} + +export interface AidenRemoteScheduledTaskProjection { + id: string; + revision: string; + name: string; + enabled: boolean; + schedule: string; + timezone: string; + mode: ScheduledTaskMode; + permission: ScheduledTaskPermission; + workspaceId?: string; + providerId?: string; + modelId?: string; + mcpServerIds?: string[]; + scriptId?: string; + prompt?: string; + notify: boolean; + running: boolean; + nextRunAt?: string; + lastRunAt?: string; + lastResult?: string; + createdAt: string; + updatedAt: string; +} + +function ownRecord(value: unknown): Record | null { + return value && typeof value === "object" && !Array.isArray(value) + ? value as Record + : null; +} + +function exactKeys( + record: Record, + required: readonly string[], + optional: readonly string[] = [], +): boolean { + const allowed = new Set([...required, ...optional]); + return required.every((key) => Object.prototype.hasOwnProperty.call(record, key)) + && Object.keys(record).every((key) => allowed.has(key)); +} + +function boundedString(value: unknown, maximum: number): value is string { + return typeof value === "string" && value.trim().length > 0 && [...value].length <= maximum; +} + +function boundedIds(value: unknown): string[] | undefined { + if (value === undefined) return undefined; + if (!Array.isArray(value) || value.length > 64) return undefined; + const ids = value.map((item) => boundedString(item, 256) ? item : ""); + if (ids.some((id) => !id) || new Set(ids).size !== ids.length) return undefined; + return ids; +} + +function safeTaskId(value: string): string { + if (!TASK_ID_PATTERN.test(value)) { + throw new AidenRemoteServiceError("invalid_request", "The scheduled-task identifier is invalid.", 400); + } + return value; +} + +function timestamp(value: number | undefined): string | undefined { + return value === undefined ? undefined : new Date(value).toISOString(); +} + +function redactSummary(value: string): string { + return [...value + .replace(/\/(?:Users|home)\/[^\s"'`]+/gu, "[local path]") + .replace(/\b(?:sk|key|token|secret|bearer)[-_][A-Za-z0-9._-]{12,}\b/giu, "[redacted]")] + .slice(0, 20_000) + .join(""); +} + +function mapRun(run: ScheduledRun) { + const failed = run.result === "error" || run.result === "blocked"; + const summary = redactSummary(run.error ?? run.output); + return { + id: run.id, + taskId: run.taskId, + status: failed ? "failed" as const : "succeeded" as const, + startedAt: new Date(run.startedAt).toISOString(), + finishedAt: new Date(run.finishedAt).toISOString(), + ...(summary ? { summary } : {}), + ...(failed ? { errorCode: run.result === "blocked" ? "blocked" : "execution_failed" } : {}), + }; +} + +function mapError(error: unknown): never { + if (error instanceof AidenRemoteServiceError) throw error; + const message = error instanceof Error ? error.message : ""; + if (/not found/iu.test(message)) { + throw new AidenRemoteServiceError("not_found", "This scheduled task no longer exists.", 404); + } + if (/changed|revision/iu.test(message)) { + throw new AidenRemoteServiceError("revision_conflict", "This scheduled task changed. Refresh it before trying again.", 409); + } + if (/already running/iu.test(message)) { + throw new AidenRemoteServiceError("operation_in_progress", "This scheduled task is already running.", 409, true); + } + if (/workspace|script|provider|model|MCP|cron|schedule|timezone|permission|prompt/iu.test(message)) { + throw new AidenRemoteServiceError("invalid_request", message.slice(0, 500), 400); + } + throw new AidenRemoteServiceError("internal_error", "Aiden could not complete this scheduled-task request.", 500); +} + +export class AidenRemoteScheduleService { + private readonly idempotency: AidenIdempotencyLedger; + private readonly scriptClaims = new Map(); + + constructor( + private readonly options: { + application: Pick; + models: Pick; + idempotency?: AidenIdempotencyLedger; + persistIdempotency?: (snapshot: AidenIdempotencySnapshot) => Promise; + now?: () => number; + }, + ) { + this.idempotency = options.idempotency ?? new AidenIdempotencyLedger(); + } + + private now(): number { + return this.options.now?.() ?? Date.now(); + } + + private pruneScripts(): void { + const now = this.now(); + for (const [digest, claim] of this.scriptClaims) { + if (claim.expiresAt <= now) this.scriptClaims.delete(digest); + } + } + + private issueScript(deviceId: string, workspaceId: string | undefined, name: string): string { + this.pruneScripts(); + if (this.scriptClaims.size >= MAX_SCRIPT_CLAIMS) { + const oldest = this.scriptClaims.keys().next().value as string | undefined; + if (oldest) this.scriptClaims.delete(oldest); + } + const token = `script_${randomBytes(32).toString("base64url")}`; + this.scriptClaims.set(createHash("sha256").update(token).digest("base64url"), { + deviceId, + ...(workspaceId ? { workspaceId } : {}), + name, + expiresAt: this.now() + SCRIPT_CLAIM_TTL_MS, + }); + return token; + } + + private consumeScript(deviceId: string, workspaceId: string | undefined, token: string): string { + this.pruneScripts(); + if (!SCRIPT_ID_PATTERN.test(token)) { + throw new AidenRemoteServiceError("handle_invalid", "This script selection is invalid.", 400); + } + const claim = this.scriptClaims.get(createHash("sha256").update(token).digest("base64url")); + if (!claim) throw new AidenRemoteServiceError("handle_expired", "This script selection expired. Refresh the inventory.", 410); + if (claim.deviceId !== deviceId) throw new AidenRemoteServiceError("handle_wrong_device", "This script selection belongs to another device.", 403); + if (claim.workspaceId !== workspaceId) throw new AidenRemoteServiceError("operation_stale", "This script selection belongs to another workspace.", 409); + return claim.name; + } + + private project(deviceId: string, task: ScheduledTask): AidenRemoteScheduledTaskProjection { + return { + id: task.id, + revision: scheduledTaskRevision(task), + name: [...task.name].slice(0, 120).join(""), + enabled: task.enabled, + schedule: task.cron, + timezone: task.timezone, + mode: task.mode, + permission: task.permission, + ...(task.workspaceId ? { workspaceId: task.workspaceId } : {}), + ...(task.providerId ? { providerId: task.providerId } : {}), + ...(task.model ? { modelId: task.model } : {}), + ...(task.mcpServerIds ? { mcpServerIds: [...task.mcpServerIds] } : {}), + ...(task.script ? { scriptId: this.issueScript(deviceId, task.workspaceId, task.script) } : {}), + ...(task.prompt ? { prompt: task.prompt } : {}), + notify: task.notify, + running: this.options.application.isRunning(task.id), + ...(timestamp(task.nextRunAt) ? { nextRunAt: timestamp(task.nextRunAt)! } : {}), + ...(timestamp(task.lastRunAt) ? { lastRunAt: timestamp(task.lastRunAt)! } : {}), + ...(task.lastResult ? { lastResult: task.lastResult } : {}), + createdAt: new Date(task.createdAt).toISOString(), + updatedAt: new Date(task.updatedAt).toISOString(), + }; + } + + private async mutation(deviceId: string, value: unknown): Promise { + const record = ownRecord(value); + if (!record || !exactKeys(record, + ["name", "schedule", "timezone", "mode", "permission", "confirmedForeground"], + ["workspaceId", "providerId", "modelId", "mcpServerIds", "scriptId", "prompt", "notify"], + ) || record.confirmedForeground !== true || !boundedString(record.name, 120) + || !boundedString(record.schedule, 500) || !boundedString(record.timezone, 120) + || (record.mode !== "llm" && record.mode !== "script") + || (record.permission !== "full" && record.permission !== "read-only") + || (record.workspaceId !== undefined && !boundedString(record.workspaceId, 128)) + || (record.notify !== undefined && typeof record.notify !== "boolean")) { + throw new AidenRemoteServiceError("permission_confirmation_required", "Creating or editing unattended work requires final foreground review.", 409); + } + const workspaceId = typeof record.workspaceId === "string" ? record.workspaceId : undefined; + const ids = boundedIds(record.mcpServerIds); + if (record.mcpServerIds !== undefined && ids === undefined) { + throw new AidenRemoteServiceError("invalid_request", "The selected MCP scope is invalid.", 400); + } + if (record.mode === "llm") { + if (!boundedString(record.prompt, 32 * 1_024)) { + throw new AidenRemoteServiceError("invalid_request", "Ask Aiden tasks require a bounded prompt.", 400); + } + const resolved = await this.options.models.resolve( + typeof record.providerId === "string" ? record.providerId : undefined, + typeof record.modelId === "string" ? record.modelId : undefined, + ); + return { + name: record.name.trim(), mode: "llm", cron: record.schedule.trim(), timezone: record.timezone.trim(), + permission: record.permission, prompt: record.prompt, providerId: resolved.providerId, + model: resolved.modelId, ...(workspaceId ? { workspaceId } : {}), + ...(ids ? { mcpServerIds: ids } : {}), ...(typeof record.notify === "boolean" ? { notify: record.notify } : {}), + }; + } + if (record.permission !== "full" || typeof record.scriptId !== "string") { + throw new AidenRemoteServiceError("invalid_request", "Script tasks require Full permission and a current script selection.", 400); + } + return { + name: record.name.trim(), mode: "script", cron: record.schedule.trim(), timezone: record.timezone.trim(), + permission: "full", script: this.consumeScript(deviceId, workspaceId, record.scriptId), + ...(workspaceId ? { workspaceId } : {}), ...(typeof record.notify === "boolean" ? { notify: record.notify } : {}), + }; + } + + private async executeIdempotent( + scope: { deviceId: string; route: string; resourceId: string; key: string }, + input: unknown, + action: () => Promise, + ): Promise { + if (!IDEMPOTENCY_KEY_PATTERN.test(scope.key)) { + throw new AidenRemoteServiceError("invalid_request", "Idempotency-Key is invalid.", 400); + } + if (!this.options.persistIdempotency) return this.idempotency.execute(scope, input, action); + let release!: () => void; + let reject!: (error: unknown) => void; + const admission = new Promise((resolve, rejectPromise) => { release = resolve; reject = rejectPromise; }); + const pending = this.idempotency.execute(scope, input, async () => { await admission; return action(); }); + try { + await this.options.persistIdempotency(this.idempotency.snapshot()); + release(); + } catch (error) { + reject(error); + await pending.catch(() => undefined); + throw new AidenRemoteServiceError("internal_error", "Aiden could not durably prepare this scheduled-task request.", 500); + } + let result: T | undefined; + let failure: unknown; + try { result = await pending; } catch (error) { failure = error; } + try { + await this.options.persistIdempotency(this.idempotency.snapshot()); + } catch { + throw new AidenRemoteServiceError("idempotency_in_flight", "The scheduled-task change may have completed, but Aiden could not record its outcome.", 409); + } + if (failure) throw failure; + return result!; + } + + async list(deviceId: string) { + return { tasks: (await this.options.application.list()).map((task) => this.project(deviceId, task)) }; + } + + async get(deviceId: string, taskId: string) { + try { return this.project(deviceId, await this.options.application.get(safeTaskId(taskId))); } + catch (error) { mapError(error); } + } + + async create(deviceId: string, key: string, value: unknown) { + const input = await this.mutation(deviceId, value); + try { + return await this.executeIdempotent( + { deviceId, route: "POST /scheduled-tasks", resourceId: "scheduled-tasks", key }, + value, + async () => this.project(deviceId, await this.options.application.save(input)), + ); + } catch (error) { mapError(error); } + } + + async update(deviceId: string, taskId: string, revision: string, value: unknown) { + const id = safeTaskId(taskId); + const input = { ...(await this.mutation(deviceId, value)), id }; + try { return this.project(deviceId, await this.options.application.save(input, { expectedRevision: revision })); } + catch (error) { mapError(error); } + } + + async remove(taskId: string, revision: string): Promise { + try { await this.options.application.remove(safeTaskId(taskId), revision); } + catch (error) { mapError(error); } + } + + async pause(deviceId: string, taskId: string, revision: string, key: string) { + const id = safeTaskId(taskId); + try { + return await this.executeIdempotent( + { deviceId, route: `POST /scheduled-tasks/${id}/pause`, resourceId: id, key }, + { revision }, + async () => this.project(deviceId, await this.options.application.pause(id, revision)), + ); + } catch (error) { mapError(error); } + } + + async resume(deviceId: string, taskId: string, revision: string, key: string) { + const id = safeTaskId(taskId); + try { + return await this.executeIdempotent( + { deviceId, route: `POST /scheduled-tasks/${id}/resume`, resourceId: id, key }, + { revision }, + async () => this.project(deviceId, await this.options.application.resume(id, revision)), + ); + } catch (error) { mapError(error); } + } + + async run(deviceId: string, taskId: string, key: string) { + const id = safeTaskId(taskId); + const runId = `run_${randomBytes(24).toString("base64url")}`; + try { + return await this.executeIdempotent( + { deviceId, route: `POST /scheduled-tasks/${id}/run`, resourceId: id, key }, + {}, + async () => { + await this.options.application.runNow(id, runId); + return { taskId: id, runId, status: "accepted" as const, acceptedAt: new Date(this.now()).toISOString() }; + }, + ); + } catch (error) { mapError(error); } + } + + async runs(taskId: string) { + try { return { runs: (await this.options.application.runs(safeTaskId(taskId))).map(mapRun) }; } + catch (error) { mapError(error); } + } + + preview(value: unknown) { + const record = ownRecord(value); + if (!record || !exactKeys(record, ["cron", "timezone"], ["count"]) + || !boundedString(record.cron, 500) || !boundedString(record.timezone, 120) + || (record.count !== undefined && (!Number.isInteger(record.count) || (record.count as number) < 1 || (record.count as number) > 20))) { + throw new AidenRemoteServiceError("invalid_request", "The schedule preview request is invalid.", 400); + } + try { + return { dates: this.options.application.preview(record.cron, record.timezone, typeof record.count === "number" ? record.count : 3).map((date) => new Date(date).toISOString()) }; + } catch (error) { mapError(error); } + } + + async scripts(deviceId: string, workspaceId?: string) { + try { + return { scripts: (await this.options.application.scripts(workspaceId)).map((name) => ({ id: this.issueScript(deviceId, workspaceId, name), name })) }; + } catch (error) { mapError(error); } + } + + async mcpServers() { + try { + const servers = await this.options.application.mcpServers(); + return { + servers: servers.slice(0, 4_000).map((server) => { + if (!boundedString(server.id, 256) || !boundedString(server.name, 256)) { + throw new Error("MCP inventory contains an invalid entry."); + } + return { id: server.id, name: server.name }; + }), + }; + } catch (error) { mapError(error); } + } + + async settings() { + const current = await this.options.application.settings(); + return { revision: current.revision, ...current.value }; + } + + async updateSettings(revision: string, value: unknown) { + const record = ownRecord(value); + if (!record || record.confirmedForeground !== true || !exactKeys(record, ["confirmedForeground"], ["enabled", "defaultMode", "defaultPermission", "defaultMcpEnabled", "defaultNotify", "defaultTimezone"]) || Object.keys(record).length < 2) { + throw new AidenRemoteServiceError("permission_confirmation_required", "Scheduled-task settings require explicit foreground confirmation.", 409); + } + if ((record.enabled !== undefined && typeof record.enabled !== "boolean") + || (record.defaultMode !== undefined && record.defaultMode !== "llm" && record.defaultMode !== "script") + || (record.defaultPermission !== undefined && record.defaultPermission !== "read-only" && record.defaultPermission !== "full") + || (record.defaultMcpEnabled !== undefined && typeof record.defaultMcpEnabled !== "boolean") + || (record.defaultNotify !== undefined && typeof record.defaultNotify !== "boolean") + || (record.defaultTimezone !== undefined && !boundedString(record.defaultTimezone, 120))) { + throw new AidenRemoteServiceError("invalid_request", "One or more scheduled-task settings are invalid.", 400); + } + const { confirmedForeground: _confirmed, ...patch } = record; + try { + const saved = await this.options.application.updateSettings(revision, patch); + return { revision: saved.revision, ...saved.value }; + } catch (error) { mapError(error); } + } +} diff --git a/main/services/aiden-remote-service-main.ts b/main/services/aiden-remote-service-main.ts new file mode 100644 index 00000000..0eabb049 --- /dev/null +++ b/main/services/aiden-remote-service-main.ts @@ -0,0 +1,346 @@ +import os from "node:os"; +import path from "node:path"; +import { app, ipcMain, logger } from "../platform.js"; +import { AidenRemoteApprovedRootService } from "./aiden-remote-approved-roots.js"; +import { DataStore } from "./data-store.js"; +import { + AidenRemoteService, + DnsSdAidenRemoteBonjourPublisher, + type AidenRemoteServiceLogEntry, +} from "./aiden-remote-service.js"; +import { + AidenRemoteStateRegistry, + createDefaultAidenRemoteState, + parseAidenRemoteStateDocument, + type AidenRemoteStateDocument, +} from "./aiden-remote-state.js"; +import { + AidenRemoteTailscaleController, + createSystemTailscaleCommandRunner, +} from "./aiden-remote-tailscale.js"; +import { loadOrCreateAidenRemoteTlsIdentity } from "./aiden-remote-tls-identity.js"; +import { AidenRemoteWorkspaceBrowserService } from "./aiden-remote-workspace-browser.js"; +import { AidenRemoteWorkspaceService } from "./aiden-remote-workspaces.js"; +import { workspaceApplicationService } from "./workspace-application-service-main.js"; +import { + AidenIdempotencyLedger, + MAX_DURABLE_LEDGER_SNAPSHOT_BYTES, + type AidenIdempotencySnapshot, +} from "./aiden-remote-operation-contract.js"; +import { AidenRemoteChatService } from "./aiden-remote-chats.js"; +import { AidenRemoteModelService } from "./aiden-remote-models.js"; +import { + AidenRemoteStreamService, + MAX_AIDEN_REMOTE_STREAM_SNAPSHOT_BYTES, + normalizeAidenRemoteStreamSnapshot, + type AidenRemoteStreamSnapshot, +} from "./aiden-remote-streams.js"; +import { chatApplicationService } from "./chat-application-service-main.js"; +import { startGenerationAndMaybeTitle } from "./chat-generation-start.js"; +import { chatStore } from "./chat-store.js"; +import { chatTitleService } from "./chat-title.js"; +import { configStore } from "./config-store.js"; +import { llmClient } from "./llm-client.js"; +import { listConfiguredProviders } from "./provider-list-main.js"; +import { AidenRemoteFileService } from "./aiden-remote-files.js"; +import { AidenRemoteWorkspaceOwnerRegistry } from "./aiden-remote-workspace-owners.js"; +import { workspaceEnvironmentApplicationService } from "./workspace-environment-application-service-main.js"; +import { workspaceWorktreeApplicationService } from "./workspace-worktree-application-service-main.js"; +import { AidenRemoteGitService } from "./aiden-remote-git.js"; +import { + gitBranches, + gitCheckout, + gitCommit, + gitCompare, + gitComparisonDiff, + gitCreateBranch, + gitDiff, + gitPush, + gitPushCapability, + gitReview, + gitWorktrees, +} from "./git.js"; +import { AidenRemoteScheduleService } from "./aiden-remote-schedules.js"; +import { usageStore } from "./usage-store.js"; +import { scheduledTaskApplicationService } from "./scheduled-task-application-service-main.js"; + +const STATE_FILE = "aiden-remote-v1.json"; +const OPERATIONS_FILE = "aiden-remote-operations-v1.json"; +const STREAMS_FILE = "aiden-remote-streams-v1.json"; +const MAX_STATE_BYTES = 512 * 1_024; + +function safeState(value: unknown): boolean { + try { + parseAidenRemoteStateDocument(value); + return true; + } catch { + return false; + } +} + +function normalizeIdempotency(value: unknown): AidenIdempotencySnapshot { + return new AidenIdempotencyLedger(value as AidenIdempotencySnapshot).snapshot(); +} + +function safeIdempotency(value: unknown): boolean { + try { + normalizeIdempotency(value); + return true; + } catch { + return false; + } +} + +function writeRemoteLog(entry: AidenRemoteServiceLogEntry): void { + const details = entry.details ?? {}; + if (entry.level === "error") logger.error("aiden-remote", entry.event, details); + else if (entry.level === "warn") logger.warn("aiden-remote", entry.event, details); + else logger.info("aiden-remote", entry.event, details); +} + +export interface AidenRemoteRuntime { + service: AidenRemoteService; + state: AidenRemoteStateRegistry; + approvedRoots: AidenRemoteApprovedRootService; + revokeDevice(deviceId: string): Promise; +} + +let runtimePromise: Promise | null = null; + +async function createRuntime(): Promise { + const userData = app.getPath("userData"); + const store = new DataStore( + STATE_FILE, + createDefaultAidenRemoteState(), + () => userData, + { + maxBytes: MAX_STATE_BYTES, + fileMode: 0o600, + normalize: parseAidenRemoteStateDocument, + isSafe: safeState, + rejectCorruptWrite: true, + rejectUnsafeWrite: true, + }, + ); + const state = new AidenRemoteStateRegistry({ + load: () => store.load(), + save: async (document) => { + await store.save(document); + ipcMain.broadcast("remote:changed", {}); + }, + }); + const operationStore = new DataStore( + OPERATIONS_FILE, + new AidenIdempotencyLedger().snapshot(), + () => userData, + { + maxBytes: MAX_DURABLE_LEDGER_SNAPSHOT_BYTES, + fileMode: 0o600, + normalize: normalizeIdempotency, + isSafe: safeIdempotency, + rejectCorruptWrite: true, + rejectUnsafeWrite: true, + }, + ); + const streamStore = new DataStore( + STREAMS_FILE, + { version: 1, streams: [] }, + () => userData, + { + maxBytes: MAX_AIDEN_REMOTE_STREAM_SNAPSHOT_BYTES, + fileMode: 0o600, + normalize: normalizeAidenRemoteStreamSnapshot, + isSafe: (value) => { + try { + normalizeAidenRemoteStreamSnapshot(value); + return true; + } catch { + return false; + } + }, + rejectCorruptWrite: true, + rejectUnsafeWrite: true, + }, + ); + const tailscale = new AidenRemoteTailscaleController( + await createSystemTailscaleCommandRunner(), + ); + let workspaceApi: + | Promise<{ + instanceId: string; + workspaces: AidenRemoteWorkspaceService; + workspaceBrowser: AidenRemoteWorkspaceBrowserService; + chats: AidenRemoteChatService; + models: AidenRemoteModelService; + streams: AidenRemoteStreamService; + files: AidenRemoteFileService; + git: AidenRemoteGitService; + schedules: AidenRemoteScheduleService; + usage: typeof usageStore; + }> + | undefined; + let workspaceApiInstanceId: string | undefined; + let activeStreams: AidenRemoteStreamService | undefined; + let activeChats: AidenRemoteChatService | undefined; + const workspaceOwners = new AidenRemoteWorkspaceOwnerRegistry(); + const hostname = os.hostname(); + const service = new AidenRemoteService({ + state, + appVersion: app.getVersion(), + hostname, + tailscale, + bonjour: new DnsSdAidenRemoteBonjourPublisher("Aiden Agent", writeRemoteLog), + workspaceApi: async (instanceId) => { + if (!workspaceApi || workspaceApiInstanceId !== instanceId) { + workspaceApiInstanceId = instanceId; + workspaceApi = (async () => { + const idempotency = new AidenIdempotencyLedger( + await operationStore.load(), + ); + await operationStore.save(idempotency.snapshot()); + const workspaceBrowser = new AidenRemoteWorkspaceBrowserService({ + instanceId, + state, + }); + const models = new AidenRemoteModelService({ + listProviders: listConfiguredProviders, + getSettings: () => configStore.getSettings(), + }); + const streams = new AidenRemoteStreamService({ + now: Date.now, + cancel: (streamId, ownerDocumentId) => + llmClient.cancel(streamId, "user_stop", ownerDocumentId), + approve: (approvalId, decision, ownerDocumentId) => + llmClient.approve(approvalId, decision, ownerDocumentId), + notifyChatChanged: () => ipcMain.broadcast("chats:changed", {}), + snapshot: normalizeAidenRemoteStreamSnapshot(await streamStore.load()), + persist: (snapshot) => streamStore.save(snapshot), + idempotency, + persistIdempotency: (snapshot) => operationStore.save(snapshot), + onPersistenceError: (error) => + logger.error("aiden-remote", "Could not persist the remote stream journal.", error), + }); + activeStreams = streams; + const chats = new AidenRemoteChatService({ + application: chatApplicationService, + chatStore, + generation: { + beginChatTurn: (chatId, turnId, ownerId) => + llmClient.beginChatTurn(chatId, turnId, ownerId), + start: (streamId, params, owner, options) => + startGenerationAndMaybeTitle( + { + start: (id, input) => llmClient.start(id, input, owner, options), + startTitle: (input) => chatTitleService.startForFirstTurn(input), + }, + streamId, + params, + ), + }, + streams, + models, + idempotency, + persistIdempotency: (snapshot) => operationStore.save(snapshot), + notifyChanged: () => ipcMain.broadcast("chats:changed", {}), + }); + activeChats = chats; + const files = new AidenRemoteFileService({ + instanceId, + application: workspaceEnvironmentApplicationService, + owners: workspaceOwners, + }); + const git = new AidenRemoteGitService({ + application: workspaceEnvironmentApplicationService, + owners: workspaceOwners, + git: { + review: gitReview, + diff: gitDiff, + branches: gitBranches, + checkout: gitCheckout, + createBranch: gitCreateBranch, + commit: gitCommit, + pushCapability: gitPushCapability, + push: gitPush, + compare: gitCompare, + comparisonDiff: gitComparisonDiff, + worktrees: gitWorktrees, + }, + worktrees: workspaceWorktreeApplicationService, + listWorkspaces: () => configStore.listWorkspaces(), + idempotency, + persistIdempotency: (snapshot) => operationStore.save(snapshot), + }); + const schedules = new AidenRemoteScheduleService({ + application: scheduledTaskApplicationService, + models, + idempotency, + persistIdempotency: (snapshot) => operationStore.save(snapshot), + }); + return { + instanceId, + workspaceBrowser, + chats, + models, + streams, + files, + git, + schedules, + usage: usageStore, + settle: () => streams.settlePersistence(), + workspaces: new AidenRemoteWorkspaceService({ + application: workspaceApplicationService, + browser: workspaceBrowser, + idempotency, + persistIdempotency: (snapshot) => operationStore.save(snapshot), + notifyChanged: () => ipcMain.broadcast("workspaces:changed", {}), + }), + }; + })(); + } + return workspaceApi; + }, + loadTlsIdentity: () => loadOrCreateAidenRemoteTlsIdentity({ + directory: path.join(userData, "aiden-remote-identity"), + hostnames: [hostname], + }), + log: writeRemoteLog, + }); + return { + service, + state, + approvedRoots: new AidenRemoteApprovedRootService(state), + revokeDevice: async (deviceId) => { + const revoked = await state.revokeDevice(deviceId); + if (revoked) { + activeStreams?.revokeDevice(deviceId); + activeChats?.revokeDevice(deviceId); + workspaceOwners.revokeDevice(deviceId); + } + return revoked; + }, + }; +} + +export function getAidenRemoteService(): Promise { + runtimePromise ??= createRuntime(); + return runtimePromise.then((runtime) => runtime.service); +} + +export function getAidenRemoteRuntime(): Promise { + runtimePromise ??= createRuntime(); + return runtimePromise; +} + +export async function initializeAidenRemoteService(): Promise { + const service = await getAidenRemoteService(); + await service.initialize(); +} + +export function stopAidenRemoteService(): void { + void runtimePromise?.then((runtime) => runtime.service.stop()); +} + +export async function stopAidenRemoteServiceAndSettle(): Promise { + const runtime = await runtimePromise; + await runtime?.service.stopAndSettle(); +} diff --git a/main/services/aiden-remote-service.test.ts b/main/services/aiden-remote-service.test.ts new file mode 100644 index 00000000..4fe752da --- /dev/null +++ b/main/services/aiden-remote-service.test.ts @@ -0,0 +1,329 @@ +import assert from "node:assert/strict"; +import { createServer } from "node:net"; +import * as fs from "node:fs/promises"; +import https from "node:https"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { AidenRemoteService } from "./aiden-remote-service.js"; +import { + AidenRemoteStateRegistry, + createDefaultAidenRemoteState, +} from "./aiden-remote-state.js"; +import { loadOrCreateAidenRemoteTlsIdentity } from "./aiden-remote-tls-identity.js"; + +async function canBind(port: number): Promise { + const server = createServer(); + return new Promise((resolve) => { + server.once("error", () => resolve(false)); + server.listen(port, "127.0.0.1", () => { + server.close(() => resolve(true)); + }); + }); +} + +async function availablePortPair(): Promise { + for (let port = 51_000; port < 55_000; port += 2) { + if (await canBind(port) && await canBind(port + 1)) return port; + } + throw new Error("No test port pair was available."); +} + +async function fixture(mode: "lan" | "tailscale" | "both" = "lan") { + const directory = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-remote-service-")); + const initial = createDefaultAidenRemoteState(); + initial.connectionMode = mode; + initial.lanPort = await availablePortPair(); + let persisted = structuredClone(initial); + const state = new AidenRemoteStateRegistry({ + load: async () => structuredClone(persisted), + save: async (document) => { + persisted = structuredClone(document); + }, + }); + const bonjour: { starts: number; stops: number; start(): void; stop(): void } = { + starts: 0, + stops: 0, + start: () => { bonjour.starts += 1; }, + stop: () => { bonjour.stops += 1; }, + }; + const tailscale = { + connects: 0, + disconnects: 0, + targets: [] as string[], + disconnectTargets: [] as string[], + status: async () => ({ installed: true, dnsName: "aiden.tailnet.ts.net" }), + connect: async (target: string) => { + tailscale.connects += 1; + tailscale.targets.push(target); + return { path: "/api/aiden/v1" as const, target }; + }, + disconnect: async (target: string) => { + tailscale.disconnects += 1; + tailscale.disconnectTargets.push(target); + }, + }; + let identityLoads = 0; + const service = new AidenRemoteService({ + state, + appVersion: "0.30.0", + hostname: "Aiden-Test", + bonjour, + tailscale, + resolveTlsEndpointPin: async () => `sha256/${Buffer.alloc(32, 9).toString("base64")}`, + loadTlsIdentity: async () => { + identityLoads += 1; + return loadOrCreateAidenRemoteTlsIdentity({ + directory: path.join(directory, "identity"), + hostnames: ["aiden-test", "aiden-test.local"], + }); + }, + }); + return { + service, + state, + bonjour, + tailscale, + identityLoads: () => identityLoads, + persisted: () => structuredClone(persisted), + directory, + cleanup: async () => { + await service.stopAndSettle(); + await fs.rm(directory, { recursive: true, force: true }); + }, + }; +} + +async function insecureHealth(port: number): Promise<{ status: number; body: unknown }> { + return new Promise((resolve, reject) => { + const request = https.get( + `https://127.0.0.1:${port}/api/aiden/v1/health`, + { rejectUnauthorized: false, timeout: 3_000 }, + (response) => { + const chunks: Buffer[] = []; + response.on("data", (chunk) => chunks.push(Buffer.from(chunk))); + response.on("end", () => { + try { + resolve({ + status: response.statusCode ?? 0, + body: JSON.parse(Buffer.concat(chunks).toString("utf8")), + }); + } catch (error) { + reject(error); + } + }); + }, + ); + request.once("error", reject); + }); +} + +async function insecureJson( + port: number, + requestPath: string, + options: { + method?: string; + headers?: Record; + body?: unknown; + } = {}, +): Promise<{ status: number; body: T }> { + const encodedBody = options.body === undefined + ? undefined + : Buffer.from(JSON.stringify(options.body)); + return new Promise((resolve, reject) => { + const request = https.request({ + host: "127.0.0.1", + port, + path: requestPath, + method: options.method ?? "GET", + rejectUnauthorized: false, + timeout: 3_000, + headers: { + ...options.headers, + ...(encodedBody === undefined ? {} : { + "content-type": "application/json", + "content-length": String(encodedBody.length), + }), + }, + }, (response) => { + const chunks: Buffer[] = []; + response.on("data", (chunk) => chunks.push(Buffer.from(chunk))); + response.on("end", () => { + try { + resolve({ + status: response.statusCode ?? 0, + body: JSON.parse(Buffer.concat(chunks).toString("utf8")) as T, + }); + } catch (error) { + reject(error); + } + }); + }); + request.once("error", reject); + if (encodedBody !== undefined) request.write(encodedBody); + request.end(); + }); +} + +test("disabled startup creates no identity, listener, or Bonjour advertisement", async () => { + const app = await fixture(); + try { + await app.service.initialize(); + assert.deepEqual(await app.service.status(), { + enabled: false, + running: false, + connectionMode: "lan", + lanPort: app.persisted().lanPort, + tailscaleConnected: false, + tailscaleInstalled: false, + pairedDeviceCount: 0, + approvedRootCount: 0, + }); + assert.equal(app.identityLoads(), 0); + assert.equal(app.bonjour.starts, 0); + } finally { + await app.cleanup(); + } +}); + +test("explicit enable serves authenticated API shell over LAN HTTPS and stops cleanly", async () => { + const app = await fixture(); + try { + await app.service.initialize(); + await app.service.setEnabled(true); + const health = await insecureHealth(app.persisted().lanPort); + assert.deepEqual(health, { status: 200, body: { ok: true, protocolVersion: 1 } }); + assert.equal(app.bonjour.starts, 1); + assert.equal((await app.service.status()).running, true); + await app.service.setEnabled(false); + assert.equal((await app.service.status()).running, false); + assert.equal(app.persisted().enabled, false); + } finally { + await app.cleanup(); + } +}); + +test("Tailscale connect ownership persists only after connect and explicit disable clears only it", async () => { + const app = await fixture("both"); + try { + await app.service.initialize(); + await app.service.setEnabled(true); + await app.service.connectTailscale(); + assert.equal(app.tailscale.connects, 1); + assert.deepEqual(app.tailscale.targets, [ + `http://127.0.0.1:${app.persisted().lanPort + 1}/api/aiden/v1`, + ]); + assert.equal(app.persisted().tailscaleOwnership?.path, "/api/aiden/v1"); + await app.service.setEnabled(false); + assert.equal(app.tailscale.disconnects, 1); + assert.equal(app.persisted().tailscaleOwnership, undefined); + assert.equal(app.persisted().enabled, false); + } finally { + await app.cleanup(); + } +}); + +test("Tailscale connect removes only a persisted origin-only route before canonical migration", async () => { + const app = await fixture("both"); + const legacyTarget = `http://127.0.0.1:${app.persisted().lanPort + 1}`; + try { + await app.state.setTailscaleOwnership({ path: "/api/aiden/v1", target: legacyTarget }); + await app.service.initialize(); + await app.service.setEnabled(true); + await app.service.connectTailscale(); + assert.deepEqual(app.tailscale.disconnectTargets, [legacyTarget]); + assert.deepEqual(app.tailscale.targets, [ + `${legacyTarget}/api/aiden/v1`, + ]); + assert.equal(app.persisted().tailscaleOwnership?.target, `${legacyTarget}/api/aiden/v1`); + } finally { + await app.cleanup(); + } +}); + +test("pairing windows expose no secret until a local desktop action begins one", async () => { + const app = await fixture(); + try { + await app.service.setEnabled(true); + const bootstrap = await app.service.beginPairing("lan"); + assert.equal(bootstrap.endpoint, `https://aiden-test.local:${app.persisted().lanPort}/api/aiden/v1`); + assert.match(bootstrap.secret, /^[A-Za-z0-9_-]{43}$/u); + assert.match(bootstrap.serverSpkiSha256, /^sha256\/[A-Za-z0-9+/]{43}=$/u); + const qr = JSON.parse(app.service.pairingQrPayload(bootstrap, "lan")); + assert.equal(qr.kind, "aiden-pairing-v1"); + assert.equal(qr.bootstrap.secret, bootstrap.secret); + assert.equal(qr.trust.mode, "private-ca"); + assert.ok(Buffer.from(qr.trust.caCertificateDerBase64, "base64").length > 100); + assert.equal(JSON.stringify(await app.service.status()).includes(bootstrap.secret), false); + } finally { + await app.cleanup(); + } +}); + +test("revoked devices can re-pair through a fresh local window without reviving the old credential", async () => { + const app = await fixture(); + try { + await app.service.setEnabled(true); + const firstBootstrap = await app.service.beginPairing("lan"); + const first = await insecureJson<{ deviceId: string; credential: string }>( + app.persisted().lanPort, + "/api/aiden/v1/pairing/exchange", + { + method: "POST", + body: { + secret: firstBootstrap.secret, + deviceName: "Physical iPhone", + deviceType: "iphone", + clientVersion: "1.0", + }, + }, + ); + assert.equal(first.status, 200); + assert.match(first.body.credential, /^[A-Za-z0-9_-]{43}$/u); + assert.equal(await app.state.revokeDevice(first.body.deviceId), true); + + const repairBootstrap = await app.service.beginPairing("lan"); + assert.notEqual(repairBootstrap.secret, firstBootstrap.secret); + const repaired = await insecureJson<{ deviceId: string; credential: string }>( + app.persisted().lanPort, + "/api/aiden/v1/pairing/exchange", + { + method: "POST", + body: { + secret: repairBootstrap.secret, + deviceName: "Physical iPhone", + deviceType: "iphone", + clientVersion: "1.0", + }, + }, + ); + assert.equal(repaired.status, 200); + assert.notEqual(repaired.body.deviceId, first.body.deviceId); + assert.notEqual(repaired.body.credential, first.body.credential); + + const headers = (credential: string) => ({ + authorization: `Bearer ${credential}`, + "aiden-protocol-version": "1", + }); + const oldCredential = await insecureJson<{ error: { code: string } }>( + app.persisted().lanPort, + "/api/aiden/v1/server", + { headers: headers(first.body.credential) }, + ); + assert.equal(oldCredential.status, 403); + assert.equal(oldCredential.body.error.code, "credential_revoked"); + const replacementCredential = await insecureJson<{ instanceId: string }>( + app.persisted().lanPort, + "/api/aiden/v1/server", + { headers: headers(repaired.body.credential) }, + ); + assert.equal(replacementCredential.status, 200); + assert.equal(replacementCredential.body.instanceId, app.persisted().instanceId); + + const serialized = JSON.stringify(app.persisted()); + assert.equal(serialized.includes(first.body.credential), false); + assert.equal(serialized.includes(repaired.body.credential), false); + } finally { + await app.cleanup(); + } +}); diff --git a/main/services/aiden-remote-service.ts b/main/services/aiden-remote-service.ts new file mode 100644 index 00000000..bcaa937b --- /dev/null +++ b/main/services/aiden-remote-service.ts @@ -0,0 +1,501 @@ +import { spawn, type ChildProcess } from "node:child_process"; +import { X509Certificate } from "node:crypto"; +import { createServer as createHttpServer, type Server as HttpServer } from "node:http"; +import { createServer as createHttpsServer, type Server as HttpsServer } from "node:https"; +import os from "node:os"; +import { AIDEN_REMOTE_BASE_PATH } from "./aiden-remote-protocol.js"; +import { AidenRemotePairingService, type AidenRemotePairingBootstrap } from "./aiden-remote-pairing.js"; +import { + createAidenRemoteRequestHandler, + type AidenRemoteRouterDependencies, +} from "./aiden-remote-router.js"; +import type { + AidenRemoteConnectionMode, + AidenRemoteStateDocument, + AidenRemoteStateRegistry, +} from "./aiden-remote-state.js"; +import type { AidenRemoteTlsIdentity } from "./aiden-remote-tls-identity.js"; +import { fetchTlsServerSpkiSha256 } from "./aiden-remote-tls-identity.js"; +import type { AidenRemoteTailscaleController, AidenTailscaleConnectionStatus } from "./aiden-remote-tailscale.js"; +import { planAidenTailscaleConnect } from "./aiden-remote-tailscale-route.js"; +import type { AidenRemoteWorkspaceBrowserService } from "./aiden-remote-workspace-browser.js"; +import type { AidenRemoteWorkspaceService } from "./aiden-remote-workspaces.js"; +import type { AidenRemoteChatService } from "./aiden-remote-chats.js"; +import type { AidenRemoteModelService } from "./aiden-remote-models.js"; +import type { AidenRemoteStreamService } from "./aiden-remote-streams.js"; +import type { AidenRemoteFileService } from "./aiden-remote-files.js"; +import type { AidenRemoteGitService } from "./aiden-remote-git.js"; +import type { AidenRemoteScheduleService } from "./aiden-remote-schedules.js"; +import type { UsageDateRange, UsageSummary } from "./types.js"; + +const MAX_CONNECTIONS = 64; +const REQUEST_TIMEOUT_MS = 30_000; + +export interface AidenRemoteBonjourPublisher { + start(input: { instanceId: string; port: number }): void; + stop(): void; +} + +export interface AidenRemoteServiceLogEntry { + level: "info" | "warn" | "error"; + event: string; + details?: Record; +} + +export interface AidenRemoteServiceOptions { + state: AidenRemoteStateRegistry; + appVersion: string; + hostname?: string; + loadTlsIdentity(): Promise; + resolveTlsEndpointPin?: (hostname: string, port?: number) => Promise; + tailscale: Pick; + bonjour: AidenRemoteBonjourPublisher; + workspaceApi?: ( + instanceId: string, + ) => + | { + workspaces: Pick; + workspaceBrowser: Pick< + AidenRemoteWorkspaceBrowserService, + "listRoots" | "listChildren" | "createSelection" + >; + chats?: Pick; + models?: Pick; + streams?: Pick; + files?: Pick; + git?: Pick; + schedules?: Pick; + usage?: { summary(range: UsageDateRange): Promise }; + settle?: () => Promise; + } + | Promise<{ + workspaces: Pick; + workspaceBrowser: Pick< + AidenRemoteWorkspaceBrowserService, + "listRoots" | "listChildren" | "createSelection" + >; + chats?: Pick; + models?: Pick; + streams?: Pick; + files?: Pick; + git?: Pick; + schedules?: Pick; + usage?: { summary(range: UsageDateRange): Promise }; + settle?: () => Promise; + }>; + now?: () => number; + log?: (entry: AidenRemoteServiceLogEntry) => void; +} + +export interface AidenRemoteServiceStatus { + enabled: boolean; + running: boolean; + connectionMode: AidenRemoteConnectionMode; + lanPort: number; + lanEndpoint?: string; + tailscaleEndpoint?: string; + tailscaleRoutePreview?: string; + tailscaleConnected: boolean; + tailscaleInstalled: boolean; + pairedDeviceCount: number; + approvedRootCount: number; + error?: string; +} + +function normalizedHostname(raw: string): string { + const value = raw.trim().replace(/\.$/u, "").toLowerCase(); + if (!/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/u.test(value)) return "localhost"; + return value; +} + +function localDnsName(hostname: string): string { + return hostname.includes(".") ? hostname : `${hostname}.local`; +} + +function tailscaleLoopbackPort(lanPort: number): number { + return lanPort === 65_535 ? 49_221 : lanPort + 1; +} + +function configureServer(server: HttpServer | HttpsServer): void { + server.maxConnections = MAX_CONNECTIONS; + server.headersTimeout = REQUEST_TIMEOUT_MS; + server.requestTimeout = REQUEST_TIMEOUT_MS; + server.keepAliveTimeout = 5_000; +} + +async function listen( + server: HttpServer | HttpsServer, + port: number, + host: string, +): Promise { + await new Promise((resolve, reject) => { + const onError = (error: Error) => { + server.off("listening", onListening); + reject(error); + }; + const onListening = () => { + server.off("error", onError); + resolve(); + }; + server.once("error", onError); + server.once("listening", onListening); + server.listen(port, host); + }); +} + +async function closeServer(server: HttpServer | HttpsServer | null): Promise { + if (!server) return; + server.closeIdleConnections?.(); + await new Promise((resolve) => { + server.close(() => resolve()); + }); +} + +export class DnsSdAidenRemoteBonjourPublisher implements AidenRemoteBonjourPublisher { + private child: ChildProcess | null = null; + + constructor( + private readonly serviceName = "Aiden Agent", + private readonly log: (entry: AidenRemoteServiceLogEntry) => void = () => undefined, + ) {} + + start(input: { instanceId: string; port: number }): void { + this.stop(); + const child = spawn( + "/usr/bin/dns-sd", + [ + "-R", this.serviceName, "_aiden-agent._tcp", "local.", + String(input.port), "v=1", `instance=${input.instanceId}`, + ], + { stdio: "ignore", windowsHide: true }, + ); + child.once("error", (error) => { + this.log({ level: "warn", event: "bonjour_failed", details: { message: error.message } }); + if (this.child === child) this.child = null; + }); + child.once("exit", () => { + if (this.child === child) this.child = null; + }); + this.child = child; + } + + stop(): void { + const child = this.child; + this.child = null; + if (child && child.exitCode === null && !child.killed) child.kill("SIGTERM"); + } +} + +export class AidenRemoteService { + private lanServer: HttpsServer | null = null; + private tailscaleServer: HttpServer | null = null; + private pairing: AidenRemotePairingService | null = null; + private tlsIdentity: AidenRemoteTlsIdentity | null = null; + private activeState: AidenRemoteStateDocument | null = null; + private lastError: string | undefined; + private operationTail: Promise = Promise.resolve(); + private settleRemoteApi: (() => Promise) | undefined; + private readonly now: () => number; + private readonly hostname: string; + + constructor(private readonly options: AidenRemoteServiceOptions) { + this.now = options.now ?? Date.now; + this.hostname = normalizedHostname(options.hostname ?? os.hostname()); + } + + private serialized(operation: () => Promise): Promise { + const result = this.operationTail.then(operation, operation); + this.operationTail = result.then(() => undefined, () => undefined); + return result; + } + + async initialize(): Promise { + await this.serialized(async () => { + const state = await this.options.state.initialize(); + if (state.enabled) await this.startConfigured(state); + }); + } + + private async startConfigured(state: AidenRemoteStateDocument): Promise { + await this.stopListeners(); + this.lastError = undefined; + try { + const tlsIdentity = await this.options.loadTlsIdentity(); + const pairing = new AidenRemotePairingService(state.instanceId, this.options.state); + const workspaceApi = await this.options.workspaceApi?.(state.instanceId); + this.settleRemoteApi = workspaceApi?.settle; + const routerDependencies: AidenRemoteRouterDependencies = { + instanceId: state.instanceId, + appVersion: this.options.appVersion, + devices: this.options.state, + pairing, + ...(workspaceApi ?? {}), + connectionMode: () => state.connectionMode, + now: this.now, + log: (entry) => { + this.options.log?.({ + level: entry.status >= 500 ? "error" : entry.status >= 400 ? "warn" : "info", + event: "request", + details: { + requestId: entry.requestId, + route: entry.route, + status: entry.status, + latencyMs: entry.latencyMs, + deviceIdSuffix: entry.deviceIdSuffix, + errorCode: entry.errorCode, + }, + }); + }, + }; + + if (state.connectionMode === "lan" || state.connectionMode === "both") { + const server = createHttpsServer( + { key: tlsIdentity.privateKey, cert: tlsIdentity.certificateChain }, + createAidenRemoteRequestHandler(routerDependencies), + ); + configureServer(server); + // macOS Bonjour commonly resolves both A and AAAA records. The IPv6 + // wildcard is dual-stack by default, so one listener serves both. + await listen(server, state.lanPort, "::"); + this.lanServer = server; + this.options.bonjour.start({ instanceId: state.instanceId, port: state.lanPort }); + } + + if (state.connectionMode === "tailscale" || state.connectionMode === "both") { + const server = createHttpServer( + createAidenRemoteRequestHandler({ + ...routerDependencies, + acceptStrippedBasePath: true, + }), + ); + configureServer(server); + await listen(server, tailscaleLoopbackPort(state.lanPort), "127.0.0.1"); + this.tailscaleServer = server; + } + this.tlsIdentity = tlsIdentity; + this.pairing = pairing; + this.activeState = structuredClone(state); + this.options.log?.({ level: "info", event: "started", details: { mode: state.connectionMode } }); + } catch (error) { + this.lastError = error instanceof Error ? error.message : "Aiden Remote failed to start."; + await this.stopListeners(); + throw error; + } + } + + private async stopListeners(): Promise { + this.pairing?.close(); + this.pairing = null; + this.options.bonjour.stop(); + const lan = this.lanServer; + const tailscale = this.tailscaleServer; + this.lanServer = null; + this.tailscaleServer = null; + this.activeState = null; + const settleRemoteApi = this.settleRemoteApi; + this.settleRemoteApi = undefined; + await Promise.all([ + closeServer(lan), + closeServer(tailscale), + settleRemoteApi?.() ?? Promise.resolve(), + ]); + } + + async stopAndSettle(): Promise { + await this.serialized(() => this.stopListeners()); + } + + stop(): void { + this.pairing?.close(); + this.options.bonjour.stop(); + this.lanServer?.close(); + this.tailscaleServer?.close(); + } + + async setEnabled(enabled: boolean): Promise { + await this.serialized(async () => { + const current = await this.options.state.snapshot(); + if (enabled) { + if (!current.enabled || !this.activeState) { + await this.startConfigured({ ...current, enabled: true }); + try { + await this.options.state.setEnabled(true); + } catch (error) { + await this.stopListeners(); + throw error; + } + } + return; + } + let disconnectError: unknown; + if (current.tailscaleOwnership) { + try { + await this.disconnectTailscaleInternal(current); + } catch (error) { + disconnectError = error; + } + } + await this.stopListeners(); + await this.options.state.setEnabled(false); + if (disconnectError) throw disconnectError; + }); + } + + async setConnectionMode(connectionMode: AidenRemoteConnectionMode): Promise { + await this.serialized(async () => { + const current = await this.options.state.snapshot(); + if (current.tailscaleOwnership && connectionMode === "lan") { + await this.disconnectTailscaleInternal(current); + } + await this.options.state.setConnectionMode(connectionMode); + if (current.enabled) { + await this.startConfigured({ ...current, connectionMode }); + } + }); + } + + private loopbackTarget(state: AidenRemoteStateDocument): string { + return `http://127.0.0.1:${tailscaleLoopbackPort(state.lanPort)}${AIDEN_REMOTE_BASE_PATH}`; + } + + async connectTailscale(): Promise { + await this.serialized(async () => { + const state = await this.options.state.snapshot(); + if (!state.enabled || (state.connectionMode !== "tailscale" && state.connectionMode !== "both")) { + throw new Error("Enable Aiden Remote with Tailscale access before connecting Serve."); + } + if (!this.tailscaleServer) throw new Error("Aiden Remote loopback service is not running."); + const target = this.loopbackTarget(state); + let ownership = state.tailscaleOwnership; + if (ownership && ownership.target !== target) { + // Pre-acceptance builds persisted an origin-only target that cannot + // route the canonical API after Tailscale strips --set-path. Remove + // only that exact owned route before creating the corrected one. + await this.options.tailscale.disconnect(ownership.target, ownership); + await this.options.state.setTailscaleOwnership(undefined); + ownership = undefined; + } + const newOwnership = await this.options.tailscale.connect( + target, + ownership, + ); + await this.options.state.setTailscaleOwnership(newOwnership); + }); + } + + private async disconnectTailscaleInternal(state: AidenRemoteStateDocument): Promise { + if (!state.tailscaleOwnership) return; + await this.options.tailscale.disconnect( + state.tailscaleOwnership.target, + state.tailscaleOwnership, + ); + await this.options.state.setTailscaleOwnership(undefined); + } + + async disconnectTailscale(): Promise { + await this.serialized(async () => { + await this.disconnectTailscaleInternal(await this.options.state.snapshot()); + }); + } + + async beginPairing(transport: "lan" | "tailscale"): Promise { + return this.serialized(async () => { + const state = await this.options.state.snapshot(); + if (!state.enabled || !this.pairing || !this.tlsIdentity) { + throw new Error("Enable Aiden Remote before pairing a device."); + } + let endpoint: string; + let serverSpkiSha256: string; + if (transport === "lan") { + if (!this.lanServer) throw new Error("Local-network access is not enabled."); + endpoint = `https://${localDnsName(this.hostname)}:${state.lanPort}${AIDEN_REMOTE_BASE_PATH}`; + serverSpkiSha256 = this.tlsIdentity.serverSpkiSha256; + } else { + if (!state.tailscaleOwnership || !this.tailscaleServer) { + throw new Error("Connect the Aiden Tailscale Serve route before pairing."); + } + const status = await this.options.tailscale.status(); + if (!status.dnsName) throw new Error("Tailscale does not report a stable DNS name."); + endpoint = `https://${status.dnsName}${AIDEN_REMOTE_BASE_PATH}`; + serverSpkiSha256 = await ( + this.options.resolveTlsEndpointPin ?? fetchTlsServerSpkiSha256 + )(status.dnsName, 443); + } + return this.pairing.begin(endpoint, serverSpkiSha256); + }); + } + + async closePairing(): Promise { + await this.serialized(async () => { + this.pairing?.close(); + }); + } + + pairingQrPayload( + bootstrap: AidenRemotePairingBootstrap, + transport: "lan" | "tailscale", + ): string { + if (!this.tlsIdentity) throw new Error("Aiden Remote transport identity is unavailable."); + const trust = transport === "lan" + ? { + mode: "private-ca" as const, + caCertificateDerBase64: new X509Certificate( + this.tlsIdentity.caCertificate, + ).raw.toString("base64"), + } + : { mode: "system" as const }; + const payload = JSON.stringify({ + kind: "aiden-pairing-v1", + bootstrap, + trust, + }); + if (Buffer.byteLength(payload, "utf8") > 4_096) { + throw new Error("Aiden Remote pairing payload is too large."); + } + return payload; + } + + async status(): Promise { + await this.operationTail; + const state = await this.options.state.snapshot(); + let tailscaleStatus: AidenTailscaleConnectionStatus = { installed: false }; + if (state.connectionMode !== "lan" || state.tailscaleOwnership) { + tailscaleStatus = await this.options.tailscale.status(); + } + const lanEndpoint = this.lanServer + ? `https://${localDnsName(this.hostname)}:${state.lanPort}${AIDEN_REMOTE_BASE_PATH}` + : undefined; + const tailscaleEndpoint = tailscaleStatus.dnsName + ? `https://${tailscaleStatus.dnsName}${AIDEN_REMOTE_BASE_PATH}` + : undefined; + const target = this.loopbackTarget(state); + let tailscaleConnected = false; + if (state.tailscaleOwnership && tailscaleStatus.serveStatus) { + try { + tailscaleConnected = planAidenTailscaleConnect( + tailscaleStatus.serveStatus, + target, + state.tailscaleOwnership, + ).action === "noop"; + } catch { + tailscaleConnected = false; + } + } + return { + enabled: state.enabled, + running: this.lanServer !== null || this.tailscaleServer !== null, + connectionMode: state.connectionMode, + lanPort: state.lanPort, + ...(lanEndpoint ? { lanEndpoint } : {}), + ...(tailscaleEndpoint ? { tailscaleEndpoint } : {}), + ...(state.connectionMode === "tailscale" || state.connectionMode === "both" + ? { tailscaleRoutePreview: `tailscale serve --yes --bg --https=443 --set-path=${AIDEN_REMOTE_BASE_PATH} ${target}` } + : {}), + tailscaleConnected, + tailscaleInstalled: tailscaleStatus.installed, + pairedDeviceCount: state.devices.length, + approvedRootCount: state.approvedRoots.length, + ...(this.lastError ? { error: this.lastError } : {}), + }; + } +} diff --git a/main/services/aiden-remote-state.test.ts b/main/services/aiden-remote-state.test.ts new file mode 100644 index 00000000..913980b8 --- /dev/null +++ b/main/services/aiden-remote-state.test.ts @@ -0,0 +1,129 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import test from "node:test"; +import { + AidenRemoteStateRegistry, + createDefaultAidenRemoteState, + parseAidenRemoteStateDocument, + type AidenRemoteStateDocument, + type AidenRemoteStateStorage, +} from "./aiden-remote-state.js"; + +function fixture(initial?: unknown) { + let stored = initial ?? createDefaultAidenRemoteState(() => Buffer.alloc(24, 7)); + const writes: AidenRemoteStateDocument[] = []; + const storage: AidenRemoteStateStorage = { + load: async () => structuredClone(stored), + save: async (document) => { + stored = structuredClone(document); + writes.push(structuredClone(document)); + }, + }; + let randomCounter = 0; + let now = 1_000; + const registry = new AidenRemoteStateRegistry(storage, { + now: () => now, + randomBytes: (size) => Buffer.alloc(size, ++randomCounter), + deriveCredentialDigest: async (credential, salt) => + createHash("sha256").update(credential).update(salt).digest(), + }); + return { + registry, + writes, + stored: () => structuredClone(stored), + setNow: (value: number) => { + now = value; + }, + }; +} + +test("remote device credentials persist only digests and authenticate with capability state", async () => { + const state = fixture(); + await state.registry.initialize(); + const issued = await state.registry.issueDevice({ + name: "Sambit’s iPhone", + type: "iphone", + clientVersion: "1.0", + }); + assert.match(issued.credential, /^[A-Za-z0-9_-]{43}$/u); + const serialized = JSON.stringify(state.stored()); + assert.equal(serialized.includes(issued.credential), false); + assert.equal(serialized.includes("credentialDigest"), true); + assert.equal(serialized.includes("lookupDigest"), true); + + const authenticated = await state.registry.authenticate(issued.credential); + assert.equal(authenticated?.id, issued.device.id); + assert.equal(authenticated?.revoked, false); + assert.equal(authenticated?.capabilities.has("workspace:manage"), true); + assert.equal(await state.registry.authenticate("x".repeat(43)), null); +}); + +test("revocation is durable and preserves the revoked classification", async () => { + const state = fixture(); + const issued = await state.registry.issueDevice({ + name: "iPad", + type: "ipad", + clientVersion: "1", + }); + assert.equal(await state.registry.revokeDevice(issued.device.id), true); + assert.equal(await state.registry.revokeDevice(issued.device.id), false); + assert.equal((await state.registry.authenticate(issued.credential))?.revoked, true); + assert.equal((await state.registry.listDevices())[0]?.revokedAt, 1_000); +}); + +test("state restore rejects excess keys, duplicate identities, and raw credentials", () => { + const base = createDefaultAidenRemoteState(() => Buffer.alloc(24, 1)); + assert.throws( + () => parseAidenRemoteStateDocument({ ...base, extra: true }), + /state is invalid/u, + ); + assert.throws( + () => + parseAidenRemoteStateDocument({ + ...base, + devices: [ + { + id: "device", + name: "Phone", + type: "iphone", + clientVersion: "1", + lookupDigest: "a".repeat(43), + credentialSalt: "b".repeat(43), + credentialDigest: "c".repeat(43), + capabilities: ["server:read"], + createdAt: 1, + lastSeenAt: 1, + credential: "must-not-be-retained", + }, + ], + }), + /invalid device/u, + ); +}); + +test("remote access is off by default and persists an explicit listen mode", async () => { + const state = fixture(); + assert.equal((await state.registry.initialize()).enabled, false); + assert.equal((await state.registry.snapshot()).connectionMode, "lan"); + await state.registry.setConnectionMode("both"); + assert.equal((await state.registry.snapshot()).connectionMode, "both"); +}); + +test("last-seen persistence is throttled and never reactivates a revoked device", async () => { + const state = fixture(); + const issued = await state.registry.issueDevice({ + name: "Phone", + type: "iphone", + clientVersion: "1", + }); + const writesAfterIssue = state.writes.length; + state.setNow(1_000 + 60_000); + await state.registry.authenticate(issued.credential); + assert.equal(state.writes.length, writesAfterIssue); + state.setNow(1_000 + 6 * 60_000); + await state.registry.authenticate(issued.credential); + assert.equal(state.writes.length, writesAfterIssue + 1); + await state.registry.revokeDevice(issued.device.id); + state.setNow(1_000 + 12 * 60_000); + assert.equal((await state.registry.authenticate(issued.credential))?.revoked, true); +}); diff --git a/main/services/aiden-remote-state.ts b/main/services/aiden-remote-state.ts new file mode 100644 index 00000000..eb823ce5 --- /dev/null +++ b/main/services/aiden-remote-state.ts @@ -0,0 +1,547 @@ +import { + createHash, + randomBytes, + scrypt, + timingSafeEqual, +} from "node:crypto"; +import type { AidenRemoteCapability } from "./aiden-remote-protocol.js"; +import { AIDEN_REMOTE_CAPABILITIES } from "./aiden-remote-protocol.js"; +import type { AidenTailscaleOwnership } from "./aiden-remote-tailscale-route.js"; + +const STATE_VERSION = 1 as const; +const DEFAULT_LAN_PORT = 49_220; +const MAX_DEVICES = 32; +const MAX_RETAINED_DEVICES = 128; +const MAX_APPROVED_ROOTS = 32; +const LAST_SEEN_WRITE_INTERVAL_MS = 5 * 60_000; + +export type AidenRemoteDeviceType = "iphone" | "ipad"; +export type AidenRemoteConnectionMode = "lan" | "tailscale" | "both"; + +interface StoredAidenRemoteDevice { + id: string; + name: string; + type: AidenRemoteDeviceType; + clientVersion: string; + lookupDigest: string; + credentialSalt: string; + credentialDigest: string; + capabilities: AidenRemoteCapability[]; + createdAt: number; + lastSeenAt: number; + revokedAt?: number; +} + +export interface AidenRemoteApprovedRoot { + id: string; + label: string; + folderPath: string; + device: string; + inode: string; + policyRevision: string; + createdAt: number; +} + +export interface AidenRemoteStateDocument { + version: typeof STATE_VERSION; + instanceId: string; + enabled: boolean; + connectionMode: AidenRemoteConnectionMode; + lanPort: number; + devices: StoredAidenRemoteDevice[]; + approvedRoots: AidenRemoteApprovedRoot[]; + tailscaleOwnership?: AidenTailscaleOwnership; +} + +export interface AidenRemoteStateStorage { + load(): Promise; + save(document: AidenRemoteStateDocument): Promise; +} + +export interface AidenRemoteDeviceProjection { + id: string; + name: string; + type: AidenRemoteDeviceType; + clientVersion: string; + capabilities: AidenRemoteCapability[]; + createdAt: number; + lastSeenAt: number; + revokedAt?: number; +} + +export interface AidenRemoteAuthenticatedDevice { + id: string; + capabilities: ReadonlySet; + revoked: boolean; +} + +export interface AidenRemoteIssuedCredential { + credential: string; + device: AidenRemoteDeviceProjection; +} + +export interface AidenRemoteStateDependencies { + now(): number; + randomBytes(size: number): Buffer; + deriveCredentialDigest(credential: string, salt: Buffer): Promise; +} + +function ownRecord(value: unknown): Record | null { + return value !== null && typeof value === "object" && !Array.isArray(value) + ? (value as Record) + : null; +} + +function exactKeys( + record: Record, + required: readonly string[], + optional: readonly string[] = [], +): boolean { + const allowed = new Set([...required, ...optional]); + return ( + required.every((key) => Object.prototype.hasOwnProperty.call(record, key)) && + Object.keys(record).every((key) => allowed.has(key)) + ); +} + +function boundedString(value: unknown, maximum: number): value is string { + if (typeof value !== "string" || value.length === 0) return false; + let characters = 0; + for (const _character of value) characters += 1; + return characters <= maximum; +} + +function digestString(value: unknown): value is string { + return typeof value === "string" && /^[A-Za-z0-9_-]{43}$/u.test(value); +} + +function safeTimestamp(value: unknown): value is number { + return Number.isSafeInteger(value) && Number(value) >= 0; +} + +function parseCapabilities(value: unknown): AidenRemoteCapability[] | null { + if (!Array.isArray(value) || value.length > AIDEN_REMOTE_CAPABILITIES.length) { + return null; + } + const capabilities = new Set(); + for (const capability of value) { + if ( + typeof capability !== "string" || + !(AIDEN_REMOTE_CAPABILITIES as readonly string[]).includes(capability) || + capabilities.has(capability as AidenRemoteCapability) + ) { + return null; + } + capabilities.add(capability as AidenRemoteCapability); + } + return [...capabilities]; +} + +function parseDevice(value: unknown): StoredAidenRemoteDevice | null { + const record = ownRecord(value); + const required = [ + "id", + "name", + "type", + "clientVersion", + "lookupDigest", + "credentialSalt", + "credentialDigest", + "capabilities", + "createdAt", + "lastSeenAt", + ] as const; + if (!record || !exactKeys(record, required, ["revokedAt"])) return null; + const capabilities = parseCapabilities(record.capabilities); + if ( + !boundedString(record.id, 128) || + !boundedString(record.name, 80) || + (record.type !== "iphone" && record.type !== "ipad") || + !boundedString(record.clientVersion, 40) || + !digestString(record.lookupDigest) || + !digestString(record.credentialSalt) || + !digestString(record.credentialDigest) || + !capabilities || + !safeTimestamp(record.createdAt) || + !safeTimestamp(record.lastSeenAt) || + (record.revokedAt !== undefined && !safeTimestamp(record.revokedAt)) + ) { + return null; + } + return { + id: record.id, + name: record.name, + type: record.type, + clientVersion: record.clientVersion, + lookupDigest: record.lookupDigest, + credentialSalt: record.credentialSalt, + credentialDigest: record.credentialDigest, + capabilities, + createdAt: record.createdAt, + lastSeenAt: record.lastSeenAt, + ...(record.revokedAt === undefined ? {} : { revokedAt: record.revokedAt }), + }; +} + +function parseApprovedRoot(value: unknown): AidenRemoteApprovedRoot | null { + const record = ownRecord(value); + if ( + !record || + !exactKeys(record, [ + "id", + "label", + "folderPath", + "device", + "inode", + "policyRevision", + "createdAt", + ]) || + !boundedString(record.id, 128) || + !boundedString(record.label, 160) || + !boundedString(record.folderPath, 4_096) || + !boundedString(record.device, 64) || + !boundedString(record.inode, 64) || + !boundedString(record.policyRevision, 128) || + !safeTimestamp(record.createdAt) + ) { + return null; + } + return record as unknown as AidenRemoteApprovedRoot; +} + +function parseOwnership(value: unknown): AidenTailscaleOwnership | undefined { + if (value === undefined) return undefined; + const record = ownRecord(value); + if ( + !record || + !exactKeys(record, ["path", "target"]) || + record.path !== "/api/aiden/v1" || + !boundedString(record.target, 2_048) + ) { + throw new Error("Aiden Remote state has an invalid Tailscale ownership record."); + } + return { path: record.path, target: record.target }; +} + +export function createDefaultAidenRemoteState( + random: (size: number) => Buffer = randomBytes, +): AidenRemoteStateDocument { + return { + version: STATE_VERSION, + instanceId: `instance_${random(24).toString("base64url")}`, + enabled: false, + connectionMode: "lan", + lanPort: DEFAULT_LAN_PORT, + devices: [], + approvedRoots: [], + }; +} + +export function parseAidenRemoteStateDocument(value: unknown): AidenRemoteStateDocument { + const record = ownRecord(value); + if ( + !record || + !exactKeys( + record, + ["version", "instanceId", "enabled", "connectionMode", "lanPort", "devices", "approvedRoots"], + ["tailscaleOwnership"], + ) || + record.version !== STATE_VERSION || + !boundedString(record.instanceId, 128) || + typeof record.enabled !== "boolean" || + !["lan", "tailscale", "both"].includes(String(record.connectionMode)) || + !Number.isInteger(record.lanPort) || + Number(record.lanPort) < 1 || + Number(record.lanPort) > 65_535 || + !Array.isArray(record.devices) || + record.devices.length > MAX_RETAINED_DEVICES || + !Array.isArray(record.approvedRoots) || + record.approvedRoots.length > MAX_APPROVED_ROOTS + ) { + throw new Error("Aiden Remote state is invalid."); + } + const devices = record.devices.map(parseDevice); + const approvedRoots = record.approvedRoots.map(parseApprovedRoot); + if (devices.some((device) => device === null)) { + throw new Error("Aiden Remote state contains an invalid device."); + } + if (approvedRoots.some((root) => root === null)) { + throw new Error("Aiden Remote state contains an invalid approved root."); + } + const deviceIds = new Set(devices.map((device) => device!.id)); + const lookupDigests = new Set(devices.map((device) => device!.lookupDigest)); + const rootIds = new Set(approvedRoots.map((root) => root!.id)); + if (deviceIds.size !== devices.length || lookupDigests.size !== devices.length) { + throw new Error("Aiden Remote state contains duplicate device identity."); + } + if (rootIds.size !== approvedRoots.length) { + throw new Error("Aiden Remote state contains duplicate approved-root identity."); + } + const tailscaleOwnership = parseOwnership(record.tailscaleOwnership); + return { + version: STATE_VERSION, + instanceId: record.instanceId, + enabled: record.enabled, + connectionMode: record.connectionMode as AidenRemoteConnectionMode, + lanPort: Number(record.lanPort), + devices: devices as StoredAidenRemoteDevice[], + approvedRoots: approvedRoots as AidenRemoteApprovedRoot[], + ...(tailscaleOwnership ? { tailscaleOwnership } : {}), + }; +} + +function fastLookupDigest(credential: string): string { + return createHash("sha256").update(credential).digest("base64url"); +} + +function decodeDigest(value: string): Buffer | null { + try { + const decoded = Buffer.from(value, "base64url"); + return decoded.length === 32 ? decoded : null; + } catch { + return null; + } +} + +function safeEqual(left: Buffer | null, right: Buffer): boolean { + return Boolean(left && left.length === right.length && timingSafeEqual(left, right)); +} + +function projectDevice(device: StoredAidenRemoteDevice): AidenRemoteDeviceProjection { + return { + id: device.id, + name: device.name, + type: device.type, + clientVersion: device.clientVersion, + capabilities: [...device.capabilities], + createdAt: device.createdAt, + lastSeenAt: device.lastSeenAt, + ...(device.revokedAt === undefined ? {} : { revokedAt: device.revokedAt }), + }; +} + +export function defaultAidenRemoteStateDependencies(): AidenRemoteStateDependencies { + return { + now: Date.now, + randomBytes, + deriveCredentialDigest: (credential, salt) => + new Promise((resolve, reject) => { + scrypt( + credential, + salt, + 32, + { N: 32_768, r: 8, p: 1, maxmem: 64 * 1_024 * 1_024 }, + (error, derived) => { + if (error) reject(error); + else resolve(derived as Buffer); + }, + ); + }), + }; +} + +export class AidenRemoteStateRegistry { + private document: AidenRemoteStateDocument | null = null; + private mutationTail: Promise = Promise.resolve(); + + constructor( + private readonly storage: AidenRemoteStateStorage, + private readonly dependencies: AidenRemoteStateDependencies = + defaultAidenRemoteStateDependencies(), + ) {} + + private serialized(operation: () => Promise): Promise { + const result = this.mutationTail.then(operation, operation); + this.mutationTail = result.then( + () => undefined, + () => undefined, + ); + return result; + } + + async initialize(): Promise { + return this.serialized(async () => { + if (this.document) return structuredClone(this.document); + const loaded = parseAidenRemoteStateDocument(await this.storage.load()); + await this.storage.save(loaded); + this.document = loaded; + return structuredClone(loaded); + }); + } + + private async current(): Promise { + if (!this.document) await this.initialize(); + return this.document!; + } + + private async mutate( + operation: (draft: AidenRemoteStateDocument) => Promise | T, + ): Promise { + // Initialize before joining the mutation queue. Calling initialize from + // inside a queued mutation would enqueue behind itself and deadlock. + if (!this.document) await this.initialize(); + return this.serialized(async () => { + const draft = structuredClone(await this.current()); + const result = await operation(draft); + const validated = parseAidenRemoteStateDocument(draft); + await this.storage.save(validated); + this.document = validated; + return result; + }); + } + + async snapshot(): Promise { + await this.mutationTail; + return structuredClone(await this.current()); + } + + async setEnabled(enabled: boolean): Promise { + await this.mutate((draft) => { + draft.enabled = enabled; + }); + } + + async setConnectionMode(connectionMode: AidenRemoteConnectionMode): Promise { + if (!["lan", "tailscale", "both"].includes(connectionMode)) { + throw new Error("Invalid Aiden Remote connection mode."); + } + await this.mutate((draft) => { + draft.connectionMode = connectionMode; + }); + } + + async listDevices(): Promise { + const document = await this.snapshot(); + return document.devices.map(projectDevice); + } + + async issueDevice(input: { + name: string; + type: AidenRemoteDeviceType; + clientVersion: string; + capabilities?: readonly AidenRemoteCapability[]; + }): Promise { + if ( + !boundedString(input.name, 80) || + (input.type !== "iphone" && input.type !== "ipad") || + !boundedString(input.clientVersion, 40) + ) { + throw new Error("Invalid pairing device metadata."); + } + const capabilities = parseCapabilities( + input.capabilities ?? AIDEN_REMOTE_CAPABILITIES, + ); + if (!capabilities) throw new Error("Invalid device capabilities."); + const credential = this.dependencies.randomBytes(32).toString("base64url"); + const salt = this.dependencies.randomBytes(32); + const credentialDigest = await this.dependencies.deriveCredentialDigest( + credential, + salt, + ); + if (credentialDigest.length !== 32) { + throw new Error("Aiden Remote credential derivation failed."); + } + const now = this.dependencies.now(); + const device: StoredAidenRemoteDevice = { + id: `device_${this.dependencies.randomBytes(24).toString("base64url")}`, + name: input.name, + type: input.type, + clientVersion: input.clientVersion, + lookupDigest: fastLookupDigest(credential), + credentialSalt: salt.toString("base64url"), + credentialDigest: credentialDigest.toString("base64url"), + capabilities, + createdAt: now, + lastSeenAt: now, + }; + await this.mutate((draft) => { + if (draft.devices.filter((entry) => entry.revokedAt === undefined).length >= MAX_DEVICES) { + throw new Error("Aiden Remote device capacity reached."); + } + if (draft.devices.length >= MAX_RETAINED_DEVICES) { + const revokedIndex = draft.devices.findIndex( + (entry) => entry.revokedAt !== undefined, + ); + if (revokedIndex < 0) throw new Error("Aiden Remote device capacity reached."); + draft.devices.splice(revokedIndex, 1); + } + draft.devices.push(device); + }); + return { credential, device: projectDevice(device) }; + } + + async authenticate(credential: string): Promise { + if (!digestString(credential)) return null; + await this.mutationTail; + const document = await this.current(); + const lookupDigest = fastLookupDigest(credential); + const device = document.devices.find( + (candidate) => candidate.lookupDigest === lookupDigest, + ); + if (!device) return null; + const salt = decodeDigest(device.credentialSalt); + const stored = decodeDigest(device.credentialDigest); + if (!salt || !stored) return null; + const actual = await this.dependencies.deriveCredentialDigest(credential, salt); + if (!safeEqual(stored, actual)) return null; + + const authenticated: AidenRemoteAuthenticatedDevice = { + id: device.id, + capabilities: new Set(device.capabilities), + revoked: device.revokedAt !== undefined, + }; + const now = this.dependencies.now(); + if ( + !authenticated.revoked && + now - device.lastSeenAt >= LAST_SEEN_WRITE_INTERVAL_MS + ) { + await this.mutate((draft) => { + const current = draft.devices.find((candidate) => candidate.id === device.id); + if (current && current.revokedAt === undefined) current.lastSeenAt = now; + }); + } + return authenticated; + } + + async revokeDevice(deviceId: string): Promise { + if (!boundedString(deviceId, 128)) return false; + return this.mutate((draft) => { + const device = draft.devices.find((candidate) => candidate.id === deviceId); + if (!device || device.revokedAt !== undefined) return false; + device.revokedAt = this.dependencies.now(); + return true; + }); + } + + async setTailscaleOwnership( + ownership: AidenTailscaleOwnership | undefined, + ): Promise { + await this.mutate((draft) => { + if (ownership) draft.tailscaleOwnership = ownership; + else delete draft.tailscaleOwnership; + }); + } + + async addApprovedRoot(root: AidenRemoteApprovedRoot): Promise { + if (!parseApprovedRoot(root)) throw new Error("Invalid approved root."); + await this.mutate((draft) => { + if (draft.approvedRoots.length >= MAX_APPROVED_ROOTS) { + throw new Error("Aiden Remote approved-root capacity reached."); + } + if (draft.approvedRoots.some((candidate) => candidate.id === root.id)) { + throw new Error("This approved root already exists."); + } + draft.approvedRoots.push(root); + }); + } + + async removeApprovedRoot(rootId: string): Promise { + if (!boundedString(rootId, 128)) return false; + return this.mutate((draft) => { + const index = draft.approvedRoots.findIndex((root) => root.id === rootId); + if (index < 0) return false; + draft.approvedRoots.splice(index, 1); + return true; + }); + } +} diff --git a/main/services/aiden-remote-streams.test.ts b/main/services/aiden-remote-streams.test.ts new file mode 100644 index 00000000..1eb6e341 --- /dev/null +++ b/main/services/aiden-remote-streams.test.ts @@ -0,0 +1,126 @@ +import assert from "node:assert/strict"; +import { EventEmitter } from "node:events"; +import test from "node:test"; +import type { ServerResponse } from "node:http"; +import { AidenRemoteStreamService } from "./aiden-remote-streams.js"; + +function fixture() { + let now = 1_000; + const cancelled: string[] = []; + const approvals: string[] = []; + const service = new AidenRemoteStreamService({ + now: () => now, + cancel: (streamId, ownerId) => { + cancelled.push(`${streamId}:${ownerId}`); + return true; + }, + approve: (approvalId, decision, ownerId) => { + approvals.push(`${approvalId}:${decision}:${ownerId}`); + return true; + }, + }); + return { service, cancelled, approvals, setNow: (value: number) => { now = value; } }; +} + +test("remote stream journals typed events and is isolated to its paired device", () => { + const app = fixture(); + const owner = app.service.create("device-1", "stream-1", "chat-1", "turn-1"); + owner.owner.send("chat:delta", { streamId: "stream-1", delta: "Hello" }); + owner.owner.send("chat:reasoning-delta", { streamId: "stream-1", delta: "Think" }); + owner.owner.send("chat:tool", { streamId: "stream-1", phase: "call", toolName: "read_file" }); + owner.owner.send("chat:tool", { streamId: "stream-1", phase: "result", toolName: "read_file" }); + owner.owner.send("chat:done", { + streamId: "stream-1", + chat: { messages: [{ id: "assistant-1", role: "assistant" }] }, + }); + const status = app.service.status("device-1", "stream-1"); + assert.equal(status.state, "done"); + assert.equal(status.lastSequence, 6); + assert.throws( + () => app.service.status("device-2", "stream-1"), + (error: unknown) => (error as { code?: string }).code === "not_found", + ); +}); + +test("SSE replay emits frozen envelopes and closes after a terminal event", () => { + const app = fixture(); + const owner = app.service.create("device-1", "stream-1", "chat-1", "turn-1"); + owner.owner.send("chat:delta", { delta: "Hello" }); + owner.owner.send("chat:done", { chat: { messages: [{ id: "assistant-1", role: "assistant" }] } }); + const output: string[] = []; + let status = 0; + let ended = false; + const response = Object.assign(new EventEmitter(), { + writeHead(value: number) { status = value; return this; }, + write(value: string) { output.push(value); return true; }, + end() { ended = true; return this; }, + }) as unknown as ServerResponse; + app.service.openEvents("device-1", "stream-1", 0, response); + assert.equal(status, 200); + assert.equal(ended, true); + assert.match(output.join(""), /event: text_delta/u); + assert.match(output.join(""), /"messageId":"assistant-1"/u); +}); + +test("cancel and approval decisions are bound to the owning device and owner identity", async () => { + const app = fixture(); + const owner = app.service.create("device-1", "stream-1", "chat-1", "turn-1"); + owner.owner.send("chat:approval", { + approvalId: "approval-1", + summary: "Write a file", + }); + await assert.rejects( + app.service.respondApproval("device-2", "approval-1", "allow", "wrong-device-key-0001"), + (error: unknown) => (error as { code?: string }).code === "approval_expired", + ); + const resolved = await app.service.respondApproval("device-1", "approval-1", "deny", "approval-deny-key-001"); + assert.deepEqual( + await app.service.respondApproval("device-1", "approval-1", "deny", "approval-deny-key-001"), + resolved, + ); + await assert.rejects( + app.service.respondApproval("device-1", "approval-1", "allow", "approval-deny-key-001"), + (error: unknown) => (error as { code?: string }).code === "idempotency_conflict", + ); + assert.equal(resolved.decision, "deny"); + assert.equal(app.approvals.length, 1); + const status = await app.service.cancel("device-1", "stream-1", "cancel-stream-key-001"); + assert.deepEqual( + await app.service.cancel("device-1", "stream-1", "cancel-stream-key-001"), + status, + ); + assert.equal(status.state, "running"); + assert.equal(app.cancelled.length, 1); +}); + +test("restart restores terminal journals and marks active work interrupted", () => { + const app = fixture(); + const owner = app.service.create("device-1", "stream-1", "chat-1", "turn-1"); + owner.owner.send("chat:delta", { delta: "Partial" }); + const snapshot = app.service.snapshot(); + const restarted = new AidenRemoteStreamService({ + now: () => 20_000, + cancel: () => false, + approve: () => false, + snapshot, + }); + const status = restarted.status("device-1", "stream-1"); + assert.equal(status.state, "interrupted"); + assert.equal(status.lastSequence, 3); +}); + +test("revocation closes only the selected device streams and approval expiry denies safely", () => { + const app = fixture(); + const first = app.service.create("device-1", "stream-1", "chat-1", "turn-1"); + const second = app.service.create("device-2", "stream-2", "chat-2", "turn-2"); + first.owner.send("chat:approval", { approvalId: "approval-1", summary: "Change a file" }); + app.service.revokeDevice("device-1"); + assert.equal(app.service.status("device-1", "stream-1").state, "cancelled"); + assert.equal(app.service.status("device-2", "stream-2").state, "queued"); + assert.equal(app.cancelled.length, 1); + + second.owner.send("chat:approval", { approvalId: "approval-2", summary: "Run a command" }); + app.setNow(1_000 + 5 * 60 * 1_000 + 1); + app.service.status("device-2", "stream-2"); + assert.equal(app.approvals.some((entry) => entry.includes("approval-2:deny")), true); +}); diff --git a/main/services/aiden-remote-streams.ts b/main/services/aiden-remote-streams.ts new file mode 100644 index 00000000..3b1ed39e --- /dev/null +++ b/main/services/aiden-remote-streams.ts @@ -0,0 +1,724 @@ +import type { ServerResponse } from "node:http"; +import type { NotificationChannel } from "../../renderer/preload-channels.js"; +import type { GenerationTimeline } from "../../renderer/shared/generation-timeline.js"; +import { + createRemoteChatGenerationOwner, + type RemoteChatGenerationOwnerController, +} from "./chat-generation-owner.js"; +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; +import { + AIDEN_REMOTE_PROTOCOL_VERSION, + parseAidenRemoteStreamEvent, +} from "./aiden-remote-protocol.js"; +import { + AidenIdempotencyLedger, + type AidenIdempotencySnapshot, + AidenOperationContractError, +} from "./aiden-remote-operation-contract.js"; + +const MAX_STREAMS = 256; +const MAX_EVENTS_PER_STREAM = 4_096; +const MAX_STREAM_EVENT_BYTES = 8 * 1_024 * 1_024; +const TERMINAL_RETENTION_MS = 24 * 60 * 60 * 1_000; +const APPROVAL_LIFETIME_MS = 5 * 60 * 1_000; +export const MAX_AIDEN_REMOTE_STREAM_SNAPSHOT_BYTES = 16 * 1_024 * 1_024; + +export type AidenRemoteStreamState = + | "queued" + | "running" + | "waiting_for_approval" + | "reconciling" + | "done" + | "error" + | "cancelled" + | "interrupted"; + +export interface AidenRemoteStreamEvent { + protocolVersion: typeof AIDEN_REMOTE_PROTOCOL_VERSION; + streamId: string; + sequence: number; + timestamp: string; + type: string; + terminal: boolean; + payload: Record; +} + +export interface AidenRemoteStreamStatus { + streamId: string; + chatId: string; + turnId: string; + state: AidenRemoteStreamState; + lastSequence: number; + updatedAt: string; +} + +export interface AidenRemoteStreamSnapshot { + version: 1; + streams: Array<{ + streamId: string; + chatId: string; + turnId: string; + deviceId: string; + state: AidenRemoteStreamState; + updatedAt: number; + events: AidenRemoteStreamEvent[]; + }>; +} + +interface StreamSubscriber { + response: ServerResponse; + heartbeat: ReturnType; +} + +interface StreamRecord { + streamId: string; + chatId: string; + turnId: string; + deviceId: string; + state: AidenRemoteStreamState; + updatedAt: number; + events: AidenRemoteStreamEvent[]; + eventBytes: number; + subscribers: Set; + owner: RemoteChatGenerationOwnerController; + cancelRequested: boolean; + cancellationSource: "device" | "server"; + activeTools: Map; + toolCounter: number; +} + +interface ApprovalRecord { + streamId: string; + deviceId: string; + ownerDocumentId: string; + expiresAt: number; + expiry: ReturnType; +} + +function ownRecord(value: unknown): Record | null { + return value !== null && typeof value === "object" && !Array.isArray(value) + ? (value as Record) + : null; +} + +function boundedText(value: unknown, maximum: number, fallback = ""): string { + return typeof value === "string" ? value.slice(0, maximum) : fallback; +} + +function terminal(state: AidenRemoteStreamState): boolean { + return state === "done" || state === "error" || state === "cancelled" || state === "interrupted"; +} + +function parseSnapshot(value: unknown): AidenRemoteStreamSnapshot { + const record = ownRecord(value); + if ( + !record || + record.version !== 1 || + !Array.isArray(record.streams) || + record.streams.length > MAX_STREAMS || + Buffer.byteLength(JSON.stringify(value), "utf8") > MAX_AIDEN_REMOTE_STREAM_SNAPSHOT_BYTES + ) { + throw new Error("Invalid Aiden Remote stream snapshot."); + } + const streamIds = new Set(); + const streams: AidenRemoteStreamSnapshot["streams"] = []; + for (const raw of record.streams) { + const stream = ownRecord(raw); + if ( + !stream || + Object.keys(stream).some((key) => !["streamId", "chatId", "turnId", "deviceId", "state", "updatedAt", "events"].includes(key)) || + !["streamId", "chatId", "turnId", "deviceId", "state", "updatedAt", "events"].every((key) => Object.prototype.hasOwnProperty.call(stream, key)) || + typeof stream.streamId !== "string" || + !/^[A-Za-z0-9._:-]{1,128}$/u.test(stream.streamId) || + streamIds.has(stream.streamId) || + typeof stream.chatId !== "string" || + !/^[A-Za-z0-9._:-]{1,128}$/u.test(stream.chatId) || + typeof stream.turnId !== "string" || + !/^[A-Za-z0-9._:-]{1,128}$/u.test(stream.turnId) || + typeof stream.deviceId !== "string" || + stream.deviceId.length === 0 || + stream.deviceId.length > 128 || + typeof stream.state !== "string" || + !["queued", "running", "waiting_for_approval", "reconciling", "done", "error", "cancelled", "interrupted"].includes(stream.state) || + !Number.isSafeInteger(stream.updatedAt) || + Number(stream.updatedAt) < 0 || + !Array.isArray(stream.events) || + stream.events.length > MAX_EVENTS_PER_STREAM + ) { + throw new Error("Invalid Aiden Remote stream snapshot."); + } + let previous = 0; + const events = stream.events.map((rawEvent) => { + const event = ownRecord(rawEvent); + const payload = ownRecord(event?.payload); + if ( + !event || + !payload || + event.protocolVersion !== AIDEN_REMOTE_PROTOCOL_VERSION || + event.streamId !== stream.streamId || + !Number.isSafeInteger(event.sequence) || + (previous === 0 + ? Number(event.sequence) < 1 + : Number(event.sequence) !== previous + 1) || + typeof event.timestamp !== "string" || + !Number.isFinite(Date.parse(event.timestamp)) || + typeof event.type !== "string" || + event.type.length === 0 || + event.type.length > 80 || + typeof event.terminal !== "boolean" || + !parseAidenRemoteStreamEvent(rawEvent) + ) { + throw new Error("Invalid Aiden Remote stream snapshot."); + } + previous = Number(event.sequence); + return structuredClone(rawEvent) as AidenRemoteStreamEvent; + }); + streamIds.add(stream.streamId); + streams.push({ + streamId: stream.streamId, + chatId: stream.chatId, + turnId: stream.turnId, + deviceId: stream.deviceId, + state: stream.state as AidenRemoteStreamState, + updatedAt: stream.updatedAt as number, + events, + }); + } + return { version: 1, streams }; +} + +export function normalizeAidenRemoteStreamSnapshot(value: unknown): AidenRemoteStreamSnapshot { + return parseSnapshot(value); +} + +function sseFrame(event: AidenRemoteStreamEvent): string { + return `id: ${event.sequence}\nevent: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`; +} + +export class AidenRemoteStreamService { + private readonly streams = new Map(); + private readonly approvals = new Map(); + private persistTail: Promise = Promise.resolve(); + private persistDirty = false; + private persistRunning = false; + private readonly idempotency: AidenIdempotencyLedger; + + constructor( + private readonly options: { + now(): number; + cancel(streamId: string, ownerDocumentId: string): boolean; + approve(approvalId: string, decision: "allow" | "deny", ownerDocumentId: string): boolean; + notifyChatChanged?: (chatId: string) => void; + snapshot?: AidenRemoteStreamSnapshot; + persist?: (snapshot: AidenRemoteStreamSnapshot) => Promise; + idempotency?: AidenIdempotencyLedger; + persistIdempotency?: (snapshot: AidenIdempotencySnapshot) => Promise; + onPersistenceError?: (error: unknown) => void; + }, + ) { + this.idempotency = options.idempotency ?? new AidenIdempotencyLedger(); + if (options.snapshot) this.restore(options.snapshot); + } + + private async executeIdempotent( + scope: { deviceId: string; route: string; resourceId: string; key: string }, + input: unknown, + action: () => Promise, + ): Promise { + if (!/^[\x21-\x7e]{16,128}$/u.test(scope.key)) { + throw new AidenRemoteServiceError("invalid_request", "Idempotency-Key is invalid.", 400); + } + if (!this.options.persistIdempotency) { + try { + return await this.idempotency.execute(scope, input, action); + } catch (error) { + return this.mapIdempotencyError(error); + } + } + let admit!: () => void; + let reject!: (error: unknown) => void; + const durable = new Promise((resolve, rejectPromise) => { + admit = resolve; + reject = rejectPromise; + }); + const pending = this.idempotency.execute(scope, input, async () => { + await durable; + return action(); + }); + try { + await this.options.persistIdempotency(this.idempotency.snapshot()); + admit(); + } catch (error) { + reject(error); + await pending.catch(() => undefined); + throw new AidenRemoteServiceError("internal_error", "Aiden could not prepare this stream request.", 500); + } + let result: T | undefined; + let failure: unknown; + try { + result = await pending; + } catch (error) { + failure = error; + } + try { + await this.options.persistIdempotency(this.idempotency.snapshot()); + } catch { + throw new AidenRemoteServiceError("idempotency_in_flight", "The stream request outcome is unknown.", 409); + } + if (failure) throw failure; + return result!; + } + + private mapIdempotencyError(error: unknown): never { + if (error instanceof AidenRemoteServiceError) throw error; + if (error instanceof AidenOperationContractError) { + throw new AidenRemoteServiceError( + error.code, + "This stream request cannot be safely repeated.", + error.code === "idempotency_capacity" ? 429 : 409, + ); + } + throw error; + } + + private ownerFor(record: Omit): RemoteChatGenerationOwnerController { + return createRemoteChatGenerationOwner({ + deviceId: record.deviceId, + streamId: record.streamId, + publish: (channel, payload) => this.projectNotification(this.streams.get(record.streamId)!, channel, payload), + }); + } + + private restore(snapshot: AidenRemoteStreamSnapshot): void { + for (const saved of parseSnapshot(snapshot).streams) { + const base: Omit = { + ...saved, + eventBytes: saved.events.reduce( + (total, event) => total + Buffer.byteLength(JSON.stringify(event), "utf8"), + 0, + ), + subscribers: new Set(), + cancelRequested: false, + cancellationSource: "server", + activeTools: new Map(), + toolCounter: 0, + }; + const record: StreamRecord = { ...base, owner: this.ownerFor(base) }; + this.streams.set(record.streamId, record); + if (!terminal(record.state)) { + this.append( + record, + "error", + { code: "server_interrupted", message: "Aiden restarted before this response finished." }, + true, + "interrupted", + ); + } + } + } + + snapshot(): AidenRemoteStreamSnapshot { + return { + version: 1, + streams: [...this.streams.values()].map((stream) => ({ + streamId: stream.streamId, + chatId: stream.chatId, + turnId: stream.turnId, + deviceId: stream.deviceId, + state: stream.state, + updatedAt: stream.updatedAt, + events: structuredClone(stream.events), + })), + }; + } + + private persist(): void { + if (!this.options.persist) return; + this.persistDirty = true; + if (this.persistRunning) return; + this.persistRunning = true; + this.persistTail = (async () => { + while (this.persistDirty) { + this.persistDirty = false; + await this.options.persist!(this.snapshot()); + } + })() + .catch((error: unknown) => this.options.onPersistenceError?.(error)) + .finally(() => { + this.persistRunning = false; + if (this.persistDirty) this.persist(); + }); + } + + async settlePersistence(): Promise { + while (this.persistRunning || this.persistDirty) await this.persistTail; + } + + private prune(): void { + const now = this.options.now(); + for (const [approvalId, approval] of this.approvals) { + if (approval.expiresAt <= now) { + clearTimeout(approval.expiry); + this.options.approve(approvalId, "deny", approval.ownerDocumentId); + this.approvals.delete(approvalId); + } + } + for (const [streamId, stream] of this.streams) { + if (terminal(stream.state) && stream.updatedAt + TERMINAL_RETENTION_MS <= now) { + stream.owner.invalidate(); + for (const subscriber of stream.subscribers) { + clearInterval(subscriber.heartbeat); + subscriber.response.end(); + } + this.streams.delete(streamId); + } + } + } + + private requireStream(deviceId: string, streamId: string): StreamRecord { + this.prune(); + const stream = this.streams.get(streamId); + if (!stream || stream.deviceId !== deviceId) { + throw new AidenRemoteServiceError("not_found", "This Aiden stream is unavailable.", 404); + } + return stream; + } + + create(deviceId: string, streamId: string, chatId: string, turnId: string): RemoteChatGenerationOwnerController { + this.prune(); + if (this.streams.has(streamId)) { + throw new AidenRemoteServiceError("already_exists", "That stream already exists.", 409); + } + if (this.streams.size >= MAX_STREAMS) { + throw new AidenRemoteServiceError("rate_limited", "Too many remote streams are retained.", 429, true); + } + const base: Omit = { + streamId, + chatId, + turnId, + deviceId, + state: "queued", + updatedAt: this.options.now(), + events: [], + eventBytes: 0, + subscribers: new Set(), + cancelRequested: false, + cancellationSource: "device", + activeTools: new Map(), + toolCounter: 0, + }; + const owner = this.ownerFor(base); + const record: StreamRecord = { ...base, owner }; + this.streams.set(streamId, record); + this.append(record, "status", { state: "queued" }, false, "queued"); + return owner; + } + + private append( + stream: StreamRecord, + type: string, + payload: Record, + isTerminal: boolean, + state?: AidenRemoteStreamState, + ): AidenRemoteStreamEvent { + if (terminal(stream.state)) return stream.events[stream.events.length - 1]!; + const event: AidenRemoteStreamEvent = { + protocolVersion: AIDEN_REMOTE_PROTOCOL_VERSION, + streamId: stream.streamId, + sequence: (stream.events[stream.events.length - 1]?.sequence ?? 0) + 1, + timestamp: new Date(this.options.now()).toISOString(), + type, + terminal: isTerminal, + payload, + }; + const bytes = Buffer.byteLength(JSON.stringify(event), "utf8"); + stream.events.push(event); + stream.eventBytes += bytes; + while ( + stream.events.length > MAX_EVENTS_PER_STREAM || + (stream.eventBytes > MAX_STREAM_EVENT_BYTES && stream.events.length > 1) + ) { + const removed = stream.events.shift(); + if (removed) stream.eventBytes -= Buffer.byteLength(JSON.stringify(removed), "utf8"); + } + stream.state = state ?? stream.state; + stream.updatedAt = this.options.now(); + const frame = sseFrame(event); + for (const subscriber of [...stream.subscribers]) { + if (!subscriber.response.write(frame)) { + // Node applies backpressure. The durable in-memory journal remains the + // source of truth, so the client can reconnect from its last event ID. + } + if (isTerminal) { + clearInterval(subscriber.heartbeat); + subscriber.response.end(); + stream.subscribers.delete(subscriber); + } + } + if (isTerminal) { + stream.owner.invalidate(); + for (const [approvalId, approval] of this.approvals) { + if (approval.streamId === stream.streamId) { + clearTimeout(approval.expiry); + this.approvals.delete(approvalId); + } + } + this.options.notifyChatChanged?.(stream.chatId); + } + this.persist(); + return event; + } + + private projectNotification( + stream: StreamRecord, + channel: NotificationChannel, + rawPayload: unknown, + ): void { + const payload = ownRecord(rawPayload) ?? {}; + if (channel === "chat:delta") { + if (payload.reset === true) { + const nextSequence = (stream.events[stream.events.length - 1]?.sequence ?? 0) + 2; + this.append( + stream, + "snapshot", + { chatId: stream.chatId, turnId: stream.turnId, nextSequence }, + false, + "reconciling", + ); + return; + } + this.append(stream, "text_delta", { text: boundedText(payload.delta, 200_000) }, false, "running"); + return; + } + if (channel === "chat:reasoning-delta") { + this.append(stream, "reasoning_delta", { text: boundedText(payload.delta, 200_000) }, false, "running"); + return; + } + if (channel === "chat:status") { + this.append(stream, "status", { state: "running" }, false, "running"); + return; + } + if (channel === "chat:tool") { + const name = boundedText(payload.toolName, 120, "Tool"); + const phase = payload.phase; + if (phase === "call") { + const toolId = `tool_${++stream.toolCounter}`; + const queue = stream.activeTools.get(name) ?? []; + queue.push(toolId); + stream.activeTools.set(name, queue); + this.append(stream, "tool_started", { toolId, name }, false, "running"); + } else { + const queue = stream.activeTools.get(name) ?? []; + const toolId = queue.shift() ?? `tool_${++stream.toolCounter}`; + if (queue.length === 0) stream.activeTools.delete(name); + const status = phase === "result" ? "succeeded" : "failed"; + this.append(stream, "tool_finished", { toolId, status }, false, "running"); + } + return; + } + if (channel === "chat:timeline") { + const timeline = ownRecord(payload.timeline) as GenerationTimeline | null; + const last = timeline?.steps?.[timeline.steps.length - 1]; + const label = last?.kind === "tool" ? last.label : "Thinking"; + this.append(stream, "timeline", { label: boundedText(label, 500, "Activity") }, false, "running"); + return; + } + if (channel === "chat:approval") { + const approvalId = boundedText(payload.approvalId, 128); + if (!approvalId) return; + const previousApproval = this.approvals.get(approvalId); + if (previousApproval) clearTimeout(previousApproval.expiry); + const expiresAt = this.options.now() + APPROVAL_LIFETIME_MS; + const expiry = setTimeout(() => { + const current = this.approvals.get(approvalId); + if (!current || current.expiresAt !== expiresAt) return; + this.options.approve(approvalId, "deny", current.ownerDocumentId); + this.approvals.delete(approvalId); + }, APPROVAL_LIFETIME_MS); + expiry.unref?.(); + this.approvals.set(approvalId, { + streamId: stream.streamId, + deviceId: stream.deviceId, + ownerDocumentId: stream.owner.owner.documentId, + expiresAt, + expiry, + }); + this.append( + stream, + "approval_required", + { + approvalId, + summary: boundedText(payload.summary, 2_000, "Aiden needs approval."), + expiresAt: new Date(expiresAt).toISOString(), + }, + false, + "waiting_for_approval", + ); + return; + } + if (channel === "chat:error") { + this.append( + stream, + "error", + { code: "internal_error", message: boundedText(payload.message, 2_000, "Generation failed.") }, + true, + "error", + ); + return; + } + if (channel === "chat:done") { + if (stream.cancelRequested) { + this.append(stream, "cancelled", { source: stream.cancellationSource }, true, "cancelled"); + return; + } + const chat = ownRecord(payload.chat); + const messages = Array.isArray(chat?.messages) ? chat.messages : []; + const assistant = [...messages].reverse().find((message) => ownRecord(message)?.role === "assistant"); + const messageId = boundedText(ownRecord(assistant)?.id, 128, `assistant_${stream.turnId}`); + this.append(stream, "done", { messageId }, true, "done"); + } + } + + markRunning(deviceId: string, streamId: string): void { + const stream = this.requireStream(deviceId, streamId); + this.append(stream, "status", { state: "running" }, false, "running"); + } + + markStartError(deviceId: string, streamId: string, error: unknown): void { + const stream = this.requireStream(deviceId, streamId); + this.append( + stream, + "error", + { + code: "internal_error", + message: error instanceof Error ? error.message.slice(0, 2_000) : "Generation could not start.", + }, + true, + "error", + ); + } + + status(deviceId: string, streamId: string): AidenRemoteStreamStatus { + const stream = this.requireStream(deviceId, streamId); + return { + streamId: stream.streamId, + chatId: stream.chatId, + turnId: stream.turnId, + state: stream.state, + lastSequence: stream.events[stream.events.length - 1]?.sequence ?? 0, + updatedAt: new Date(stream.updatedAt).toISOString(), + }; + } + + async cancel(deviceId: string, streamId: string, key: string): Promise { + try { + return await this.executeIdempotent( + { deviceId, route: "POST /streams/{id}/cancel", resourceId: streamId, key }, + { streamId }, + async () => { + const stream = this.requireStream(deviceId, streamId); + if (!terminal(stream.state) && !stream.cancelRequested) { + stream.cancelRequested = true; + stream.cancellationSource = "device"; + this.options.cancel(streamId, stream.owner.owner.documentId); + } + return this.status(deviceId, streamId); + }, + ); + } catch (error) { + return this.mapIdempotencyError(error); + } + } + + revokeDevice(deviceId: string): void { + for (const stream of this.streams.values()) { + if (stream.deviceId !== deviceId || terminal(stream.state)) continue; + stream.cancelRequested = true; + stream.cancellationSource = "server"; + this.options.cancel(stream.streamId, stream.owner.owner.documentId); + this.append(stream, "cancelled", { source: "server" }, true, "cancelled"); + } + } + + async respondApproval( + deviceId: string, + approvalId: string, + decision: "allow" | "deny", + key: string, + ): Promise<{ approvalId: string; decision: "allow" | "deny"; resolvedAt: string }> { + try { + return await this.executeIdempotent( + { deviceId, route: "POST /approvals/{id}/respond", resourceId: approvalId, key }, + { approvalId, decision }, + async () => { + this.prune(); + const approval = this.approvals.get(approvalId); + if (!approval || approval.deviceId !== deviceId || approval.expiresAt <= this.options.now()) { + throw new AidenRemoteServiceError("approval_expired", "This approval is no longer available.", 409); + } + if (!this.options.approve(approvalId, decision, approval.ownerDocumentId)) { + clearTimeout(approval.expiry); + this.approvals.delete(approvalId); + throw new AidenRemoteServiceError("approval_already_resolved", "This approval was already resolved.", 409); + } + clearTimeout(approval.expiry); + this.approvals.delete(approvalId); + const stream = this.requireStream(deviceId, approval.streamId); + if (!terminal(stream.state)) { + this.append(stream, "status", { state: "running" }, false, "running"); + } + return { approvalId, decision, resolvedAt: new Date(this.options.now()).toISOString() }; + }, + ); + } catch (error) { + return this.mapIdempotencyError(error); + } + } + + openEvents(deviceId: string, streamId: string, after: number, response: ServerResponse): void { + const stream = this.requireStream(deviceId, streamId); + const lastSequence = stream.events[stream.events.length - 1]?.sequence ?? 0; + if (after > lastSequence) { + throw new AidenRemoteServiceError("invalid_request", "The stream cursor is ahead of Aiden.", 400); + } + const earliest = stream.events[0]?.sequence ?? 1; + if (after < earliest - 1) { + const snapshot = this.append( + stream, + "snapshot", + { + chatId: stream.chatId, + turnId: stream.turnId, + nextSequence: (stream.events[stream.events.length - 1]?.sequence ?? 0) + 2, + }, + false, + ); + after = snapshot.sequence - 1; + } + response.writeHead(200, { + "aiden-protocol-version": String(AIDEN_REMOTE_PROTOCOL_VERSION), + "cache-control": "no-store", + connection: "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "x-accel-buffering": "no", + "x-content-type-options": "nosniff", + }); + for (const event of stream.events) { + if (event.sequence > after) response.write(sseFrame(event)); + } + if (terminal(stream.state)) { + response.end(); + return; + } + const subscriber: StreamSubscriber = { + response, + heartbeat: setInterval(() => response.write(": heartbeat\n\n"), 15_000), + }; + subscriber.heartbeat.unref?.(); + stream.subscribers.add(subscriber); + response.once("close", () => { + clearInterval(subscriber.heartbeat); + stream.subscribers.delete(subscriber); + }); + } +} diff --git a/main/services/aiden-remote-tailscale-route.test.ts b/main/services/aiden-remote-tailscale-route.test.ts new file mode 100644 index 00000000..95eac1dd --- /dev/null +++ b/main/services/aiden-remote-tailscale-route.test.ts @@ -0,0 +1,142 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { planAidenTailscaleConnect, planAidenTailscaleDisconnect, type AidenTailscaleStatus } from "./aiden-remote-tailscale-route.js"; + +const target = "http://127.0.0.1:43177/api/aiden/v1"; +const status = (proxy?: string, funnel = false): AidenTailscaleStatus => ({ + TCP: { "443": { HTTPS: true } }, + Web: { "aiden-device.example.ts.net:443": { Funnel: funnel, Handlers: { "/other": { Proxy: "http://127.0.0.1:9" }, ...(proxy ? { "/api/aiden/v1": { Proxy: proxy } } : {}) } } }, +}); + +test("Tailscale planning owns only the exact Aiden path and never resets or changes Funnel", () => { + const before = status(); + const unrelated = JSON.stringify(before); + const connect = planAidenTailscaleConnect(before, target); + assert.deepEqual(connect, { action: "set", args: ["serve", "--https=443", "--set-path=/api/aiden/v1", target], ownership: { path: "/api/aiden/v1", target } }); + assert.equal(JSON.stringify(before), unrelated); + assert.equal(planAidenTailscaleConnect(status(target), target, connect.ownership).action, "noop"); + assert.deepEqual(planAidenTailscaleDisconnect(status(target), target, connect.ownership), { action: "clear", args: ["serve", "--https=443", "--set-path=/api/aiden/v1", "off"] }); + assert.equal(planAidenTailscaleDisconnect(status(), target).action, "noop"); + assert.throws(() => planAidenTailscaleConnect(status(target), target), /tailscale_route_conflict/); + assert.throws(() => planAidenTailscaleDisconnect(status(target), target), /tailscale_route_conflict/); + assert.throws(() => planAidenTailscaleConnect(status("http://127.0.0.1:2/api/aiden/v1"), target), /tailscale_route_conflict/); + assert.throws(() => planAidenTailscaleDisconnect(status("http://127.0.0.1:2/api/aiden/v1"), target), /tailscale_route_conflict/); + assert.throws(() => planAidenTailscaleConnect(status(undefined, true), target), /tailscale_funnel_conflict/); + assert.deepEqual( + planAidenTailscaleDisconnect(status(target, true), target, connect.ownership), + { action: "clear", args: ["serve", "--https=443", "--set-path=/api/aiden/v1", "off"] }, + ); + for (const unsafeTarget of [ + "https://127.0.0.1:43177", + "http://127.0.0.2:43177", + "http://0.0.0.0:43177", + "http://127.0.0.1:43177/private", + "http://127.0.0.1:43177/api/aiden/v1/private", + "http://user:secret@127.0.0.1:43177", + "http://aiden.example.test:43177", + ]) { + assert.throws(() => planAidenTailscaleConnect(status(), unsafeTarget), /tailscale_target_invalid/); + assert.throws(() => planAidenTailscaleDisconnect(status(), unsafeTarget), /tailscale_target_invalid/); + } + assert.doesNotThrow(() => planAidenTailscaleConnect(status(), "http://[::1]:43177/api/aiden/v1")); + assert.doesNotThrow(() => planAidenTailscaleConnect(status(), "http://localhost:43177/api/aiden/v1")); +}); + +test("Tailscale permits origin-only legacy targets for exact owned cleanup but never for connect", () => { + const legacyTarget = "http://127.0.0.1:43177"; + const ownership = { path: "/api/aiden/v1" as const, target: legacyTarget }; + assert.throws( + () => planAidenTailscaleConnect(status(), legacyTarget), + /tailscale_target_invalid/, + ); + assert.deepEqual( + planAidenTailscaleDisconnect(status(legacyTarget), legacyTarget, ownership), + { action: "clear", args: ["serve", "--https=443", "--set-path=/api/aiden/v1", "off"] }, + ); +}); + +test("Tailscale rejects raw and encoded path/query/fragment aliases before URL normalization", () => { + for (const unsafeTarget of [ + `${target}/./`, + `${target}/../`, + `${target}/a/../`, + `${target}/%2e/`, + `${target}/%2e%2e/`, + `${target}/%2E%2E/`, + `${target}//`, + `${target}?`, + `${target}#`, + `${target}/?`, + `${target}/#`, + `${target}?probe=1`, + `${target}#probe`, + "http://127.0.0.1:0/api/aiden/v1", + "http://127.0.0.1:00001/api/aiden/v1", + "http://127.0.0.1:65536/api/aiden/v1", + ]) { + assert.throws(() => planAidenTailscaleConnect(status(), unsafeTarget), /tailscale_target_invalid/); + assert.throws(() => planAidenTailscaleDisconnect(status(), unsafeTarget), /tailscale_target_invalid/); + } +}); + +test("Tailscale connect requires explicit TCP HTTPS capability while owned disconnect remains safe", () => { + const withoutCapability: AidenTailscaleStatus = { Web: status(target).Web }; + const disabledCapability: AidenTailscaleStatus = { ...status(target), TCP: { "443": { HTTPS: false } } }; + for (const unavailable of [withoutCapability, disabledCapability]) { + assert.throws(() => planAidenTailscaleConnect(unavailable, target), /tailscale_https_unavailable/); + assert.throws(() => planAidenTailscaleConnect(unavailable, target, { path: "/api/aiden/v1", target }), /tailscale_https_unavailable/); + assert.deepEqual( + planAidenTailscaleDisconnect(unavailable, target, { path: "/api/aiden/v1", target }), + { action: "clear", args: ["serve", "--https=443", "--set-path=/api/aiden/v1", "off"] }, + ); + } + assert.deepEqual( + planAidenTailscaleConnect({}, target, undefined, true), + { + action: "set", + args: ["serve", "--https=443", "--set-path=/api/aiden/v1", target], + ownership: { path: "/api/aiden/v1", target }, + }, + ); + assert.throws( + () => planAidenTailscaleConnect(disabledCapability, target, undefined, true), + /tailscale_https_unavailable/, + ); +}); + +test("Tailscale rejects malformed and noncanonical Web listener authorities", () => { + for (const authority of [ + "aiden-device.example.ts.net:0443", + "aiden-device.example.ts.net:+443", + "aiden-device.example.ts.net:443 ", + "aiden-device.example.ts.net", + "aiden-device.example.ts.net:not-a-port", + "aiden-device.example.ts.net:0", + "[aiden-device.example.ts.net]:443", + ]) { + const malformedStatus: AidenTailscaleStatus = { + TCP: { "443": { HTTPS: true } }, + Web: { [authority]: { Handlers: {} } }, + }; + assert.throws(() => planAidenTailscaleConnect(malformedStatus, target), /tailscale_route_conflict/); + assert.throws( + () => planAidenTailscaleDisconnect(malformedStatus, target, { path: "/api/aiden/v1", target }), + /tailscale_route_conflict/, + ); + } +}); + +test("Tailscale rejects multiple canonical HTTPS listeners instead of selecting one", () => { + const multipleListeners: AidenTailscaleStatus = { + TCP: { "443": { HTTPS: true } }, + Web: { + "aiden-device.example.ts.net:443": { Handlers: {} }, + "other-device.example.ts.net:443": { Handlers: {} }, + }, + }; + assert.throws(() => planAidenTailscaleConnect(multipleListeners, target), /tailscale_route_conflict/); + assert.throws( + () => planAidenTailscaleDisconnect(multipleListeners, target, { path: "/api/aiden/v1", target }), + /tailscale_route_conflict/, + ); +}); diff --git a/main/services/aiden-remote-tailscale-route.ts b/main/services/aiden-remote-tailscale-route.ts new file mode 100644 index 00000000..7ce4c4c0 --- /dev/null +++ b/main/services/aiden-remote-tailscale-route.ts @@ -0,0 +1,146 @@ +import { isIP } from "node:net"; +import { AIDEN_REMOTE_BASE_PATH } from "./aiden-remote-protocol.js"; + +export interface AidenTailscaleHandler { Proxy: string } +export interface AidenTailscaleStatus { TCP?: Record; Web?: Record; Funnel?: boolean }> } + +// The public Serve prefix is the canonical API base. Tailscale strips this +// prefix before proxying to the loopback listener, while the client retains +// the one OpenAPI-approved `/api/aiden/v1` endpoint spelling. +export const AIDEN_TAILSCALE_PATH = AIDEN_REMOTE_BASE_PATH; +export interface AidenTailscaleOwnership { path: typeof AIDEN_TAILSCALE_PATH; target: string } + +const CANONICAL_LOOPBACK_HTTP_TARGET = new RegExp( + `^http://(?:localhost|127\\.0\\.0\\.1|\\[::1\\]):([1-9]\\d{0,4})${AIDEN_REMOTE_BASE_PATH}$`, +); +const LEGACY_LOOPBACK_HTTP_ORIGIN = /^http:\/\/(?:localhost|127\.0\.0\.1|\[::1\]):([1-9]\d{0,4})\/?$/; + +interface ParsedTailscaleAuthority { + port: number; + canonicalPort: boolean; +} + +function hasUnsafeAuthorityCharacter(value: string): boolean { + for (const character of value) { + const codePoint = character.codePointAt(0); + if (codePoint === undefined || codePoint <= 0x20 || codePoint === 0x7f || "[]/?#@\\%".includes(character)) return true; + } + return false; +} + +/** + * Tailscale serializes Web keys from net.JoinHostPort as an SNI host and a + * decimal port. Do not use URL parsing here: it accepts/canonicalizes aliases + * that are not the exact keys Tailscale reports. + */ +function parseTailscaleAuthority(authority: string): ParsedTailscaleAuthority | undefined { + let host: string; + let rawPort: string; + if (authority.startsWith("[")) { + const closingBracket = authority.indexOf("]"); + if ( + closingBracket <= 1 || + authority[closingBracket + 1] !== ":" || + authority.indexOf("[", 1) !== -1 || + authority.indexOf("]", closingBracket + 1) !== -1 + ) { + return undefined; + } + host = authority.slice(1, closingBracket); + rawPort = authority.slice(closingBracket + 2); + if (isIP(host) !== 6) return undefined; + } else { + const colon = authority.lastIndexOf(":"); + if (colon <= 0 || authority.indexOf(":") !== colon) return undefined; + host = authority.slice(0, colon); + rawPort = authority.slice(colon + 1); + } + if (!host || hasUnsafeAuthorityCharacter(host) || !/^\d{1,5}$/u.test(rawPort)) return undefined; + + const port = Number(rawPort); + if (!Number.isInteger(port) || port < 1 || port > 65_535) return undefined; + return { port, canonicalPort: rawPort === String(port) }; +} + +function httpsEndpoint(status: AidenTailscaleStatus): { handlers: Record; funnel: boolean } { + const entries: Array<[string, { Handlers?: Record; Funnel?: boolean }]> = []; + for (const [authority, listener] of Object.entries(status.Web ?? {})) { + const parsed = parseTailscaleAuthority(authority); + if (!parsed || !parsed.canonicalPort) throw new Error("tailscale_route_conflict"); + if (parsed.port === 443) entries.push([authority, listener]); + } + if (entries.length > 1) throw new Error("tailscale_route_conflict"); + return { + handlers: entries[0]?.[1].Handlers ?? {}, + funnel: entries[0]?.[1].Funnel === true, + }; +} + +function assertHttpsCapability(status: AidenTailscaleStatus, nodeHttpsAvailable: boolean): void { + const configuredListener = status.TCP?.["443"]; + if (configuredListener !== undefined && configuredListener?.HTTPS !== true) { + throw new Error("tailscale_https_unavailable"); + } + if (configuredListener?.HTTPS !== true && !nodeHttpsAvailable) { + throw new Error("tailscale_https_unavailable"); + } +} + +function assertServerOwnedLoopbackTarget(target: string, allowLegacyOrigin = false): void { + // Tailscale strips the mounted public prefix before reverse proxying. The + // target restores exactly that canonical API base so the shared LAN and + // loopback router receive identical paths. Validate the wire form before + // WHATWG URL normalization; arbitrary local paths remain forbidden. + const canonicalTarget = CANONICAL_LOOPBACK_HTTP_TARGET.exec(target); + const legacyTarget = allowLegacyOrigin ? LEGACY_LOOPBACK_HTTP_ORIGIN.exec(target) : null; + const rawTarget = canonicalTarget ?? legacyTarget; + const rawPort = Number(rawTarget?.[1]); + if (!rawTarget || !Number.isInteger(rawPort) || rawPort > 65_535) { + throw new Error("tailscale_target_invalid"); + } + let endpoint: URL; + try { + endpoint = new URL(target); + } catch { + throw new Error("tailscale_target_invalid"); + } + if ( + endpoint.protocol !== "http:" || + endpoint.username || + endpoint.password || + endpoint.pathname !== (canonicalTarget ? AIDEN_REMOTE_BASE_PATH : "/") || + endpoint.search || + endpoint.hash || + !endpoint.port || + !["localhost", "127.0.0.1", "[::1]"].includes(endpoint.hostname) + ) { + throw new Error("tailscale_target_invalid"); + } +} + +export function planAidenTailscaleConnect( + status: AidenTailscaleStatus, + target: string, + ownership?: AidenTailscaleOwnership, + nodeHttpsAvailable = false, +): { action: "set" | "noop"; args?: string[]; ownership: AidenTailscaleOwnership } { + assertHttpsCapability(status, nodeHttpsAvailable); + assertServerOwnedLoopbackTarget(target); + const endpoint = httpsEndpoint(status); + if (endpoint.funnel) throw new Error("tailscale_funnel_conflict"); + const handler = endpoint.handlers[AIDEN_TAILSCALE_PATH]; + const nextOwnership = { path: AIDEN_TAILSCALE_PATH, target } as const; + if (!handler) return { action: "set", args: ["serve", "--https=443", `--set-path=${AIDEN_TAILSCALE_PATH}`, target], ownership: nextOwnership }; + if (handler.Proxy === target && ownership?.path === AIDEN_TAILSCALE_PATH && ownership.target === target) return { action: "noop", ownership }; + throw new Error("tailscale_route_conflict"); +} + +export function planAidenTailscaleDisconnect(status: AidenTailscaleStatus, target: string, ownership?: AidenTailscaleOwnership): { action: "clear" | "noop"; args?: string[] } { + // Origin-only targets were persisted by pre-acceptance builds. They may be + // recognized only for exact owned cleanup, never for a new connection. + assertServerOwnedLoopbackTarget(target, true); + const handler = httpsEndpoint(status).handlers[AIDEN_TAILSCALE_PATH]; + if (!handler) return { action: "noop" }; + if (handler.Proxy !== target || ownership?.path !== AIDEN_TAILSCALE_PATH || ownership.target !== target) throw new Error("tailscale_route_conflict"); + return { action: "clear", args: ["serve", "--https=443", `--set-path=${AIDEN_TAILSCALE_PATH}`, "off"] }; +} diff --git a/main/services/aiden-remote-tailscale.test.ts b/main/services/aiden-remote-tailscale.test.ts new file mode 100644 index 00000000..68d56701 --- /dev/null +++ b/main/services/aiden-remote-tailscale.test.ts @@ -0,0 +1,101 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { + AidenRemoteTailscaleController, + type AidenTailscaleCommandRunner, +} from "./aiden-remote-tailscale.js"; + +const target = "http://127.0.0.1:43177/api/aiden/v1"; + +function fixture(options: { emptyServeStatus?: boolean; certDomains?: unknown } = {}) { + const calls: string[][] = []; + let connected = false; + const runner: AidenTailscaleCommandRunner = { + run: async (args) => { + calls.push([...args]); + if (args[0] === "status") { + return JSON.stringify({ + Self: { DNSName: "aiden.tailnet.ts.net." }, + CertDomains: options.certDomains ?? ["aiden.tailnet.ts.net"], + }); + } + if (args[0] === "serve" && args[1] === "status") { + if (options.emptyServeStatus && !connected) return "{}"; + return JSON.stringify({ + TCP: { "443": { HTTPS: true } }, + Web: { + "aiden.tailnet.ts.net:443": { + Handlers: connected + ? { "/api/aiden/v1": { Proxy: target }, "/other": { Proxy: "http://127.0.0.1:9" } } + : { "/other": { Proxy: "http://127.0.0.1:9" } }, + }, + }, + }); + } + if (args.includes("off")) connected = false; + else connected = true; + return ""; + }, + }; + return { controller: new AidenRemoteTailscaleController(runner), calls }; +} + +test("Tailscale controller connects and verifies only Aiden's route", async () => { + const app = fixture(); + const ownership = await app.controller.connect(target); + assert.deepEqual(ownership, { path: "/api/aiden/v1", target }); + assert.deepEqual(app.calls.find((args) => args.includes("--set-path=/api/aiden/v1") && !args.includes("off")), [ + "serve", "--yes", "--bg", "--https=443", "--set-path=/api/aiden/v1", target, + ]); + assert.equal(app.calls.some((args) => args.includes("reset")), false); + assert.equal(app.calls.some((args) => args.includes("funnel")), false); + + await app.controller.disconnect(target, ownership); + assert.deepEqual(app.calls[app.calls.length - 2], [ + "serve", "--https=443", "--set-path=/api/aiden/v1", "off", + ]); +}); + +test("Tailscale controller reports stable URL identity without mutating configuration", async () => { + const app = fixture(); + assert.deepEqual(await app.controller.status(), { + installed: true, + dnsName: "aiden.tailnet.ts.net", + httpsAvailable: true, + serveStatus: { + TCP: { "443": { HTTPS: true } }, + Web: { + "aiden.tailnet.ts.net:443": { + Handlers: { "/other": { Proxy: "http://127.0.0.1:9" } }, + }, + }, + }, + }); + assert.equal(app.calls.length, 2); +}); + +test("Tailscale controller creates the first HTTPS listener only after exact certificate-domain proof", async () => { + const app = fixture({ emptyServeStatus: true }); + const ownership = await app.controller.connect(target); + assert.deepEqual(ownership, { path: "/api/aiden/v1", target }); + assert.deepEqual(app.calls.find((args) => args.includes("--set-path=/api/aiden/v1") && !args.includes("off")), [ + "serve", "--yes", "--bg", "--https=443", "--set-path=/api/aiden/v1", target, + ]); +}); + +test("Tailscale controller refuses first-listener mutation without exact HTTPS eligibility", async () => { + for (const certDomains of [[], ["other.tailnet.ts.net"], ["aiden.tailnet.ts.net.evil"]]) { + const app = fixture({ emptyServeStatus: true, certDomains }); + await assert.rejects(app.controller.connect(target), /tailscale_https_unavailable/u); + assert.equal(app.calls.some((args) => args.includes("--set-path=/api/aiden/v1")), false); + } +}); + +test("missing Tailscale is explicit and cannot mutate routes", async () => { + const controller = new AidenRemoteTailscaleController(null); + assert.deepEqual(await controller.status(), { + installed: false, + errorCode: "not_installed", + }); + await assert.rejects(controller.connect(target), /tailscale_not_installed/u); +}); diff --git a/main/services/aiden-remote-tailscale.ts b/main/services/aiden-remote-tailscale.ts new file mode 100644 index 00000000..281378f5 --- /dev/null +++ b/main/services/aiden-remote-tailscale.ts @@ -0,0 +1,188 @@ +import { execFile } from "node:child_process"; +import * as fs from "node:fs/promises"; +import { promisify } from "node:util"; +import { parseAidenRemoteJson } from "./aiden-remote-protocol.js"; +import { + planAidenTailscaleConnect, + planAidenTailscaleDisconnect, + type AidenTailscaleOwnership, + type AidenTailscaleStatus, +} from "./aiden-remote-tailscale-route.js"; + +const execFileAsync = promisify(execFile); +const TAILSCALE_CANDIDATES = [ + "/Applications/Tailscale.app/Contents/MacOS/Tailscale", + "/usr/local/bin/tailscale", + "/opt/homebrew/bin/tailscale", +] as const; +const MAX_STATUS_BYTES = 256 * 1_024; + +export interface AidenTailscaleCommandRunner { + run(args: readonly string[]): Promise; +} + +export interface AidenTailscaleConnectionStatus { + installed: boolean; + dnsName?: string; + httpsAvailable?: boolean; + serveStatus?: AidenTailscaleStatus; + errorCode?: "not_installed" | "not_connected" | "https_unavailable" | "status_unavailable"; +} + +interface AidenTailscaleNodeStatus { + dnsName?: string; + httpsAvailable: boolean; +} + +function record(value: unknown): Record | null { + return value !== null && typeof value === "object" && !Array.isArray(value) + ? value as Record + : null; +} + +function parseBoundedJson(serialized: string, label: string): unknown { + if (Buffer.byteLength(serialized, "utf8") > MAX_STATUS_BYTES) { + throw new Error(`${label}_too_large`); + } + return parseAidenRemoteJson(serialized, label); +} + +function normalizeDnsName(value: unknown): string | undefined { + if (typeof value !== "string") return undefined; + const dnsName = value.trim().replace(/\.$/u, "").toLowerCase(); + return /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/u.test(dnsName) + ? dnsName + : undefined; +} + +function parseNodeStatus(serialized: string): AidenTailscaleNodeStatus { + const root = record(parseBoundedJson(serialized, "Tailscale status")); + const self = record(root?.Self); + const dnsName = normalizeDnsName(self?.DNSName); + const certDomains = Array.isArray(root?.CertDomains) + ? root.CertDomains.map(normalizeDnsName).filter((value): value is string => value !== undefined) + : []; + return { + ...(dnsName ? { dnsName } : {}), + // An exact certificate-domain match proves that the tailnet owner has + // already enabled HTTPS. Aiden never follows or accepts Tailscale's + // interactive authorization flow on the owner's behalf. + httpsAvailable: dnsName !== undefined && certDomains.includes(dnsName), + }; +} + +function parseServeStatus(serialized: string): AidenTailscaleStatus { + const value = parseBoundedJson(serialized, "Tailscale Serve status"); + if (!record(value)) throw new Error("tailscale_status_invalid"); + return value as AidenTailscaleStatus; +} + +export async function resolveTailscaleBinary(): Promise { + for (const candidate of TAILSCALE_CANDIDATES) { + try { + await fs.access(candidate, fs.constants.X_OK); + return candidate; + } catch { + // Continue through fixed, trusted installation locations. + } + } + return null; +} + +export async function createSystemTailscaleCommandRunner(): Promise { + const binary = await resolveTailscaleBinary(); + if (!binary) return null; + return { + run: async (args) => { + const { stdout } = await execFileAsync(binary, [...args], { + encoding: "utf8", + maxBuffer: MAX_STATUS_BYTES, + timeout: 15_000, + windowsHide: true, + }); + return stdout; + }, + }; +} + +export class AidenRemoteTailscaleController { + constructor(private readonly runner: AidenTailscaleCommandRunner | null) {} + + private async serveStatus(): Promise { + if (!this.runner) throw new Error("tailscale_not_installed"); + return parseServeStatus(await this.runner.run(["serve", "status", "--json"])); + } + + private async nodeStatus(): Promise { + if (!this.runner) throw new Error("tailscale_not_installed"); + return parseNodeStatus(await this.runner.run(["status", "--json"])); + } + + async status(): Promise { + if (!this.runner) return { installed: false, errorCode: "not_installed" }; + try { + const [nodeStatus, serveStatus] = await Promise.all([ + this.nodeStatus(), + this.serveStatus(), + ]); + const errorCode = !nodeStatus.dnsName + ? "not_connected" as const + : !nodeStatus.httpsAvailable + ? "https_unavailable" as const + : undefined; + return { + installed: true, + ...(nodeStatus.dnsName ? { dnsName: nodeStatus.dnsName } : {}), + httpsAvailable: nodeStatus.httpsAvailable, + ...(errorCode ? { errorCode } : {}), + serveStatus, + }; + } catch { + return { installed: true, errorCode: "status_unavailable" }; + } + } + + async connect( + target: string, + ownership?: AidenTailscaleOwnership, + ): Promise { + if (!this.runner) throw new Error("tailscale_not_installed"); + const [nodeStatus, serveStatus] = await Promise.all([ + this.nodeStatus(), + this.serveStatus(), + ]); + if (!nodeStatus.dnsName) throw new Error("tailscale_not_connected"); + const plan = planAidenTailscaleConnect( + serveStatus, + target, + ownership, + nodeStatus.httpsAvailable, + ); + if (plan.action === "set") { + const [command, ...args] = plan.args!; + await this.runner.run([command!, "--yes", "--bg", ...args]); + } + const verified = planAidenTailscaleConnect( + await this.serveStatus(), + target, + plan.ownership, + ); + if (verified.action !== "noop") throw new Error("tailscale_route_verification_failed"); + return plan.ownership; + } + + async disconnect( + target: string, + ownership?: AidenTailscaleOwnership, + ): Promise { + if (!this.runner) throw new Error("tailscale_not_installed"); + const plan = planAidenTailscaleDisconnect(await this.serveStatus(), target, ownership); + if (plan.action === "clear") await this.runner.run(plan.args!); + const verified = planAidenTailscaleDisconnect( + await this.serveStatus(), + target, + ownership, + ); + if (verified.action !== "noop") throw new Error("tailscale_route_verification_failed"); + } +} diff --git a/main/services/aiden-remote-tls-identity.test.ts b/main/services/aiden-remote-tls-identity.test.ts new file mode 100644 index 00000000..f18607d7 --- /dev/null +++ b/main/services/aiden-remote-tls-identity.test.ts @@ -0,0 +1,69 @@ +import assert from "node:assert/strict"; +import { X509Certificate } from "node:crypto"; +import * as fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { loadOrCreateAidenRemoteTlsIdentity } from "./aiden-remote-tls-identity.js"; + +async function temporaryDirectory(): Promise { + return fs.mkdtemp(path.join(os.tmpdir(), "aiden-remote-tls-")); +} + +test("TLS identity persists one P-256 server key and renews without changing its pin", async () => { + const directory = await temporaryDirectory(); + try { + const first = await loadOrCreateAidenRemoteTlsIdentity({ + directory, + hostnames: ["Aiden-Studio"], + }); + const restarted = await loadOrCreateAidenRemoteTlsIdentity({ directory }); + const renewed = await loadOrCreateAidenRemoteTlsIdentity({ + directory, + renewBeforeMs: 20 * 365 * 24 * 60 * 60 * 1_000, + }); + + assert.equal(restarted.serverSpkiSha256, first.serverSpkiSha256); + assert.equal(renewed.serverSpkiSha256, first.serverSpkiSha256); + assert.equal(renewed.privateKey, first.privateKey); + const certificate = new X509Certificate(first.certificate); + assert.equal(certificate.checkHost("aiden-studio"), "aiden-studio"); + assert.equal(certificate.checkHost("aiden-studio.local"), "aiden-studio.local"); + assert.equal(certificate.checkIP("127.0.0.1"), "127.0.0.1"); + assert.equal(certificate.publicKey.asymmetricKeyType, "ec"); + assert.match(first.serverSpkiSha256, /^sha256\/[A-Za-z0-9+/]{43}=$/u); + } finally { + await fs.rm(directory, { force: true, recursive: true }); + } +}); + +test("TLS identity directory and every persisted identity file are owner-only", async () => { + const directory = await temporaryDirectory(); + try { + await loadOrCreateAidenRemoteTlsIdentity({ directory }); + assert.equal((await fs.stat(directory)).mode & 0o777, 0o700); + for (const name of [ + "ca-key.pem", + "ca-certificate.pem", + "server-key.pem", + "server-certificate.pem", + ]) { + assert.equal((await fs.stat(path.join(directory, name))).mode & 0o777, 0o600); + } + } finally { + await fs.rm(directory, { force: true, recursive: true }); + } +}); + +test("TLS identity fails closed instead of silently rotating an incomplete identity", async () => { + const directory = await temporaryDirectory(); + try { + await fs.writeFile(path.join(directory, "server-key.pem"), "partial", { mode: 0o600 }); + await assert.rejects( + loadOrCreateAidenRemoteTlsIdentity({ directory }), + /server identity is incomplete/u, + ); + } finally { + await fs.rm(directory, { force: true, recursive: true }); + } +}); diff --git a/main/services/aiden-remote-tls-identity.ts b/main/services/aiden-remote-tls-identity.ts new file mode 100644 index 00000000..06bcd687 --- /dev/null +++ b/main/services/aiden-remote-tls-identity.ts @@ -0,0 +1,319 @@ +import { execFile } from "node:child_process"; +import { + createHash, + createPublicKey, + randomBytes, + X509Certificate, +} from "node:crypto"; +import * as fs from "node:fs/promises"; +import path from "node:path"; +import tls from "node:tls"; +import { promisify } from "node:util"; + +const execFileAsync = promisify(execFile); +const DEFAULT_OPENSSL_PATH = "/usr/bin/openssl"; +const RENEW_BEFORE_MS = 7 * 24 * 60 * 60 * 1_000; +const CERTIFICATE_DAYS = 90; +const CA_CERTIFICATE_DAYS = 3_650; + +export interface AidenRemoteTlsIdentity { + privateKey: string; + certificate: string; + certificateChain: string; + caCertificate: string; + serverSpkiSha256: string; + validUntil: string; +} + +export interface AidenRemoteTlsIdentityOptions { + directory: string; + hostnames?: string[]; + now?: Date; + renewBeforeMs?: number; + opensslPath?: string; +} + +interface IdentityPaths { + caKey: string; + caCertificate: string; + serverKey: string; + serverCertificate: string; +} + +function identityPaths(directory: string): IdentityPaths { + return { + caKey: path.join(directory, "ca-key.pem"), + caCertificate: path.join(directory, "ca-certificate.pem"), + serverKey: path.join(directory, "server-key.pem"), + serverCertificate: path.join(directory, "server-certificate.pem"), + }; +} + +async function exists(target: string): Promise { + try { + await fs.access(target); + return true; + } catch { + return false; + } +} + +function validatedHostnames(values: string[]): string[] { + const result = new Set(["localhost"]); + for (const raw of values) { + const value = raw.trim().replace(/\.$/u, "").toLowerCase(); + if ( + value.length === 0 || + value.length > 253 || + !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/u.test(value) || + value.split(".").some((label) => label.length === 0 || label.length > 63) + ) { + continue; + } + result.add(value); + if (!value.includes(".")) result.add(`${value}.local`); + } + return [...result]; +} + +function spkiDigest(value: string | Buffer): string { + const certificate = new X509Certificate(value); + const spki = certificate.publicKey.export({ type: "spki", format: "der" }); + return `sha256/${createHash("sha256").update(spki).digest("base64")}`; +} + +export async function fetchTlsServerSpkiSha256( + hostname: string, + port = 443, +): Promise { + if ( + !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/u.test(hostname) || + !Number.isInteger(port) || + port < 1 || + port > 65_535 + ) { + throw new Error("Aiden Remote TLS endpoint is invalid."); + } + return new Promise((resolve, reject) => { + const socket = tls.connect({ + host: hostname, + port, + servername: hostname, + rejectUnauthorized: true, + }); + const timeout = setTimeout(() => { + socket.destroy(new Error("Aiden Remote TLS endpoint timed out.")); + }, 5_000); + socket.once("secureConnect", () => { + try { + const certificate = socket.getPeerCertificate(true); + if (!certificate.raw?.length) throw new Error("Aiden Remote TLS endpoint has no certificate."); + resolve(spkiDigest(certificate.raw)); + } catch (error) { + reject(error); + } finally { + clearTimeout(timeout); + socket.end(); + } + }); + socket.once("error", (error) => { + clearTimeout(timeout); + reject(error); + }); + }); +} + +function keySpkiDigest(privateKey: string): string { + const spki = createPublicKey(privateKey).export({ type: "spki", format: "der" }); + return `sha256/${createHash("sha256").update(spki).digest("base64")}`; +} + +function certificateNeedsRenewal( + certificatePem: string, + now: Date, + renewBeforeMs: number, +): boolean { + const certificate = new X509Certificate(certificatePem); + const validFrom = new Date(certificate.validFrom).getTime(); + const validTo = new Date(certificate.validTo).getTime(); + const current = now.getTime(); + return current < validFrom || validTo - current <= renewBeforeMs; +} + +async function secureFile(target: string): Promise { + await fs.chmod(target, 0o600); +} + +async function assertPairCompleteness( + first: string, + second: string, + label: string, +): Promise { + const [hasFirst, hasSecond] = await Promise.all([exists(first), exists(second)]); + if (hasFirst !== hasSecond) { + throw new Error(`Aiden Remote ${label} identity is incomplete; refusing to rotate it implicitly.`); + } + return hasFirst; +} + +async function createCertificateAuthority( + paths: IdentityPaths, + opensslPath: string, +): Promise { + await execFileAsync(opensslPath, [ + "ecparam", "-name", "prime256v1", "-genkey", "-noout", "-out", paths.caKey, + ]); + await secureFile(paths.caKey); + await issueCertificateAuthorityCertificate(paths, opensslPath); +} + +async function issueCertificateAuthorityCertificate( + paths: IdentityPaths, + opensslPath: string, +): Promise { + await execFileAsync(opensslPath, [ + "req", "-x509", "-new", "-key", paths.caKey, "-sha256", + "-days", String(CA_CERTIFICATE_DAYS), + "-subj", "/CN=Aiden Agent Local CA", + "-addext", "basicConstraints=critical,CA:TRUE,pathlen:0", + "-addext", "keyUsage=critical,keyCertSign,cRLSign", + "-out", paths.caCertificate, + ]); + await secureFile(paths.caCertificate); +} + +async function createServerKey(paths: IdentityPaths, opensslPath: string): Promise { + await execFileAsync(opensslPath, [ + "ecparam", "-name", "prime256v1", "-genkey", "-noout", "-out", paths.serverKey, + ]); + await secureFile(paths.serverKey); +} + +async function issueServerCertificate( + directory: string, + paths: IdentityPaths, + opensslPath: string, + hostnames: string[], +): Promise { + const suffix = randomBytes(12).toString("hex"); + const requestPath = path.join(directory, `.server-${suffix}.csr.pem`); + const certificatePath = path.join(directory, `.server-${suffix}.certificate.pem`); + const extensionsPath = path.join(directory, `.server-${suffix}.extensions.cnf`); + const subjectAltName = [ + "IP:127.0.0.1", + "IP:::1", + ...hostnames.map((hostname) => `DNS:${hostname}`), + ].join(","); + try { + await fs.writeFile( + extensionsPath, + [ + "[server_ext]", + `subjectAltName=${subjectAltName}`, + "basicConstraints=critical,CA:FALSE", + "keyUsage=critical,digitalSignature", + "extendedKeyUsage=serverAuth", + "", + ].join("\n"), + { encoding: "utf8", mode: 0o600 }, + ); + await execFileAsync(opensslPath, [ + "req", "-new", "-key", paths.serverKey, + "-subj", "/CN=Aiden Agent Local", + "-out", requestPath, + ]); + await execFileAsync(opensslPath, [ + "x509", "-req", "-in", requestPath, + "-CA", paths.caCertificate, "-CAkey", paths.caKey, + "-set_serial", `0x${randomBytes(16).toString("hex")}`, + "-days", String(CERTIFICATE_DAYS), "-sha256", + "-extfile", extensionsPath, "-extensions", "server_ext", + "-out", certificatePath, + ]); + await secureFile(certificatePath); + await fs.rename(certificatePath, paths.serverCertificate); + await secureFile(paths.serverCertificate); + } finally { + await Promise.all([ + fs.rm(requestPath, { force: true }), + fs.rm(certificatePath, { force: true }), + fs.rm(extensionsPath, { force: true }), + ]); + } +} + +export async function loadOrCreateAidenRemoteTlsIdentity( + options: AidenRemoteTlsIdentityOptions, +): Promise { + const opensslPath = options.opensslPath ?? DEFAULT_OPENSSL_PATH; + const now = options.now ?? new Date(); + const renewBeforeMs = options.renewBeforeMs ?? RENEW_BEFORE_MS; + if (!path.isAbsolute(options.directory)) { + throw new Error("Aiden Remote TLS identity directory must be absolute."); + } + await fs.mkdir(options.directory, { recursive: true, mode: 0o700 }); + await fs.chmod(options.directory, 0o700); + const paths = identityPaths(options.directory); + + const hasCa = await assertPairCompleteness( + paths.caKey, + paths.caCertificate, + "certificate authority", + ); + if (!hasCa) await createCertificateAuthority(paths, opensslPath); + + const hasServer = await assertPairCompleteness( + paths.serverKey, + paths.serverCertificate, + "server", + ); + if (!hasServer) await createServerKey(paths, opensslPath); + + let [caKey, caCertificate, serverKey, existingServerCertificate] = await Promise.all([ + fs.readFile(paths.caKey, "utf8"), + fs.readFile(paths.caCertificate, "utf8"), + fs.readFile(paths.serverKey, "utf8"), + hasServer ? fs.readFile(paths.serverCertificate, "utf8") : Promise.resolve(null), + ]); + const ca = new X509Certificate(caCertificate); + if (!ca.ca || keySpkiDigest(caKey) !== spkiDigest(caCertificate)) { + throw new Error("Aiden Remote certificate authority identity is invalid."); + } + if (certificateNeedsRenewal(caCertificate, now, renewBeforeMs)) { + await issueCertificateAuthorityCertificate(paths, opensslPath); + caCertificate = await fs.readFile(paths.caCertificate, "utf8"); + caKey = await fs.readFile(paths.caKey, "utf8"); + } + + let certificate = existingServerCertificate; + if (certificate) { + try { + if (keySpkiDigest(serverKey) !== spkiDigest(certificate)) certificate = null; + } catch { + certificate = null; + } + } + if ( + !certificate || + certificateNeedsRenewal(certificate, now, renewBeforeMs) + ) { + await issueServerCertificate( + options.directory, + paths, + opensslPath, + validatedHostnames(options.hostnames ?? []), + ); + certificate = await fs.readFile(paths.serverCertificate, "utf8"); + } + + await Promise.all(Object.values(paths).map(secureFile)); + const parsedCertificate = new X509Certificate(certificate); + return { + privateKey: serverKey, + certificate, + certificateChain: `${certificate.trim()}\n${caCertificate.trim()}\n`, + caCertificate, + serverSpkiSha256: spkiDigest(certificate), + validUntil: new Date(parsedCertificate.validTo).toISOString(), + }; +} diff --git a/main/services/aiden-remote-workspace-browser.test.ts b/main/services/aiden-remote-workspace-browser.test.ts new file mode 100644 index 00000000..da74f5a3 --- /dev/null +++ b/main/services/aiden-remote-workspace-browser.test.ts @@ -0,0 +1,144 @@ +import assert from "node:assert/strict"; +import * as fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { AidenRemoteWorkspaceBrowserService } from "./aiden-remote-workspace-browser.js"; +import type { AidenRemoteApprovedRoot } from "./aiden-remote-state.js"; + +async function fixture() { + const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-browser-test-")); + const rootDirectory = path.join(temporary, "Approved"); + const outsideDirectory = path.join(temporary, "Outside"); + await fs.mkdir(rootDirectory); + await fs.mkdir(outsideDirectory); + const rootPath = await fs.realpath(rootDirectory); + const outside = await fs.realpath(outsideDirectory); + await fs.mkdir(path.join(rootPath, "Alpha")); + await fs.mkdir(path.join(rootPath, "Beta")); + await fs.mkdir(path.join(rootPath, ".Hidden")); + await fs.mkdir(path.join(rootPath, "Library")); + await fs.writeFile(path.join(rootPath, "file.txt"), "not a directory"); + await fs.symlink(outside, path.join(rootPath, "Escape")); + const identity = await fs.stat(rootPath, { bigint: true }); + const root: AidenRemoteApprovedRoot = { + id: "root-1", + label: "Projects", + folderPath: rootPath, + device: identity.dev.toString(), + inode: identity.ino.toString(), + policyRevision: "remote-browser-v1:no-hidden-system", + createdAt: 1, + }; + let approvedRoots = [root]; + let now = 1_000; + const service = new AidenRemoteWorkspaceBrowserService({ + instanceId: "instance-1", + state: { + snapshot: async () => ({ approvedRoots }) as never, + }, + now: () => now, + }); + return { + service, + root, + rootPath, + setRoots: (value: AidenRemoteApprovedRoot[]) => { approvedRoots = value; }, + advance: (milliseconds: number) => { now += milliseconds; }, + cleanup: () => fs.rm(temporary, { recursive: true, force: true }), + }; +} + +test("approved-root browsing exposes only opaque directory entries and consumes selections once", async () => { + const app = await fixture(); + try { + const roots = await app.service.listRoots("device-1"); + assert.equal(roots.roots.length, 1); + assert.match(roots.roots[0]!.location, /^loc_[A-Za-z0-9_-]{43}$/u); + assert.equal(JSON.stringify(roots).includes(app.rootPath), false); + + const page = await app.service.listChildren( + "device-1", + roots.roots[0]!.location, + ); + assert.deepEqual(page.entries.map((entry) => entry.name), ["Alpha", "Beta"]); + assert.equal(page.entries.every((entry) => /^loc_[A-Za-z0-9_-]{43}$/u.test(entry.location)), true); + assert.equal(JSON.stringify(page).includes(app.rootPath), false); + + const selection = await app.service.createSelection( + "device-1", + page.entries[0]!.location, + ); + const claims = await app.service.consumeSelection("device-1", selection.selection); + assert.equal(claims.canonicalPath, path.join(app.rootPath, "Alpha")); + await assert.rejects( + app.service.consumeSelection("device-1", selection.selection), + (error: unknown) => (error as { code?: string }).code === "handle_invalid", + ); + } finally { + await app.cleanup(); + } +}); + +test("browser handles are device-bound, expire, and fail closed after root-policy removal", async () => { + const app = await fixture(); + try { + const location = (await app.service.listRoots("device-1")).roots[0]!.location; + await assert.rejects( + app.service.listChildren("device-2", location), + (error: unknown) => (error as { code?: string }).code === "handle_wrong_device", + ); + + const selection = await app.service.createSelection("device-1", location); + const consumed = await app.service.consumeSelection("device-1", selection.selection); + + app.setRoots([]); + await assert.rejects( + app.service.listChildren("device-1", location), + (error: unknown) => (error as { code?: string }).code === "root_policy_changed", + ); + await assert.rejects( + app.service.revalidateConsumedSelection("device-1", consumed), + (error: unknown) => (error as { code?: string }).code === "root_policy_changed", + ); + + app.setRoots([app.root]); + const expiring = (await app.service.listRoots("device-1")).roots[0]!.location; + app.advance(10 * 60_000 + 1); + await assert.rejects( + app.service.listChildren("device-1", expiring), + (error: unknown) => (error as { code?: string }).code === "handle_expired", + ); + } finally { + await app.cleanup(); + } +}); + +test("directory pagination binds cursors to the exact location and snapshot", async () => { + const app = await fixture(); + try { + await Promise.all( + Array.from({ length: 205 }, (_, index) => + fs.mkdir(path.join(app.rootPath, `Paged-${String(index).padStart(3, "0")}`)), + ), + ); + const rootLocation = (await app.service.listRoots("device-1")).roots[0]!.location; + const first = await app.service.listChildren("device-1", rootLocation); + assert.equal(first.entries.length, 200); + assert.match(first.nextCursor ?? "", /^cur_[A-Za-z0-9_-]{43}$/u); + const second = await app.service.listChildren( + "device-1", + rootLocation, + first.nextCursor, + ); + assert.equal(second.entries.length, 7); + + await fs.mkdir(path.join(app.rootPath, "Snapshot-Changed")); + await assert.rejects( + app.service.listChildren("device-1", rootLocation, first.nextCursor), + (error: unknown) => (error as { code?: string }).code === "filesystem_identity_changed", + ); + } finally { + await app.cleanup(); + } +}); diff --git a/main/services/aiden-remote-workspace-browser.ts b/main/services/aiden-remote-workspace-browser.ts new file mode 100644 index 00000000..e0e4c46a --- /dev/null +++ b/main/services/aiden-remote-workspace-browser.ts @@ -0,0 +1,440 @@ +import { createHash } from "node:crypto"; +import * as fs from "node:fs/promises"; +import path from "node:path"; +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; +import { + AidenOpaqueHandleError, + AidenOpaqueHandleStore, + inspectAidenFilesystemIdentity, + type AidenOpaqueHandleClaims, +} from "./aiden-remote-opaque-handles.js"; +import type { + AidenRemoteApprovedRoot, + AidenRemoteStateRegistry, +} from "./aiden-remote-state.js"; + +const LOCATION_TTL_MS = 10 * 60_000; +const CURSOR_TTL_MS = 2 * 60_000; +const SELECTION_TTL_MS = 2 * 60_000; +const PAGE_SIZE = 200; +const MAX_DIRECTORY_ENTRIES = 5_000; +const MAX_DEPTH = 20; +const SYSTEM_DIRECTORY_NAMES = new Set([ + ".DocumentRevisions-V100", + ".Spotlight-V100", + ".TemporaryItems", + ".Trashes", + "Library", + "System", +]); + +export interface AidenRemoteBrowserRootProjection { + id: string; + label: string; + location: string; + policyRevision: string; +} + +export interface AidenRemoteBrowserPage { + rootId: string; + label: string; + breadcrumbs: Array<{ label: string; location: string }>; + entries: Array<{ id: string; name: string; location: string }>; + nextCursor?: string; +} + +export interface AidenRemoteWorkspaceSelection { + selection: string; + displayName: string; + expiresAt: string; +} + +interface DirectoryEntryIdentity { + name: string; + canonicalPath: string; + filesystemDevice: string; + filesystemInode: string; +} + +function tokenDigest(token: string): string { + return createHash("sha256").update(token).digest("base64url"); +} + +function opaqueEntryId(rootId: string, entry: DirectoryEntryIdentity): string { + return createHash("sha256") + .update(`${rootId}\0${entry.filesystemDevice}\0${entry.filesystemInode}`) + .digest("base64url"); +} + +function compareNames(left: DirectoryEntryIdentity, right: DirectoryEntryIdentity): number { + return left.name < right.name ? -1 : left.name > right.name ? 1 : 0; +} + +function safeLabel(value: string, fallback: string): string { + const candidate = [...(value || fallback)] + .map((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 0x1f || code === 0x7f ? "�" : character; + }) + .join(""); + return [...candidate].slice(0, 255).join("") || fallback; +} + +function mapHandleError(error: unknown): never { + if (!(error instanceof AidenOpaqueHandleError)) throw error; + const status = error.code === "handle_wrong_device" + ? 403 + : error.code === "handle_expired" + ? 410 + : error.code === "handle_capacity" + ? 429 + : error.code === "root_policy_changed" || + error.code === "filesystem_identity_changed" || + error.code === "path_outside_root" + ? 409 + : 400; + throw new AidenRemoteServiceError( + error.code, + error.code === "handle_expired" + ? "This folder-browser link expired. Refresh the approved roots and try again." + : error.code === "handle_capacity" + ? "Aiden's folder-browser handle capacity is temporarily full." + : "This folder-browser link is no longer valid.", + status, + error.code === "handle_capacity", + ); +} + +export class AidenRemoteWorkspaceBrowserService { + constructor( + private readonly options: { + instanceId: string; + state: Pick; + handles?: AidenOpaqueHandleStore; + now?: () => number; + }, + ) {} + + private get handles(): AidenOpaqueHandleStore { + this.handleStore ??= this.options.handles ?? new AidenOpaqueHandleStore({ now: this.now }); + return this.handleStore; + } + + private handleStore: AidenOpaqueHandleStore | undefined; + private readonly now = (): number => this.options.now?.() ?? Date.now(); + + private async approvedRoot(rootId: string): Promise { + const root = (await this.options.state.snapshot()).approvedRoots.find( + (candidate) => candidate.id === rootId, + ); + if (!root) { + throw new AidenRemoteServiceError( + "root_policy_changed", + "This approved folder is no longer available for remote browsing.", + 409, + ); + } + return root; + } + + private async claimsForPath( + deviceId: string, + root: AidenRemoteApprovedRoot, + candidatePath: string, + extra: Pick< + AidenOpaqueHandleClaims, + "expiresAt" | "depth" | "snapshotId" | "cursorOffset" | "parentHandleDigest" + >, + ): Promise { + try { + const rootIdentity = await fs.stat(root.folderPath, { bigint: true }); + const canonicalRoot = await fs.realpath(root.folderPath); + if ( + !rootIdentity.isDirectory() || + canonicalRoot !== root.folderPath || + rootIdentity.dev.toString() !== root.device || + rootIdentity.ino.toString() !== root.inode + ) { + throw new AidenOpaqueHandleError("filesystem_identity_changed"); + } + const identity = await inspectAidenFilesystemIdentity(root.folderPath, candidatePath); + if (identity.kind !== "directory") { + throw new AidenOpaqueHandleError("handle_invalid"); + } + return { + instanceId: this.options.instanceId, + deviceId, + rootId: root.id, + policyRevision: root.policyRevision, + ...identity, + ...extra, + }; + } catch (error) { + if (error instanceof AidenRemoteServiceError) throw error; + if (error instanceof AidenOpaqueHandleError) mapHandleError(error); + throw new AidenRemoteServiceError( + "workspace_unavailable", + "This approved folder is not currently available on the Mac.", + 409, + ); + } + } + + private async resolveLocation( + deviceId: string, + token: string, + ): Promise<{ claims: AidenOpaqueHandleClaims; root: AidenRemoteApprovedRoot }> { + try { + const stored = this.handles.claimsFor(token, "loc"); + const root = await this.approvedRoot(stored.rootId); + const current = await this.claimsForPath(deviceId, root, stored.canonicalPath, { + expiresAt: stored.expiresAt, + depth: stored.depth, + snapshotId: stored.snapshotId, + cursorOffset: stored.cursorOffset, + parentHandleDigest: stored.parentHandleDigest, + }); + return { claims: this.handles.resolve(token, "loc", current), root }; + } catch (error) { + mapHandleError(error); + } + } + + async listRoots(deviceId: string): Promise<{ roots: AidenRemoteBrowserRootProjection[] }> { + const state = await this.options.state.snapshot(); + const roots: AidenRemoteBrowserRootProjection[] = []; + for (const root of state.approvedRoots) { + const claims = await this.claimsForPath(deviceId, root, root.folderPath, { + expiresAt: this.now() + LOCATION_TTL_MS, + depth: 0, + snapshotId: undefined, + cursorOffset: undefined, + parentHandleDigest: undefined, + }); + roots.push({ + id: root.id, + label: safeLabel(root.label, "Approved folder"), + location: this.handles.issue("loc", claims), + policyRevision: root.policyRevision, + }); + } + return { roots }; + } + + private async directoryEntries( + root: AidenRemoteApprovedRoot, + directoryPath: string, + ): Promise { + const entries = await fs.readdir(directoryPath, { withFileTypes: true }); + if (entries.length > MAX_DIRECTORY_ENTRIES) { + throw new AidenRemoteServiceError( + "workspace_unavailable", + "This folder contains too many entries to browse remotely.", + 409, + false, + { limit: MAX_DIRECTORY_ENTRIES }, + ); + } + const directories: DirectoryEntryIdentity[] = []; + for (const entry of entries) { + if ( + !entry.isDirectory() || + entry.name.startsWith(".") || + SYSTEM_DIRECTORY_NAMES.has(entry.name) + ) { + continue; + } + try { + const identity = await inspectAidenFilesystemIdentity( + root.folderPath, + path.join(directoryPath, entry.name), + ); + if (identity.kind !== "directory") continue; + directories.push({ + name: safeLabel(entry.name, "Folder"), + canonicalPath: identity.canonicalPath, + filesystemDevice: identity.filesystemDevice, + filesystemInode: identity.filesystemInode, + }); + } catch { + // A raced, unreadable, or redirected entry is omitted. The parent + // location remains usable and no local path crosses the wire. + } + } + return directories.sort(compareNames); + } + + private async breadcrumbs( + deviceId: string, + root: AidenRemoteApprovedRoot, + currentPath: string, + ): Promise> { + const relative = path.relative(root.folderPath, currentPath); + const segments = relative ? relative.split(path.sep) : []; + const result: Array<{ label: string; location: string }> = []; + let candidate = root.folderPath; + for (let index = 0; index <= segments.length; index += 1) { + if (index > 0) candidate = path.join(candidate, segments[index - 1]!); + const claims = await this.claimsForPath(deviceId, root, candidate, { + expiresAt: this.now() + LOCATION_TTL_MS, + depth: index, + snapshotId: undefined, + cursorOffset: undefined, + parentHandleDigest: undefined, + }); + result.push({ + label: index === 0 ? safeLabel(root.label, "Approved folder") : safeLabel(segments[index - 1]!, "Folder"), + location: this.handles.issue("loc", claims), + }); + } + return result; + } + + async listChildren( + deviceId: string, + location: string, + cursor?: string, + ): Promise { + const { claims, root } = await this.resolveLocation(deviceId, location); + const depth = claims.depth ?? 0; + if (depth > MAX_DEPTH) { + throw new AidenRemoteServiceError( + "handle_invalid", + "This folder is deeper than Aiden's remote browser limit.", + 400, + false, + { limit: MAX_DEPTH }, + ); + } + const entries = await this.directoryEntries(root, claims.canonicalPath); + const snapshotId = createHash("sha256") + .update(entries.map((entry) => `${entry.name}\0${entry.filesystemDevice}\0${entry.filesystemInode}`).join("\n")) + .digest("base64url"); + let offset = 0; + if (cursor) { + try { + const storedCursor = this.handles.claimsFor(cursor, "cur"); + const currentCursor = await this.claimsForPath(deviceId, root, claims.canonicalPath, { + expiresAt: storedCursor.expiresAt, + depth, + snapshotId, + cursorOffset: storedCursor.cursorOffset, + parentHandleDigest: tokenDigest(location), + }); + const resolved = this.handles.resolve(cursor, "cur", currentCursor); + offset = resolved.cursorOffset ?? -1; + if (!Number.isSafeInteger(offset) || offset < 0 || offset >= entries.length) { + throw new AidenOpaqueHandleError("handle_invalid"); + } + } catch (error) { + mapHandleError(error); + } + } + const pageEntries = entries.slice(offset, offset + PAGE_SIZE); + const projectedEntries = await Promise.all( + pageEntries.map(async (entry) => ({ + id: opaqueEntryId(root.id, entry), + name: entry.name, + location: this.handles.issue( + "loc", + await this.claimsForPath(deviceId, root, entry.canonicalPath, { + expiresAt: this.now() + LOCATION_TTL_MS, + depth: depth + 1, + snapshotId: undefined, + cursorOffset: undefined, + parentHandleDigest: undefined, + }), + ), + })), + ); + const nextOffset = offset + pageEntries.length; + const nextCursor = nextOffset < entries.length + ? this.handles.issue("cur", { + ...claims, + expiresAt: this.now() + CURSOR_TTL_MS, + snapshotId, + cursorOffset: nextOffset, + parentHandleDigest: tokenDigest(location), + }) + : undefined; + return { + rootId: root.id, + label: safeLabel(path.basename(claims.canonicalPath), root.label), + breadcrumbs: await this.breadcrumbs(deviceId, root, claims.canonicalPath), + entries: projectedEntries, + ...(nextCursor ? { nextCursor } : {}), + }; + } + + async createSelection( + deviceId: string, + location: string, + ): Promise { + const { claims, root } = await this.resolveLocation(deviceId, location); + const expiresAt = this.now() + SELECTION_TTL_MS; + const fresh = await this.claimsForPath(deviceId, root, claims.canonicalPath, { + expiresAt, + depth: claims.depth, + snapshotId: undefined, + cursorOffset: undefined, + parentHandleDigest: undefined, + }); + return { + selection: this.handles.issue("sel", fresh), + displayName: safeLabel(path.basename(fresh.canonicalPath), root.label), + expiresAt: new Date(expiresAt).toISOString(), + }; + } + + async consumeSelection(deviceId: string, selection: string): Promise { + try { + const stored = this.handles.claimsFor(selection, "sel"); + const root = await this.approvedRoot(stored.rootId); + const current = await this.claimsForPath(deviceId, root, stored.canonicalPath, { + expiresAt: stored.expiresAt, + depth: stored.depth, + snapshotId: stored.snapshotId, + cursorOffset: stored.cursorOffset, + parentHandleDigest: stored.parentHandleDigest, + }); + return this.handles.consumeSelection( + selection, + current, + (claims) => ({ ...claims }), + this.now(), + ); + } catch (error) { + mapHandleError(error); + } + } + + async revalidateConsumedSelection( + deviceId: string, + claims: AidenOpaqueHandleClaims, + ): Promise { + if (this.now() >= claims.expiresAt) { + mapHandleError(new AidenOpaqueHandleError("handle_expired")); + } + if (claims.deviceId !== deviceId || claims.instanceId !== this.options.instanceId) { + mapHandleError(new AidenOpaqueHandleError("handle_wrong_device")); + } + const root = await this.approvedRoot(claims.rootId); + const current = await this.claimsForPath(deviceId, root, claims.canonicalPath, { + expiresAt: claims.expiresAt, + depth: claims.depth, + snapshotId: claims.snapshotId, + cursorOffset: claims.cursorOffset, + parentHandleDigest: claims.parentHandleDigest, + }); + if ( + current.policyRevision !== claims.policyRevision || + current.canonicalRootPath !== claims.canonicalRootPath || + current.canonicalPath !== claims.canonicalPath || + current.filesystemDevice !== claims.filesystemDevice || + current.filesystemInode !== claims.filesystemInode + ) { + mapHandleError(new AidenOpaqueHandleError("filesystem_identity_changed")); + } + return current; + } +} diff --git a/main/services/aiden-remote-workspace-http.test.ts b/main/services/aiden-remote-workspace-http.test.ts new file mode 100644 index 00000000..944cd841 --- /dev/null +++ b/main/services/aiden-remote-workspace-http.test.ts @@ -0,0 +1,179 @@ +import assert from "node:assert/strict"; +import * as fs from "node:fs/promises"; +import { createServer } from "node:http"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { createAidenRemoteRequestHandler } from "./aiden-remote-router.js"; +import { AidenRemoteWorkspaceBrowserService } from "./aiden-remote-workspace-browser.js"; +import { AidenRemoteWorkspaceService } from "./aiden-remote-workspaces.js"; +import { createWorkspaceApplicationService } from "./workspace-application-service.js"; +import { WorkspaceMutationGate } from "./workspace-mutation-gate.js"; +import { WorkspaceOperationRegistry } from "./workspace-operation-registry.js"; +import type { Workspace } from "./types.js"; + +test("HTTP client completes approved-folder selection and revision-checked workspace CRUD", async () => { + const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-workspace-http-")); + const approvedDirectory = path.join(temporary, "Approved"); + await fs.mkdir(approvedDirectory); + const approvedPath = await fs.realpath(approvedDirectory); + await fs.mkdir(path.join(approvedPath, "Selected")); + const approvedIdentity = await fs.stat(approvedPath, { bigint: true }); + let sequence = 0; + let workspaces: Workspace[] = [{ + id: "default", + name: "Default", + permission: "ask", + createdAt: 1, + updatedAt: 1, + }]; + const application = createWorkspaceApplicationService({ + configStore: { + listWorkspaces: async () => structuredClone(workspaces), + getWorkspace: async (id) => structuredClone(workspaces.find((workspace) => workspace.id === id)), + saveWorkspace: async (workspace) => { + const saved = { ...workspace, updatedAt: workspace.updatedAt + 1 }; + const index = workspaces.findIndex((candidate) => candidate.id === saved.id); + if (index >= 0) workspaces[index] = saved; + else workspaces.push(saved); + return structuredClone(saved); + }, + removeWorkspace: async (id) => { + workspaces = workspaces.filter((workspace) => workspace.id !== id); + }, + }, + llmClient: { cancelWorkspaceAndSettle: async () => undefined }, + scheduleService: { + cancelWorkspace: async () => undefined, + resumeWorkspace: async () => undefined, + }, + terminalService: { closeForWorkspace: () => undefined }, + workspaceMutationGate: new WorkspaceMutationGate(), + workspaceOperationRegistry: new WorkspaceOperationRegistry(), + createScratchWorkspaceDirectory: async () => { + const scratch = path.join(temporary, `Scratch-${sequence}`); + await fs.mkdir(scratch); + return { name: "Scratch", folderPath: scratch }; + }, + realpath: (value) => fs.realpath(value), + stat: (value) => fs.stat(value), + removeEmptyDirectory: (value) => fs.rmdir(value), + createId: () => `workspace-${++sequence}`, + now: () => 10_000 + sequence, + logError: () => undefined, + }); + const state = { + snapshot: async () => ({ + approvedRoots: [{ + id: "root-1", + label: "Projects", + folderPath: approvedPath, + device: approvedIdentity.dev.toString(), + inode: approvedIdentity.ino.toString(), + policyRevision: "remote-browser-v1:no-hidden-system", + createdAt: 1, + }], + }) as never, + }; + const browser = new AidenRemoteWorkspaceBrowserService({ + instanceId: "instance-1", + state, + }); + const workspaceService = new AidenRemoteWorkspaceService({ + application, + browser, + }); + const handler = createAidenRemoteRequestHandler({ + instanceId: "instance-1", + appVersion: "test", + devices: { + authenticate: async () => ({ + id: "device-1", + revoked: false, + capabilities: new Set([ + "workspace:read" as const, + "workspace:browse" as const, + "workspace:manage" as const, + ]), + }), + }, + pairing: { exchange: async () => { throw new Error("not used"); } }, + workspaces: workspaceService, + workspaceBrowser: browser, + connectionMode: () => "lan", + now: Date.now, + log: () => undefined, + }); + const server = createServer(handler); + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", resolve); + }); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("HTTP test server did not bind"); + const base = `http://127.0.0.1:${address.port}/api/aiden/v1`; + const headers = { + authorization: `Bearer ${"a".repeat(43)}`, + "aiden-protocol-version": "1", + }; + try { + const roots = await (await fetch(`${base}/workspace-browser/roots`, { headers })).json(); + const page = await ( + await fetch( + `${base}/workspace-browser/children?location=${roots.roots[0].location}`, + { headers }, + ) + ).json(); + assert.deepEqual(page.entries.map((entry: { name: string }) => entry.name), ["Selected"]); + const selectionResponse = await fetch(`${base}/workspace-browser/selections`, { + method: "POST", + headers: { ...headers, "content-type": "application/json" }, + body: JSON.stringify({ location: page.entries[0].location }), + }); + assert.equal(selectionResponse.status, 201); + const selection = await selectionResponse.json(); + + const createResponse = await fetch(`${base}/workspaces`, { + method: "POST", + headers: { + ...headers, + "content-type": "application/json", + "idempotency-key": "workspace-http-create-0001", + }, + body: JSON.stringify({ + mode: "selected-folder", + selection: selection.selection, + name: "Remote Selected", + }), + }); + assert.equal(createResponse.status, 201); + const created = await createResponse.json(); + assert.equal(created.name, "Remote Selected"); + assert.equal(created.hasFolder, true); + assert.equal(JSON.stringify(created).includes(approvedPath), false); + + const patchResponse = await fetch(`${base}/workspaces/${created.id}`, { + method: "PATCH", + headers: { + ...headers, + "content-type": "application/json", + "if-match": created.revision, + }, + body: JSON.stringify({ confirmedForeground: true, permission: "full" }), + }); + assert.equal(patchResponse.status, 200); + const updated = await patchResponse.json(); + assert.equal(updated.permission, "full"); + + const deleteResponse = await fetch(`${base}/workspaces/${created.id}`, { + method: "DELETE", + headers: { ...headers, "if-match": updated.revision }, + }); + assert.equal(deleteResponse.status, 204); + const listed = await (await fetch(`${base}/workspaces`, { headers })).json(); + assert.deepEqual(listed.workspaces.map((workspace: { id: string }) => workspace.id), ["default"]); + } finally { + await new Promise((resolve) => server.close(() => resolve())); + await fs.rm(temporary, { recursive: true, force: true }); + } +}); diff --git a/main/services/aiden-remote-workspace-owners.ts b/main/services/aiden-remote-workspace-owners.ts new file mode 100644 index 00000000..abb1db4b --- /dev/null +++ b/main/services/aiden-remote-workspace-owners.ts @@ -0,0 +1,42 @@ +import type { WorkspaceOperationDocumentOwner } from "./workspace-operation-registry.js"; + +class AidenRemoteWorkspaceOperationOwner implements WorkspaceOperationDocumentOwner { + private invalidated = false; + private readonly listeners = new Set<() => void>(); + + isDestroyed(): boolean { + return this.invalidated; + } + + onInvalidated(listener: () => void): () => void { + if (this.invalidated) listener(); + else this.listeners.add(listener); + return () => this.listeners.delete(listener); + } + + revoke(): void { + if (this.invalidated) return; + this.invalidated = true; + for (const listener of this.listeners) listener(); + this.listeners.clear(); + } +} + +/** Remote ownership survives a socket disconnect and ends only on revocation. */ +export class AidenRemoteWorkspaceOwnerRegistry { + private readonly owners = new Map(); + + owner(deviceId: string): WorkspaceOperationDocumentOwner { + let owner = this.owners.get(deviceId); + if (!owner || owner.isDestroyed()) { + owner = new AidenRemoteWorkspaceOperationOwner(); + this.owners.set(deviceId, owner); + } + return owner; + } + + revokeDevice(deviceId: string): void { + this.owners.get(deviceId)?.revoke(); + this.owners.delete(deviceId); + } +} diff --git a/main/services/aiden-remote-workspaces.test.ts b/main/services/aiden-remote-workspaces.test.ts new file mode 100644 index 00000000..b312c478 --- /dev/null +++ b/main/services/aiden-remote-workspaces.test.ts @@ -0,0 +1,240 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import type { Workspace } from "./types.js"; +import { + AidenRemoteWorkspaceService, + projectAidenRemoteWorkspace, +} from "./aiden-remote-workspaces.js"; +import { + AidenIdempotencyLedger, + type AidenIdempotencySnapshot, +} from "./aiden-remote-operation-contract.js"; + +function workspace(overrides: Partial = {}): Workspace { + return { + id: "workspace-1", + name: "Workspace", + permission: "ask", + createdAt: 1_000, + updatedAt: 2_000, + ...overrides, + }; +} + +function fixture( + initial: Workspace[] = [workspace()], + options: { + idempotency?: AidenIdempotencyLedger; + persistIdempotency?: (snapshot: AidenIdempotencySnapshot) => Promise; + } = {}, +) { + let workspaces = initial.map((value) => structuredClone(value)); + let createCalls = 0; + let notifications = 0; + const service = new AidenRemoteWorkspaceService({ + application: { + list: async () => structuredClone(workspaces), + get: async (id: string) => structuredClone(workspaces.find((value) => value.id === id) ?? null), + create: async (input: unknown) => { + createCalls += 1; + const value = workspace({ + id: `workspace-${createCalls + 1}`, + name: (input as { name: string }).name, + }); + workspaces.push(value); + return structuredClone(value); + }, + createScratch: async () => { + createCalls += 1; + const value = workspace({ id: `workspace-${createCalls + 1}`, name: "Scratch", folderPath: "/private/scratch" }); + workspaces.push(value); + return structuredClone(value); + }, + createFromFolder: async ( + folderPath: string, + name?: string, + options?: { assertCurrent?: (identity: { canonicalPath: string; filesystemDevice: string; filesystemInode: string }) => Promise | void }, + ) => { + await options?.assertCurrent?.({ + canonicalPath: folderPath, + filesystemDevice: "1", + filesystemInode: "2", + }); + createCalls += 1; + const value = workspace({ id: `workspace-${createCalls + 1}`, name: name ?? "Selected", folderPath }); + workspaces.push(value); + return structuredClone(value); + }, + update: async (id: string, patch: unknown) => { + const index = workspaces.findIndex((value) => value.id === id); + if (index < 0) throw new Error("missing"); + workspaces[index] = { ...workspaces[index]!, ...(patch as Partial), updatedAt: workspaces[index]!.updatedAt + 1 }; + return structuredClone(workspaces[index]!); + }, + remove: async (id: string) => { + const existing = workspaces.find((value) => value.id === id); + if (existing?.managedWorktree) throw new Error("Delete worktree through the managed worktree workflow."); + workspaces = workspaces.filter((value) => value.id !== id); + }, + }, + browser: { + consumeSelection: async () => ({ + instanceId: "instance-1", + deviceId: "device-1", + rootId: "root-1", + policyRevision: "policy-1", + canonicalRootPath: "/approved", + canonicalPath: "/approved/Selected", + filesystemDevice: "1", + filesystemInode: "2", + expiresAt: 60_000, + kind: "directory" as const, + }), + revalidateConsumedSelection: async (_deviceId, claims) => claims, + }, + ...options, + notifyChanged: () => { notifications += 1; }, + }); + return { + service, + createCalls: () => createCalls, + notifications: () => notifications, + }; +} + +test("workspace projections omit local paths and internal managed-worktree identity", () => { + const projected = projectAidenRemoteWorkspace(workspace({ + folderPath: "/private/repository/worktree", + managedWorktree: { + repositoryPath: "/private/repository", + worktreePath: "/private/repository/worktree", + branch: "feature/mobile", + worktreeGitDir: "/private/repository/.git/worktrees/mobile", + ownershipToken: "secret-token", + createdFromHead: "abc123", + }, + })); + const serialized = JSON.stringify(projected); + assert.equal(projected.hasFolder, true); + assert.equal(projected.isManagedWorktree, true); + assert.equal(projected.branchName, "feature/mobile"); + assert.equal(projected.repositoryName, "repository"); + assert.equal(serialized.includes("/private/"), false); + assert.equal(serialized.includes("secret-token"), false); + assert.match(projected.revision, /^rev_[A-Za-z0-9_-]{43}$/u); +}); + +test("workspace creates are exact, idempotent per device, and notify Electron once", async () => { + const app = fixture([]); + const key = "workspace-create-key-0001"; + const first = await app.service.create("device-1", key, { + mode: "folderless", + name: "Remote Project", + }); + const replay = await app.service.create("device-1", key, { + mode: "folderless", + name: "Remote Project", + }); + assert.deepEqual(replay, first); + assert.equal(app.createCalls(), 1); + assert.equal(app.notifications(), 1); + + await assert.rejects( + app.service.create("device-1", key, { mode: "scratch" }), + (error: unknown) => (error as { code?: string }).code === "idempotency_conflict", + ); + await app.service.create("device-2", key, { mode: "scratch" }); + assert.equal(app.createCalls(), 2); +}); + +test("workspace idempotency is persisted before mutation and replays after restart", async () => { + let persisted: AidenIdempotencySnapshot | undefined; + const states: string[] = []; + const first = fixture([], { + persistIdempotency: async (snapshot) => { + persisted = structuredClone(snapshot); + states.push(snapshot.entries[0]?.state ?? "missing"); + }, + }); + const key = "durable-create-key-0001"; + const created = await first.service.create("device-1", key, { + mode: "folderless", + name: "Durable", + }); + assert.deepEqual(states, ["in_flight", "fulfilled"]); + assert.equal(first.createCalls(), 1); + assert.ok(persisted); + + const restarted = fixture([], { + idempotency: new AidenIdempotencyLedger(persisted), + }); + const replay = await restarted.service.create("device-1", key, { + mode: "folderless", + name: "Durable", + }); + assert.deepEqual(replay, created); + assert.equal(restarted.createCalls(), 0); + assert.equal(restarted.notifications(), 0); +}); + +test("selected-folder creation consumes only a browser-issued path and never accepts raw paths", async () => { + const app = fixture([]); + const created = await app.service.create("device-1", "selected-folder-key-01", { + mode: "selected-folder", + selection: `sel_${"a".repeat(43)}`, + name: "Chosen", + }); + assert.equal(created.name, "Chosen"); + assert.equal(created.hasFolder, true); + assert.equal(JSON.stringify(created).includes("/approved/"), false); + await assert.rejects( + app.service.create("device-1", "selected-folder-key-02", { + mode: "selected-folder", + selection: `sel_${"b".repeat(43)}`, + folderPath: "/private/escape", + }), + (error: unknown) => (error as { code?: string }).code === "invalid_request", + ); +}); + +test("workspace update and removal require the exact current revision", async () => { + const app = fixture(); + const current = await app.service.get("workspace-1"); + await assert.rejects( + app.service.update("workspace-1", "rev_stale", { + confirmedForeground: true, + name: "Changed", + }), + (error: unknown) => + (error as { code?: string }).code === "revision_conflict" && + (error as { details?: { currentRevision?: string } }).details?.currentRevision === current.revision, + ); + const updated = await app.service.update("workspace-1", current.revision, { + confirmedForeground: true, + permission: "full", + }); + assert.equal(updated.permission, "full"); + assert.notEqual(updated.revision, current.revision); + await app.service.remove("workspace-1", updated.revision); + assert.equal(app.notifications(), 2); + await assert.rejects( + app.service.get("workspace-1"), + (error: unknown) => (error as { code?: string }).code === "not_found", + ); +}); + +test("managed worktrees cannot be unregistered through generic workspace CRUD", async () => { + const app = fixture([workspace({ + managedWorktree: { + repositoryPath: "/repo", + worktreePath: "/repo/worktree", + branch: "feature", + createdFromHead: "abc123", + }, + })]); + const current = await app.service.get("workspace-1"); + await assert.rejects( + app.service.remove("workspace-1", current.revision), + (error: unknown) => (error as { code?: string }).code === "workspace_unavailable", + ); +}); diff --git a/main/services/aiden-remote-workspaces.ts b/main/services/aiden-remote-workspaces.ts new file mode 100644 index 00000000..07375872 --- /dev/null +++ b/main/services/aiden-remote-workspaces.ts @@ -0,0 +1,403 @@ +import { createHash } from "node:crypto"; +import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; +import { + AidenIdempotencyLedger, + type AidenIdempotencySnapshot, + AidenOperationContractError, + assertRevision, +} from "./aiden-remote-operation-contract.js"; +import type { AidenRemoteWorkspaceBrowserService } from "./aiden-remote-workspace-browser.js"; +import type { createWorkspaceApplicationService } from "./workspace-application-service.js"; +import type { Workspace, WorkspacePermission } from "./types.js"; + +const WORKSPACE_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/u; +const IDEMPOTENCY_KEY_PATTERN = /^[\x21-\x7e]{16,128}$/u; +const PERMISSIONS: readonly WorkspacePermission[] = ["full", "ask", "none"]; + +export interface AidenRemoteWorkspaceProjection { + id: string; + name: string; + permission: WorkspacePermission; + hasFolder: boolean; + isManagedWorktree: boolean; + branchName?: string; + repositoryName?: string; + createdAt: string; + updatedAt: string; + revision: string; +} + +type WorkspaceApplicationService = ReturnType; + +function ownRecord(value: unknown): Record | null { + return value !== null && typeof value === "object" && !Array.isArray(value) + ? (value as Record) + : null; +} + +function exactKeys( + record: Record, + required: readonly string[], + optional: readonly string[] = [], +): boolean { + const allowed = new Set([...required, ...optional]); + return ( + required.every((key) => Object.prototype.hasOwnProperty.call(record, key)) && + Object.keys(record).every((key) => allowed.has(key)) + ); +} + +function boundedString(value: unknown, maximum: number): value is string { + return typeof value === "string" && value.length > 0 && [...value].length <= maximum; +} + +function safeWorkspaceId(value: string): string { + if (!WORKSPACE_ID_PATTERN.test(value)) { + throw new AidenRemoteServiceError( + "invalid_request", + "The workspace identifier is invalid.", + 400, + ); + } + return value; +} + +export function workspaceRevision(workspace: Workspace): string { + const value = JSON.stringify({ + id: workspace.id, + name: workspace.name, + permission: workspace.permission, + hasFolder: Boolean(workspace.folderPath), + managedBranch: workspace.managedWorktree?.branch ?? null, + createdAt: workspace.createdAt, + updatedAt: workspace.updatedAt, + }); + return `rev_${createHash("sha256").update(value).digest("base64url")}`; +} + +function displayNameFromPath(value: string | undefined): string | undefined { + if (!value) return undefined; + const components = value.split(/[\\/]/u).filter(Boolean); + return components[components.length - 1]?.slice(0, 120); +} + +export function projectAidenRemoteWorkspace( + workspace: Workspace, +): AidenRemoteWorkspaceProjection { + return { + id: workspace.id, + name: workspace.name, + permission: workspace.permission, + hasFolder: Boolean(workspace.folderPath), + isManagedWorktree: Boolean(workspace.managedWorktree), + ...(workspace.managedWorktree?.branch + ? { branchName: workspace.managedWorktree.branch.slice(0, 255) } + : {}), + ...(displayNameFromPath(workspace.managedWorktree?.repositoryPath) + ? { repositoryName: displayNameFromPath(workspace.managedWorktree?.repositoryPath)! } + : {}), + createdAt: new Date(workspace.createdAt).toISOString(), + updatedAt: new Date(workspace.updatedAt).toISOString(), + revision: workspaceRevision(workspace), + }; +} + +function parseCreate(value: unknown): + | { mode: "folderless"; name: string } + | { mode: "scratch" } + | { mode: "selected-folder"; selection: string; name?: string } { + const record = ownRecord(value); + if (!record || typeof record.mode !== "string") { + throw new AidenRemoteServiceError("invalid_request", "The workspace creation request is invalid.", 400); + } + if (record.mode === "folderless") { + if (!exactKeys(record, ["mode", "name"]) || !boundedString(record.name, 120)) { + throw new AidenRemoteServiceError("invalid_request", "A folderless workspace requires a valid name.", 400); + } + return { mode: "folderless", name: record.name.trim() }; + } + if (record.mode === "scratch") { + if (!exactKeys(record, ["mode"])) { + throw new AidenRemoteServiceError("invalid_request", "The scratch workspace request is invalid.", 400); + } + return { mode: "scratch" }; + } + if (record.mode === "selected-folder") { + if ( + !exactKeys(record, ["mode", "selection"], ["name"]) || + typeof record.selection !== "string" || + !/^sel_[A-Za-z0-9_-]{43}$/u.test(record.selection) || + (record.name !== undefined && !boundedString(record.name, 120)) + ) { + throw new AidenRemoteServiceError("invalid_request", "The selected-folder workspace request is invalid.", 400); + } + return { + mode: "selected-folder", + selection: record.selection, + ...(typeof record.name === "string" ? { name: record.name.trim() } : {}), + }; + } + throw new AidenRemoteServiceError("invalid_request", "The workspace creation mode is invalid.", 400); +} + +function parsePatch(value: unknown): { + name?: string; + permission?: WorkspacePermission; +} { + const record = ownRecord(value); + if ( + !record || + !exactKeys(record, ["confirmedForeground"], ["name", "permission"]) || + record.confirmedForeground !== true || + (record.name === undefined && record.permission === undefined) || + (record.name !== undefined && !boundedString(record.name, 120)) || + (record.permission !== undefined && !PERMISSIONS.includes(record.permission as WorkspacePermission)) + ) { + throw new AidenRemoteServiceError( + "permission_confirmation_required", + "Workspace changes require an explicit foreground confirmation.", + 409, + ); + } + return { + ...(typeof record.name === "string" ? { name: record.name.trim() } : {}), + ...(record.permission !== undefined + ? { permission: record.permission as WorkspacePermission } + : {}), + }; +} + +function mapOperationError(error: unknown, currentRevision?: string): never { + if (!(error instanceof AidenOperationContractError)) throw error; + const status = error.code === "revision_conflict" + ? 409 + : error.code === "idempotency_capacity" + ? 429 + : 409; + throw new AidenRemoteServiceError( + error.code, + error.code === "revision_conflict" + ? "The workspace changed. Refresh it before trying again." + : "This workspace request cannot be safely repeated.", + status, + error.code === "idempotency_capacity", + currentRevision ? { currentRevision } : undefined, + ); +} + +function requireWorkspaceRevision(expected: string, workspace: Workspace): void { + const currentRevision = workspaceRevision(workspace); + try { + assertRevision(expected, currentRevision); + } catch (error) { + mapOperationError(error, currentRevision); + } +} + +export class AidenRemoteWorkspaceService { + private readonly idempotency: AidenIdempotencyLedger; + + constructor( + private readonly options: { + application: Pick< + WorkspaceApplicationService, + "list" | "get" | "create" | "createScratch" | "createFromFolder" | "update" | "remove" + >; + browser: Pick< + AidenRemoteWorkspaceBrowserService, + "consumeSelection" | "revalidateConsumedSelection" + >; + idempotency?: AidenIdempotencyLedger; + persistIdempotency?: (snapshot: AidenIdempotencySnapshot) => Promise; + notifyChanged?: () => void; + }, + ) { + this.idempotency = options.idempotency ?? new AidenIdempotencyLedger(); + } + + private async executeIdempotent( + scope: { deviceId: string; route: string; resourceId: string; key: string }, + input: unknown, + action: () => Promise, + ): Promise { + if (!this.options.persistIdempotency) { + return this.idempotency.execute(scope, input, action); + } + let release!: () => void; + let reject!: (error: unknown) => void; + const durableAdmission = new Promise((resolve, rejectPromise) => { + release = resolve; + reject = rejectPromise; + }); + const pending = this.idempotency.execute(scope, input, async () => { + await durableAdmission; + return action(); + }); + try { + await this.options.persistIdempotency(this.idempotency.snapshot()); + release(); + } catch (error) { + reject(error); + await pending.catch(() => undefined); + throw new AidenRemoteServiceError( + "internal_error", + "Aiden could not durably prepare this workspace request.", + 500, + ); + } + let result: T; + let actionError: unknown; + let actionFailed = false; + try { + result = await pending; + } catch (error) { + actionFailed = true; + actionError = error; + } + try { + await this.options.persistIdempotency(this.idempotency.snapshot()); + } catch { + throw new AidenRemoteServiceError( + "idempotency_in_flight", + "The workspace change may have completed, but Aiden could not durably record its outcome.", + 409, + ); + } + if (actionFailed) throw actionError; + return result!; + } + + async list(): Promise<{ workspaces: AidenRemoteWorkspaceProjection[] }> { + return { + workspaces: (await this.options.application.list()).map(projectAidenRemoteWorkspace), + }; + } + + async get(workspaceId: string): Promise { + const workspace = await this.options.application.get(safeWorkspaceId(workspaceId)); + if (!workspace) { + throw new AidenRemoteServiceError("not_found", "This Aiden workspace no longer exists.", 404); + } + return projectAidenRemoteWorkspace(workspace); + } + + async create( + deviceId: string, + idempotencyKey: string, + input: unknown, + ): Promise { + if (!IDEMPOTENCY_KEY_PATTERN.test(idempotencyKey)) { + throw new AidenRemoteServiceError("invalid_request", "Idempotency-Key is invalid.", 400); + } + const parsed = parseCreate(input); + try { + return await this.executeIdempotent( + { + deviceId, + route: "POST /workspaces", + resourceId: "workspace-registry", + key: idempotencyKey, + }, + parsed, + async () => { + let workspace: Workspace; + if (parsed.mode === "folderless") { + workspace = await this.options.application.create({ + name: parsed.name, + permission: "ask", + }); + } else if (parsed.mode === "scratch") { + workspace = await this.options.application.createScratch(); + } else { + const selection = await this.options.browser.consumeSelection( + deviceId, + parsed.selection, + ); + try { + workspace = await this.options.application.createFromFolder( + selection.canonicalPath, + parsed.name, + { + assertCurrent: async (identity) => { + const current = await this.options.browser.revalidateConsumedSelection( + deviceId, + selection, + ); + if ( + identity.canonicalPath !== current.canonicalPath || + identity.filesystemDevice !== current.filesystemDevice || + identity.filesystemInode !== current.filesystemInode + ) { + throw new AidenRemoteServiceError( + "filesystem_identity_changed", + "The selected folder changed before Aiden could register it.", + 409, + ); + } + }, + }, + ); + } catch (error) { + if (error instanceof Error && /already registered/u.test(error.message)) { + throw new AidenRemoteServiceError( + "already_exists", + "That folder is already registered as an Aiden workspace.", + 409, + ); + } + throw error; + } + } + this.options.notifyChanged?.(); + return projectAidenRemoteWorkspace(workspace); + }, + ); + } catch (error) { + if (error instanceof AidenRemoteServiceError) throw error; + mapOperationError(error); + } + } + + async update( + workspaceId: string, + expectedRevision: string, + input: unknown, + ): Promise { + const id = safeWorkspaceId(workspaceId); + const current = await this.options.application.get(id); + if (!current) { + throw new AidenRemoteServiceError("not_found", "This Aiden workspace no longer exists.", 404); + } + requireWorkspaceRevision(expectedRevision, current); + const patch = parsePatch(input); + const updated = await this.options.application.update(id, patch, { + assertCurrent: (workspace) => requireWorkspaceRevision(expectedRevision, workspace), + }); + this.options.notifyChanged?.(); + return projectAidenRemoteWorkspace(updated); + } + + async remove(workspaceId: string, expectedRevision: string): Promise { + const id = safeWorkspaceId(workspaceId); + const current = await this.options.application.get(id); + if (!current) { + throw new AidenRemoteServiceError("not_found", "This Aiden workspace no longer exists.", 404); + } + requireWorkspaceRevision(expectedRevision, current); + try { + await this.options.application.remove(id, { + assertCurrent: (workspace) => requireWorkspaceRevision(expectedRevision, workspace), + }); + } catch (error) { + if (error instanceof Error && /managed worktree|Delete worktree/u.test(error.message)) { + throw new AidenRemoteServiceError( + "workspace_unavailable", + "Managed worktrees must be removed through Aiden's worktree workflow.", + 409, + ); + } + throw error; + } + this.options.notifyChanged?.(); + } +} diff --git a/main/services/chat-application-service-main.ts b/main/services/chat-application-service-main.ts new file mode 100644 index 00000000..1fd759de --- /dev/null +++ b/main/services/chat-application-service-main.ts @@ -0,0 +1,22 @@ +import { logger } from "../platform.js"; +import { chatStore } from "./chat-store.js"; +import { configStore } from "./config-store.js"; +import { llmClient } from "./llm-client.js"; +import { piCompactionSessionStore } from "./pi-compaction-session-store.js"; +import { piRuntimeEffectStore } from "./pi-runtime-effect-store.js"; +import { subagentRunStore } from "./subagents/subagent-run-store.js"; +import { workspaceMutationGate } from "./workspace-mutation-gate.js"; +import { workspaceOperationRegistry } from "./workspace-operation-registry.js"; +import { createChatApplicationService } from "./chat-application-service.js"; + +export const chatApplicationService = createChatApplicationService({ + chatStore, + configStore, + llmClient, + workspaceMutationGate, + workspaceOperationRegistry, + subagentRunStore, + piRuntimeEffectStore, + piCompactionSessionStore, + logError: (area, message, error) => logger.error(area, message, error), +}); diff --git a/main/services/chat-application-service.test.ts b/main/services/chat-application-service.test.ts new file mode 100644 index 00000000..0ba6c6fc --- /dev/null +++ b/main/services/chat-application-service.test.ts @@ -0,0 +1,186 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import type { Chat, Workspace } from "./types.js"; +import { + createChatApplicationService, + type ChatApplicationDependencies, + type ChatApplicationOwner, +} from "./chat-application-service.js"; +import { WorkspaceOperationRegistry } from "./workspace-operation-registry.js"; + +function chat(id = "chat-1", workspaceId = "workspace-1"): Chat { + return { + id, + title: "Chat", + workspaceId, + createdAt: 1, + updatedAt: 1, + messages: [], + }; +} + +function owner(): ChatApplicationOwner & { invalidate(): void } { + let destroyed = false; + const listeners = new Set<() => void>(); + return { + documentId: "renderer:document-1", + isDestroyed: () => destroyed, + onInvalidated: (listener) => { + listeners.add(listener); + return () => listeners.delete(listener); + }, + invalidate: () => { + destroyed = true; + for (const listener of [...listeners]) listener(); + }, + }; +} + +function fixture(overrides: Partial = {}) { + const workspace: Workspace = { + id: "workspace-1", + name: "Workspace", + permission: "ask", + createdAt: 1, + updatedAt: 1, + }; + let assertCreateCurrent: (() => void) | undefined; + let finishDeletionCalls = 0; + const deps = { + chatStore: { + list: async () => [chat()], + get: async (id: string) => chat(id), + create: async (input: { assertCurrent?: () => void; workspaceId?: string }) => { + assertCreateCurrent = input.assertCurrent; + input.assertCurrent?.(); + return chat("created", input.workspaceId); + }, + rename: async () => chat(), + moveEmptyChatToWorkspace: async (id: string, workspaceId: string) => + chat(id, workspaceId), + remove: async () => undefined, + }, + configStore: { getWorkspace: async (id: string) => id === workspace.id ? workspace : null }, + llmClient: { + isChatOwnedByInactiveRenderer: () => false, + waitForChatIdle: async () => true, + requiresAppendReconciliation: () => false, + markAppendReconciliationRequired: () => undefined, + clearAppendReconciliationRequired: () => undefined, + beginChatWorkspaceChange: () => () => undefined, + beginChatDeletion: () => () => { finishDeletionCalls += 1; }, + cancelChat: async () => undefined, + }, + workspaceMutationGate: { + admit: () => ({ signal: new AbortController().signal, release: () => undefined }), + }, + workspaceOperationRegistry: new WorkspaceOperationRegistry(), + subagentRunStore: { + deleteChat: async () => undefined, + completeChatDeletion: async () => undefined, + pendingChatDeletions: async () => [], + }, + piRuntimeEffectStore: { deleteChat: async () => undefined }, + piCompactionSessionStore: { deleteChat: async () => undefined }, + logError: () => undefined, + ...overrides, + } as unknown as ChatApplicationDependencies; + return { + service: createChatApplicationService(deps), + assertCreateCurrent: () => assertCreateCurrent?.(), + finishDeletionCalls: () => finishDeletionCalls, + }; +} + +test("shared chat creation preserves workspace and renderer-owner commit gates", async () => { + const application = fixture(); + const requestOwner = owner(); + const created = await application.service.create( + { workspaceId: "workspace-1", title: "Chat" }, + requestOwner, + ); + assert.equal(created?.id, "created"); + requestOwner.invalidate(); + assert.throws(application.assertCreateCurrent, /renderer document is no longer active/u); + + await assert.rejects( + application.service.create( + { workspaceId: "missing", title: "Chat" }, + owner(), + ), + /selected workspace is no longer available/u, + ); +}); + +test("shared chat reads retain inactive-renderer reconciliation semantics", async () => { + let checks = 0; + const application = fixture({ + llmClient: { + isChatOwnedByInactiveRenderer: () => ++checks <= 2, + waitForChatIdle: async () => false, + requiresAppendReconciliation: () => false, + markAppendReconciliationRequired: () => undefined, + clearAppendReconciliationRequired: () => undefined, + beginChatWorkspaceChange: () => () => undefined, + beginChatDeletion: () => () => undefined, + cancelChat: async () => undefined, + } as ChatApplicationDependencies["llmClient"], + }); + assert.deepEqual((await application.service.get("chat-1")).reconciliation, { + chatId: "chat-1", + workspaceId: "workspace-1", + }); +}); + +test("shared chat deletion keeps admission closed while a durable delete is pending", async () => { + const events: string[] = []; + const application = fixture({ + chatStore: { + list: async () => [], + get: async () => chat(), + create: async () => chat(), + rename: async () => chat(), + moveEmptyChatToWorkspace: async () => chat(), + remove: async () => { throw new Error("disk failed"); }, + } as ChatApplicationDependencies["chatStore"], + subagentRunStore: { + deleteChat: async () => { events.push("private-delete"); }, + completeChatDeletion: async () => { events.push("complete"); }, + pendingChatDeletions: async () => ["chat-1"], + } as ChatApplicationDependencies["subagentRunStore"], + piRuntimeEffectStore: { deleteChat: async () => { events.push("effects-delete"); } }, + piCompactionSessionStore: { deleteChat: async () => { events.push("compaction-delete"); } }, + }); + + await assert.rejects(application.service.remove("chat-1"), /disk failed/u); + assert.deepEqual(events, ["private-delete", "effects-delete", "compaction-delete"]); + assert.equal(application.finishDeletionCalls(), 0); +}); + +test("chat deletion checks a remote revision before cancellation or private-history changes", async () => { + const effects: string[] = []; + const application = fixture({ + llmClient: { + isChatOwnedByInactiveRenderer: () => false, + waitForChatIdle: async () => true, + requiresAppendReconciliation: () => false, + markAppendReconciliationRequired: () => undefined, + clearAppendReconciliationRequired: () => undefined, + beginChatWorkspaceChange: () => () => undefined, + beginChatDeletion: () => () => undefined, + cancelChat: async () => { effects.push("cancel"); }, + } as ChatApplicationDependencies["llmClient"], + subagentRunStore: { + deleteChat: async () => { effects.push("subagents"); }, + completeChatDeletion: async () => undefined, + pendingChatDeletions: async () => [], + }, + }); + await assert.rejects( + application.service.remove("chat-1", { + assertCurrent: () => { throw new Error("stale revision"); }, + }), + /stale revision/u, + ); + assert.deepEqual(effects, []); +}); diff --git a/main/services/chat-application-service.ts b/main/services/chat-application-service.ts new file mode 100644 index 00000000..8c2d9532 --- /dev/null +++ b/main/services/chat-application-service.ts @@ -0,0 +1,217 @@ +import { ASSISTANT_WORKSPACE_ID } from "../../renderer/shared/assistant.js"; +import { appendReconciliationFailureMessage } from "../../renderer/shared/chat-message-contract.js"; +import { persistedChatWorkspaceId } from "../../renderer/shared/chat-workspace.js"; +import type { ParsedPublicChatCreate } from "../handlers/chat-create-params.js"; +import type { chatStore } from "./chat-store.js"; +import type { configStore } from "./config-store.js"; +import { isChatCreateReconciliationRequiredError } from "./chat-store-core.js"; +import type { llmClient } from "./llm-client.js"; +import type { Chat } from "./types.js"; +import type { piCompactionSessionStore } from "./pi-compaction-session-store.js"; +import type { piRuntimeEffectStore } from "./pi-runtime-effect-store.js"; +import type { subagentRunStore } from "./subagents/subagent-run-store.js"; +import { chatForRenderer } from "./visible-chat-projection.js"; +import type { workspaceMutationGate } from "./workspace-mutation-gate.js"; +import { + admitOwnedWorkspaceOperation, + type workspaceOperationRegistry, + type WorkspaceOperationDocumentOwner, +} from "./workspace-operation-registry.js"; + +export interface ChatApplicationOwner extends WorkspaceOperationDocumentOwner { + documentId: string; +} + +export interface ChatApplicationMutationOptions { + assertCurrent?: (chat: Chat) => void | Promise; +} + +export interface ChatApplicationDependencies { + chatStore: Pick< + typeof chatStore, + "list" | "get" | "create" | "rename" | "moveEmptyChatToWorkspace" | "remove" + >; + configStore: Pick; + llmClient: Pick< + typeof llmClient, + | "isChatOwnedByInactiveRenderer" + | "waitForChatIdle" + | "requiresAppendReconciliation" + | "markAppendReconciliationRequired" + | "clearAppendReconciliationRequired" + | "beginChatWorkspaceChange" + | "beginChatDeletion" + | "cancelChat" + >; + workspaceMutationGate: Pick; + workspaceOperationRegistry: typeof workspaceOperationRegistry; + subagentRunStore: Pick< + typeof subagentRunStore, + "deleteChat" | "completeChatDeletion" | "pendingChatDeletions" + >; + piRuntimeEffectStore: Pick; + piCompactionSessionStore: Pick; + logError(area: string, message: string, error: unknown): void; +} + +export function createChatApplicationService(deps: ChatApplicationDependencies) { + const markReconciliationRequired = (owner: ChatApplicationOwner): never => { + deps.llmClient.markAppendReconciliationRequired(owner.documentId); + owner.onInvalidated(() => { + deps.llmClient.clearAppendReconciliationRequired(owner.documentId); + }); + throw new Error(appendReconciliationFailureMessage("blocked")); + }; + + return { + list(workspaceId?: string) { + return deps.chatStore.list(workspaceId); + }, + + async get(chatId: string) { + let reconciliationRequired = false; + if (deps.llmClient.isChatOwnedByInactiveRenderer(chatId)) { + reconciliationRequired = !(await deps.llmClient.waitForChatIdle(chatId)); + } + const chat = await deps.chatStore.get(chatId); + reconciliationRequired ||= deps.llmClient.isChatOwnedByInactiveRenderer(chatId); + return { + chat: chatForRenderer(chat), + reconciliation: reconciliationRequired + ? { + chatId, + workspaceId: persistedChatWorkspaceId(chat?.workspaceId), + } + : null, + }; + }, + + waitUntilIdle(chatId: string) { + return deps.llmClient.waitForChatIdle(chatId); + }, + + async create(input: ParsedPublicChatCreate, owner: ChatApplicationOwner) { + if (deps.llmClient.requiresAppendReconciliation(owner.documentId)) { + throw new Error(appendReconciliationFailureMessage("blocked")); + } + if (input.workspaceId === ASSISTANT_WORKSPACE_ID) { + throw new Error("Aiden Assistant chats require the Assistant chat creation path."); + } + const mutationAdmission = deps.workspaceMutationGate.admit(input.workspaceId); + let workspaceOperation: + ReturnType | undefined; + try { + workspaceOperation = admitOwnedWorkspaceOperation( + deps.workspaceOperationRegistry, + owner, + input.workspaceId, + ); + if (!(await deps.configStore.getWorkspace(input.workspaceId))) { + throw new Error("The selected workspace is no longer available."); + } + const assertCurrent = () => { + if (owner.isDestroyed()) { + throw new Error("The renderer document is no longer active."); + } + if (mutationAdmission.signal.aborted || workspaceOperation?.signal.aborted) { + throw new Error("The workspace changed before the chat was created."); + } + if (deps.llmClient.requiresAppendReconciliation(owner.documentId)) { + throw new Error(appendReconciliationFailureMessage("blocked")); + } + }; + try { + return chatForRenderer(await deps.chatStore.create({ ...input, assertCurrent })); + } catch (error) { + if (isChatCreateReconciliationRequiredError(error)) { + return markReconciliationRequired(owner); + } + throw error; + } + } finally { + workspaceOperation?.release(); + mutationAdmission.release(); + } + }, + + rename(chatId: string, title: string, options: ChatApplicationMutationOptions = {}) { + return deps.chatStore.rename(chatId, title, async (chat) => options.assertCurrent?.(chat)); + }, + + async moveEmptyToWorkspace( + chatId: string, + workspaceId: string, + options: ChatApplicationMutationOptions = {}, + ) { + const finishMove = deps.llmClient.beginChatWorkspaceChange(chatId); + if (!finishMove) { + throw new Error("Finish or stop the current response before changing workspaces."); + } + try { + if (!(await deps.configStore.getWorkspace(workspaceId))) { + throw new Error(`Workspace ${workspaceId} not found.`); + } + return chatForRenderer( + await deps.chatStore.moveEmptyChatToWorkspace( + chatId, + workspaceId, + async (chat) => options.assertCurrent?.(chat), + ), + ); + } finally { + finishMove(); + } + }, + + async remove( + chatId: string, + options: ChatApplicationMutationOptions = {}, + ): Promise { + const finishDeletion = deps.llmClient.beginChatDeletion(chatId); + let releaseAdmission = false; + try { + const current = await deps.chatStore.get(chatId); + if (!current) throw new Error(`Chat ${chatId} not found`); + await options.assertCurrent?.(current); + await deps.llmClient.cancelChat(chatId); + try { + await deps.subagentRunStore.deleteChat(chatId); + } catch (error) { + deps.logError("subagents", "Could not delete private subagent history.", error); + throw new Error("Aiden could not delete this chat's subagent history."); + } + try { + await deps.piRuntimeEffectStore.deleteChat(chatId); + } catch (error) { + deps.logError("pi", "Could not delete private Pi effect history.", error); + throw new Error("Aiden could not delete this chat's tool-effect history."); + } + try { + await deps.piCompactionSessionStore.deleteChat(chatId); + } catch (error) { + deps.logError("pi", "Could not delete the private compaction journal.", error); + throw new Error("Aiden could not delete this chat's compaction history."); + } + await deps.chatStore.remove(chatId, async (chat) => { + if (!chat) throw new Error(`Chat ${chatId} not found`); + await options.assertCurrent?.(chat); + }); + await deps.subagentRunStore.completeChatDeletion(chatId); + releaseAdmission = true; + } finally { + if (!releaseAdmission) { + try { + releaseAdmission = !(await deps.subagentRunStore.pendingChatDeletions()).includes(chatId); + } catch (error) { + deps.logError( + "subagents", + "Could not inspect pending chat deletion state.", + error, + ); + } + } + if (releaseAdmission) finishDeletion(); + } + }, + }; +} diff --git a/main/services/chat-generation-owner.test.ts b/main/services/chat-generation-owner.test.ts new file mode 100644 index 00000000..4ee29a64 --- /dev/null +++ b/main/services/chat-generation-owner.test.ts @@ -0,0 +1,52 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { createRemoteChatGenerationOwner } from "./chat-generation-owner.js"; + +test("remote generation ownership survives delivery disconnects until explicit invalidation", () => { + const delivered: Array<[string, unknown]> = []; + let transportAvailable = true; + const remote = createRemoteChatGenerationOwner({ + deviceId: "paired-device-secret-shaped-id", + streamId: "stream-1", + publish: (channel, payload) => { + if (!transportAvailable) throw new Error("subscriber disconnected"); + delivered.push([channel, payload]); + }, + }); + + assert.equal(remote.owner.id, 0); + assert.equal(remote.owner.documentId.includes("paired-device-secret-shaped-id"), false); + remote.owner.send("chat:delta", { delta: "hello" }); + assert.deepEqual(delivered, [["chat:delta", { delta: "hello" }]]); + + transportAvailable = false; + assert.throws(() => remote.owner.send("chat:delta", { delta: "offline" }), /disconnected/u); + assert.equal(remote.owner.isDestroyed(), false); + + let invalidations = 0; + const remove = remote.owner.onInvalidated(() => { invalidations += 1; }); + remote.invalidate(); + remote.invalidate(); + remove(); + assert.equal(invalidations, 1); + assert.equal(remote.owner.isDestroyed(), true); + assert.throws( + () => remote.owner.send("chat:done", { streamId: "stream-1" }), + /no longer active/u, + ); +}); + +test("remote generation owners reject unbounded identities", () => { + assert.throws( + () => createRemoteChatGenerationOwner({ deviceId: "", streamId: "stream", publish: () => undefined }), + /device identity/u, + ); + assert.throws( + () => createRemoteChatGenerationOwner({ + deviceId: "device", + streamId: "s".repeat(129), + publish: () => undefined, + }), + /stream identity/u, + ); +}); diff --git a/main/services/chat-generation-owner.ts b/main/services/chat-generation-owner.ts index 098530ef..c92e9b6c 100644 --- a/main/services/chat-generation-owner.ts +++ b/main/services/chat-generation-owner.ts @@ -1,6 +1,96 @@ -import { rendererDocumentOwner, type RendererDocumentOwner } from "./renderer-document-owner.js"; +import { createHash } from "node:crypto"; +import type { NotificationChannel } from "../../renderer/preload-channels.js"; +import { rendererDocumentOwner } from "./renderer-document-owner.js"; -export type ChatGenerationOwner = RendererDocumentOwner; +/** + * Delivery and lifecycle authority for one generation. Renderer documents, + * background services, and paired remote devices implement this same narrow + * boundary without gaining each other's capabilities. + */ +export interface ChatGenerationOwner { + /** Nonzero values identify renderer WebContents; headless owners use zero. */ + id: number; + /** Stable turn-admission identity, independent of a network connection. */ + documentId: string; + isDestroyed(): boolean; + send(channel: NotificationChannel, payload: unknown): void; + onInvalidated(listener: () => void): () => void; +} + +export interface RemoteChatGenerationOwnerController { + owner: ChatGenerationOwner; + /** Explicit revocation/terminal cleanup; transport disconnect is not revocation. */ + invalidate(): void; +} + +function boundedRemoteIdentity(value: unknown, label: string): string { + if ( + typeof value !== "string" || + value.length === 0 || + value.length > 128 || + Buffer.byteLength(value, "utf8") > 512 + ) { + throw new Error(`Invalid remote ${label}.`); + } + return value; +} + +function identityDigest(value: string): string { + return createHash("sha256").update(value).digest("base64url"); +} + +/** + * Create a paired-device generation owner whose authority survives socket and + * SSE subscriber disconnects. The supplied publisher must target a durable + * stream journal; only explicit invalidation revokes generation ownership. + */ +export function createRemoteChatGenerationOwner(input: { + deviceId: string; + streamId: string; + publish(channel: NotificationChannel, payload: unknown): void; +}): RemoteChatGenerationOwnerController { + const deviceId = boundedRemoteIdentity(input.deviceId, "device identity"); + const streamId = boundedRemoteIdentity(input.streamId, "stream identity"); + let invalidated = false; + const listeners = new Set<() => void>(); + const owner: ChatGenerationOwner = { + id: 0, + documentId: `remote:${identityDigest(deviceId)}:${identityDigest(streamId)}`, + isDestroyed: () => invalidated, + send: (channel, payload) => { + if (invalidated) throw new Error("The remote generation owner is no longer active."); + input.publish(channel, payload); + }, + onInvalidated: (listener) => { + if (invalidated) { + listener(); + return () => undefined; + } + listeners.add(listener); + let active = true; + return () => { + if (!active) return; + active = false; + listeners.delete(listener); + }; + }, + }; + return { + owner, + invalidate: () => { + if (invalidated) return; + invalidated = true; + for (const listener of [...listeners]) { + listeners.delete(listener); + try { + listener(); + } catch { + // Revocation must notify every listener even if one cleanup fails. + } + } + }, + }; +} /** Bind a generation, its stream, cancellation, and approvals to one renderer document. */ export function chatGenerationOwner(event: Electron.IpcMainInvokeEvent): ChatGenerationOwner { diff --git a/main/services/chat-store-core.ts b/main/services/chat-store-core.ts index 396c941f..dcef6b34 100644 --- a/main/services/chat-store-core.ts +++ b/main/services/chat-store-core.ts @@ -747,13 +747,19 @@ export function createChatStore( }); }, - async rename(id: string, title: string): Promise { + async rename( + id: string, + title: string, + assertCurrent: (chat: Chat) => void | Promise = () => undefined, + ): Promise { return serialized(async () => { const chat = await readChat(id); if (!chat) throw new Error(`Chat ${id} not found`); + await assertCurrent(chat); chat.title = title.trim() || chat.title; chat.updatedAt = Date.now(); await writeChatAndMeta(chat); + return chat; }); }, @@ -779,10 +785,12 @@ export function createChatStore( async moveEmptyChatToWorkspace( id: string, workspaceId: string, + assertCurrent: (chat: Chat) => void | Promise = () => undefined, ): Promise { return serialized(async () => { const chat = await readChat(id); if (!chat) throw new Error(`Chat ${id} not found`); + await assertCurrent(chat); if (chat.messages.length > 0) { throw new Error("Only a new chat can change workspaces."); } @@ -815,8 +823,13 @@ export function createChatStore( }); }, - async remove(id: string): Promise { + async remove( + id: string, + assertCurrent?: (chat: Chat | null) => void | Promise, + ): Promise { return serialized(async () => { + const chat = await readChat(id); + if (assertCurrent) await assertCurrent(chat); const payload = await chatPath(id); await removeCrashLeftStages(path.dirname(payload)); let removedPayload = false; diff --git a/main/services/provider-list-main.ts b/main/services/provider-list-main.ts new file mode 100644 index 00000000..af04173e --- /dev/null +++ b/main/services/provider-list-main.ts @@ -0,0 +1,19 @@ +import { logger } from "../platform.js"; +import { listProvidersWithLegacyPiCredentialMigration } from "./legacy-pi-credential-migration.js"; +import { mergeCodexProvider } from "./provider-list-core.js"; +import { providerRegistry } from "./provider-registry.js"; + +/** Authoritative configured provider list shared by Electron and Remote Access. */ +export async function listConfiguredProviders() { + const customProviders = await listProvidersWithLegacyPiCredentialMigration(); + const providers = [ + ...(await providerRegistry.listBuiltinProviders()), + ...customProviders.filter((provider) => !providerRegistry.isBuiltinProvider(provider.id)), + ]; + try { + return mergeCodexProvider(providers, await providerRegistry.codex.snapshot()); + } catch { + logger.warn("providers", "ChatGPT / Codex status was unavailable while listing providers."); + return mergeCodexProvider(providers, null); + } +} diff --git a/main/services/schedule-execution.ts b/main/services/schedule-execution.ts index 61927b34..f52bc5c2 100644 --- a/main/services/schedule-execution.ts +++ b/main/services/schedule-execution.ts @@ -374,7 +374,7 @@ export function createScheduleExecution(store: ScheduleStore = scheduleStore) { } return { - async run(task: ScheduledTask): Promise { + async run(task: ScheduledTask, runId?: string): Promise { if (activeControllers.has(task.id)) { throw new Error("This scheduled task is already running."); } @@ -415,6 +415,7 @@ export function createScheduleExecution(store: ScheduleStore = scheduleStore) { } try { const run = await store.recordRun({ + id: runId, taskId: task.id, startedAt, finishedAt: Date.now(), diff --git a/main/services/schedule-service-core.ts b/main/services/schedule-service-core.ts index b2c80498..4987a2da 100644 --- a/main/services/schedule-service-core.ts +++ b/main/services/schedule-service-core.ts @@ -7,7 +7,7 @@ import type { } from "./types.js"; interface ScheduleExecutionLike { - run(task: ScheduledTask): Promise; + run(task: ScheduledTask, runId?: string): Promise; cancel(taskId: string): boolean; cancelAll(): void; } @@ -107,7 +107,7 @@ export function createScheduleServiceCore( function dispatch( taskId: string, - options: { automatic: boolean }, + options: { automatic: boolean; runId?: string }, ): Promise { if (runningTasks.has(taskId)) { throw new Error("This scheduled task is already running."); @@ -142,7 +142,7 @@ export function createScheduleServiceCore( const claimed = options.automatic ? await advanceBeforeRun(task) : task; if (state.cancelRequested) throw new Error("This scheduled task was cancelled."); - return execution.run(claimed); + return execution.run(claimed, options.runId); } finally { if (!workspaceResolved) { workspaceResolved = true; @@ -374,10 +374,13 @@ export function createScheduleServiceCore( async remove( id: string, - options: { signal?: AbortSignal } = {}, + options: { signal?: AbortSignal; expectedUpdatedAt?: number } = {}, ): Promise { await withTaskLifecycle(id, async () => { const current = await store.get(id); + if (options.expectedUpdatedAt !== undefined && current?.updatedAt !== options.expectedUpdatedAt) { + throw new Error("This automation changed. Refresh it before trying again."); + } throwIfAborted(options.signal, "removal"); stopJob(id); await cancelAndSettle(id); @@ -392,10 +395,13 @@ export function createScheduleServiceCore( async pause( id: string, - options: { signal?: AbortSignal } = {}, + options: { signal?: AbortSignal; expectedUpdatedAt?: number } = {}, ): Promise { return withTaskLifecycle(id, async () => { const current = await store.get(id); + if (options.expectedUpdatedAt !== undefined && current?.updatedAt !== options.expectedUpdatedAt) { + throw new Error("This automation changed. Refresh it before trying again."); + } throwIfAborted(options.signal, "pause"); stopJob(id); await cancelAndSettle(id); @@ -419,10 +425,13 @@ export function createScheduleServiceCore( async resume( id: string, - options: { signal?: AbortSignal } = {}, + options: { signal?: AbortSignal; expectedUpdatedAt?: number } = {}, ): Promise { return withTaskLifecycle(id, async () => { const current = await store.get(id); + if (options.expectedUpdatedAt !== undefined && current?.updatedAt !== options.expectedUpdatedAt) { + throw new Error("This automation changed. Refresh it before trying again."); + } throwIfAborted(options.signal, "resume"); const task = await store.setEnabled(id, true); const restore = async () => { @@ -448,10 +457,10 @@ export function createScheduleServiceCore( runNow( id: string, - options: { signal?: AbortSignal } = {}, + options: { signal?: AbortSignal; runId?: string } = {}, ): Promise { throwIfAborted(options.signal, "run"); - const operation = dispatch(id, { automatic: false }); + const operation = dispatch(id, { automatic: false, runId: options.runId }); if (!options.signal) return operation; const cancel = () => { const state = runningTasks.get(id); diff --git a/main/services/scheduled-task-application-service-main.ts b/main/services/scheduled-task-application-service-main.ts new file mode 100644 index 00000000..ee4472ea --- /dev/null +++ b/main/services/scheduled-task-application-service-main.ts @@ -0,0 +1,26 @@ +import { ipcMain } from "../platform.js"; +import { configStore } from "./config-store.js"; +import { selectedMcpServers } from "./mcp-selection.js"; +import { listScheduledScripts } from "./schedule-script.js"; +import { scheduleService } from "./schedule-service.js"; +import { nextScheduledRuns, scheduleStore, systemTimezone, validateTimezone } from "./schedule-store.js"; +import { scheduledSettingsPatch } from "./scheduled-settings-core.js"; +import { createScheduledTaskApplicationService } from "./scheduled-task-application-service.js"; + +export const scheduledTaskApplicationService = createScheduledTaskApplicationService({ + store: scheduleStore, + service: scheduleService, + getSettings: () => configStore.getSettings(), + setSettings: (patch) => configStore.setSettings(patch), + getWorkspace: (id) => configStore.getWorkspace(id), + listMcpServers: () => configStore.listMcpServers(), + validateMcpSelection: (configured, selected) => { + selectedMcpServers(configured, selected); + }, + listScripts: listScheduledScripts, + nextRuns: nextScheduledRuns, + systemTimezone, + validateTimezone, + settingsPatch: (input) => scheduledSettingsPatch(input, validateTimezone), + notifyChanged: (payload) => ipcMain.broadcast("schedule:updated", payload), +}); diff --git a/main/services/scheduled-task-application-service.test.ts b/main/services/scheduled-task-application-service.test.ts new file mode 100644 index 00000000..bd248af3 --- /dev/null +++ b/main/services/scheduled-task-application-service.test.ts @@ -0,0 +1,119 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { createScheduledTaskApplicationService, scheduledTaskRevision } from "./scheduled-task-application-service.js"; +import type { AppSettings, ScheduledRun, ScheduledTask, ScheduledTaskInput } from "./types.js"; + +function fixture() { + let clock = 10; + let settings: AppSettings = {}; + const tasks = new Map(); + const runs = new Map(); + const savedOptions: Array<{ expectedUpdatedAt?: number; runId?: string }> = []; + const service = createScheduledTaskApplicationService({ + store: { + list: async () => [...tasks.values()], + get: async (id) => tasks.get(id), + runs: async (id) => runs.get(id) ?? [], + }, + service: { + save: async (input: ScheduledTaskInput, options = {}) => { + savedOptions.push({ expectedUpdatedAt: options.expectedUpdatedAt }); + const existing = input.id ? tasks.get(input.id) : undefined; + if (options.expectedUpdatedAt !== undefined && existing?.updatedAt !== options.expectedUpdatedAt) { + throw new Error("revision"); + } + clock += 1; + const task: ScheduledTask = { + id: existing?.id ?? "task-1", name: input.name, enabled: input.enabled ?? true, + mode: input.mode, cron: input.cron, timezone: input.timezone ?? "UTC", + workspaceId: input.workspaceId, providerId: input.providerId, model: input.model, + prompt: input.prompt, script: input.script, permission: input.permission ?? "read-only", + mcpServerIds: input.mcpServerIds, notify: input.notify ?? true, + createdAt: existing?.createdAt ?? clock, updatedAt: clock, + }; + tasks.set(task.id, task); + return task; + }, + remove: async (id, options = {}) => { + savedOptions.push({ expectedUpdatedAt: options.expectedUpdatedAt }); + if (tasks.get(id)?.updatedAt !== options.expectedUpdatedAt) throw new Error("revision"); + tasks.delete(id); + }, + pause: async (id, options = {}) => { + savedOptions.push({ expectedUpdatedAt: options.expectedUpdatedAt }); + const task = tasks.get(id)!; + if (task.updatedAt !== options.expectedUpdatedAt) throw new Error("revision"); + const next = { ...task, enabled: false, updatedAt: ++clock }; + tasks.set(id, next); + return next; + }, + resume: async (id, options = {}) => { + const task = tasks.get(id)!; + if (task.updatedAt !== options.expectedUpdatedAt) throw new Error("revision"); + const next = { ...task, enabled: true, updatedAt: ++clock }; + tasks.set(id, next); + return next; + }, + runNow: async (id, options = {}) => { + savedOptions.push({ runId: options.runId }); + const run: ScheduledRun = { id: options.runId ?? "run-local", taskId: id, startedAt: 1, finishedAt: 2, result: "success", output: "ok" }; + runs.set(id, [run]); + return run; + }, + setGlobalEnabled: async () => undefined, + isRunning: () => false, + }, + getSettings: async () => settings, + setSettings: async (patch) => (settings = { ...settings, ...patch }), + getWorkspace: async (id) => id === "workspace-1" ? ({ id, name: "One", permission: "full", folderPath: "/safe", createdAt: 1, updatedAt: 1 }) : undefined, + listMcpServers: async () => [], + validateMcpSelection: () => undefined, + listScripts: async () => ["safe.sh"], + nextRuns: (_cron, _timezone, count) => Array.from({ length: count }, (_, index) => index + 1), + systemTimezone: () => "UTC", + validateTimezone: (value) => value, + settingsPatch: (input) => ({ ...(typeof input.enabled === "boolean" ? { scheduledTasksEnabled: input.enabled } : {}) }), + notifyChanged: () => undefined, + }); + return { service, tasks, savedOptions }; +} + +test("shared scheduled-task service validates inventory and carries revisions into lifecycle commits", async () => { + const value = fixture(); + await assert.rejects( + value.service.save({ name: "Unsafe", mode: "script", cron: "* * * * *", timezone: "UTC", permission: "full", script: "raw.sh" }), + /inventory/u, + ); + const task = await value.service.save({ name: "Safe", mode: "script", cron: "* * * * *", timezone: "UTC", permission: "full", script: "safe.sh", workspaceId: "workspace-1" }); + const revision = scheduledTaskRevision(task); + await assert.rejects(value.service.pause(task.id, "rev_stale"), /changed/u); + const paused = await value.service.pause(task.id, revision); + assert.equal(paused.enabled, false); + assert.equal(value.savedOptions[value.savedOptions.length - 1]?.expectedUpdatedAt, task.updatedAt); +}); + +test("shared scheduled-task service carries a caller-owned run ID and revision-checks settings", async () => { + const value = fixture(); + const task = await value.service.save({ name: "Run", mode: "llm", cron: "* * * * *", timezone: "UTC", permission: "read-only", prompt: "hello" }); + await value.service.runNow(task.id, "run_remote"); + assert.equal(value.savedOptions[value.savedOptions.length - 1]?.runId, "run_remote"); + const current = await value.service.settings(); + await assert.rejects(value.service.updateSettings("rev_stale", { enabled: false }), /changed/u); + const updated = await value.service.updateSettings(current.revision, { enabled: false }); + assert.equal(updated.value.enabled, false); +}); + +test("concurrent desktop and remote lifecycle edits admit only one revision", async () => { + const value = fixture(); + const task = await value.service.save({ + name: "Concurrent", mode: "llm", cron: "* * * * *", timezone: "UTC", + permission: "read-only", prompt: "hello", + }); + const revision = scheduledTaskRevision(task); + const results = await Promise.allSettled([ + value.service.pause(task.id, revision), + value.service.pause(task.id, revision), + ]); + assert.equal(results.filter((result) => result.status === "fulfilled").length, 1); + assert.equal(results.filter((result) => result.status === "rejected").length, 1); +}); diff --git a/main/services/scheduled-task-application-service.ts b/main/services/scheduled-task-application-service.ts new file mode 100644 index 00000000..d651e058 --- /dev/null +++ b/main/services/scheduled-task-application-service.ts @@ -0,0 +1,236 @@ +import { createHash } from "node:crypto"; +import type { + AppSettings, + McpServer, + ScheduledRun, + ScheduledTask, + ScheduledTaskInput, + ScheduledTaskSettings, + Workspace, +} from "./types.js"; + +export interface ScheduledTaskApplicationDependencies { + store: { + list(): Promise; + get(id: string): Promise; + runs(id: string): Promise; + }; + service: { + save( + input: ScheduledTaskInput, + options?: { expectedUpdatedAt?: number; signal?: AbortSignal }, + ): Promise; + remove(id: string, options?: { signal?: AbortSignal; expectedUpdatedAt?: number }): Promise; + pause(id: string, options?: { signal?: AbortSignal; expectedUpdatedAt?: number }): Promise; + resume(id: string, options?: { signal?: AbortSignal; expectedUpdatedAt?: number }): Promise; + runNow( + id: string, + options?: { signal?: AbortSignal; runId?: string }, + ): Promise; + setGlobalEnabled(enabled: boolean): Promise; + isRunning(id: string): boolean; + }; + getSettings(): Promise; + setSettings(patch: Partial): Promise; + getWorkspace(id: string): Promise; + listMcpServers(): Promise; + validateMcpSelection(configured: readonly McpServer[], selected: readonly string[]): void; + listScripts(input: { workspaceRoot?: string }): Promise; + nextRuns(cron: string, timezone: string, count: number): number[]; + systemTimezone(): string; + validateTimezone(value: string): string; + settingsPatch(input: Record): Partial; + notifyChanged(payload: Record): void; +} + +export interface RevisionedScheduledTaskSettings { + revision: string; + value: ScheduledTaskSettings; +} + +function settingsValue( + input: AppSettings, + dependencies: Pick, +): ScheduledTaskSettings { + return { + enabled: input.scheduledTasksEnabled !== false, + defaultMode: input.scheduledDefaultMode === "script" ? "script" : "llm", + defaultPermission: input.scheduledDefaultPermission === "full" ? "full" : "read-only", + defaultMcpEnabled: input.scheduledDefaultMcpEnabled === true, + defaultNotify: input.scheduledDefaultNotify !== false, + defaultTimezone: dependencies.validateTimezone( + input.scheduledDefaultTimezone ?? dependencies.systemTimezone(), + ), + }; +} + +export function scheduledTaskRevision(task: ScheduledTask): string { + return `rev_${createHash("sha256") + .update(JSON.stringify({ id: task.id, updatedAt: task.updatedAt })) + .digest("base64url")}`; +} + +export function scheduledSettingsRevision(settings: ScheduledTaskSettings): string { + return `rev_${createHash("sha256") + .update(JSON.stringify(settings)) + .digest("base64url")}`; +} + +function expectedTaskRevision(task: ScheduledTask, expected?: string): number | undefined { + if (expected === undefined) return undefined; + if (scheduledTaskRevision(task) !== expected) { + throw new Error("This automation changed. Refresh it before trying again."); + } + return task.updatedAt; +} + +/** Shared main-process schedule orchestration for Electron and Aiden Remote. */ +export function createScheduledTaskApplicationService( + dependencies: ScheduledTaskApplicationDependencies, +) { + let settingsTail: Promise = Promise.resolve(); + + const validateInput = async (input: ScheduledTaskInput): Promise => { + if (input.workspaceId) { + const workspace = await dependencies.getWorkspace(input.workspaceId); + if (!workspace) throw new Error(`Workspace ${input.workspaceId} not found.`); + if (workspace.permission === "none") { + throw new Error("Scheduled tasks require a workspace with local access."); + } + } + if ((input.mcpServerIds?.length ?? 0) > 0) { + dependencies.validateMcpSelection( + await dependencies.listMcpServers(), + input.mcpServerIds!, + ); + } + if (input.mode === "script") { + const workspace = input.workspaceId + ? await dependencies.getWorkspace(input.workspaceId) + : undefined; + const scripts = await dependencies.listScripts({ + workspaceRoot: workspace?.folderPath, + }); + if (!input.script || !scripts.includes(input.script)) { + throw new Error("Select a script from Aiden's current script inventory."); + } + } + }; + + const get = async (id: string): Promise => { + const task = await dependencies.store.get(id); + if (!task) throw new Error(`Scheduled task ${id} not found.`); + return task; + }; + + const save = async ( + input: ScheduledTaskInput, + options: { expectedRevision?: string; signal?: AbortSignal } = {}, + ): Promise => { + await validateInput(input); + const existing = input.id ? await get(input.id) : undefined; + const task = await dependencies.service.save(input, { + ...(existing + ? { expectedUpdatedAt: expectedTaskRevision(existing, options.expectedRevision) } + : {}), + ...(options.signal ? { signal: options.signal } : {}), + }); + dependencies.notifyChanged({ taskId: task.id }); + return task; + }; + + const mutateExisting = async ( + id: string, + expectedRevision: string | undefined, + operation: (expectedUpdatedAt: number | undefined) => Promise, + ): Promise => { + const expectedUpdatedAt = expectedTaskRevision(await get(id), expectedRevision); + const result = await operation(expectedUpdatedAt); + dependencies.notifyChanged({ taskId: id }); + return result; + }; + + const settings = async (): Promise => { + const value = settingsValue(await dependencies.getSettings(), dependencies); + return { revision: scheduledSettingsRevision(value), value }; + }; + + const updateSettings = async ( + expectedRevision: string, + patch: Record, + ): Promise => { + const previous = settingsTail; + let release!: () => void; + settingsTail = new Promise((resolve) => { + release = resolve; + }); + await previous.catch(() => undefined); + try { + const current = await settings(); + if (current.revision !== expectedRevision) { + throw new Error("Scheduled-task settings changed. Refresh them before trying again."); + } + const parsed = dependencies.settingsPatch(patch); + if (Object.keys(parsed).length === 0) { + throw new Error("At least one scheduled-task setting must change."); + } + const saved = await dependencies.setSettings(parsed); + const next = settingsValue(saved, dependencies); + if (next.enabled !== current.value.enabled) { + await dependencies.service.setGlobalEnabled(next.enabled); + } + dependencies.notifyChanged({ settings: true }); + return { revision: scheduledSettingsRevision(next), value: next }; + } finally { + release(); + } + }; + + return { + list: () => dependencies.store.list(), + get, + save, + remove: (id: string, revision?: string, signal?: AbortSignal) => + mutateExisting(id, revision, (expectedUpdatedAt) => + dependencies.service.remove(id, { signal, expectedUpdatedAt })), + pause: (id: string, revision?: string, signal?: AbortSignal) => + mutateExisting(id, revision, (expectedUpdatedAt) => + dependencies.service.pause(id, { signal, expectedUpdatedAt })), + resume: (id: string, revision?: string, signal?: AbortSignal) => + mutateExisting(id, revision, (expectedUpdatedAt) => + dependencies.service.resume(id, { signal, expectedUpdatedAt })), + runNow: async (id: string, runId?: string) => { + await get(id); + const run = dependencies.service.runNow(id, { runId }); + if (runId) { + void run.catch(() => undefined); + return undefined; + } + return run; + }, + runs: async (id: string) => { + await get(id); + return dependencies.store.runs(id); + }, + preview: (cron: string, timezone: string, count = 3) => + dependencies.nextRuns(cron, timezone, count), + scripts: async (workspaceId?: string) => { + const workspace = workspaceId ? await dependencies.getWorkspace(workspaceId) : undefined; + if (workspaceId && !workspace) throw new Error(`Workspace ${workspaceId} not found.`); + if (workspace?.permission === "none") { + throw new Error("This workspace does not allow script inventory access."); + } + return dependencies.listScripts({ workspaceRoot: workspace?.folderPath }); + }, + mcpServers: async () => (await dependencies.listMcpServers()) + .filter((server) => server.enabled) + .map((server) => ({ id: server.id, name: server.name })), + settings, + updateSettings, + isRunning: (id: string) => dependencies.service.isRunning(id), + }; +} + +export type ScheduledTaskApplicationService = ReturnType< + typeof createScheduledTaskApplicationService +>; diff --git a/main/services/subagents/subagent-phase3-contract.test.ts b/main/services/subagents/subagent-phase3-contract.test.ts index ce3fe74c..03eac59e 100644 --- a/main/services/subagents/subagent-phase3-contract.test.ts +++ b/main/services/subagents/subagent-phase3-contract.test.ts @@ -77,15 +77,17 @@ test("historical inspector reads require a live document and matching chat owner }); test("run-store failures keep filesystem details out of renderer-visible errors", async () => { - const [llm, historyHandler, chatHandler] = await Promise.all([ + const [llm, historyHandler, chatHandler, chatApplicationService] = await Promise.all([ source("main/services/llm-client.ts"), source("main/handlers/subagents.ts"), source("main/handlers/chats.ts"), + source("main/services/chat-application-service.ts"), ]); assert.match(llm, /error: "local storage failed"/u); assert.match(historyHandler, /Aiden could not load subagent history/u); + assert.match(chatHandler, /chatApplicationService\.remove\(/u); assert.match( - chatHandler, + chatApplicationService, /Aiden could not delete this chat's subagent history/u, ); }); @@ -127,29 +129,31 @@ test("private run-store I/O is descriptor-bound, generation-checked, and package }); test("chat removal deletes private child history before the chat can disappear", async () => { - const [handler, llm] = await Promise.all([ + const [handler, applicationService, llm] = await Promise.all([ source("main/handlers/chats.ts"), + source("main/services/chat-application-service.ts"), source("main/services/llm-client.ts"), ]); - const beginDeletion = handler.indexOf("llmClient.beginChatDeletion(chatId)"); - const cancel = handler.indexOf("llmClient.cancelChat(chatId)"); - const deleteRuns = handler.indexOf( - "await subagentRunStore.deleteChat(chatId)", + assert.match(handler, /chatApplicationService\.remove\(asString\(id, "id"\)\)/u); + const beginDeletion = applicationService.indexOf("deps.llmClient.beginChatDeletion(chatId)"); + const cancel = applicationService.indexOf("deps.llmClient.cancelChat(chatId)"); + const deleteRuns = applicationService.indexOf( + "await deps.subagentRunStore.deleteChat(chatId)", cancel, ); - const deleteChat = handler.indexOf( - "await chatStore.remove(chatId)", + const deleteChat = applicationService.indexOf( + "await deps.chatStore.remove(chatId,", deleteRuns, ); - const completeDeletion = handler.indexOf( - "await subagentRunStore.completeChatDeletion(chatId)", + const completeDeletion = applicationService.indexOf( + "await deps.subagentRunStore.completeChatDeletion(chatId)", deleteChat, ); - const pendingDeletionCheck = handler.indexOf( - "await subagentRunStore.pendingChatDeletions()", + const pendingDeletionCheck = applicationService.indexOf( + "await deps.subagentRunStore.pendingChatDeletions()", completeDeletion, ); - const releaseAdmission = handler.indexOf( + const releaseAdmission = applicationService.indexOf( "if (releaseAdmission) finishDeletion()", completeDeletion, ); @@ -176,15 +180,16 @@ test("chat removal deletes private child history before the chat can disappear", assert.ok(registerInitialization > admissionCheck); assert.ok(requireExistingChat > registerInitialization); assert.doesNotMatch( - handler.slice(beginDeletion, deleteRuns), - /if \(!\(await chatStore\.get/u, + applicationService.slice(beginDeletion, deleteRuns), + /if \(!\(await deps\.chatStore\.get/u, ); }); test("renderer invalidation detaches while authority changes and shutdown still cancel", async () => { - const [llm, workspaces, main] = await Promise.all([ + const [llm, workspaces, workspaceApplicationService, main] = await Promise.all([ source("main/services/llm-client.ts"), source("main/handlers/workspaces.ts"), + source("main/services/workspace-application-service.ts"), source("main/index.ts"), ]); assert.match( @@ -193,13 +198,15 @@ test("renderer invalidation detaches while authority changes and shutdown still ); assert.match(llm, /this\.cancel\(streamId, "workspace_authority_change"\)/u); assert.match(llm, /this\.cancel\(streamId, "application_shutdown"\)/u); + assert.match(workspaces, /workspaceApplicationService\.update\(/u); + assert.match(workspaces, /workspaceApplicationService\.remove\(/u); assert.match( - workspaces, - /await llmClient\.cancelWorkspaceAndSettle\(existing\.id\)/u, + workspaceApplicationService, + /await deps\.llmClient\.cancelWorkspaceAndSettle\(existing\.id\)/u, ); assert.match( - workspaces, - /await llmClient\.cancelWorkspaceAndSettle\(workspaceId\)/u, + workspaceApplicationService, + /await deps\.llmClient\.cancelWorkspaceAndSettle\(id\)/u, ); assert.match(llm, /subagentRuntimeRegistry\.abortGeneration\(streamId\)/u); assert.match(llm, /subagentRuntimeRegistry\.abortChat\(chatId\)/u); @@ -217,8 +224,9 @@ test("renderer invalidation detaches while authority changes and shutdown still }); test("empty-chat workspace moves serialize against generation authority and terminal persistence", async () => { - const [handler, llm, chatStore] = await Promise.all([ + const [handler, applicationService, llm, chatStore] = await Promise.all([ source("main/handlers/chats.ts"), + source("main/services/chat-application-service.ts"), source("main/services/llm-client.ts"), source("main/services/chat-store-core.ts"), ]); @@ -226,20 +234,23 @@ test("empty-chat workspace moves serialize against generation authority and term const moveHandler = handler.indexOf( 'ipcMain.handle(\n "chats:moveEmptyToWorkspace"', ); - const beginMove = handler.indexOf( - "llmClient.beginChatWorkspaceChange(chatId)", - moveHandler, + assert.match( + handler.slice(moveHandler), + /chatApplicationService\.moveEmptyToWorkspace\(/u, + ); + const beginMove = applicationService.indexOf( + "deps.llmClient.beginChatWorkspaceChange(chatId)", ); - const workspaceLookup = handler.indexOf( - "await configStore.getWorkspace(nextWorkspaceId)", + const workspaceLookup = applicationService.indexOf( + "await deps.configStore.getWorkspace(workspaceId)", beginMove, ); - const moveCommit = handler.indexOf( - "await chatStore.moveEmptyChatToWorkspace(", + const moveCommit = applicationService.indexOf( + "await deps.chatStore.moveEmptyChatToWorkspace(", workspaceLookup, ); assert.ok(moveHandler >= 0); - assert.ok(beginMove > moveHandler); + assert.ok(beginMove >= 0); assert.ok(workspaceLookup > beginMove); assert.ok(moveCommit > workspaceLookup); @@ -318,8 +329,10 @@ test("renderer message appends serialize against detached terminal persistence", ); assert.match( handler, - /"chats:waitUntilIdle"[\s\S]{0,160}llmClient\.waitForChatIdle\(asString\(id, "id"\)\)/u, + /"chats:waitUntilIdle"[\s\S]{0,180}chatApplicationService\.waitUntilIdle\(asString\(id, "id"\)\)/u, ); + const applicationService = await source("main/services/chat-application-service.ts"); + assert.match(applicationService, /return deps\.llmClient\.waitForChatIdle\(chatId\)/u); }); test("renderer turn tokens cross append and generation IPC without an admission gap", async () => { @@ -371,24 +384,28 @@ test("main announces normalized settlement only after generation ownership exits }); test("replacement chat reads mark bounded wait timeouts for retained renderer reconciliation", async () => { - const [handler, llm] = await Promise.all([ + const [handler, applicationService, llm] = await Promise.all([ source("main/handlers/chats.ts"), + source("main/services/chat-application-service.ts"), source("main/services/llm-client.ts"), ]); const getHandler = ipcHandlerStart(handler, "chats:get"); - const inactiveCheck = handler.indexOf( - "llmClient.isChatOwnedByInactiveRenderer(chatId)", - getHandler, + assert.match( + handler.slice(getHandler), + /chatApplicationService\.get\(asString\(id, "id"\)\)/u, + ); + const inactiveCheck = applicationService.indexOf( + "deps.llmClient.isChatOwnedByInactiveRenderer(chatId)", ); - const idleWait = handler.indexOf( - "await llmClient.waitForChatIdle(chatId)", + const idleWait = applicationService.indexOf( + "await deps.llmClient.waitForChatIdle(chatId)", inactiveCheck, ); - const read = handler.indexOf( - "const chat = await chatStore.get(chatId)", + const read = applicationService.indexOf( + "const chat = await deps.chatStore.get(chatId)", idleWait, ); - const response = handler.indexOf( + const response = applicationService.indexOf( "reconciliation: reconciliationRequired", read, ); @@ -399,12 +416,12 @@ test("replacement chat reads mark bounded wait timeouts for retained renderer re assert.ok(read > idleWait); assert.ok(response > read); assert.match( - handler, - /reconciliationRequired = !\(await llmClient\.waitForChatIdle\(chatId\)\)/u, + applicationService, + /reconciliationRequired = !\(await deps\.llmClient\.waitForChatIdle\(chatId\)\)/u, ); assert.match( - handler, - /reconciliationRequired \|\|= llmClient\.isChatOwnedByInactiveRenderer\(chatId\)/u, + applicationService, + /reconciliationRequired \|\|= deps\.llmClient\.isChatOwnedByInactiveRenderer\(chatId\)/u, ); const inactiveOwnerStart = llm.indexOf( "isChatOwnedByInactiveRenderer(chatId: string)", @@ -458,9 +475,10 @@ test("application startup reconciles private runs and worktree deletions before }); test("persisted chat workspace ownership closes generation admission before setup", async () => { - const [llm, workspaces] = await Promise.all([ + const [llm, workspaces, workspaceApplicationService] = await Promise.all([ source("main/services/llm-client.ts"), source("main/handlers/workspaces.ts"), + source("main/services/workspace-application-service.ts"), ]); const chatRead = llm.indexOf( "const chat = await chatStore.get(params.chatId)", @@ -486,37 +504,41 @@ test("persisted chat workspace ownership closes generation admission before setu assert.ok(prepare > admissionCheck); const updateHandler = ipcHandlerStart(workspaces, "workspaces:update"); - const beginMutation = workspaces.indexOf( - "workspaceMutationGate.begin(workspaceId)", - updateHandler, + assert.match( + workspaces.slice(updateHandler), + /workspaceApplicationService\.update\(asString\(id, "id"\), patch\)/u, + ); + const beginMutation = workspaceApplicationService.indexOf( + "deps.workspaceMutationGate.begin(id)", ); - const drainWorkspaceOperations = workspaces.indexOf( - "await workspaceOperationRegistry.cancelAndSettle(workspaceId)", + const drainWorkspaceOperations = workspaceApplicationService.indexOf( + "await deps.workspaceOperationRegistry.cancelAndSettle(id)", beginMutation, ); - const cancelGeneration = workspaces.indexOf( - "await llmClient.cancelWorkspaceAndSettle(existing.id)", + const cancelGeneration = workspaceApplicationService.indexOf( + "await deps.llmClient.cancelWorkspaceAndSettle(existing.id)", beginMutation, ); - const saveWorkspace = workspaces.indexOf( - "configStore.saveWorkspace(next)", + const saveWorkspace = workspaceApplicationService.indexOf( + "deps.configStore.saveWorkspace(next)", cancelGeneration, ); assert.ok(updateHandler >= 0); - assert.ok(beginMutation > updateHandler); + assert.ok(beginMutation >= 0); assert.ok(drainWorkspaceOperations > beginMutation); assert.ok(cancelGeneration > beginMutation); assert.ok(cancelGeneration > drainWorkspaceOperations); assert.ok(saveWorkspace > cancelGeneration); assert.match( - workspaces.slice(updateHandler, saveWorkspace), - /await llmClient\.cancelWorkspaceAndSettle\(existing\.id\)/u, + workspaceApplicationService.slice(beginMutation, saveWorkspace), + /await deps\.llmClient\.cancelWorkspaceAndSettle\(existing\.id\)/u, ); }); test("managed worktree deletion and terminal creation share workspace mutation admission", async () => { - const [workspaces, terminal, terminalService, git] = await Promise.all([ + const [workspaces, worktreeApplicationService, terminal, terminalService, git] = await Promise.all([ source("main/handlers/workspaces.ts"), + source("main/services/workspace-worktree-application-service.ts"), source("main/handlers/terminal.ts"), source("main/services/terminal.ts"), source("main/services/git.ts"), @@ -525,23 +547,26 @@ test("managed worktree deletion and terminal creation share workspace mutation a workspaces, "git:deleteManagedWorktree", ); - const beginMutation = workspaces.indexOf( - "workspaceMutationGate.begin(id)", - deleteHandler, + const beginMutation = worktreeApplicationService.indexOf( + "dependencies.beginWorkspaceMutation(workspaceId)", ); - const destructiveDelete = workspaces.indexOf( - "gitDeleteManagedWorktree(", - deleteHandler, + const destructiveDelete = worktreeApplicationService.indexOf( + "dependencies.deleteManagedWorktree(managed, signal)", + beginMutation, ); assert.ok(deleteHandler >= 0); - assert.ok(beginMutation > deleteHandler); - assert.ok(destructiveDelete > beginMutation); assert.match( workspaces.slice(deleteHandler), - /withWorkspaceRecordOperation\([\s\S]+worktreeRegistered:[\s\S]+gitManagedWorktreeRegistered\([\s\S]+managed\.worktreeGitDir,[\s\S]+managed\.ownershipToken/u, + /workspaceWorktreeApplicationService\.remove\(owner, id\)/u, ); + assert.ok(beginMutation >= 0); + assert.ok(destructiveDelete > beginMutation); assert.match( - workspaces.slice(deleteHandler), + worktreeApplicationService, + /environment\.runRecord\([\s\S]+worktreeRegistered:[\s\S]+managedWorktreeRegistered\(managed\)/u, + ); + assert.match( + worktreeApplicationService.slice(beginMutation), /reconciledResult: \(\) => \(\{ branchDeleted: false \}\)/u, ); assert.match( @@ -700,14 +725,16 @@ test("managed worktree cleanup is root-bound, resumable, and packaged as signed }); test("every workspace path capability is renderer-document owned and mutation admitted", async () => { - const [workspaces, operations, git] = await Promise.all([ + const [workspaces, environment, operations, git] = await Promise.all([ source("main/handlers/workspaces.ts"), + source("main/services/workspace-environment-application-service.ts"), source("main/services/workspace-operation-registry.ts"), source("main/services/git.ts"), ]); assert.match(workspaces, /rendererDocumentOwner\(\s*event,/u); - assert.match(workspaces, /admitRendererOwnedWorkspaceOperation\(/u); + assert.match(workspaces, /workspaceEnvironmentApplicationService\.run(?:Optional)?\(/u); + assert.match(environment, /admitOwnedWorkspaceOperation\(/u); assert.doesNotMatch(workspaces, /sender\.once\("destroyed"/u); assert.match(operations, /owner\.onInvalidated\(cancel\)/u); @@ -748,11 +775,12 @@ test("every workspace path capability is renderer-document owned and mutation ad }); test("managed worktree identity gates generation, terminal, scheduled, and workspace capabilities", async () => { - const [llm, terminal, scheduled, workspaces, admission] = await Promise.all([ + const [llm, terminal, scheduled, workspaces, environment, admission] = await Promise.all([ source("main/services/llm-client.ts"), source("main/handlers/terminal.ts"), source("main/services/schedule-execution.ts"), source("main/handlers/workspaces.ts"), + source("main/services/workspace-environment-application-service.ts"), source("main/services/managed-worktree-admission.ts"), ]); assert.match( @@ -776,8 +804,8 @@ test("managed worktree identity gates generation, terminal, scheduled, and works /executeLlm[\s\S]+assertManagedWorktreeAdmission\(workspace\)/u, ); assert.match( - workspaces, - /workspaceDirectory[\s\S]+assertManagedWorktreeAdmission\(workspace\)/u, + environment, + /resolve[\s\S]+assertManagedWorktreeAdmission\(workspace\)/u, ); assert.match( workspaces, @@ -790,9 +818,11 @@ test("managed worktree identity gates generation, terminal, scheduled, and works }); test("terminal writes pause across workspace mutations and documents lose PTYs on reload", async () => { - const [terminal, workspaces, main] = await Promise.all([ + const [terminal, workspaces, workspaceApplicationService, worktreeApplicationService, main] = await Promise.all([ source("main/handlers/terminal.ts"), source("main/handlers/workspaces.ts"), + source("main/services/workspace-application-service.ts"), + source("main/services/workspace-worktree-application-service.ts"), source("main/index.ts"), ]); const accessCheck = terminal.indexOf("async function ensureSessionAccess"); @@ -825,41 +855,46 @@ test("terminal writes pause across workspace mutations and documents lose PTYs o assert.ok(guardedWrite > writeHandler); const updateHandler = ipcHandlerStart(workspaces, "workspaces:update"); - const permissionChange = workspaces.indexOf( - "if (next.permission !== existing.permission)", - updateHandler, + assert.match( + workspaces.slice(updateHandler), + /workspaceApplicationService\.update\(asString\(id, "id"\), patch\)/u, + ); + const updateMethod = workspaceApplicationService.indexOf("async update("); + const permissionChange = workspaceApplicationService.indexOf( + "if (next.permission === existing.permission)", + updateMethod, ); - const updateTerminalClose = workspaces.indexOf( - "terminalService.closeForWorkspace(existing.id)", + const updateTerminalClose = workspaceApplicationService.indexOf( + "deps.terminalService.closeForWorkspace(existing.id)", permissionChange, ); - const updateScheduleRestoration = workspaces.lastIndexOf( + const updateScheduleRestoration = workspaceApplicationService.lastIndexOf( "withWorkspaceScheduleRestoration(", updateTerminalClose, ); - const updateScheduleCancel = workspaces.indexOf( - "await scheduleService.cancelWorkspace(existing.id)", + const updateScheduleCancel = workspaceApplicationService.indexOf( + "await deps.scheduleService.cancelWorkspace(existing.id)", permissionChange, ); assert.ok(updateTerminalClose > permissionChange); assert.ok(updateScheduleRestoration > permissionChange); assert.ok(updateTerminalClose > updateScheduleRestoration); - const updateGenerationDrain = workspaces.indexOf( - "await llmClient.cancelWorkspaceAndSettle(existing.id)", + const updateGenerationDrain = workspaceApplicationService.indexOf( + "await deps.llmClient.cancelWorkspaceAndSettle(existing.id)", permissionChange, ); assert.ok(updateGenerationDrain > updateTerminalClose); assert.ok(updateScheduleCancel > updateTerminalClose); - const updateSave = workspaces.indexOf( - "await configStore.saveWorkspace(next)", + const updateSave = workspaceApplicationService.indexOf( + "await deps.configStore.saveWorkspace(next)", updateScheduleCancel, ); - const armPostSaveResume = workspaces.indexOf( + const armPostSaveResume = workspaceApplicationService.indexOf( "ensureResumedOnExit()", updateSave, ); - const firstPostSaveResume = workspaces.indexOf( - "await scheduleService.resumeWorkspace(saved.id)", + const firstPostSaveResume = workspaceApplicationService.indexOf( + "await deps.scheduleService.resumeWorkspace(saved.id)", armPostSaveResume, ); assert.ok(updateSave > updateScheduleCancel); @@ -867,38 +902,43 @@ test("terminal writes pause across workspace mutations and documents lose PTYs o assert.ok(firstPostSaveResume > armPostSaveResume); const removeHandler = ipcHandlerStart(workspaces, "workspaces:remove"); - const removeTerminalClose = workspaces.indexOf( - "terminalService.closeForWorkspace(workspaceId)", - removeHandler, + assert.match( + workspaces.slice(removeHandler), + /workspaceApplicationService\.remove\(asString\(id, "id"\)\)/u, ); - const removeWorkspaceRead = workspaces.indexOf( - "await configStore.getWorkspace(workspaceId)", - removeHandler, + const removeMethod = workspaceApplicationService.indexOf("async remove("); + const removeTerminalClose = workspaceApplicationService.indexOf( + "deps.terminalService.closeForWorkspace(id)", + removeMethod, ); - const managedRemovalGuard = workspaces.indexOf( + const removeWorkspaceRead = workspaceApplicationService.indexOf( + "await deps.configStore.getWorkspace(id)", + removeMethod, + ); + const managedRemovalGuard = workspaceApplicationService.indexOf( "assertWorkspaceRecordRemovalAllowed(existing)", removeWorkspaceRead, ); - const removeOperationDrain = workspaces.indexOf( - "await workspaceOperationRegistry.cancelAndSettle(workspaceId)", - removeHandler, + const removeOperationDrain = workspaceApplicationService.indexOf( + "await deps.workspaceOperationRegistry.cancelAndSettle(id)", + removeMethod, ); - const removeScheduleRestoration = workspaces.indexOf( + const removeScheduleRestoration = workspaceApplicationService.indexOf( "withWorkspaceScheduleRestoration(", removeWorkspaceRead, ); - assert.ok(removeTerminalClose > removeHandler); - assert.ok(removeOperationDrain > removeTerminalClose); + assert.ok(removeTerminalClose > removeMethod); + assert.ok(removeOperationDrain > removeMethod); assert.ok(removeWorkspaceRead > removeOperationDrain); - assert.ok(removeWorkspaceRead > removeTerminalClose); - assert.ok(managedRemovalGuard > removeWorkspaceRead); + assert.ok(removeTerminalClose > removeWorkspaceRead); + assert.ok(managedRemovalGuard > removeTerminalClose); assert.ok(removeScheduleRestoration > managedRemovalGuard); - const removeGenerationDrain = workspaces.indexOf( - "await llmClient.cancelWorkspaceAndSettle(workspaceId)", + const removeGenerationDrain = workspaceApplicationService.indexOf( + "await deps.llmClient.cancelWorkspaceAndSettle(id)", removeWorkspaceRead, ); - const removeRecord = workspaces.indexOf( - "await configStore.removeWorkspace(workspaceId)", + const removeRecord = workspaceApplicationService.indexOf( + "await deps.configStore.removeWorkspace(id)", removeGenerationDrain, ); assert.ok(removeGenerationDrain > removeWorkspaceRead); @@ -909,63 +949,68 @@ test("terminal writes pause across workspace mutations and documents lose PTYs o workspaces, "git:deleteManagedWorktree", ); - const managedTerminalClose = workspaces.indexOf( - "terminalService.closeForWorkspace(id)", - managedDelete, + assert.match( + workspaces.slice(managedDelete), + /workspaceWorktreeApplicationService\.remove\(owner, id\)/u, + ); + const managedRemove = worktreeApplicationService.indexOf("const remove = async ("); + const managedTerminalClose = worktreeApplicationService.indexOf( + "dependencies.closeWorkspaceTerminals(workspaceId)", + managedRemove, ); - const managedScheduleCancel = workspaces.indexOf( - "await scheduleService.cancelWorkspace(id)", - managedDelete, + const managedScheduleCancel = worktreeApplicationService.indexOf( + "await dependencies.cancelWorkspaceSchedules(workspaceId)", + managedTerminalClose, ); - const managedScheduleRestoration = workspaces.indexOf( + const managedScheduleRestoration = worktreeApplicationService.indexOf( "withWorkspaceScheduleRestoration(", - managedDelete, + managedRemove, ); - assert.ok(managedTerminalClose > managedDelete); - assert.ok(managedScheduleRestoration > managedDelete); + assert.ok(managedTerminalClose > managedRemove); + assert.ok(managedScheduleRestoration > managedRemove); assert.ok(managedTerminalClose > managedScheduleRestoration); - const managedGenerationDrain = workspaces.indexOf( - "await llmClient.cancelWorkspaceAndSettle(id)", + const managedGenerationDrain = worktreeApplicationService.indexOf( + "await dependencies.cancelWorkspaceGeneration(workspaceId)", managedTerminalClose, ); - const managedOperationDrain = workspaces.indexOf( - "await workspaceOperationRegistry.cancelAndSettle(id", - managedDelete, + const managedOperationDrain = worktreeApplicationService.indexOf( + "await dependencies.cancelWorkspaceOperations(workspaceId, signal)", + managedRemove, ); - const managedWorktreeRemoval = workspaces.indexOf( - "const result = await removeManagedWorkspace", + const managedWorktreeRemoval = worktreeApplicationService.indexOf( + "const deletion = await removeManagedWorkspace", managedGenerationDrain, ); - const managedDeletionFinalize = workspaces.indexOf( - "await gitFinalizeManagedWorktreeDeletion", + const managedDeletionFinalize = worktreeApplicationService.indexOf( + "await dependencies.finalizeManagedWorktreeDeletion(managed)", managedWorktreeRemoval, ); - const managedDeleteReturn = workspaces.indexOf( - "return result", + const managedDeleteReturn = worktreeApplicationService.indexOf( + "return deletion", managedDeletionFinalize, ); assert.ok(managedGenerationDrain > managedTerminalClose); - assert.ok(managedOperationDrain > managedDelete); + assert.ok(managedOperationDrain > managedRemove); assert.ok(managedGenerationDrain > managedOperationDrain); assert.ok(managedWorktreeRemoval > managedGenerationDrain); assert.ok(managedDeletionFinalize > managedWorktreeRemoval); assert.ok(managedDeleteReturn > managedDeletionFinalize); assert.ok(managedScheduleCancel > managedTerminalClose); assert.match( - workspaces.slice(managedDelete), + worktreeApplicationService.slice(managedRemove), /destructiveMutationAttempted:[\s\S]+GitManagedWorktreeDeleteError[\s\S]+destructiveMutationAttempted/u, ); assert.match( - workspaces.slice(managedDelete), - /worktreeUsable:[\s\S]+gitManagedWorktreeUsable\(/u, + worktreeApplicationService.slice(managedRemove), + /worktreeUsable:[\s\S]+managedWorktreeUsable\(managed\)/u, ); assert.match( - workspaces.slice(managedDelete), - /deletionPending:[\s\S]+gitManagedWorktreeDeletionPending\(/u, + worktreeApplicationService.slice(managedRemove), + /deletionPending:[\s\S]+managedWorktreeDeletionPending\(managed\)/u, ); assert.match( - workspaces, - /commitManagedWorktreeCreation\([\s\S]+removeWorkspaceRecord:[\s\S]+configStore\.removeWorkspace\(saved\.id\)[\s\S]+rollbackWorktree:/u, + worktreeApplicationService, + /commitManagedWorktreeCreation\([\s\S]+removeWorkspaceRecord:[\s\S]+removeWorkspace\(savedWorkspace\.id\)[\s\S]+rollbackWorktree:/u, ); const didStartLoading = main.indexOf('webContents.on("did-start-loading"'); diff --git a/main/services/workspace-application-service-main.ts b/main/services/workspace-application-service-main.ts new file mode 100644 index 00000000..b5a0f024 --- /dev/null +++ b/main/services/workspace-application-service-main.ts @@ -0,0 +1,29 @@ +import * as fs from "node:fs/promises"; +import { logger } from "../platform.js"; +import { configStore } from "./config-store.js"; +import { llmClient } from "./llm-client.js"; +import { scheduleService } from "./schedule-service.js"; +import { createScratchWorkspaceDirectory } from "./scratch-workspace.js"; +import { terminalService } from "./terminal.js"; +import { workspaceMutationGate } from "./workspace-mutation-gate.js"; +import { workspaceOperationRegistry } from "./workspace-operation-registry.js"; +import { + createWorkspaceApplicationService, + defaultWorkspaceId, +} from "./workspace-application-service.js"; + +export const workspaceApplicationService = createWorkspaceApplicationService({ + configStore, + llmClient, + scheduleService, + terminalService, + workspaceMutationGate, + workspaceOperationRegistry, + createScratchWorkspaceDirectory, + realpath: (value) => fs.realpath(value), + stat: (value) => fs.stat(value), + removeEmptyDirectory: (value) => fs.rmdir(value), + createId: defaultWorkspaceId, + now: Date.now, + logError: (area, message, error) => logger.error(area, message, error), +}); diff --git a/main/services/workspace-application-service.test.ts b/main/services/workspace-application-service.test.ts new file mode 100644 index 00000000..8b5547fa --- /dev/null +++ b/main/services/workspace-application-service.test.ts @@ -0,0 +1,188 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import type { Workspace } from "./types.js"; +import { + createWorkspaceApplicationService, + type WorkspaceApplicationDependencies, +} from "./workspace-application-service.js"; +import { WorkspaceMutationGate } from "./workspace-mutation-gate.js"; +import { WorkspaceOperationRegistry } from "./workspace-operation-registry.js"; + +function workspace(overrides: Partial = {}): Workspace { + return { + id: "workspace-1", + name: "Workspace", + permission: "ask", + createdAt: 1, + updatedAt: 1, + ...overrides, + }; +} + +function fixture(options: { existing?: Workspace | null; saveError?: Error } = {}) { + const events: string[] = []; + const saved: Workspace[] = []; + let existing = options.existing === undefined ? workspace() : options.existing; + const deps = { + configStore: { + listWorkspaces: async () => existing ? [existing] : [], + getWorkspace: async () => existing, + saveWorkspace: async (value: Workspace) => { + events.push("save"); + if (options.saveError) throw options.saveError; + existing = value; + saved.push(value); + return value; + }, + removeWorkspace: async () => { events.push("remove-record"); existing = null; }, + }, + llmClient: { + cancelWorkspaceAndSettle: async () => { events.push("cancel-generations"); }, + }, + scheduleService: { + cancelWorkspace: async () => { events.push("cancel-schedules"); }, + resumeWorkspace: async () => { events.push("resume-schedules"); }, + }, + terminalService: { + closeForWorkspace: () => { events.push("close-terminal"); }, + }, + workspaceMutationGate: new WorkspaceMutationGate(), + workspaceOperationRegistry: new WorkspaceOperationRegistry(), + createScratchWorkspaceDirectory: async () => ({ + name: "Scratch", + folderPath: "/tmp/aiden-scratch-test", + }), + realpath: async (value: string) => value, + stat: async () => ({ isDirectory: () => true }), + removeEmptyDirectory: async () => { events.push("remove-empty-directory"); }, + createId: () => "workspace-new", + now: () => 123, + logError: () => undefined, + } as unknown as WorkspaceApplicationDependencies; + return { + service: createWorkspaceApplicationService(deps), + deps, + events, + saved, + }; +} + +test("shared workspace creation preserves defaults and rejects path authority", async () => { + const application = fixture({ existing: null }); + const created = await application.service.create({ name: " Project ", permission: "invalid" }); + assert.deepEqual(created, { + id: "workspace-new", + name: "Project", + permission: "ask", + createdAt: 123, + updatedAt: 123, + }); + assert.throws( + () => application.service.create({ folderPath: "/private/escape" }), + /folder picker/u, + ); +}); + +test("shared workspace permission updates preserve cancellation and schedule restoration gates", async () => { + const application = fixture({ existing: workspace({ permission: "full" }) }); + const updated = await application.service.update("workspace-1", { + name: " Renamed ", + permission: "none", + }); + assert.equal(updated.name, "Renamed"); + assert.equal(updated.permission, "none"); + assert.deepEqual(application.events, [ + "close-terminal", + "cancel-generations", + "cancel-schedules", + "save", + ]); + + // The mutation lease must have released after the first update. + await application.service.update("workspace-1", { name: "Again" }); + assert.equal(application.saved[application.saved.length - 1]?.name, "Again"); +}); + +test("shared workspace mutations assert the current revision inside their mutation lease", async () => { + const application = fixture(); + await assert.rejects( + application.service.update( + "workspace-1", + { name: "Must not save" }, + { assertCurrent: () => { throw new Error("revision changed"); } }, + ), + /revision changed/u, + ); + assert.deepEqual(application.events, []); + + await assert.rejects( + application.service.remove( + "workspace-1", + { assertCurrent: () => { throw new Error("revision changed"); } }, + ), + /revision changed/u, + ); + assert.deepEqual(application.events, []); +}); + +test("shared workspace removal never unregisters a managed worktree", async () => { + const application = fixture({ + existing: workspace({ + folderPath: "/repo/worktree", + managedWorktree: { + repositoryPath: "/repo", + worktreePath: "/repo/worktree", + branch: "feature", + createdFromHead: "abc123", + }, + }), + }); + await assert.rejects(application.service.remove("workspace-1"), /Delete worktree/u); + assert.deepEqual(application.events, ["close-terminal"]); + + // The mutation lease releases even when removal is refused. + await assert.rejects(application.service.remove("workspace-1"), /Delete worktree/u); +}); + +test("shared scratch creation removes an empty directory when persistence fails", async () => { + const application = fixture({ + existing: null, + saveError: new Error("persistence failed"), + }); + await assert.rejects(application.service.createScratch(), /persistence failed/u); + assert.deepEqual(application.events, ["save", "remove-empty-directory"]); +}); + +test("shared folder creation serializes duplicate registration and accepts a reviewed display name", async () => { + const application = fixture({ existing: null }); + const [first, second] = await Promise.allSettled([ + application.service.createFromFolder("/approved/project", "Remote Project"), + application.service.createFromFolder("/approved/project", "Duplicate"), + ]); + assert.equal(first.status, "fulfilled"); + assert.equal(first.status === "fulfilled" ? first.value.name : "", "Remote Project"); + assert.equal(second.status, "rejected"); + assert.match( + second.status === "rejected" ? String(second.reason) : "", + /already registered/u, + ); + assert.equal(application.saved.length, 1); +}); + +test("shared folder creation revalidates selected identity before persistence", async () => { + const application = fixture({ existing: null }); + await assert.rejects( + application.service.createFromFolder( + "/approved/project", + "Project", + { + assertCurrent: ({ canonicalPath }) => { + assert.equal(canonicalPath, "/approved/project"); + throw new Error("selected folder changed"); + }, + }, + ), + /selected folder changed/u, + ); + assert.equal(application.saved.length, 0); +}); diff --git a/main/services/workspace-application-service.ts b/main/services/workspace-application-service.ts new file mode 100644 index 00000000..e7c66a28 --- /dev/null +++ b/main/services/workspace-application-service.ts @@ -0,0 +1,268 @@ +import * as path from "node:path"; +import type { configStore } from "./config-store.js"; +import type { llmClient } from "./llm-client.js"; +import type { scheduleService } from "./schedule-service.js"; +import type { createScratchWorkspaceDirectory } from "./scratch-workspace.js"; +import type { terminalService } from "./terminal.js"; +import type { Workspace, WorkspacePermission } from "./types.js"; +import type { workspaceMutationGate } from "./workspace-mutation-gate.js"; +import type { workspaceOperationRegistry } from "./workspace-operation-registry.js"; +import { assertWorkspaceRecordRemovalAllowed } from "./workspace-record-removal.js"; +import { withWorkspaceScheduleRestoration } from "./workspace-schedule-restoration.js"; + +const PERMISSIONS: readonly WorkspacePermission[] = ["full", "ask", "none"]; + +function nonEmptyString(value: unknown, name: string): string { + if (typeof value !== "string" || value.length === 0) { + throw new Error(`Expected non-empty string for "${name}".`); + } + return value; +} + +function defaultWorkspaceId(): string { + return `w-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; +} + +export interface WorkspaceApplicationDependencies { + configStore: Pick< + typeof configStore, + "listWorkspaces" | "getWorkspace" | "saveWorkspace" | "removeWorkspace" + >; + llmClient: Pick; + scheduleService: Pick< + typeof scheduleService, + "cancelWorkspace" | "resumeWorkspace" + >; + terminalService: Pick; + workspaceMutationGate: Pick; + workspaceOperationRegistry: Pick; + createScratchWorkspaceDirectory: typeof createScratchWorkspaceDirectory; + realpath(value: string): Promise; + stat(value: string): Promise<{ + isDirectory(): boolean; + dev?: number | bigint; + ino?: number | bigint; + }>; + removeEmptyDirectory(value: string): Promise; + createId(): string; + now(): number; + logError(area: string, message: string, error: unknown): void; +} + +export interface WorkspaceApplicationMutationOptions { + assertCurrent?: (workspace: Workspace) => void; +} + +export interface WorkspaceFolderCreationOptions { + assertCurrent?: (identity: { + canonicalPath: string; + filesystemDevice?: string; + filesystemInode?: string; + }) => Promise | void; +} + +export function createWorkspaceApplicationService(deps: WorkspaceApplicationDependencies) { + let folderCreationTail: Promise = Promise.resolve(); + + const serializeFolderCreation = (operation: () => Promise): Promise => { + const result = folderCreationTail.then(operation, operation); + folderCreationTail = result.then(() => undefined, () => undefined); + return result; + }; + + const saveFolderWorkspace = async ( + folderPath: string, + permission: WorkspacePermission, + name?: string, + options: WorkspaceFolderCreationOptions = {}, + ): Promise => { + return serializeFolderCreation(async () => { + const canonicalPath = await deps.realpath(folderPath); + const identity = await deps.stat(canonicalPath); + if (!identity.isDirectory()) { + throw new Error("Choose a folder for this workspace."); + } + await options.assertCurrent?.({ + canonicalPath, + ...(identity.dev === undefined ? {} : { filesystemDevice: String(identity.dev) }), + ...(identity.ino === undefined ? {} : { filesystemInode: String(identity.ino) }), + }); + const existing = await deps.configStore.listWorkspaces(); + if (existing.some((workspace) => workspace.folderPath === canonicalPath)) { + throw new Error("That folder is already registered as an Aiden workspace."); + } + const now = deps.now(); + return deps.configStore.saveWorkspace({ + id: deps.createId(), + name: name?.trim() || path.basename(canonicalPath) || "Workspace", + folderPath: canonicalPath, + permission, + createdAt: now, + updatedAt: now, + }); + }); + }; + + return { + list() { + return deps.configStore.listWorkspaces(); + }, + + async get(workspaceId: string) { + return (await deps.configStore.getWorkspace(workspaceId)) ?? null; + }, + + create(input: unknown) { + const fields = ( + typeof input === "object" && input !== null ? input : {} + ) as Record; + if ("folderPath" in fields) { + throw new Error("Choose workspace folders through Aiden's folder picker."); + } + const name = + (typeof fields.name === "string" && fields.name.trim()) || "Workspace"; + const permission = PERMISSIONS.includes(fields.permission as WorkspacePermission) + ? (fields.permission as WorkspacePermission) + : "ask"; + const now = deps.now(); + return deps.configStore.saveWorkspace({ + id: deps.createId(), + name, + permission, + createdAt: now, + updatedAt: now, + }); + }, + + createFromFolder( + folderPath: string, + name?: string, + options: WorkspaceFolderCreationOptions = {}, + ) { + return saveFolderWorkspace( + nonEmptyString(folderPath, "folderPath"), + "ask", + name, + options, + ); + }, + + async createScratch() { + const scratch = await deps.createScratchWorkspaceDirectory(); + const now = deps.now(); + const workspace: Workspace = { + id: deps.createId(), + name: scratch.name, + folderPath: scratch.folderPath, + permission: "ask", + createdAt: now, + updatedAt: now, + }; + try { + return await deps.configStore.saveWorkspace(workspace); + } catch (error) { + await deps.removeEmptyDirectory(scratch.folderPath).catch(() => undefined); + throw error; + } + }, + + async update( + workspaceId: string, + patch: unknown, + options: WorkspaceApplicationMutationOptions = {}, + ) { + const id = nonEmptyString(workspaceId, "id"); + const finishMutation = deps.workspaceMutationGate.begin(id); + try { + await deps.workspaceOperationRegistry.cancelAndSettle(id); + const existing = await deps.configStore.getWorkspace(id); + if (!existing) throw new Error(`Workspace ${id} not found.`); + options.assertCurrent?.(existing); + const fields = ( + typeof patch === "object" && patch !== null ? patch : {} + ) as Record; + if ("folderPath" in fields) { + throw new Error("Workspace folders cannot be changed from renderer input."); + } + const next: Workspace = { + ...existing, + name: + typeof fields.name === "string" && fields.name.trim() + ? fields.name.trim() + : existing.name, + permission: PERMISSIONS.includes(fields.permission as WorkspacePermission) + ? (fields.permission as WorkspacePermission) + : existing.permission, + }; + if (next.permission === existing.permission) { + return await deps.configStore.saveWorkspace(next); + } + return await withWorkspaceScheduleRestoration( + { + restoreOnExit: existing.permission !== "none", + resume: () => deps.scheduleService.resumeWorkspace(existing.id), + onResumeError: (error) => { + deps.logError( + "schedule", + "Could not restore scheduled tasks after workspace update failed.", + error, + ); + }, + }, + async ({ ensureResumedOnExit, keepPaused }) => { + deps.terminalService.closeForWorkspace(existing.id); + await deps.llmClient.cancelWorkspaceAndSettle(existing.id); + await deps.scheduleService.cancelWorkspace(existing.id); + const saved = await deps.configStore.saveWorkspace(next); + if (saved.permission !== "none") { + ensureResumedOnExit(); + await deps.scheduleService.resumeWorkspace(saved.id); + } + keepPaused(); + return saved; + }, + ); + } finally { + finishMutation(); + } + }, + + async remove( + workspaceId: string, + options: WorkspaceApplicationMutationOptions = {}, + ): Promise { + const id = nonEmptyString(workspaceId, "id"); + const finishMutation = deps.workspaceMutationGate.begin(id); + try { + await deps.workspaceOperationRegistry.cancelAndSettle(id); + const existing = await deps.configStore.getWorkspace(id); + if (existing) options.assertCurrent?.(existing); + deps.terminalService.closeForWorkspace(id); + assertWorkspaceRecordRemovalAllowed(existing); + await withWorkspaceScheduleRestoration( + { + restoreOnExit: existing?.permission !== "none", + resume: () => deps.scheduleService.resumeWorkspace(id), + onResumeError: (error) => { + deps.logError( + "schedule", + "Could not restore scheduled tasks after workspace removal failed.", + error, + ); + }, + }, + async ({ keepPaused }) => { + await deps.llmClient.cancelWorkspaceAndSettle(id); + await deps.scheduleService.cancelWorkspace(id); + await deps.configStore.removeWorkspace(id); + keepPaused(); + }, + ); + } finally { + finishMutation(); + } + }, + }; +} + +export { defaultWorkspaceId }; diff --git a/main/services/workspace-environment-application-service-main.ts b/main/services/workspace-environment-application-service-main.ts new file mode 100644 index 00000000..a862a9a5 --- /dev/null +++ b/main/services/workspace-environment-application-service-main.ts @@ -0,0 +1,16 @@ +import * as fs from "node:fs/promises"; +import { configStore } from "./config-store.js"; +import { assertManagedWorktreeAdmission } from "./managed-worktree-admission.js"; +import { createWorkspaceEnvironmentApplicationService } from "./workspace-environment-application-service.js"; +import { workspaceMutationGate } from "./workspace-mutation-gate.js"; +import { workspaceOperationRegistry } from "./workspace-operation-registry.js"; + +export const workspaceEnvironmentApplicationService = + createWorkspaceEnvironmentApplicationService({ + configStore, + workspaceMutationGate, + workspaceOperationRegistry, + assertManagedWorktreeAdmission, + realpath: fs.realpath, + stat: fs.stat, + }); diff --git a/main/services/workspace-environment-application-service.test.ts b/main/services/workspace-environment-application-service.test.ts new file mode 100644 index 00000000..12d7c963 --- /dev/null +++ b/main/services/workspace-environment-application-service.test.ts @@ -0,0 +1,82 @@ +import assert from "node:assert/strict"; +import * as fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import type { Workspace } from "./types.js"; +import { createWorkspaceEnvironmentApplicationService } from "./workspace-environment-application-service.js"; +import { WorkspaceMutationGate } from "./workspace-mutation-gate.js"; +import { + WorkspaceOperationRegistry, + type WorkspaceOperationDocumentOwner, +} from "./workspace-operation-registry.js"; + +class Owner implements WorkspaceOperationDocumentOwner { + private destroyed = false; + private listeners = new Set<() => void>(); + isDestroyed(): boolean { return this.destroyed; } + onInvalidated(listener: () => void): () => void { + this.listeners.add(listener); + return () => this.listeners.delete(listener); + } + invalidate(): void { + this.destroyed = true; + for (const listener of this.listeners) listener(); + this.listeners.clear(); + } +} + +test("workspace environment operations share persisted resolution and owner cancellation", async () => { + const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-environment-service-")); + const root = path.join(temporary, "workspace"); + await fs.mkdir(root); + const workspace: Workspace = { + id: "workspace-1", + name: "Project", + folderPath: root, + permission: "ask", + createdAt: 1, + updatedAt: 2, + }; + const mutationGate = new WorkspaceMutationGate(); + const operationRegistry = new WorkspaceOperationRegistry(); + const service = createWorkspaceEnvironmentApplicationService({ + configStore: { getWorkspace: async (id) => id === workspace.id ? workspace : undefined }, + workspaceMutationGate: mutationGate, + workspaceOperationRegistry: operationRegistry, + assertManagedWorktreeAdmission: async () => undefined, + realpath: fs.realpath, + stat: fs.stat, + }); + + try { + const owner = new Owner(); + const resolved = await service.run(owner, workspace.id, async (context) => context); + assert.equal(resolved.folderPath, await fs.realpath(root)); + assert.equal(resolved.workspace.id, workspace.id); + + const cancellableOwner = new Owner(); + let markStarted!: () => void; + const started = new Promise((resolve) => { markStarted = resolve; }); + const pending = service.run(cancellableOwner, workspace.id, async (_context, signal) => { + markStarted(); + await new Promise((_resolve, reject) => { + signal.addEventListener("abort", () => reject(signal.reason), { once: true }); + if (signal.aborted) reject(signal.reason); + }); + return "unreachable"; + }); + await started; + cancellableOwner.invalidate(); + await assert.rejects(pending, /renderer document is no longer active/u); + + const finishMutation = mutationGate.begin(workspace.id); + await assert.rejects( + () => service.run(new Owner(), workspace.id, async () => undefined), + /workspace is changing/u, + ); + finishMutation(); + } finally { + await fs.rm(temporary, { recursive: true, force: true }); + } +}); diff --git a/main/services/workspace-environment-application-service.ts b/main/services/workspace-environment-application-service.ts new file mode 100644 index 00000000..7465172e --- /dev/null +++ b/main/services/workspace-environment-application-service.ts @@ -0,0 +1,151 @@ +import type { Workspace } from "./types.js"; +import type { + WorkspaceOperationDocumentOwner, + workspaceOperationRegistry, +} from "./workspace-operation-registry.js"; +import { admitOwnedWorkspaceOperation } from "./workspace-operation-registry.js"; +import type { workspaceMutationGate } from "./workspace-mutation-gate.js"; + +export interface WorkspaceEnvironmentDirectory { + folderPath: string; + workspace: Workspace; +} + +export interface WorkspaceEnvironmentApplicationDependencies { + configStore: { + getWorkspace(id: string): Promise; + }; + workspaceMutationGate: Pick; + workspaceOperationRegistry: Pick; + assertManagedWorktreeAdmission(workspace: Workspace): Promise; + realpath(value: string): Promise; + stat(value: string): Promise<{ isDirectory(): boolean }>; +} + +function workspaceId(value: string): string { + if (!/^[A-Za-z0-9_-]{1,128}$/u.test(value)) { + throw new Error("The workspace identifier is invalid."); + } + return value; +} + +/** + * Shared ownership and persisted-workspace resolution for renderer and remote + * Files/Git operations. Transport callers never supply a filesystem path. + */ +export function createWorkspaceEnvironmentApplicationService( + dependencies: WorkspaceEnvironmentApplicationDependencies, +) { + const resolve = async ( + workspaceIdValue: string, + required: boolean, + allowNoAccess = false, + ): Promise => { + const id = workspaceId(workspaceIdValue); + const workspace = await dependencies.configStore.getWorkspace(id); + if (!workspace) throw new Error(`Workspace ${id} was not found.`); + if (workspace.permission === "none" && !allowNoAccess) { + if (!required) return undefined; + throw new Error(`${workspace.name} does not allow local file access.`); + } + if (!workspace.folderPath) { + if (!required) return undefined; + throw new Error(`${workspace.name} does not have a folder.`); + } + await dependencies.assertManagedWorktreeAdmission(workspace); + try { + const folderPath = await dependencies.realpath(workspace.folderPath); + const stats = await dependencies.stat(folderPath); + if (!stats.isDirectory()) throw new Error("not a directory"); + return { folderPath, workspace }; + } catch { + if (!required) return undefined; + throw new Error(`${workspace.name}'s folder is no longer available.`); + } + }; + + const run = async ( + owner: WorkspaceOperationDocumentOwner, + workspaceIdValue: string, + operation: ( + resolved: WorkspaceEnvironmentDirectory, + signal: AbortSignal, + ) => Promise, + options: { allowNoAccess?: boolean } = {}, + ): Promise => { + const id = workspaceId(workspaceIdValue); + if (dependencies.workspaceMutationGate.isChanging(id)) { + throw new Error("The workspace is changing. Try again in a moment."); + } + const admission = admitOwnedWorkspaceOperation( + dependencies.workspaceOperationRegistry, + owner, + id, + ); + try { + const resolved = await resolve(id, true, options.allowNoAccess === true); + if ( + !resolved || + owner.isDestroyed() || + admission.signal.aborted || + dependencies.workspaceMutationGate.isChanging(id) + ) { + throw new Error("The workspace changed before the operation could start."); + } + return await operation(resolved, admission.signal); + } finally { + admission.release(); + } + }; + + const runRecord = async ( + owner: WorkspaceOperationDocumentOwner, + workspaceIdValue: string, + operation: (workspace: Workspace, signal: AbortSignal) => Promise, + ): Promise => { + const id = workspaceId(workspaceIdValue); + if (dependencies.workspaceMutationGate.isChanging(id)) { + throw new Error("The workspace is changing. Try again in a moment."); + } + const admission = admitOwnedWorkspaceOperation( + dependencies.workspaceOperationRegistry, + owner, + id, + ); + try { + const workspace = await dependencies.configStore.getWorkspace(id); + if ( + !workspace || + owner.isDestroyed() || + admission.signal.aborted || + dependencies.workspaceMutationGate.isChanging(id) + ) { + throw new Error("The workspace changed before the operation could start."); + } + return await operation(workspace, admission.signal); + } finally { + admission.release(); + } + }; + + const runOptional = async ( + owner: WorkspaceOperationDocumentOwner, + workspaceIdValue: string, + operation: ( + resolved: WorkspaceEnvironmentDirectory | undefined, + signal: AbortSignal, + ) => Promise, + ): Promise => runRecord(owner, workspaceIdValue, async (_workspace, signal) => { + const resolved = await resolve(workspaceIdValue, false); + if (signal.aborted || dependencies.workspaceMutationGate.isChanging(workspaceIdValue)) { + throw new Error("The workspace changed before the operation could start."); + } + return operation(resolved, signal); + }); + + return { resolve, run, runRecord, runOptional }; +} + +export type WorkspaceEnvironmentApplicationService = ReturnType< + typeof createWorkspaceEnvironmentApplicationService +>; diff --git a/main/services/workspace-operation-registry.ts b/main/services/workspace-operation-registry.ts index 8f13b5d4..2cb62307 100644 --- a/main/services/workspace-operation-registry.ts +++ b/main/services/workspace-operation-registry.ts @@ -93,8 +93,8 @@ export class WorkspaceOperationRegistry { * it. A navigation, reload, renderer crash, or WebContents destruction aborts * the operation even when the surrounding window survives. */ -export function admitRendererOwnedWorkspaceOperation( - registry: WorkspaceOperationRegistry, +export function admitOwnedWorkspaceOperation( + registry: Pick, owner: WorkspaceOperationDocumentOwner, workspaceId: string, ): WorkspaceOperationAdmission { @@ -127,4 +127,7 @@ export function admitRendererOwnedWorkspaceOperation( }; } +/** Backward-compatible name for renderer-only call sites during extraction. */ +export const admitRendererOwnedWorkspaceOperation = admitOwnedWorkspaceOperation; + export const workspaceOperationRegistry = new WorkspaceOperationRegistry(); diff --git a/main/services/workspace-worktree-application-service-main.ts b/main/services/workspace-worktree-application-service-main.ts new file mode 100644 index 00000000..86cb1bb8 --- /dev/null +++ b/main/services/workspace-worktree-application-service-main.ts @@ -0,0 +1,88 @@ +import * as fs from "node:fs/promises"; +import { ipcMain, logger } from "../platform.js"; +import { configStore } from "./config-store.js"; +import { ensureUserDataDir } from "./data-store.js"; +import { + gitCreateWorktree, + gitDeleteManagedWorktree, + gitFinalizeManagedWorktreeDeletion, + gitManagedWorktreeDeletionPending, + gitManagedWorktreeRegistered, + gitManagedWorktreeUsable, + gitRollbackWorktree, +} from "./git.js"; +import { llmClient } from "./llm-client.js"; +import { scheduleService } from "./schedule-service.js"; +import { terminalService } from "./terminal.js"; +import { defaultWorkspaceId } from "./workspace-application-service.js"; +import { workspaceEnvironmentApplicationService } from "./workspace-environment-application-service-main.js"; +import { workspaceMutationGate } from "./workspace-mutation-gate.js"; +import { workspaceOperationRegistry } from "./workspace-operation-registry.js"; +import { createWorkspaceWorktreeApplicationService } from "./workspace-worktree-application-service.js"; + +export const workspaceWorktreeApplicationService = createWorkspaceWorktreeApplicationService({ + environment: workspaceEnvironmentApplicationService, + ensureWorktreeRoot: () => ensureUserDataDir("worktrees"), + createWorktree: gitCreateWorktree, + rollbackWorktree: gitRollbackWorktree, + deleteManagedWorktree: (managed, signal) => gitDeleteManagedWorktree( + managed.repositoryPath, + managed.worktreePath, + managed.branch, + managed.createdFromHead, + signal, + managed.worktreeGitDir, + managed.ownershipToken, + managed.worktreeDevice, + managed.worktreeInode, + ), + managedWorktreeDeletionPending: (managed) => gitManagedWorktreeDeletionPending( + managed.worktreePath, + managed.worktreeGitDir!, + managed.ownershipToken!, + ), + managedWorktreeRegistered: (managed) => gitManagedWorktreeRegistered( + managed.repositoryPath, + managed.worktreePath, + managed.branch, + managed.worktreeGitDir, + managed.ownershipToken, + ), + managedWorktreeUsable: (managed) => gitManagedWorktreeUsable( + managed.repositoryPath, + managed.worktreePath, + managed.branch, + managed.worktreeGitDir, + managed.ownershipToken, + managed.worktreeDevice, + managed.worktreeInode, + ), + finalizeManagedWorktreeDeletion: (managed) => gitFinalizeManagedWorktreeDeletion( + managed.worktreePath, + managed.worktreeGitDir!, + managed.ownershipToken!, + ), + workspacePathExists: async (worktreePath) => { + try { + await fs.stat(worktreePath); + return true; + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ENOENT") return false; + throw error; + } + }, + saveWorkspace: (workspace) => configStore.saveWorkspace(workspace), + removeWorkspace: (workspaceId) => configStore.removeWorkspace(workspaceId), + beginWorkspaceMutation: (workspaceId) => workspaceMutationGate.begin(workspaceId), + workspaceIsChanging: (workspaceId) => workspaceMutationGate.isChanging(workspaceId), + cancelWorkspaceOperations: (workspaceId, exceptSignal) => + workspaceOperationRegistry.cancelAndSettle(workspaceId, { exceptSignal }), + closeWorkspaceTerminals: (workspaceId) => terminalService.closeForWorkspace(workspaceId), + cancelWorkspaceGeneration: (workspaceId) => llmClient.cancelWorkspaceAndSettle(workspaceId), + cancelWorkspaceSchedules: (workspaceId) => scheduleService.cancelWorkspace(workspaceId), + resumeWorkspaceSchedules: (workspaceId) => scheduleService.resumeWorkspace(workspaceId), + createWorkspaceId: defaultWorkspaceId, + now: Date.now, + notifyChanged: () => ipcMain.broadcast("workspaces:changed", {}), + logError: (area, message, error) => logger.error(area, message, error), +}); diff --git a/main/services/workspace-worktree-application-service.test.ts b/main/services/workspace-worktree-application-service.test.ts new file mode 100644 index 00000000..bbaac050 --- /dev/null +++ b/main/services/workspace-worktree-application-service.test.ts @@ -0,0 +1,121 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import type { Workspace } from "./types.js"; +import { createWorkspaceWorktreeApplicationService } from "./workspace-worktree-application-service.js"; + +const owner = { + isDestroyed: () => false, + onInvalidated: () => () => undefined, +}; + +test("shared managed-worktree workflow preserves creation rollback gates and destructive deletion ordering", async () => { + const events: string[] = []; + const source: Workspace = { + id: "workspace-source", + name: "Source", + folderPath: "/canonical/source", + permission: "ask", + createdAt: 1, + updatedAt: 2, + }; + let managed: Workspace | undefined; + const signal = new AbortController().signal; + const service = createWorkspaceWorktreeApplicationService({ + environment: { + resolve: async (id) => id === source.id + ? { folderPath: source.folderPath!, workspace: source } + : managed + ? { folderPath: managed.folderPath!, workspace: managed } + : undefined, + run: async (_owner, id, operation) => { + assert.equal(id, source.id); + return operation({ folderPath: source.folderPath!, workspace: source }, signal); + }, + runRecord: async (_owner, id, operation) => { + assert.equal(id, managed?.id); + return operation(managed!, signal); + }, + }, + ensureWorktreeRoot: async () => "/aiden/worktrees", + createWorktree: async (folderPath, root, branch) => { + events.push(`create:${folderPath}:${root}:${branch}`); + return { + path: "/aiden/worktrees/mobile", + workspacePath: "/aiden/worktrees/mobile", + repositoryPath: "/canonical/source", + worktreeGitDir: "/canonical/source/.git/worktrees/mobile", + ownershipToken: "a".repeat(64), + worktreeDevice: 1, + worktreeInode: 2, + createdFromHead: "b".repeat(40), + head: "b".repeat(40), + branch, + bare: false, + detached: false, + current: false, + }; + }, + rollbackWorktree: async () => { events.push("rollback"); }, + deleteManagedWorktree: async () => { + events.push("delete-git"); + return { branchDeleted: true }; + }, + managedWorktreeDeletionPending: async () => false, + managedWorktreeRegistered: async () => false, + managedWorktreeUsable: async () => false, + finalizeManagedWorktreeDeletion: async () => { events.push("finalize"); }, + workspacePathExists: async () => false, + saveWorkspace: async (workspace) => { + events.push("save"); + managed = workspace; + return workspace; + }, + removeWorkspace: async (id) => { + events.push(`remove:${id}`); + managed = undefined; + }, + beginWorkspaceMutation: () => { + events.push("begin-mutation"); + return () => { events.push("finish-mutation"); }; + }, + workspaceIsChanging: () => false, + cancelWorkspaceOperations: async () => { events.push("cancel-operations"); }, + closeWorkspaceTerminals: () => { events.push("close-terminals"); }, + cancelWorkspaceGeneration: async () => { events.push("cancel-generation"); }, + cancelWorkspaceSchedules: async () => { events.push("cancel-schedules"); }, + resumeWorkspaceSchedules: async () => { events.push("resume-schedules"); }, + createWorkspaceId: () => "workspace-managed", + now: () => 3, + notifyChanged: () => { events.push("notify"); }, + logError: () => undefined, + }); + + const created = await service.create(owner, source.id, "feature/mobile", "Mobile Workspace"); + assert.equal(created.id, "workspace-managed"); + assert.equal(created.name, "Mobile Workspace"); + assert.equal(created.permission, source.permission); + assert.deepEqual(events, [ + "create:/canonical/source:/aiden/worktrees:feature/mobile", + "save", + "notify", + ]); + + events.length = 0; + const removed = await service.remove(owner, created.id, (workspace) => { + events.push(`validate:${workspace.id}`); + }); + assert.equal(removed.branchDeleted, true); + assert.deepEqual(events, [ + "validate:workspace-managed", + "begin-mutation", + "cancel-operations", + "close-terminals", + "cancel-generation", + "cancel-schedules", + "delete-git", + "remove:workspace-managed", + "finalize", + "notify", + "finish-mutation", + ]); +}); diff --git a/main/services/workspace-worktree-application-service.ts b/main/services/workspace-worktree-application-service.ts new file mode 100644 index 00000000..661f4815 --- /dev/null +++ b/main/services/workspace-worktree-application-service.ts @@ -0,0 +1,187 @@ +import path from "node:path"; +import type { GitCreatedWorktree, GitDeleteWorktreeResult } from "./git.js"; +import { GitManagedWorktreeDeleteError } from "./git.js"; +import { + commitManagedWorktreeCreation, + ManagedWorktreeCreationError, +} from "./managed-worktree-creation-core.js"; +import { removeManagedWorkspace } from "./managed-worktree-removal-core.js"; +import type { Workspace, ManagedWorktree } from "./types.js"; +import type { WorkspaceEnvironmentApplicationService } from "./workspace-environment-application-service.js"; +import type { WorkspaceOperationDocumentOwner } from "./workspace-operation-registry.js"; +import { withWorkspaceScheduleRestoration } from "./workspace-schedule-restoration.js"; + +export interface WorkspaceWorktreeApplicationDependencies { + environment: Pick; + ensureWorktreeRoot(): Promise; + createWorktree( + folderPath: string, + root: string, + branch: string, + signal?: AbortSignal, + ): Promise; + rollbackWorktree(folderPath: string, created: GitCreatedWorktree): Promise; + deleteManagedWorktree(managed: ManagedWorktree, signal: AbortSignal): Promise; + managedWorktreeDeletionPending(managed: ManagedWorktree): Promise; + managedWorktreeRegistered(managed: ManagedWorktree): Promise; + managedWorktreeUsable(managed: ManagedWorktree): Promise; + finalizeManagedWorktreeDeletion(managed: ManagedWorktree): Promise; + workspacePathExists(worktreePath: string): Promise; + saveWorkspace(workspace: Workspace): Promise; + removeWorkspace(workspaceId: string): Promise; + beginWorkspaceMutation(workspaceId: string): () => void; + workspaceIsChanging(workspaceId: string): boolean; + cancelWorkspaceOperations(workspaceId: string, exceptSignal: AbortSignal): Promise; + closeWorkspaceTerminals(workspaceId: string): void; + cancelWorkspaceGeneration(workspaceId: string): Promise; + cancelWorkspaceSchedules(workspaceId: string): Promise; + resumeWorkspaceSchedules(workspaceId: string): Promise; + createWorkspaceId(): string; + now(): number; + notifyChanged(): void; + logError(area: string, message: string, error: unknown): void; +} + +function displayName(source: Workspace, branch: string, requested?: string): string { + const trimmed = requested?.trim(); + if (trimmed) return [...trimmed].slice(0, 120).join(""); + const base = path.basename(source.folderPath ?? source.name); + return [...`${base} · ${branch}`].slice(0, 120).join(""); +} + +/** + * Shared renderer/remote orchestration for Aiden-owned Git worktrees. All + * filesystem and Git-admin identity is reloaded from persisted Mac state. + */ +export function createWorkspaceWorktreeApplicationService( + dependencies: WorkspaceWorktreeApplicationDependencies, +) { + const create = async ( + owner: WorkspaceOperationDocumentOwner, + sourceWorkspaceId: string, + branch: string, + requestedName?: string, + ): Promise => dependencies.environment.run( + owner, + sourceWorkspaceId, + async (resolved, signal) => { + const worktree = await dependencies.createWorktree( + resolved.folderPath, + await dependencies.ensureWorktreeRoot(), + branch, + signal, + ); + const now = dependencies.now(); + const workspace: Workspace = { + id: dependencies.createWorkspaceId(), + name: displayName(resolved.workspace, branch, requestedName), + folderPath: worktree.workspacePath, + permission: resolved.workspace.permission, + managedWorktree: { + repositoryPath: worktree.repositoryPath, + worktreePath: worktree.path, + branch, + worktreeGitDir: worktree.worktreeGitDir, + ownershipToken: worktree.ownershipToken, + worktreeDevice: worktree.worktreeDevice, + worktreeInode: worktree.worktreeInode, + createdFromHead: worktree.createdFromHead, + }, + createdAt: now, + updatedAt: now, + }; + try { + const saved = await commitManagedWorktreeCreation({ + validateBeforeSave: async () => { + const latest = await dependencies.environment.resolve(sourceWorkspaceId, true); + if ( + !latest || + signal.aborted || + latest.folderPath !== resolved.folderPath || + latest.workspace.permission !== resolved.workspace.permission + ) { + throw new Error("The source workspace changed while Aiden was creating the worktree."); + } + }, + saveWorkspace: () => dependencies.saveWorkspace(workspace), + validateAfterSave: () => { + if (signal.aborted || dependencies.workspaceIsChanging(sourceWorkspaceId)) { + throw new Error("The source workspace changed while Aiden was saving the worktree."); + } + }, + removeWorkspaceRecord: (savedWorkspace) => dependencies.removeWorkspace(savedWorkspace.id), + rollbackWorktree: () => dependencies.rollbackWorktree(resolved.folderPath, worktree), + }); + dependencies.notifyChanged(); + return saved; + } catch (error) { + if (error instanceof ManagedWorktreeCreationError) { + dependencies.logError("git", error.logMessage, error.errors); + } + throw error; + } + }, + ); + + const remove = async ( + owner: WorkspaceOperationDocumentOwner, + workspaceId: string, + validateWorkspace: (workspace: Workspace) => void = () => undefined, + ): Promise => dependencies.environment.runRecord( + owner, + workspaceId, + async (workspace, signal) => { + validateWorkspace(workspace); + const managed = workspace.managedWorktree; + if (!managed) throw new Error("This workspace is not an Aiden-managed worktree."); + const finishMutation = dependencies.beginWorkspaceMutation(workspaceId); + try { + await dependencies.cancelWorkspaceOperations(workspaceId, signal); + const result = await withWorkspaceScheduleRestoration( + { + restoreOnExit: workspace.permission !== "none", + resume: () => dependencies.resumeWorkspaceSchedules(workspaceId), + onResumeError: (error) => dependencies.logError( + "schedule", + "Could not restore scheduled tasks after managed worktree deletion failed.", + error, + ), + }, + async ({ keepPaused }) => { + dependencies.closeWorkspaceTerminals(workspaceId); + await dependencies.cancelWorkspaceGeneration(workspaceId); + await dependencies.cancelWorkspaceSchedules(workspaceId); + const deletion = await removeManagedWorkspace({ + deleteWorktree: () => dependencies.deleteManagedWorktree(managed, signal), + destructiveMutationAttempted: (error) => + error instanceof GitManagedWorktreeDeleteError + ? error.destructiveMutationAttempted + : undefined, + deletionPending: () => dependencies.managedWorktreeDeletionPending(managed), + workspacePathExists: () => dependencies.workspacePathExists(managed.worktreePath), + worktreeRegistered: () => dependencies.managedWorktreeRegistered(managed), + worktreeUsable: () => dependencies.managedWorktreeUsable(managed), + onDestructiveBoundary: keepPaused, + removeWorkspaceRecord: () => dependencies.removeWorkspace(workspaceId), + reconciledResult: () => ({ branchDeleted: false }), + }); + if (managed.worktreeGitDir && managed.ownershipToken) { + await dependencies.finalizeManagedWorktreeDeletion(managed); + } + return deletion; + }, + ); + dependencies.notifyChanged(); + return result; + } finally { + finishMutation(); + } + }, + ); + + return { create, remove }; +} + +export type WorkspaceWorktreeApplicationService = ReturnType< + typeof createWorkspaceWorktreeApplicationService +>; diff --git a/package-lock.json b/package-lock.json index fe05ea55..0dbcfcb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "lucide-react": "^0.542.0", "mdast-util-to-string": "4.0.0", "node-pty": "^1.1.0", + "qrcode": "^1.5.4", "radix-ui": "^1.4.3", "re2-wasm": "1.0.2", "react": "^19.1.1", @@ -51,6 +52,7 @@ "@rolldown/plugin-babel": "^0.2.1", "@tailwindcss/vite": "^4.2.2", "@types/node": "^24.3.1", + "@types/qrcode": "^1.5.5", "@types/react": "^19.1.12", "@types/react-dom": "^19.1.9", "@typescript-eslint/eslint-plugin": "^8.18.0", @@ -4983,6 +4985,16 @@ "undici-types": "~7.18.0" } }, + "node_modules/@types/qrcode": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz", + "integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/react": { "version": "19.2.17", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", @@ -5442,7 +5454,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -6355,6 +6366,15 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001805", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001805.tgz", @@ -6546,7 +6566,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -6559,7 +6578,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/combined-stream": { @@ -6868,6 +6886,15 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/decode-named-character-reference": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", @@ -7028,6 +7055,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, "node_modules/dir-compare": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", @@ -8687,7 +8720,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -9696,7 +9728,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12206,6 +12237,15 @@ "node": ">=8" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -12275,7 +12315,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12454,6 +12493,15 @@ "node": ">=10.4.0" } }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -12703,6 +12751,182 @@ "node": ">=16.0.0" } }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/qrcode/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/qrcode/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qrcode/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.15.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", @@ -13155,7 +13379,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -13170,6 +13393,12 @@ "node": ">=0.10.0" } }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, "node_modules/resedit": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", @@ -13556,6 +13785,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -15452,6 +15687,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, "node_modules/which-typed-array": { "version": "1.1.22", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", diff --git a/package.json b/package.json index 13fa870b..1fae1bf8 100644 --- a/package.json +++ b/package.json @@ -41,9 +41,14 @@ "dev:electron": "wait-on http-get://127.0.0.1:4143/main-window.html && node scripts/prepare-macos-dev-runtime.mjs --run", "dev:brand": "node scripts/prepare-macos-dev-runtime.mjs", "lint": "eslint .", - "pretest": "npm run build:worktree-remover && npm run test:terminal:coverage && npm run test:onboarding && npm run test:assistant-automations && npm run test:slash-commands && npm run test:provider-failure && npm run test:compaction && npm run test:subagents", + "pretest": "npm run build:worktree-remover && npm run test:aiden-remote && npm run test:aiden-service-boundary && npm run test:ios-release && npm run test:terminal:coverage && npm run test:onboarding && npm run test:assistant-automations && npm run test:slash-commands && npm run test:provider-failure && npm run test:compaction && npm run test:subagents", "pretest:coverage": "npm run build:worktree-remover && npm run build:subagent-run-store && npm run test:preflight && npm run test:scheduled && npm run test:google-provider && npm run test:config-recovery && npm run test:command-system && npm run test:slash-commands && npm run test:compaction && npm run test:subagents", "test:preflight": "npm run test:artificial-analysis && npm run test:model-pad && tsx --test main/services/appearance-preview-core.test.ts main/services/generation-timeline.test.ts main/services/local-runtime-status.test.ts main/services/mcp-tool-result.test.ts main/services/pi-thinking-disclosure.integration.test.ts renderer/components/activity-feed.test.tsx renderer/components/chat-sidebar.test.tsx renderer/components/composer.test.tsx renderer/main/chat-transition.test.tsx renderer/components/reasoning-block.test.tsx renderer/components/reasoning-visibility-control.test.tsx renderer/components/thinking-control.test.tsx renderer/lib/agent-steps.test.ts renderer/lib/dialog-motion-contract.test.ts renderer/lib/pill-appearance.test.ts renderer/lib/reasoning-disclosure.test.ts renderer/lib/streaming-motion-contract.test.ts renderer/lib/streaming-reveal.test.ts renderer/lib/voice-recorder-core.test.ts renderer/pill-preload-channels.test.ts renderer/shared/anthropic-thinking.test.ts renderer/shared/app-update.test.ts renderer/shared/claim-check.test.ts renderer/shared/codex-thinking.test.ts renderer/shared/google-thinking.test.ts renderer/shared/provider-deployment.test.ts", + "test:aiden-remote": "tsx --test main/handlers/aiden-remote.test.ts main/services/aiden-remote-approved-roots.test.ts main/services/aiden-remote-chat-http.test.ts main/services/aiden-remote-chats.test.ts main/services/aiden-remote-files.test.ts main/services/aiden-remote-git.test.ts main/services/aiden-remote-models.test.ts main/services/aiden-remote-protocol.test.ts main/services/aiden-remote-opaque-handles.test.ts main/services/aiden-remote-operation-contract.test.ts main/services/aiden-remote-pairing.test.ts main/services/aiden-remote-router.test.ts main/services/aiden-remote-schedules.test.ts main/services/aiden-remote-service.test.ts main/services/aiden-remote-state.test.ts main/services/aiden-remote-streams.test.ts main/services/aiden-remote-tailscale-route.test.ts main/services/aiden-remote-tailscale.test.ts main/services/aiden-remote-tls-identity.test.ts main/services/aiden-remote-workspace-browser.test.ts main/services/aiden-remote-workspace-http.test.ts main/services/aiden-remote-workspaces.test.ts renderer/components/settings/remote-access-settings.test.tsx renderer/lib/settings-section.test.ts && node --test scripts/aiden-remote-lan-transport-spike.test.mjs", + "test:aiden-service-boundary": "tsx --test main/services/chat-application-service.test.ts main/services/chat-generation-owner.test.ts main/services/workspace-application-service.test.ts main/services/workspace-environment-application-service.test.ts main/services/workspace-worktree-application-service.test.ts main/services/scheduled-task-application-service.test.ts", + "ios:asc-monitor": "node scripts/ios-asc-monitor.mjs", + "ios:activitykit-process-proof": "node scripts/ios-live-activity-process-proof.mjs", + "test:ios-release": "ruby ios/ci/select_testflight_build_number_test.rb && node --test scripts/check-ios-testflight-policy.test.mjs scripts/check-ios-app-store-metadata.test.mjs scripts/check-ios-shipping-target.test.mjs scripts/ios-asc-monitor.test.mjs scripts/ios-live-activity-process-proof.test.mjs", "test:branding": "tsx --test main/runtime-mode.test.ts main/runtime-profile-core.test.ts main/runtime-profile-bootstrap.test.ts main/services/app-updater-core.test.ts && node --test scripts/prepare-ci-release.test.mjs scripts/prepare-macos-dev-runtime.test.mjs scripts/check-release-consumers.test.mjs scripts/publish-github-release.test.mjs", "test:scheduled": "tsx --test main/handlers/scheduled-tasks-parse.test.ts main/services/assistant/mcp-tool.test.ts main/services/assistant/tool-loop-guard.test.ts main/services/mcp-selection.test.ts main/services/scheduled-settings-core.test.ts main/services/schedule-guard.test.ts main/services/schedule-notification.test.ts main/services/schedule-service-core.test.ts main/services/schedule-store.test.ts main/services/schedule-script.test.ts main/services/schedule-tool.test.ts renderer/lib/scheduled-task-view.test.ts", "test:artificial-analysis": "tsx --test main/services/artificial-analysis-action-core.test.ts main/services/artificial-analysis-cache.test.ts main/services/artificial-analysis-runtime-core.test.ts main/services/artificial-analysis-catalog-core.test.ts main/services/provider-model-info-core.test.ts renderer/lib/artificial-analysis-query-state.test.ts renderer/lib/model-data-control.test.ts renderer/lib/settings-section.test.ts", @@ -116,6 +121,7 @@ "lucide-react": "^0.542.0", "mdast-util-to-string": "4.0.0", "node-pty": "^1.1.0", + "qrcode": "^1.5.4", "radix-ui": "^1.4.3", "re2-wasm": "1.0.2", "react": "^19.1.1", @@ -140,6 +146,7 @@ "@rolldown/plugin-babel": "^0.2.1", "@tailwindcss/vite": "^4.2.2", "@types/node": "^24.3.1", + "@types/qrcode": "^1.5.5", "@types/react": "^19.1.12", "@types/react-dom": "^19.1.9", "@typescript-eslint/eslint-plugin": "^8.18.0", diff --git a/protocol/aiden-appearance-v1.json b/protocol/aiden-appearance-v1.json new file mode 100644 index 00000000..0621c0a9 --- /dev/null +++ b/protocol/aiden-appearance-v1.json @@ -0,0 +1,29 @@ +{ + "version": 1, + "presets": [ + { + "id": "aiden", + "label": "Aiden", + "light": { "canvas": "#F6F7F9", "sidebar": "#EEF0F3", "raised": "#FFFFFF", "foreground": "#3D3F41", "secondary": "#6B7280", "accent": "#006AD6", "success": "#30D158", "warning": "#FF9F0A", "danger": "#FF453A" }, + "dark": { "canvas": "#181B21", "sidebar": "#20242C", "raised": "#292E37", "foreground": "#D1D4DA", "secondary": "#9AA3AE", "accent": "#3E97F6", "success": "#32D17A", "warning": "#FFB020", "danger": "#FF5E57" } + }, + { + "id": "slate", + "label": "Slate", + "light": { "canvas": "#F2F5F9", "sidebar": "#E6EBF2", "raised": "#FFFFFF", "foreground": "#3A434E", "secondary": "#637083", "accent": "#087581", "success": "#2DB67D", "warning": "#E0A72E", "danger": "#E24D5B" }, + "dark": { "canvas": "#181E26", "sidebar": "#202833", "raised": "#29323E", "foreground": "#D1D6DE", "secondary": "#94A3BB", "accent": "#21A9BE", "success": "#35C08A", "warning": "#D4A72C", "danger": "#F87171" } + }, + { + "id": "berry", + "label": "Berry", + "light": { "canvas": "#FBF4F7", "sidebar": "#F1E8EE", "raised": "#FFFFFF", "foreground": "#443F4A", "secondary": "#6E6470", "accent": "#B42C70", "success": "#22C7A8", "warning": "#E3A23C", "danger": "#E24C5A" }, + "dark": { "canvas": "#1D1822", "sidebar": "#251D2B", "raised": "#2E2435", "foreground": "#D5CFD6", "secondary": "#A39AA6", "accent": "#E8629F", "success": "#32D1B2", "warning": "#D9A441", "danger": "#F0717A" } + }, + { + "id": "moss", + "label": "Moss", + "light": { "canvas": "#F3F6F4", "sidebar": "#E7ECE8", "raised": "#FFFFFF", "foreground": "#3F4943", "secondary": "#65736B", "accent": "#157862", "success": "#3DBF7D", "warning": "#D4A22A", "danger": "#E05353" }, + "dark": { "canvas": "#18201C", "sidebar": "#202A25", "raised": "#29342E", "foreground": "#D1D6D3", "secondary": "#95A39B", "accent": "#42B596", "success": "#47D18C", "warning": "#D9B43A", "danger": "#EB6B6B" } + } + ] +} diff --git a/protocol/aiden-remote/v1/fixtures/contract.json b/protocol/aiden-remote/v1/fixtures/contract.json new file mode 100644 index 00000000..dc9c4dae --- /dev/null +++ b/protocol/aiden-remote/v1/fixtures/contract.json @@ -0,0 +1,204 @@ +{ + "contractRevision": 2, + "protocolVersion": 1, + "generated": false, + "notice": "Synthetic fixtures only. Values are not credentials and contain no machine-local paths.", + "capabilities": [ + "server:read", + "chat:read", + "chat:write", + "approval:respond", + "workspace:read", + "workspace:browse", + "workspace:manage", + "files:read", + "files:write", + "git:read", + "git:write", + "schedule:read", + "schedule:write" + ], + "health": { "ok": true, "protocolVersion": 1 }, + "pairingBootstrap": { + "protocolVersion": 1, + "instanceId": "instance_fixture_01", + "endpoint": "https://aiden-fixture.example.test/api/aiden/v1", + "serverSpkiSha256": "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "secret": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "expiresAt": "2026-08-18T19:05:00.000Z" + }, + "pairingExchange": { + "protocolVersion": 1, + "instanceId": "instance_fixture_01", + "deviceId": "device_fixture_01", + "credential": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", + "capabilities": ["server:read", "chat:read", "chat:write", "workspace:read", "workspace:browse", "workspace:manage"], + "endpoint": "https://aiden-fixture.example.test/api/aiden/v1", + "serverSpkiSha256": "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + }, + "server": { + "protocolVersion": 1, + "instanceId": "instance_fixture_01", + "name": "Fixture Aiden", + "appVersion": "0.30.0", + "capabilities": ["server:read", "chat:read", "chat:write", "workspace:read", "workspace:browse", "workspace:manage"], + "connectionMode": "both", + "minimumClientVersion": "1.0.0", + "serverTime": "2026-08-18T19:00:00.000Z" + }, + "workspaces": [ + { + "id": "workspace_fixture_01", + "name": "Aiden Agent", + "permission": "ask", + "hasFolder": true, + "isManagedWorktree": false, + "git": { "isRepo": true, "branch": "feature/mobile", "uncommitted": 2 }, + "createdAt": "2026-08-18T18:00:00.000Z", + "updatedAt": "2026-08-18T18:30:00.000Z", + "revision": "workspace_revision_7" + }, + { + "id": "workspace_fixture_02", + "name": "Scratch", + "permission": "none", + "hasFolder": true, + "isManagedWorktree": true, + "branchName": "aiden/scratch", + "repositoryName": "Fixture Repository", + "createdAt": "2026-08-18T18:10:00.000Z", + "updatedAt": "2026-08-18T18:10:00.000Z", + "revision": "workspace_revision_1" + } + ], + "browser": { + "roots": [{ "id": "root_fixture_01", "label": "Projects", "location": "loc_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "policyRevision": "root_policy_3" }], + "page": { + "rootId": "root_fixture_01", + "label": "Projects", + "breadcrumbs": [{ "label": "Projects", "location": "loc_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }], + "entries": [ + { "id": "directory_fixture_01", "name": "Aiden", "location": "loc_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" }, + { "id": "directory_fixture_02", "name": "Examples", "location": "loc_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" } + ], + "nextCursor": "cur_DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + }, + "selection": { "selection": "sel_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "displayName": "Aiden", "expiresAt": "2026-08-18T19:05:00.000Z" } + }, + "chat": { + "id": "chat_fixture_01", + "workspaceId": "workspace_fixture_01", + "title": "Remote protocol review", + "providerId": "provider_fixture", + "modelId": "model_fixture", + "messages": [ + { "id": "message_fixture_user_01", "role": "user", "text": "Review the protocol.", "createdAt": "2026-08-18T19:00:00.000Z" }, + { "id": "message_fixture_assistant_01", "role": "assistant", "text": "Starting the review.", "createdAt": "2026-08-18T19:00:01.000Z" } + ], + "createdAt": "2026-08-18T18:59:00.000Z", + "updatedAt": "2026-08-18T19:00:01.000Z", + "revision": "chat_revision_4" + }, + "turnStart": { + "turnId": "turn_fixture_01", + "streamId": "stream_fixture_01", + "status": "accepted", + "message": { "id": "message_fixture_user_02", "role": "user", "text": "Continue.", "createdAt": "2026-08-18T19:01:00.000Z" } + }, + "streamStatus": { + "streamId": "stream_fixture_01", + "chatId": "chat_fixture_01", + "turnId": "turn_fixture_01", + "state": "waiting_for_approval", + "lastSequence": 9, + "updatedAt": "2026-08-18T19:01:09.000Z" + }, + "events": [ + { "protocolVersion": 1, "streamId": "stream_fixture_01", "sequence": 1, "timestamp": "2026-08-18T19:01:01.000Z", "type": "snapshot", "terminal": false, "payload": { "chatId": "chat_fixture_01", "turnId": "turn_fixture_01", "nextSequence": 2 } }, + { "protocolVersion": 1, "streamId": "stream_fixture_01", "sequence": 2, "timestamp": "2026-08-18T19:01:02.000Z", "type": "status", "terminal": false, "payload": { "state": "running" } }, + { "protocolVersion": 1, "streamId": "stream_fixture_01", "sequence": 3, "timestamp": "2026-08-18T19:01:03.000Z", "type": "reasoning_delta", "terminal": false, "payload": { "text": "Checking invariants." } }, + { "protocolVersion": 1, "streamId": "stream_fixture_01", "sequence": 4, "timestamp": "2026-08-18T19:01:04.000Z", "type": "text_delta", "terminal": false, "payload": { "text": "The protocol " } }, + { "protocolVersion": 1, "streamId": "stream_fixture_01", "sequence": 5, "timestamp": "2026-08-18T19:01:05.000Z", "type": "tool_started", "terminal": false, "payload": { "toolId": "tool_fixture_01", "name": "read" } }, + { "protocolVersion": 1, "streamId": "stream_fixture_01", "sequence": 6, "timestamp": "2026-08-18T19:01:06.000Z", "type": "timeline", "terminal": false, "payload": { "label": "Reading project context" } }, + { "protocolVersion": 1, "streamId": "stream_fixture_01", "sequence": 7, "timestamp": "2026-08-18T19:01:07.000Z", "type": "tool_finished", "terminal": false, "payload": { "toolId": "tool_fixture_01", "status": "succeeded" } }, + { "protocolVersion": 1, "streamId": "stream_fixture_01", "sequence": 8, "timestamp": "2026-08-18T19:01:08.000Z", "type": "approval_required", "terminal": false, "payload": { "approvalId": "approval_fixture_01", "summary": "Allow a write operation?", "expiresAt": "2026-08-18T19:06:08.000Z" } }, + { "protocolVersion": 1, "streamId": "stream_fixture_01", "sequence": 9, "timestamp": "2026-08-18T19:01:09.000Z", "type": "heartbeat", "terminal": false, "payload": {} }, + { "protocolVersion": 1, "streamId": "stream_fixture_01", "sequence": 10, "timestamp": "2026-08-18T19:01:10.000Z", "type": "done", "terminal": true, "payload": { "messageId": "message_fixture_assistant_02" } }, + { "protocolVersion": 1, "streamId": "stream_fixture_error", "sequence": 1, "timestamp": "2026-08-18T19:02:00.000Z", "type": "error", "terminal": true, "payload": { "code": "server_interrupted", "message": "Aiden restarted before the turn completed." } }, + { "protocolVersion": 1, "streamId": "stream_fixture_cancelled", "sequence": 1, "timestamp": "2026-08-18T19:03:00.000Z", "type": "cancelled", "terminal": true, "payload": { "source": "device" } } + ], + "fileIndex": { + "snapshotId": "file_snapshot_fixture_01", + "entries": [ + { "id": "file_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "displayPath": "README.md", "name": "README.md", "kind": "file", "size": 1200, "language": "markdown" }, + { "id": "file_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", "displayPath": "src", "name": "src", "kind": "directory" } + ], + "truncated": false, + "maxEntries": 4000, + "maxDepth": 20 + }, + "fileDocument": { + "id": "file_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "displayPath": "README.md", + "content": "# Fixture\n", + "version": "sha256:fixture_version_01", + "truncated": false + }, + "git": { + "operationId": "git_operation_fixture_01", + "status": "snapshot", + "snapshotId": "git_snapshot_fixture_01", + "capability": { "allowed": true }, + "result": { "kind": "review", "branch": "feature/mobile", "uncommitted": 2, "files": [{ "id": "file_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "displayPath": "README.md", "status": "modified" }] } + }, + "scheduledTask": { + "id": "task_fixture_01", + "revision": "task_revision_2", + "name": "Daily review", + "enabled": true, + "schedule": "0 9 * * *", + "timezone": "America/New_York", + "mode": "llm", + "permission": "read-only", + "workspaceId": "workspace_fixture_01", + "providerId": "provider_fixture", + "modelId": "model_fixture", + "mcpServerIds": ["mcp_fixture_01"], + "notify": true, + "running": false, + "createdAt": "2026-08-18T18:00:00.000Z", + "updatedAt": "2026-08-18T18:30:00.000Z" + }, + "scheduleSettings": { + "revision": "schedule_settings_revision_3", + "enabled": true, + "defaultMode": "llm", + "defaultPermission": "read-only", + "defaultMcpEnabled": false, + "defaultNotify": true, + "defaultTimezone": "America/New_York" + }, + "scheduleRunAccepted": { + "taskId": "task_fixture_01", + "runId": "run_fixture_01", + "status": "accepted", + "acceptedAt": "2026-08-18T19:10:00.000Z" + }, + "scheduleRun": { + "id": "run_fixture_01", + "taskId": "task_fixture_01", + "status": "succeeded", + "startedAt": "2026-08-18T19:10:00.000Z", + "finishedAt": "2026-08-18T19:10:03.000Z", + "summary": "Review completed." + }, + "error": { + "error": { + "code": "revision_conflict", + "message": "The workspace changed. Refresh and try again.", + "requestId": "request_fixture_01", + "retryable": false, + "details": { "currentRevision": "workspace_revision_8" } + } + } +} diff --git a/protocol/aiden-remote/v1/openapi.json b/protocol/aiden-remote/v1/openapi.json new file mode 100644 index 00000000..ff19f8c7 --- /dev/null +++ b/protocol/aiden-remote/v1/openapi.json @@ -0,0 +1,305 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Aiden Remote API", + "version": "1.0.0", + "description": "Normative Aiden On The Go REST/SSE contract. Filesystem paths and credentials are never wire identifiers." + }, + "servers": [{ "url": "https://{host}/api/aiden/v1", "variables": { "host": { "default": "aiden.invalid" } } }], + "security": [{ "deviceBearer": [], "protocolVersion": [] }], + "paths": { + "/health": { + "get": { + "operationId": "health", + "security": [], + "responses": { "200": { "description": "Minimal readiness", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Health" } } } } } + } + }, + "/pairing/exchange": { + "post": { + "operationId": "pairingExchange", + "security": [], + "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PairingExchangeRequest" } } } }, + "responses": { "200": { "description": "Issued device credential", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PairingExchangeResponse" } } } }, "default": { "$ref": "#/components/responses/Error" } } + } + }, + "/server": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "getServer", "x-aiden-capability": "server:read", "responses": { "200": { "description": "Safe server projection", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Server" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "listWorkspaces", "x-aiden-capability": "workspace:read", "responses": { "200": { "description": "Workspace list", "content": { "application/json": { "schema": { "type": "object", "required": ["workspaces"], "properties": { "workspaces": { "type": "array", "items": { "$ref": "#/components/schemas/Workspace" } } }, "additionalProperties": false } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "post": { "operationId": "createWorkspace", "x-aiden-capability": "workspace:manage", "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkspaceCreate" } } } }, "responses": { "201": { "description": "Workspace created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Workspace" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}": { + "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }, { "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "getWorkspace", "x-aiden-capability": "workspace:read", "responses": { "200": { "description": "Workspace", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Workspace" } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "patch": { "operationId": "updateWorkspace", "x-aiden-capability": "workspace:manage", "parameters": [{ "$ref": "#/components/parameters/IfMatch" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkspacePatch" } } } }, "responses": { "200": { "description": "Updated workspace", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Workspace" } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "delete": { "operationId": "unregisterWorkspace", "x-aiden-capability": "workspace:manage", "parameters": [{ "$ref": "#/components/parameters/IfMatch" }], "responses": { "204": { "description": "Registry record removed; folder untouched" }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspace-browser/roots": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "listWorkspaceBrowserRoots", "x-aiden-capability": "workspace:browse", "responses": { "200": { "description": "Approved roots", "content": { "application/json": { "schema": { "type": "object", "required": ["roots"], "properties": { "roots": { "type": "array", "items": { "$ref": "#/components/schemas/BrowserRoot" } } }, "additionalProperties": false } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspace-browser/children": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "listWorkspaceBrowserChildren", "x-aiden-capability": "workspace:browse", "parameters": [{ "name": "location", "in": "query", "required": true, "schema": { "type": "string", "pattern": "^loc_[A-Za-z0-9_-]{43}$" } }, { "name": "cursor", "in": "query", "required": false, "schema": { "type": "string", "pattern": "^cur_[A-Za-z0-9_-]{43}$" } }], "responses": { "200": { "description": "Bounded directory page", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BrowserPage" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspace-browser/selections": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "createWorkspaceSelection", "x-aiden-capability": "workspace:browse", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["location"], "properties": { "location": { "type": "string", "pattern": "^loc_[A-Za-z0-9_-]{43}$" } }, "additionalProperties": false } } } }, "responses": { "201": { "description": "Single-use selection nonce", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkspaceSelection" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/chats": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "listChats", "x-aiden-capability": "chat:read", "parameters": [{ "name": "workspaceId", "in": "query", "required": false, "schema": { "type": "string" } }], "responses": { "200": { "description": "Chat list", "content": { "application/json": { "schema": { "type": "object", "required": ["chats"], "properties": { "chats": { "type": "array", "items": { "$ref": "#/components/schemas/Chat" } } }, "additionalProperties": false } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "post": { "operationId": "createChat", "x-aiden-capability": "chat:write", "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["workspaceId"], "properties": { "workspaceId": { "type": "string" }, "providerId": { "type": "string" }, "modelId": { "type": "string" } }, "additionalProperties": false } } } }, "responses": { "201": { "description": "Chat created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Chat" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/chats/{chatId}": { + "parameters": [{ "$ref": "#/components/parameters/ChatId" }, { "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "getChat", "x-aiden-capability": "chat:read", "responses": { "200": { "description": "Chat snapshot", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Chat" } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "patch": { "operationId": "updateChat", "x-aiden-capability": "chat:write", "parameters": [{ "$ref": "#/components/parameters/IfMatch" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["title"], "properties": { "title": { "type": "string", "minLength": 1, "maxLength": 200 } }, "additionalProperties": false } } } }, "responses": { "200": { "description": "Updated chat", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Chat" } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "delete": { "operationId": "deleteChat", "x-aiden-capability": "chat:write", "parameters": [{ "$ref": "#/components/parameters/IfMatch" }], "responses": { "204": { "description": "Chat removed" }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/chats/{chatId}/move": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "moveChat", "x-aiden-capability": "chat:write", "parameters": [{ "$ref": "#/components/parameters/ChatId" }, { "$ref": "#/components/parameters/IfMatch" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["workspaceId", "confirmedForeground"], "properties": { "workspaceId": { "type": "string", "minLength": 1, "maxLength": 128 }, "confirmedForeground": { "const": true } }, "additionalProperties": false } } } }, "responses": { "200": { "description": "Chat moved to the selected workspace", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Chat" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/chats/{chatId}/turns": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "startTurn", "x-aiden-capability": "chat:write", "parameters": [{ "$ref": "#/components/parameters/ChatId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TurnStartRequest" } } } }, "responses": { "202": { "description": "Turn accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TurnStartResponse" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/chats/{chatId}/attachments": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "uploadChatAttachment", "x-aiden-capability": "chat:write", "parameters": [{ "$ref": "#/components/parameters/ChatId" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AttachmentUpload" } } } }, "responses": { "201": { "description": "Short-lived, single-use attachment reference", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AttachmentReference" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/chats/{chatId}/attachments/{attachmentId}": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "delete": { "operationId": "removeChatAttachment", "x-aiden-capability": "chat:write", "parameters": [{ "$ref": "#/components/parameters/ChatId" }, { "name": "attachmentId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^att_[A-Za-z0-9_-]{43}$" } }], "responses": { "204": { "description": "Temporary attachment removed" }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/streams/{streamId}": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "getStream", "x-aiden-capability": "chat:read", "parameters": [{ "$ref": "#/components/parameters/StreamId" }], "responses": { "200": { "description": "Stream status", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StreamStatus" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/streams/{streamId}/events": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "streamEvents", "x-aiden-capability": "chat:read", "parameters": [{ "$ref": "#/components/parameters/StreamId" }, { "name": "Last-Event-ID", "in": "header", "required": false, "schema": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, { "name": "after", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }], "responses": { "200": { "description": "Resumable event stream", "content": { "text/event-stream": { "schema": { "type": "string" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/streams/{streamId}/cancel": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "cancelStream", "x-aiden-capability": "chat:write", "parameters": [{ "$ref": "#/components/parameters/StreamId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "202": { "description": "Cancellation accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StreamStatus" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/approvals/{approvalId}/respond": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "respondApproval", "x-aiden-capability": "approval:respond", "parameters": [{ "name": "approvalId", "in": "path", "required": true, "schema": { "type": "string" } }, { "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["decision"], "properties": { "decision": { "enum": ["allow", "deny"] } }, "additionalProperties": false } } } }, "responses": { "200": { "description": "Approval resolved", "content": { "application/json": { "schema": { "type": "object", "required": ["approvalId", "decision", "resolvedAt"], "properties": { "approvalId": { "type": "string" }, "decision": { "enum": ["allow", "deny"] }, "resolvedAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/models": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "listModels", "x-aiden-capability": "chat:read", "responses": { "200": { "description": "Configured provider/model projection", "content": { "application/json": { "schema": { "type": "object", "required": ["providers", "defaults"], "properties": { "providers": { "type": "array", "items": { "$ref": "#/components/schemas/Provider" } }, "defaults": { "type": "object", "additionalProperties": { "type": "string" } } }, "additionalProperties": false } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/usage": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "getUsageSummary", "x-aiden-capability": "server:read", "parameters": [{ "name": "range", "in": "query", "required": true, "schema": { "enum": ["7d", "30d", "90d", "1y", "all"] } }], "responses": { "200": { "description": "Privacy-safe aggregate usage from this Aiden Agent installation", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UsageSummary" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/files": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "listWorkspaceFiles", "x-aiden-capability": "files:read", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }], "responses": { "200": { "description": "Bounded recursive file index", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FileIndex" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/files/{fileId}": { + "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }, { "name": "fileId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^file_[A-Za-z0-9_-]{43}$" } }, { "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "readWorkspaceFile", "x-aiden-capability": "files:read", "responses": { "200": { "description": "Readable text document", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FileDocument" } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "put": { "operationId": "writeWorkspaceFile", "x-aiden-capability": "files:write", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["content", "expectedVersion"], "properties": { "content": { "type": "string", "maxLength": 5242880 }, "expectedVersion": { "type": "string", "maxLength": 128 } }, "additionalProperties": false } } } }, "responses": { "200": { "description": "Authoritative saved document", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FileDocument" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/git/review": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "reviewGitWorkspace", "x-aiden-capability": "git:read", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }], "responses": { "200": { "description": "Bounded repository review snapshot", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/git/diff": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "readGitDiff", "x-aiden-capability": "git:read", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitDiffRequest" } } } }, "responses": { "200": { "description": "Bounded diff from an immutable review snapshot", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/git/branches": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "listGitBranches", "x-aiden-capability": "git:read", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }], "responses": { "200": { "description": "Allowlisted branch projection", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "post": { "operationId": "createGitBranch", "x-aiden-capability": "git:write", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitCreateBranchRequest" } } } }, "responses": { "202": { "description": "Branch creation accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/git/checkout": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "checkoutGitBranch", "x-aiden-capability": "git:write", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitCheckoutRequest" } } } }, "responses": { "202": { "description": "Checkout accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/git/commit": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "commitGitWorkspace", "x-aiden-capability": "git:write", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitCommitRequest" } } } }, "responses": { "202": { "description": "Snapshot-bound commit accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/git/push-capability": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "getGitPushCapability", "x-aiden-capability": "git:read", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }], "responses": { "200": { "description": "Safe push capability and reason", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/git/push": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "pushGitWorkspace", "x-aiden-capability": "git:write", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitPushRequest" } } } }, "responses": { "202": { "description": "Reviewed push accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/git/compare": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "compareGitBranch", "x-aiden-capability": "git:read", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitCompareRequest" } } } }, "responses": { "200": { "description": "Immutable comparison snapshot", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/git/comparison-diff": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "readGitComparisonDiff", "x-aiden-capability": "git:read", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitComparisonDiffRequest" } } } }, "responses": { "200": { "description": "Bounded comparison diff", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/git/worktrees": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "listGitWorktrees", "x-aiden-capability": "git:read", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }], "responses": { "200": { "description": "Worktrees without filesystem or Git-admin paths", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "post": { "operationId": "createGitWorktree", "x-aiden-capability": "git:write", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitCreateWorktreeRequest" } } } }, "responses": { "202": { "description": "Managed worktree creation accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/workspaces/{workspaceId}/git/managed-worktree": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "delete": { "operationId": "removeManagedGitWorktree", "x-aiden-capability": "git:write", "parameters": [{ "$ref": "#/components/parameters/WorkspaceId" }, { "$ref": "#/components/parameters/IfMatch" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ForegroundConfirmation" } } } }, "responses": { "202": { "description": "Persisted-ID-bound managed worktree removal accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GitResult" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/scheduled-tasks": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "listScheduledTasks", "x-aiden-capability": "schedule:read", "responses": { "200": { "description": "Task list", "content": { "application/json": { "schema": { "type": "object", "required": ["tasks"], "properties": { "tasks": { "type": "array", "items": { "$ref": "#/components/schemas/ScheduledTask" } } }, "additionalProperties": false } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "post": { "operationId": "createScheduledTask", "x-aiden-capability": "schedule:write", "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduledTaskMutation" } } } }, "responses": { "201": { "description": "Created task", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduledTask" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/scheduled-tasks/{taskId}": { + "parameters": [{ "name": "taskId", "in": "path", "required": true, "schema": { "type": "string" } }, { "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "getScheduledTask", "x-aiden-capability": "schedule:read", "responses": { "200": { "description": "Task", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduledTask" } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "patch": { "operationId": "updateScheduledTask", "x-aiden-capability": "schedule:write", "parameters": [{ "$ref": "#/components/parameters/IfMatch" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduledTaskMutation" } } } }, "responses": { "200": { "description": "Updated task", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduledTask" } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "delete": { "operationId": "removeScheduledTask", "x-aiden-capability": "schedule:write", "parameters": [{ "$ref": "#/components/parameters/IfMatch" }], "responses": { "204": { "description": "Task removed" }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/scheduled-tasks/{taskId}/pause": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "pauseScheduledTask", "x-aiden-capability": "schedule:write", "parameters": [{ "$ref": "#/components/parameters/TaskId" }, { "$ref": "#/components/parameters/IfMatch" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "202": { "description": "Pause accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduledTask" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/scheduled-tasks/{taskId}/resume": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "resumeScheduledTask", "x-aiden-capability": "schedule:write", "parameters": [{ "$ref": "#/components/parameters/TaskId" }, { "$ref": "#/components/parameters/IfMatch" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "202": { "description": "Resume accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduledTask" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/scheduled-tasks/{taskId}/run": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "runScheduledTask", "x-aiden-capability": "schedule:write", "parameters": [{ "$ref": "#/components/parameters/TaskId" }, { "$ref": "#/components/parameters/IdempotencyKey" }], "responses": { "202": { "description": "Durable run accepted; socket loss does not cancel it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduleRunAccepted" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/scheduled-tasks/{taskId}/runs": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "listScheduledTaskRuns", "x-aiden-capability": "schedule:read", "parameters": [{ "name": "taskId", "in": "path", "required": true, "schema": { "type": "string" } }], "responses": { "200": { "description": "Bounded redacted run history", "content": { "application/json": { "schema": { "type": "object", "required": ["runs"], "properties": { "runs": { "type": "array", "items": { "$ref": "#/components/schemas/ScheduleRun" } } }, "additionalProperties": false } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/scheduled-tasks/preview": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "post": { "operationId": "previewSchedule", "x-aiden-capability": "schedule:read", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["cron", "timezone"], "properties": { "cron": { "type": "string" }, "timezone": { "type": "string" }, "count": { "type": "integer", "minimum": 1, "maximum": 20 } }, "additionalProperties": false } } } }, "responses": { "200": { "description": "Next run timestamps", "content": { "application/json": { "schema": { "type": "object", "required": ["dates"], "properties": { "dates": { "type": "array", "items": { "type": "string", "format": "date-time" } } }, "additionalProperties": false } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/scheduled-tasks/scripts": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "listScheduledScripts", "x-aiden-capability": "schedule:read", "parameters": [{ "name": "workspaceId", "in": "query", "required": false, "schema": { "type": "string" } }], "responses": { "200": { "description": "Server-inventoried scripts without paths", "content": { "application/json": { "schema": { "type": "object", "required": ["scripts"], "properties": { "scripts": { "type": "array", "items": { "type": "object", "required": ["id", "name"], "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "additionalProperties": false } } }, "additionalProperties": false } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/scheduled-tasks/mcp-servers": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "listScheduledMcpServers", "x-aiden-capability": "schedule:read", "responses": { "200": { "description": "Enabled MCP server IDs and display names only", "content": { "application/json": { "schema": { "type": "object", "required": ["servers"], "properties": { "servers": { "type": "array", "items": { "type": "object", "required": ["id", "name"], "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "additionalProperties": false } } }, "additionalProperties": false } } } }, "default": { "$ref": "#/components/responses/Error" } } } + }, + "/scheduled-tasks/settings": { + "parameters": [{ "$ref": "#/components/parameters/ProtocolVersion" }], + "get": { "operationId": "getScheduledTaskSettings", "x-aiden-capability": "schedule:read", "responses": { "200": { "description": "Settings projection", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduleSettings" } } } }, "default": { "$ref": "#/components/responses/Error" } } }, + "patch": { "operationId": "updateScheduledTaskSettings", "x-aiden-capability": "schedule:write", "parameters": [{ "$ref": "#/components/parameters/IfMatch" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduleSettingsMutation" } } } }, "responses": { "200": { "description": "Updated settings", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduleSettings" } } } }, "default": { "$ref": "#/components/responses/Error" } } } + } + }, + "components": { + "securitySchemes": { "deviceBearer": { "type": "http", "scheme": "bearer", "bearerFormat": "Aiden device credential" }, "protocolVersion": { "type": "apiKey", "in": "header", "name": "Aiden-Protocol-Version", "description": "Must be exactly 1." } }, + "responses": { "Error": { "description": "Stable safe error envelope", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } } }, + "parameters": { + "IdempotencyKey": { "name": "Idempotency-Key", "in": "header", "required": true, "schema": { "type": "string", "minLength": 16, "maxLength": 128 } }, + "ProtocolVersion": { "name": "Aiden-Protocol-Version", "in": "header", "required": true, "schema": { "const": "1" } }, + "IfMatch": { "name": "If-Match", "in": "header", "required": true, "schema": { "type": "string", "minLength": 1, "maxLength": 128 } }, + "WorkspaceId": { "name": "workspaceId", "in": "path", "required": true, "schema": { "type": "string", "minLength": 1, "maxLength": 128 } }, + "ChatId": { "name": "chatId", "in": "path", "required": true, "schema": { "type": "string", "minLength": 1, "maxLength": 128 } }, + "StreamId": { "name": "streamId", "in": "path", "required": true, "schema": { "type": "string", "minLength": 1, "maxLength": 128 } }, + "TaskId": { "name": "taskId", "in": "path", "required": true, "schema": { "type": "string", "minLength": 1, "maxLength": 128 } } + }, + "schemas": { + "Health": { "type": "object", "required": ["ok", "protocolVersion"], "properties": { "ok": { "const": true }, "protocolVersion": { "const": 1 } }, "additionalProperties": false }, + "PairingBootstrap": { "type": "object", "description": "Canonical JSON encoded in the locally displayed QR before any network request.", "required": ["protocolVersion", "instanceId", "endpoint", "serverSpkiSha256", "secret", "expiresAt"], "properties": { "protocolVersion": { "const": 1 }, "instanceId": { "type": "string", "minLength": 1, "maxLength": 128 }, "endpoint": { "type": "string", "format": "uri", "pattern": "^https://(?:\\[(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,6})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0}::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,5})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){1}::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,4})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){2}::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,3})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){3}::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,2})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){4}::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,1})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){5}::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,0})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){6}::|(?:[0-9A-Fa-f]{1,4}:){6}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|::(?:[0-9A-Fa-f]{1,4}:){0,5}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0}::(?:[0-9A-Fa-f]{1,4}:){0,4}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){1}::(?:[0-9A-Fa-f]{1,4}:){0,3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){2}::(?:[0-9A-Fa-f]{1,4}:){0,2}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){3}::(?:[0-9A-Fa-f]{1,4}:){0,1}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){4}::(?:[0-9A-Fa-f]{1,4}:){0,0}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})\\]|(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?=[A-Za-z0-9.-]{1,253}(?::(?:[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?/api/aiden/v1$)(?!(?:[A-Za-z0-9-]+\\.)*[0-9]+(?::[0-9]+)?/api/aiden/v1$)(?=[A-Za-z0-9.-]*[A-Za-z-])[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*)(?::(?:[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?/api/aiden/v1$", "maxLength": 2048 }, "serverSpkiSha256": { "type": "string", "pattern": "^sha256/[A-Za-z0-9+/]{43}=$" }, "secret": { "type": "string", "pattern": "^[A-Za-z0-9_-]{43}$" }, "expiresAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, + "PairingExchangeRequest": { "type": "object", "required": ["secret", "deviceName", "deviceType", "clientVersion"], "properties": { "secret": { "type": "string", "pattern": "^[A-Za-z0-9_-]{43}$" }, "deviceName": { "type": "string", "minLength": 1, "maxLength": 80 }, "deviceType": { "enum": ["iphone", "ipad"] }, "clientVersion": { "type": "string", "minLength": 1, "maxLength": 40 } }, "additionalProperties": false }, + "PairingExchangeResponse": { "type": "object", "required": ["protocolVersion", "instanceId", "deviceId", "credential", "capabilities", "endpoint", "serverSpkiSha256"], "properties": { "protocolVersion": { "const": 1 }, "instanceId": { "type": "string", "minLength": 1, "maxLength": 128 }, "deviceId": { "type": "string", "minLength": 1, "maxLength": 128 }, "credential": { "type": "string", "pattern": "^[A-Za-z0-9_-]{43}$" }, "capabilities": { "type": "array", "items": { "enum": ["server:read", "chat:read", "chat:write", "approval:respond", "workspace:read", "workspace:browse", "workspace:manage", "files:read", "files:write", "git:read", "git:write", "schedule:read", "schedule:write"] }, "uniqueItems": true }, "endpoint": { "type": "string", "format": "uri", "pattern": "^https://(?:\\[(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,6})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0}::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,5})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){1}::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,4})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){2}::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,3})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){3}::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,2})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){4}::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,1})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){5}::(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,0})?|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){6}::|(?:[0-9A-Fa-f]{1,4}:){6}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|::(?:[0-9A-Fa-f]{1,4}:){0,5}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0}::(?:[0-9A-Fa-f]{1,4}:){0,4}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){1}::(?:[0-9A-Fa-f]{1,4}:){0,3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){2}::(?:[0-9A-Fa-f]{1,4}:){0,2}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){3}::(?:[0-9A-Fa-f]{1,4}:){0,1}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){4}::(?:[0-9A-Fa-f]{1,4}:){0,0}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})\\]|(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(?=[A-Za-z0-9.-]{1,253}(?::(?:[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?/api/aiden/v1$)(?!(?:[A-Za-z0-9-]+\\.)*[0-9]+(?::[0-9]+)?/api/aiden/v1$)(?=[A-Za-z0-9.-]*[A-Za-z-])[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*)(?::(?:[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?/api/aiden/v1$", "maxLength": 2048 }, "serverSpkiSha256": { "type": "string", "pattern": "^sha256/[A-Za-z0-9+/]{43}=$" } }, "additionalProperties": false }, + "Server": { "type": "object", "required": ["protocolVersion", "instanceId", "name", "appVersion", "capabilities", "connectionMode", "serverTime"], "properties": { "protocolVersion": { "const": 1 }, "instanceId": { "type": "string" }, "name": { "type": "string" }, "appVersion": { "type": "string" }, "capabilities": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, "connectionMode": { "enum": ["lan", "tailscale", "both"] }, "minimumClientVersion": { "type": "string" }, "serverTime": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, + "UsageTokens": { "type": "object", "required": ["input", "output", "cacheRead", "cacheWrite", "reasoning", "total"], "properties": { "input": { "type": "integer", "minimum": 0 }, "output": { "type": "integer", "minimum": 0 }, "cacheRead": { "type": "integer", "minimum": 0 }, "cacheWrite": { "type": "integer", "minimum": 0 }, "cacheWrite1h": { "type": "integer", "minimum": 0 }, "reasoning": { "type": "integer", "minimum": 0 }, "total": { "type": "integer", "minimum": 0 } }, "additionalProperties": false }, + "UsageSummary": { "type": "object", "required": ["range", "startDate", "endDate", "totals", "days", "models"], "properties": { "range": { "enum": ["7d", "30d", "90d", "1y", "all"] }, "startDate": { "type": "string", "format": "date" }, "endDate": { "type": "string", "format": "date" }, "totals": { "type": "object", "required": ["requests", "completedRequests", "failedRequests", "cancelledRequests", "reportedTokenRequests", "unmeteredRequests", "localRequests", "costedRequests", "unpricedHostedRequests", "hostedCostUsd", "activeDays", "currentStreak", "longestStreak", "tokens"], "properties": { "requests": { "type": "integer", "minimum": 0 }, "completedRequests": { "type": "integer", "minimum": 0 }, "failedRequests": { "type": "integer", "minimum": 0 }, "cancelledRequests": { "type": "integer", "minimum": 0 }, "reportedTokenRequests": { "type": "integer", "minimum": 0 }, "unmeteredRequests": { "type": "integer", "minimum": 0 }, "localRequests": { "type": "integer", "minimum": 0 }, "costedRequests": { "type": "integer", "minimum": 0 }, "unpricedHostedRequests": { "type": "integer", "minimum": 0 }, "hostedCostUsd": { "type": "number", "minimum": 0 }, "activeDays": { "type": "integer", "minimum": 0 }, "currentStreak": { "type": "integer", "minimum": 0 }, "longestStreak": { "type": "integer", "minimum": 0 }, "tokens": { "$ref": "#/components/schemas/UsageTokens" } }, "additionalProperties": false }, "days": { "type": "array", "items": { "type": "object" } }, "models": { "type": "array", "items": { "type": "object" } } }, "additionalProperties": false }, + "Workspace": { "type": "object", "required": ["id", "name", "permission", "hasFolder", "isManagedWorktree", "createdAt", "updatedAt", "revision"], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "permission": { "enum": ["full", "ask", "none"] }, "hasFolder": { "type": "boolean" }, "isManagedWorktree": { "type": "boolean" }, "branchName": { "type": "string" }, "repositoryName": { "type": "string" }, "git": { "type": "object", "properties": { "isRepo": { "type": "boolean" }, "branch": { "type": "string" }, "uncommitted": { "type": "integer", "minimum": 0 } }, "required": ["isRepo"], "additionalProperties": false }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" }, "revision": { "type": "string" } }, "additionalProperties": false }, + "WorkspaceCreate": { "oneOf": [{ "type": "object", "required": ["mode", "name"], "properties": { "mode": { "const": "folderless" }, "name": { "type": "string", "minLength": 1, "maxLength": 120 } }, "additionalProperties": false }, { "type": "object", "required": ["mode"], "properties": { "mode": { "const": "scratch" } }, "additionalProperties": false }, { "type": "object", "required": ["mode", "selection"], "properties": { "mode": { "const": "selected-folder" }, "selection": { "type": "string", "pattern": "^sel_[A-Za-z0-9_-]{43}$" }, "name": { "type": "string", "minLength": 1, "maxLength": 120 } }, "additionalProperties": false }] }, + "ForegroundConfirmation": { "type": "object", "required": ["confirmedForeground"], "properties": { "confirmedForeground": { "const": true } }, "additionalProperties": false }, + "WorkspacePatch": { "type": "object", "required": ["confirmedForeground"], "minProperties": 2, "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "permission": { "enum": ["full", "ask", "none"] }, "confirmedForeground": { "const": true } }, "additionalProperties": false }, + "BrowserRoot": { "type": "object", "required": ["id", "label", "location", "policyRevision"], "properties": { "id": { "type": "string", "maxLength": 128 }, "label": { "type": "string", "maxLength": 120 }, "location": { "type": "string", "pattern": "^loc_[A-Za-z0-9_-]{43}$" }, "policyRevision": { "type": "string", "maxLength": 128 } }, "additionalProperties": false }, + "BrowserEntry": { "type": "object", "required": ["id", "name", "location"], "properties": { "id": { "type": "string", "maxLength": 128 }, "name": { "type": "string", "maxLength": 255 }, "location": { "type": "string", "pattern": "^loc_[A-Za-z0-9_-]{43}$" } }, "additionalProperties": false }, + "BrowserPage": { "type": "object", "required": ["rootId", "label", "breadcrumbs", "entries"], "properties": { "rootId": { "type": "string", "maxLength": 128 }, "label": { "type": "string", "maxLength": 120 }, "breadcrumbs": { "type": "array", "maxItems": 20, "items": { "type": "object", "required": ["label", "location"], "properties": { "label": { "type": "string", "maxLength": 255 }, "location": { "type": "string", "pattern": "^loc_[A-Za-z0-9_-]{43}$" } }, "additionalProperties": false } }, "entries": { "type": "array", "maxItems": 200, "items": { "$ref": "#/components/schemas/BrowserEntry" } }, "nextCursor": { "type": "string", "pattern": "^cur_[A-Za-z0-9_-]{43}$" } }, "additionalProperties": false }, + "WorkspaceSelection": { "type": "object", "required": ["selection", "displayName", "expiresAt"], "properties": { "selection": { "type": "string", "pattern": "^sel_[A-Za-z0-9_-]{43}$" }, "displayName": { "type": "string", "maxLength": 255 }, "expiresAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, + "MessageAttachment": { "type": "object", "required": ["id", "name", "mimeType", "kind", "size"], "properties": { "id": { "type": "string", "pattern": "^[A-Za-z0-9._:-]{1,256}$" }, "name": { "type": "string", "minLength": 1, "maxLength": 255, "pattern": "^[^/\\\\\\u0000-\\u001F\\u007F]+$" }, "mimeType": { "type": "string", "maxLength": 120 }, "kind": { "enum": ["image", "text"] }, "size": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "additionalProperties": false }, + "AttachmentReference": { "type": "object", "required": ["id", "name", "mimeType", "kind", "size", "expiresAt"], "properties": { "id": { "type": "string", "pattern": "^att_[A-Za-z0-9_-]{43}$" }, "name": { "type": "string", "minLength": 1, "maxLength": 255, "pattern": "^[^/\\\\\\u0000-\\u001F\\u007F]+$" }, "mimeType": { "type": "string", "maxLength": 120 }, "kind": { "enum": ["image", "text"] }, "size": { "type": "integer", "minimum": 0, "maximum": 8388608 }, "expiresAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, + "AttachmentUpload": { "oneOf": [ + { "type": "object", "required": ["name", "mimeType", "kind", "data"], "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 255, "pattern": "^[^/\\\\\\u0000-\\u001F\\u007F]+$" }, "mimeType": { "enum": ["image/png", "image/jpeg"] }, "kind": { "const": "image" }, "data": { "type": "string", "contentEncoding": "base64", "maxLength": 11184812 } }, "additionalProperties": false }, + { "type": "object", "required": ["name", "mimeType", "kind", "text"], "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 255, "pattern": "^[^/\\\\\\u0000-\\u001F\\u007F]+$" }, "mimeType": { "enum": ["text/plain", "text/markdown", "text/csv", "application/json", "application/xml", "application/yaml", "application/x-yaml", "application/javascript", "application/typescript"] }, "kind": { "const": "text" }, "text": { "type": "string", "maxLength": 100000 } }, "additionalProperties": false } + ] }, + "Message": { "type": "object", "required": ["id", "role", "text", "createdAt"], "properties": { "id": { "type": "string", "maxLength": 128 }, "role": { "enum": ["user", "assistant"] }, "text": { "type": "string", "maxLength": 200000 }, "attachments": { "type": "array", "maxItems": 20, "items": { "$ref": "#/components/schemas/MessageAttachment" } }, "createdAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, + "Chat": { "type": "object", "required": ["id", "workspaceId", "title", "messages", "createdAt", "updatedAt", "revision"], "properties": { "id": { "type": "string" }, "workspaceId": { "type": "string" }, "title": { "type": "string" }, "providerId": { "type": "string" }, "modelId": { "type": "string" }, "messages": { "type": "array", "items": { "$ref": "#/components/schemas/Message" } }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" }, "revision": { "type": "string" } }, "additionalProperties": false }, + "TurnStartRequest": { "type": "object", "required": ["text"], "anyOf": [{ "properties": { "text": { "minLength": 1 } } }, { "required": ["attachmentIds"], "properties": { "attachmentIds": { "minItems": 1 } } }], "properties": { "text": { "type": "string", "maxLength": 200000 }, "providerId": { "type": "string" }, "modelId": { "type": "string" }, "thinkingLevel": { "type": "string" }, "attachmentIds": { "type": "array", "minItems": 1, "maxItems": 10, "uniqueItems": true, "items": { "type": "string", "pattern": "^att_[A-Za-z0-9_-]{43}$" } } }, "additionalProperties": false }, + "TurnStartResponse": { "type": "object", "required": ["turnId", "streamId", "status", "message"], "properties": { "turnId": { "type": "string" }, "streamId": { "type": "string" }, "status": { "const": "accepted" }, "message": { "$ref": "#/components/schemas/Message" } }, "additionalProperties": false }, + "StreamStatus": { "type": "object", "required": ["streamId", "chatId", "turnId", "state", "lastSequence", "updatedAt"], "properties": { "streamId": { "type": "string" }, "chatId": { "type": "string" }, "turnId": { "type": "string" }, "state": { "enum": ["queued", "running", "waiting_for_approval", "reconciling", "done", "error", "cancelled", "interrupted"] }, "lastSequence": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "updatedAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, + "StreamEvent": { + "type": "object", + "required": ["protocolVersion", "streamId", "sequence", "timestamp", "type", "terminal", "payload"], + "properties": { + "protocolVersion": { "const": 1 }, + "streamId": { "type": "string", "minLength": 1, "maxLength": 128 }, + "sequence": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, + "timestamp": { "type": "string", "format": "date-time" }, + "type": { "type": "string", "minLength": 1, "maxLength": 80 }, + "terminal": { "type": "boolean" }, + "payload": { "type": "object", "maxProperties": 32 } + }, + "allOf": [ + { "if": { "properties": { "type": { "const": "snapshot" } }, "required": ["type"] }, "then": { "properties": { "terminal": { "const": false }, "payload": { "type": "object", "required": ["chatId", "turnId", "nextSequence"], "properties": { "chatId": { "type": "string", "maxLength": 128 }, "turnId": { "type": "string", "maxLength": 128 }, "nextSequence": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 } }, "additionalProperties": false } } } }, + { "if": { "properties": { "type": { "const": "status" } }, "required": ["type"] }, "then": { "properties": { "terminal": { "const": false }, "payload": { "type": "object", "required": ["state"], "properties": { "state": { "enum": ["queued", "running", "waiting_for_approval", "reconciling"] } }, "additionalProperties": false } } } }, + { "if": { "properties": { "type": { "enum": ["text_delta", "reasoning_delta"] } }, "required": ["type"] }, "then": { "properties": { "terminal": { "const": false }, "payload": { "type": "object", "required": ["text"], "properties": { "text": { "type": "string", "maxLength": 200000 } }, "additionalProperties": false } } } }, + { "if": { "properties": { "type": { "const": "tool_started" } }, "required": ["type"] }, "then": { "properties": { "terminal": { "const": false }, "payload": { "type": "object", "required": ["toolId", "name"], "properties": { "toolId": { "type": "string", "maxLength": 128 }, "name": { "type": "string", "maxLength": 120 } }, "additionalProperties": false } } } }, + { "if": { "properties": { "type": { "const": "tool_finished" } }, "required": ["type"] }, "then": { "properties": { "terminal": { "const": false }, "payload": { "type": "object", "required": ["toolId", "status"], "properties": { "toolId": { "type": "string", "maxLength": 128 }, "status": { "enum": ["succeeded", "failed", "cancelled"] } }, "additionalProperties": false } } } }, + { "if": { "properties": { "type": { "const": "timeline" } }, "required": ["type"] }, "then": { "properties": { "terminal": { "const": false }, "payload": { "type": "object", "required": ["label"], "properties": { "label": { "type": "string", "maxLength": 500 } }, "additionalProperties": false } } } }, + { "if": { "properties": { "type": { "const": "approval_required" } }, "required": ["type"] }, "then": { "properties": { "terminal": { "const": false }, "payload": { "type": "object", "required": ["approvalId", "summary", "expiresAt"], "properties": { "approvalId": { "type": "string", "maxLength": 128 }, "summary": { "type": "string", "maxLength": 2000 }, "expiresAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false } } } }, + { "if": { "properties": { "type": { "const": "done" } }, "required": ["type"] }, "then": { "properties": { "terminal": { "const": true }, "payload": { "type": "object", "required": ["messageId"], "properties": { "messageId": { "type": "string", "maxLength": 128 } }, "additionalProperties": false } } } }, + { "if": { "properties": { "type": { "const": "error" } }, "required": ["type"] }, "then": { "properties": { "terminal": { "const": true }, "payload": { "type": "object", "required": ["code", "message"], "properties": { "code": { "$ref": "#/components/schemas/ErrorCode" }, "message": { "type": "string", "maxLength": 2000 } }, "additionalProperties": false } } } }, + { "if": { "properties": { "type": { "const": "cancelled" } }, "required": ["type"] }, "then": { "properties": { "terminal": { "const": true }, "payload": { "type": "object", "required": ["source"], "properties": { "source": { "enum": ["device", "server"] } }, "additionalProperties": false } } } }, + { "if": { "properties": { "type": { "const": "heartbeat" } }, "required": ["type"] }, "then": { "properties": { "terminal": { "const": false }, "payload": { "type": "object", "maxProperties": 0, "additionalProperties": false } } } }, + { "if": { "properties": { "type": { "not": { "enum": ["snapshot", "status", "text_delta", "reasoning_delta", "tool_started", "tool_finished", "timeline", "approval_required", "done", "error", "cancelled", "heartbeat"] } } }, "required": ["type"] }, "then": { "properties": { "terminal": { "const": false } } } } + ], + "additionalProperties": true + }, + "Provider": { "type": "object", "required": ["id", "label", "models"], "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "models": { "type": "array", "items": { "type": "object", "required": ["id", "label"], "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "thinkingLevels": { "type": "array", "items": { "type": "string" } } }, "additionalProperties": false } } }, "additionalProperties": false }, + "FileEntry": { "type": "object", "required": ["id", "displayPath", "name", "kind"], "properties": { "id": { "type": "string", "pattern": "^file_[A-Za-z0-9_-]{43}$" }, "displayPath": { "type": "string", "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$", "maxLength": 4096 }, "name": { "type": "string", "maxLength": 255 }, "kind": { "enum": ["file", "directory", "symlink"] }, "size": { "type": "integer", "minimum": 0, "maximum": 5242880 }, "language": { "type": "string", "maxLength": 80 } }, "additionalProperties": false }, + "FileIndex": { "type": "object", "required": ["snapshotId", "entries", "truncated", "maxEntries", "maxDepth"], "properties": { "snapshotId": { "type": "string" }, "entries": { "type": "array", "maxItems": 4000, "items": { "$ref": "#/components/schemas/FileEntry" } }, "truncated": { "type": "boolean" }, "maxEntries": { "const": 4000 }, "maxDepth": { "const": 20 } }, "additionalProperties": false }, + "FileDocument": { "type": "object", "required": ["id", "displayPath", "content", "version", "truncated"], "properties": { "id": { "type": "string", "pattern": "^file_[A-Za-z0-9_-]{43}$" }, "displayPath": { "type": "string", "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$", "maxLength": 4096 }, "content": { "type": "string", "maxLength": 5242880 }, "version": { "type": "string", "maxLength": 128 }, "truncated": { "type": "boolean" }, "warning": { "type": "string", "maxLength": 500 } }, "additionalProperties": false }, + "GitDiffRequest": { "type": "object", "required": ["snapshotId", "fileId"], "properties": { "snapshotId": { "type": "string" }, "fileId": { "type": "string" } }, "additionalProperties": false }, + "GitCreateBranchRequest": { "type": "object", "required": ["name", "startPoint", "confirmedForeground"], "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "startPoint": { "type": "string", "minLength": 1, "maxLength": 500 }, "confirmedForeground": { "const": true } }, "additionalProperties": false }, + "GitCheckoutRequest": { "type": "object", "required": ["branch", "snapshotId", "confirmedForeground"], "properties": { "branch": { "type": "string", "minLength": 1, "maxLength": 500 }, "snapshotId": { "type": "string" }, "confirmedForeground": { "const": true } }, "additionalProperties": false }, + "GitCommitRequest": { "type": "object", "required": ["snapshotId", "message", "scope", "confirmedForeground"], "properties": { "snapshotId": { "type": "string" }, "message": { "type": "string", "minLength": 1, "maxLength": 20000 }, "scope": { "enum": ["all-reviewed", "staged-reviewed"] }, "confirmedForeground": { "const": true } }, "additionalProperties": false }, + "GitPushRequest": { "type": "object", "required": ["snapshotId", "remote", "branch", "confirmedForeground"], "properties": { "snapshotId": { "type": "string", "maxLength": 128 }, "remote": { "type": "string", "pattern": "^[A-Za-z0-9._-]{1,200}$" }, "branch": { "type": "string", "minLength": 1, "maxLength": 500 }, "confirmedForeground": { "const": true } }, "additionalProperties": false }, + "GitCompareRequest": { "type": "object", "required": ["baseRef"], "properties": { "baseRef": { "type": "string", "minLength": 1, "maxLength": 500 } }, "additionalProperties": false }, + "GitComparisonDiffRequest": { "type": "object", "required": ["comparisonId", "fileId"], "properties": { "comparisonId": { "type": "string" }, "fileId": { "type": "string" } }, "additionalProperties": false }, + "GitCreateWorktreeRequest": { "type": "object", "required": ["branch", "name", "confirmedForeground"], "properties": { "branch": { "type": "string", "minLength": 1, "maxLength": 500 }, "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "confirmedForeground": { "const": true } }, "additionalProperties": false }, + "GitFileProjection": { "type": "object", "required": ["id", "displayPath", "status"], "properties": { "id": { "type": "string", "pattern": "^file_[A-Za-z0-9_-]{43}$" }, "displayPath": { "type": "string", "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$", "maxLength": 4096 }, "status": { "enum": ["added", "modified", "deleted", "renamed", "untracked", "conflicted"] }, "staged": { "type": "boolean" }, "additions": { "type": "integer", "minimum": 0 }, "deletions": { "type": "integer", "minimum": 0 } }, "additionalProperties": false }, + "GitProjection": { "oneOf": [ + { "type": "object", "required": ["kind", "branch", "uncommitted", "files"], "properties": { "kind": { "const": "review" }, "branch": { "type": "string" }, "uncommitted": { "type": "integer", "minimum": 0 }, "files": { "type": "array", "maxItems": 4000, "items": { "$ref": "#/components/schemas/GitFileProjection" } } }, "additionalProperties": false }, + { "type": "object", "required": ["kind", "displayPath", "diff", "truncated"], "properties": { "kind": { "const": "diff" }, "displayPath": { "type": "string", "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$", "maxLength": 4096 }, "diff": { "type": "string", "maxLength": 2000000 }, "truncated": { "type": "boolean" } }, "additionalProperties": false }, + { "type": "object", "required": ["kind", "current", "branches"], "properties": { "kind": { "const": "branches" }, "current": { "type": "string" }, "branches": { "type": "array", "items": { "type": "string" } } }, "additionalProperties": false }, + { "type": "object", "required": ["kind", "comparisonId", "base", "head", "files"], "properties": { "kind": { "const": "comparison" }, "comparisonId": { "type": "string" }, "base": { "type": "string" }, "head": { "type": "string" }, "files": { "type": "array", "maxItems": 4000, "items": { "$ref": "#/components/schemas/GitFileProjection" } } }, "additionalProperties": false }, + { "type": "object", "required": ["kind", "allowed"], "properties": { "kind": { "const": "push-capability" }, "allowed": { "type": "boolean" }, "reason": { "type": "string", "maxLength": 500 }, "remote": { "type": "string", "pattern": "^[A-Za-z0-9._-]{1,200}$" }, "branch": { "type": "string", "maxLength": 500 } }, "additionalProperties": false }, + { "type": "object", "required": ["kind", "worktrees"], "properties": { "kind": { "const": "worktrees" }, "worktrees": { "type": "array", "items": { "type": "object", "required": ["id", "name", "branch", "managed"], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "branch": { "type": "string" }, "managed": { "type": "boolean" } }, "additionalProperties": false } } }, "additionalProperties": false }, + { "type": "object", "required": ["kind", "message"], "properties": { "kind": { "const": "mutation" }, "message": { "type": "string" }, "branch": { "type": "string" }, "commitId": { "type": "string" }, "workspaceId": { "type": "string" }, "warning": { "type": "string" } }, "additionalProperties": false } + ] }, + "GitResult": { "type": "object", "required": ["operationId", "status"], "properties": { "operationId": { "type": "string" }, "status": { "enum": ["snapshot", "accepted", "running", "succeeded", "failed", "conflict"] }, "snapshotId": { "type": "string" }, "capability": { "type": "object", "properties": { "allowed": { "type": "boolean" }, "reason": { "type": "string" } }, "required": ["allowed"], "additionalProperties": false }, "result": { "$ref": "#/components/schemas/GitProjection" } }, "additionalProperties": false }, + "ScheduledTask": { "type": "object", "required": ["id", "revision", "name", "enabled", "schedule", "timezone", "mode", "permission", "notify", "running", "createdAt", "updatedAt"], "properties": { "id": { "type": "string" }, "revision": { "type": "string" }, "name": { "type": "string" }, "enabled": { "type": "boolean" }, "schedule": { "type": "string" }, "timezone": { "type": "string" }, "mode": { "enum": ["llm", "script"] }, "permission": { "enum": ["full", "read-only"] }, "workspaceId": { "type": "string" }, "providerId": { "type": "string" }, "modelId": { "type": "string" }, "mcpServerIds": { "type": "array", "items": { "type": "string" } }, "scriptId": { "type": "string" }, "prompt": { "type": "string", "maxLength": 32768 }, "notify": { "type": "boolean" }, "running": { "type": "boolean" }, "nextRunAt": { "type": "string", "format": "date-time" }, "lastRunAt": { "type": "string", "format": "date-time" }, "lastResult": { "enum": ["success", "error", "silent", "blocked"] }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, + "ScheduledTaskMutation": { "type": "object", "required": ["name", "schedule", "timezone", "mode", "permission", "confirmedForeground"], "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "schedule": { "type": "string" }, "timezone": { "type": "string" }, "mode": { "enum": ["llm", "script"] }, "permission": { "enum": ["full", "read-only"] }, "workspaceId": { "type": "string" }, "providerId": { "type": "string" }, "modelId": { "type": "string" }, "mcpServerIds": { "type": "array", "items": { "type": "string" } }, "scriptId": { "type": "string" }, "prompt": { "type": "string" }, "notify": { "type": "boolean" }, "confirmedForeground": { "const": true } }, "additionalProperties": false }, + "ScheduleRunAccepted": { "type": "object", "required": ["taskId", "runId", "status", "acceptedAt"], "properties": { "taskId": { "type": "string" }, "runId": { "type": "string" }, "status": { "enum": ["accepted", "running"] }, "acceptedAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, + "ScheduleRun": { "type": "object", "required": ["id", "taskId", "status", "startedAt"], "properties": { "id": { "type": "string" }, "taskId": { "type": "string" }, "status": { "enum": ["accepted", "running", "succeeded", "failed", "cancelled"] }, "startedAt": { "type": "string", "format": "date-time" }, "finishedAt": { "type": "string", "format": "date-time" }, "summary": { "type": "string", "maxLength": 20000 }, "errorCode": { "type": "string" } }, "additionalProperties": false }, + "ScheduleSettings": { "type": "object", "required": ["revision", "enabled", "defaultMode", "defaultPermission", "defaultMcpEnabled", "defaultNotify", "defaultTimezone"], "properties": { "revision": { "type": "string" }, "enabled": { "type": "boolean" }, "defaultMode": { "enum": ["llm", "script"] }, "defaultPermission": { "enum": ["full", "read-only"] }, "defaultMcpEnabled": { "type": "boolean" }, "defaultNotify": { "type": "boolean" }, "defaultTimezone": { "type": "string" } }, "additionalProperties": false }, + "ScheduleSettingsMutation": { "type": "object", "required": ["confirmedForeground"], "minProperties": 2, "properties": { "enabled": { "type": "boolean" }, "defaultMode": { "enum": ["llm", "script"] }, "defaultPermission": { "enum": ["full", "read-only"] }, "defaultMcpEnabled": { "type": "boolean" }, "defaultNotify": { "type": "boolean" }, "defaultTimezone": { "type": "string", "maxLength": 120 }, "confirmedForeground": { "const": true } }, "additionalProperties": false }, + "ErrorCode": { "enum": ["invalid_request", "payload_too_large", "rate_limited", "authentication_required", "credential_revoked", "capability_denied", "pairing_closed", "pairing_expired", "pairing_already_used", "server_identity_changed", "not_found", "already_exists", "revision_conflict", "idempotency_conflict", "idempotency_capacity", "idempotency_in_flight", "workspace_unavailable", "workspace_changing", "permission_confirmation_required", "handle_invalid", "handle_expired", "handle_wrong_device", "root_policy_changed", "filesystem_identity_changed", "path_outside_root", "handle_capacity", "turn_already_active", "stream_gone", "approval_already_resolved", "approval_expired", "operation_in_progress", "operation_stale", "git_capability_denied", "schedule_disabled", "schedule_run_in_progress", "server_interrupted", "internal_error"] }, + "ErrorDetails": { "type": "object", "properties": { "currentRevision": { "type": "string", "maxLength": 128 }, "retryAfterSeconds": { "type": "integer", "minimum": 0, "maximum": 86400 }, "chatId": { "type": "string", "maxLength": 128 }, "minimumClientVersion": { "type": "string", "maxLength": 40 }, "limit": { "type": "integer", "minimum": 0, "maximum": 1000000 }, "field": { "type": "string", "maxLength": 120 } }, "additionalProperties": false }, + "ErrorEnvelope": { "type": "object", "required": ["error"], "properties": { "error": { "type": "object", "required": ["code", "message", "requestId", "retryable"], "properties": { "code": { "$ref": "#/components/schemas/ErrorCode" }, "message": { "type": "string", "maxLength": 2000 }, "requestId": { "type": "string", "maxLength": 128 }, "retryable": { "type": "boolean" }, "details": { "$ref": "#/components/schemas/ErrorDetails" } }, "additionalProperties": false } }, "additionalProperties": false } + } + } +} diff --git a/renderer/shared/aiden-remote.ts b/renderer/shared/aiden-remote.ts new file mode 100644 index 00000000..fad8a04f --- /dev/null +++ b/renderer/shared/aiden-remote.ts @@ -0,0 +1,51 @@ +export type AidenRemoteConnectionMode = "lan" | "tailscale" | "both"; + +export interface AidenRemoteStatusView { + enabled: boolean; + running: boolean; + connectionMode: AidenRemoteConnectionMode; + lanPort: number; + lanEndpoint?: string; + tailscaleEndpoint?: string; + tailscaleRoutePreview?: string; + tailscaleConnected: boolean; + tailscaleInstalled: boolean; + pairedDeviceCount: number; + approvedRootCount: number; + error?: string; +} + +export interface AidenRemoteDeviceView { + id: string; + name: string; + type: "iphone" | "ipad"; + clientVersion: string; + capabilities: string[]; + createdAt: number; + lastSeenAt: number; + revokedAt?: number; +} + +export interface AidenRemoteApprovedRootView { + id: string; + label: string; + folderPath: string; + createdAt: number; +} + +export interface AidenRemoteSettingsSnapshot { + status: AidenRemoteStatusView; + devices: AidenRemoteDeviceView[]; + approvedRoots: AidenRemoteApprovedRootView[]; +} + +export interface AidenRemotePairingBootstrapView { + protocolVersion: 1; + instanceId: string; + endpoint: string; + serverSpkiSha256: string; + secret: string; + expiresAt: string; + /** Versioned scanner envelope; includes the LAN trust anchor when required. */ + qrPayload: string; +} diff --git a/scripts/aiden-remote-lan-transport-spike.mjs b/scripts/aiden-remote-lan-transport-spike.mjs new file mode 100644 index 00000000..546e59c3 --- /dev/null +++ b/scripts/aiden-remote-lan-transport-spike.mjs @@ -0,0 +1,230 @@ +import { execFile } from "node:child_process"; +import { createHash, randomBytes, X509Certificate } from "node:crypto"; +import { mkdtemp, readFile, rm } from "node:fs/promises"; +import https from "node:https"; +import os from "node:os"; +import path from "node:path"; +import tls from "node:tls"; +import { fileURLToPath } from "node:url"; +import { promisify } from "node:util"; + +const execFileAsync = promisify(execFile); + +export function spkiSha256(certificatePem) { + const certificate = new X509Certificate(certificatePem); + const spki = certificate.publicKey.export({ type: "spki", format: "der" }); + return `sha256/${createHash("sha256").update(spki).digest("base64")}`; +} + +export function generatePairingSecret() { + return randomBytes(32).toString("base64url"); +} + +export function assertCertificateCurrentlyValid(certificatePem, now = new Date()) { + const certificate = new X509Certificate(certificatePem); + if (now < new Date(certificate.validFrom) || now > new Date(certificate.validTo)) { + throw new Error("Aiden server certificate is outside its validity window."); + } +} + +async function createCertificateAuthority(directory) { + const keyPath = path.join(directory, "ca.key.pem"); + const certificatePath = path.join(directory, "ca.certificate.pem"); + await execFileAsync("openssl", ["ecparam", "-name", "prime256v1", "-genkey", "-noout", "-out", keyPath]); + await execFileAsync("openssl", ["req", "-x509", "-new", "-key", keyPath, "-sha256", "-days", "3650", "-subj", "/CN=Aiden Phase 0 Local CA", "-addext", "basicConstraints=critical,CA:TRUE,pathlen:0", "-addext", "keyUsage=critical,keyCertSign,cRLSign", "-out", certificatePath]); + return { keyPath, certificatePath, certificate: await readFile(certificatePath, "utf8") }; +} + +async function issueCertificate(directory, name, certificateAuthority, keyPath) { + const resolvedKeyPath = keyPath ?? path.join(directory, `${name}.key.pem`); + const certificatePath = path.join(directory, `${name}.certificate.pem`); + const requestPath = path.join(directory, `${name}.request.pem`); + const argumentsBeforeKey = keyPath + ? [] + : ["ecparam", "-name", "prime256v1", "-genkey", "-noout", "-out", resolvedKeyPath]; + if (argumentsBeforeKey.length > 0) await execFileAsync("openssl", argumentsBeforeKey); + await execFileAsync("openssl", [ + "req", + "-new", + "-key", + resolvedKeyPath, + "-subj", + `/CN=127.0.0.1/O=Aiden Phase 0/OU=${name}`, + "-addext", + "subjectAltName=IP:127.0.0.1", + "-addext", + "basicConstraints=critical,CA:FALSE", + "-addext", + "keyUsage=critical,digitalSignature", + "-addext", + "extendedKeyUsage=serverAuth", + "-out", requestPath, + ]); + await execFileAsync("openssl", ["x509", "-req", "-in", requestPath, "-CA", certificateAuthority.certificatePath, "-CAkey", certificateAuthority.keyPath, "-CAcreateserial", "-days", "30", "-sha256", "-copy_extensions", "copy", "-out", certificatePath]); + const [privateKey, certificate] = await Promise.all([ + readFile(resolvedKeyPath, "utf8"), + readFile(certificatePath, "utf8"), + ]); + return { + keyPath: resolvedKeyPath, + privateKey, + certificate, + caCertificate: certificateAuthority.certificate, + certificateChain: `${certificate}\n${certificateAuthority.certificate}`, + fingerprint: spkiSha256(certificate), + }; +} + +async function loadIssuedIdentity(directory, name, certificateAuthority, keyPath) { + const [privateKey, certificate] = await Promise.all([ + readFile(keyPath, "utf8"), + readFile(path.join(directory, `${name}.certificate.pem`), "utf8"), + ]); + return { + keyPath, + privateKey, + certificate, + caCertificate: certificateAuthority.certificate, + certificateChain: `${certificate}\n${certificateAuthority.certificate}`, + fingerprint: spkiSha256(certificate), + }; +} + +async function withHttpsServer(identity, body, action) { + const server = https.createServer( + { key: identity.privateKey, cert: identity.certificateChain }, + (_request, response) => { + response.writeHead(200, { "content-type": "application/json" }); + response.end(JSON.stringify(body)); + }, + ); + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", resolve); + }); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("HTTPS spike did not bind TCP."); + try { + return await action(`https://127.0.0.1:${address.port}/health`); + } finally { + await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); + } +} + +export async function pinnedJsonRequest(url, certificatePem, caCertificatePem, expectedFingerprint, options = {}) { + assertCertificateCurrentlyValid(certificatePem, options.now); + return new Promise((resolve, reject) => { + const request = https.get( + url, + { + ca: caCertificatePem, + rejectUnauthorized: true, + checkServerIdentity(hostname, certificate) { + const standardError = tls.checkServerIdentity(hostname, certificate); + if (standardError) return standardError; + const actual = spkiSha256(certificate.raw); + if (actual === expectedFingerprint) return undefined; + const error = new Error("Aiden server public-key fingerprint changed; re-pairing is required."); + error.code = "ERR_AIDEN_PIN_MISMATCH"; + return error; + }, + }, + (response) => { + const chunks = []; + response.on("data", (chunk) => chunks.push(chunk)); + response.on("end", () => { + if (response.statusCode !== 200) { + reject(new Error(`HTTPS spike returned ${response.statusCode}.`)); + return; + } + try { + resolve(JSON.parse(Buffer.concat(chunks).toString("utf8"))); + } catch (error) { + reject(error); + } + }); + }, + ); + request.once("error", reject); + request.setTimeout(5_000, () => request.destroy(new Error("HTTPS spike timed out."))); + }); +} + +async function rejected(request) { + try { + await request(); + return false; + } catch { + return true; + } +} + +export async function runLanTransportSpike() { + const directory = await mkdtemp(path.join(os.tmpdir(), "aiden-lan-transport-spike-")); + try { + const certificateAuthority = await createCertificateAuthority(directory); + const original = await issueCertificate(directory, "original", certificateAuthority); + const renewed = await issueCertificate(directory, "renewed", certificateAuthority, original.keyPath); + const rotated = await issueCertificate(directory, "rotated", certificateAuthority); + // Reload from persisted key/certificate files rather than retaining the issued object. + // The companion process-spawn test proves this same identity survives a real process boundary. + const restarted = await loadIssuedIdentity( + directory, + "original", + certificateAuthority, + original.keyPath, + ); + + const originalResponse = await withHttpsServer(original, { ok: true, identity: "original" }, (url) => + pinnedJsonRequest(url, original.certificate, original.caCertificate, original.fingerprint), + ); + const renewedResponse = await withHttpsServer(renewed, { ok: true, identity: "renewed" }, (url) => + pinnedJsonRequest(url, renewed.certificate, renewed.caCertificate, original.fingerprint), + ); + const restartedResponse = await withHttpsServer(restarted, { ok: true, identity: "restart" }, (url) => + pinnedJsonRequest(url, restarted.certificate, restarted.caCertificate, original.fingerprint), + ); + const wrongPinRejected = await withHttpsServer(original, { ok: true }, (url) => + rejected(() => pinnedJsonRequest(url, original.certificate, original.caCertificate, rotated.fingerprint)), + ); + const rotatedKeyRejected = await withHttpsServer(rotated, { ok: true }, (url) => + rejected(() => pinnedJsonRequest(url, rotated.certificate, rotated.caCertificate, original.fingerprint)), + ); + const repairedResponse = await withHttpsServer(rotated, { ok: true, identity: "repaired" }, (url) => + pinnedJsonRequest(url, rotated.certificate, rotated.caCertificate, rotated.fingerprint), + ); + const wrongHostRejected = await withHttpsServer(original, { ok: true }, (url) => + rejected(() => pinnedJsonRequest(url.replace("127.0.0.1", "localhost"), original.certificate, original.caCertificate, original.fingerprint)), + ); + const expiredCertificateRejected = await withHttpsServer(original, { ok: true }, (url) => + rejected(() => pinnedJsonRequest(url, original.certificate, original.caCertificate, original.fingerprint, { now: new Date("2100-01-01T00:00:00Z") })), + ); + + return { + protocolVersion: 1, + algorithm: "P-256 SPKI SHA-256", + hostnameAndChainValidated: true, + originalAccepted: originalResponse.ok === true, + stableInstallIdentityAccepted: + restartedResponse.ok === true && restarted.fingerprint === original.fingerprint, + sameKeyRenewalAccepted: + renewedResponse.ok === true && renewed.fingerprint === original.fingerprint, + changedKeyRejected: rotatedKeyRejected && rotated.fingerprint !== original.fingerprint, + changedKeyRecoveryAccepted: repairedResponse.ok === true, + wrongPinRejected, + wrongHostRejected, + expiredCertificateRejected, + }; + } finally { + await rm(directory, { force: true, recursive: true }); + } +} + +const isMain = process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1]); +if (isMain) { + const result = await runLanTransportSpike(); + process.stdout.write(`${JSON.stringify(result, null, 2)}\n`); + if (!Object.values(result).every((value) => value === true || typeof value === "string" || value === 1)) { + process.exitCode = 1; + } +} diff --git a/scripts/aiden-remote-lan-transport-spike.test.mjs b/scripts/aiden-remote-lan-transport-spike.test.mjs new file mode 100644 index 00000000..730333d2 --- /dev/null +++ b/scripts/aiden-remote-lan-transport-spike.test.mjs @@ -0,0 +1,438 @@ +import assert from "node:assert/strict"; +import { spawn } from "node:child_process"; +import { createHash, randomBytes, X509Certificate } from "node:crypto"; +import { once } from "node:events"; +import { access, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises"; +import https from "node:https"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { generatePairingSecret, runLanTransportSpike, spkiSha256 } from "./aiden-remote-lan-transport-spike.mjs"; + +const physicalServerPath = path.resolve("scripts/aiden-remote-physical-device-spike-server.mjs"); + +async function launchPhysicalServer( + identityDirectory, + mode, + initialize = false, + phase7 = false, + extraArguments = [], +) { + const arguments_ = [ + physicalServerPath, + "--host", "127.0.0.1", + "--identity-dir", identityDirectory, + "--mode", mode, + ]; + if (initialize) arguments_.push("--initialize"); + if (phase7) arguments_.push("--phase7"); + arguments_.push(...extraArguments); + const child = spawn(process.execPath, arguments_, { stdio: ["ignore", "pipe", "pipe"] }); + let stdout = ""; + let stderr = ""; + child.stderr.setEncoding("utf8"); + child.stderr.on("data", (chunk) => { stderr += chunk; }); + child.stdout.setEncoding("utf8"); + const expectsRepairPayload = extraArguments.includes("--repair-secret-file"); + const payloads = await new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject(new Error("Physical spike server startup timed out.")), 10_000); + child.once("exit", (code) => { + clearTimeout(timeout); + reject(new Error(`Physical spike server exited with ${code}: ${stderr}`)); + }); + child.stdout.on("data", (chunk) => { + stdout += chunk; + const lines = stdout.split("\n").filter((line) => line.length > 0); + if (lines.length < (expectsRepairPayload ? 2 : 1)) return; + clearTimeout(timeout); + try { + resolve(lines.slice(0, expectsRepairPayload ? 2 : 1).map((line) => JSON.parse(line))); + } catch (error) { + reject(error); + } + }); + }); + const [pairingPayload, repairPairingPayload] = payloads; + return { + pairingPayload, + repairPairingPayload, + bootstrap: pairingPayload.bootstrap, + async stop() { + child.kill("SIGTERM"); + await once(child, "exit"); + }, + }; +} + +async function requestJSON(url, { method = "GET", headers = {}, body, ca } = {}) { + const encodedBody = body === undefined ? undefined : Buffer.from(JSON.stringify(body)); + const response = await new Promise((resolve, reject) => { + const request = https.request(url, { + method, + ca, + headers: { + ...headers, + ...(encodedBody ? { + "content-type": "application/json", + "content-length": String(encodedBody.length), + } : {}), + }, + }, (incoming) => resolve(incoming)); + request.once("error", reject); + if (encodedBody) request.write(encodedBody); + request.end(); + }); + const chunks = []; + for await (const chunk of response) chunks.push(chunk); + const data = Buffer.concat(chunks).toString("utf8"); + return { + status: response.statusCode, + body: data.length > 0 ? JSON.parse(data) : undefined, + }; +} + +test("LAN transport spike validates hostname/chain and pins a stable P-256 public key", async () => { + const result = await runLanTransportSpike(); + assert.deepEqual(result, { + protocolVersion: 1, + algorithm: "P-256 SPKI SHA-256", + hostnameAndChainValidated: true, + originalAccepted: true, + stableInstallIdentityAccepted: true, + sameKeyRenewalAccepted: true, + changedKeyRejected: true, + changedKeyRecoveryAccepted: true, + wrongPinRejected: true, + wrongHostRejected: true, + expiredCertificateRejected: true, + }); +}); + +test("pairing bootstrap secrets are independent 32-byte base64url values", () => { + const first = generatePairingSecret(); + const second = generatePairingSecret(); + assert.match(first, /^[A-Za-z0-9_-]{43}$/); + assert.equal(Buffer.from(first, "base64url").length, 32); + assert.notEqual(first, second); +}); + +test("SPKI fingerprint format is QR-safe and versioned by algorithm label", () => { + assert.throws(() => spkiSha256("not a certificate")); +}); + +test("physical identity survives process restart and encodes renewal, rotation, host, and expiry scenarios", async () => { + const identityDirectory = await mkdtemp(path.join(os.tmpdir(), "aiden-physical-identity-test-")); + try { + const first = await launchPhysicalServer(identityDirectory, "original", true); + assert.equal(first.pairingPayload.kind, "aiden-pairing-v1"); + assert.equal(first.pairingPayload.trust.mode, "private-ca"); + assert.ok(Buffer.from(first.pairingPayload.trust.caCertificateDerBase64, "base64").length > 100); + const originalPin = first.bootstrap.serverSpkiSha256; + await first.stop(); + + const restarted = await launchPhysicalServer(identityDirectory, "original"); + assert.equal(restarted.bootstrap.serverSpkiSha256, originalPin); + await restarted.stop(); + + const renewed = await launchPhysicalServer(identityDirectory, "renewed"); + assert.equal(renewed.bootstrap.serverSpkiSha256, originalPin); + await renewed.stop(); + + const rotated = await launchPhysicalServer(identityDirectory, "rotated"); + assert.notEqual(rotated.bootstrap.serverSpkiSha256, originalPin); + await rotated.stop(); + + const wrongHostCertificate = new X509Certificate( + await readFile(path.join(identityDirectory, "wrong-host-certificate.pem"), "utf8"), + ); + assert.equal(wrongHostCertificate.checkHost("127.0.0.1"), undefined); + const expiredCertificate = new X509Certificate( + await readFile(path.join(identityDirectory, "expired-certificate.pem"), "utf8"), + ); + assert.ok(new Date(expiredCertificate.validTo) <= new Date()); + } finally { + await rm(identityDirectory, { recursive: true, force: true }); + } +}); + +test("physical proof server can model a system-trusted Tailscale endpoint over a loopback HTTP target", async () => { + const identityDirectory = await mkdtemp(path.join(os.tmpdir(), "aiden-physical-tailscale-test-")); + let server; + try { + const endpoint = "https://aiden.tailnet.ts.net/api/aiden/v1"; + const fingerprint = `sha256/${randomBytes(32).toString("base64")}`; + server = await launchPhysicalServer( + identityDirectory, + "original", + true, + false, + [ + "--phase6", + "--listen-http", + "--system-trust", + "--public-endpoint", endpoint, + "--public-spki-sha256", fingerprint, + ], + ); + assert.equal(server.pairingPayload.trust.mode, "system"); + assert.deepEqual(Object.keys(server.pairingPayload.trust), ["mode"]); + assert.equal(server.bootstrap.endpoint, endpoint); + assert.equal(server.bootstrap.serverSpkiSha256, fingerprint); + } finally { + if (server) await server.stop(); + await rm(identityDirectory, { recursive: true, force: true }); + } +}); + +test("Phase 7 physical server consumes bounded attachment references exactly once", async () => { + const identityDirectory = await mkdtemp(path.join(os.tmpdir(), "aiden-physical-attachment-test-")); + let server; + try { + server = await launchPhysicalServer(identityDirectory, "original", true, true); + const ca = new X509Certificate( + Buffer.from(server.pairingPayload.trust.caCertificateDerBase64, "base64"), + ).toString(); + const endpoint = server.bootstrap.endpoint; + const exchange = await requestJSON(`${endpoint}/pairing/exchange`, { + method: "POST", + ca, + body: { secret: server.bootstrap.secret }, + }); + assert.equal(exchange.status, 200); + const authorized = { + ca, + headers: { + authorization: `Bearer ${exchange.body.credential}`, + "aiden-protocol-version": "1", + }, + }; + const workspace = await requestJSON(`${endpoint}/workspaces`, { + ...authorized, + method: "POST", + body: { mode: "folderless", name: "Attachment Proof" }, + }); + assert.equal(workspace.status, 201); + const chat = await requestJSON(`${endpoint}/chats`, { + ...authorized, + method: "POST", + headers: { ...authorized.headers, "idempotency-key": "create-chat" }, + body: { workspaceId: workspace.body.id }, + }); + assert.equal(chat.status, 201); + const uploaded = await requestJSON(`${endpoint}/chats/${chat.body.id}/attachments`, { + ...authorized, + method: "POST", + body: { name: "proof.md", mimeType: "text/markdown", kind: "text", text: "# LAN proof" }, + }); + assert.equal(uploaded.status, 201); + assert.match(uploaded.body.id, /^att_[A-Za-z0-9_-]{43}$/u); + assert.equal(uploaded.body.size, 11); + assert.equal(uploaded.body.text, undefined); + assert.equal(uploaded.body.path, undefined); + + const turn = await requestJSON(`${endpoint}/chats/${chat.body.id}/turns`, { + ...authorized, + method: "POST", + headers: { ...authorized.headers, "idempotency-key": "attachment-turn" }, + body: { text: "", attachmentIds: [uploaded.body.id] }, + }); + assert.equal(turn.status, 202); + assert.deepEqual(turn.body.message.attachments, [{ + id: uploaded.body.id, + name: "proof.md", + mimeType: "text/markdown", + kind: "text", + size: 11, + }]); + + const replay = await requestJSON(`${endpoint}/chats/${chat.body.id}/turns`, { + ...authorized, + method: "POST", + headers: { ...authorized.headers, "idempotency-key": "attachment-replay" }, + body: { text: "Replay", attachmentIds: [uploaded.body.id] }, + }); + assert.equal(replay.status, 409); + assert.equal(replay.body.error.code, "handle_invalid"); + + const discarded = await requestJSON(`${endpoint}/chats/${chat.body.id}/attachments`, { + ...authorized, + method: "POST", + body: { name: "discard.txt", mimeType: "text/plain", kind: "text", text: "discard" }, + }); + assert.equal(discarded.status, 201); + const removed = await requestJSON( + `${endpoint}/chats/${chat.body.id}/attachments/${discarded.body.id}`, + { ...authorized, method: "DELETE" }, + ); + assert.equal(removed.status, 204); + } finally { + if (server) await server.stop(); + await rm(identityDirectory, { recursive: true, force: true }); + } +}); + +test("physical auth revocation and re-pair rotation survive same-identity restarts", async () => { + const identityDirectory = await mkdtemp(path.join(os.tmpdir(), "aiden-physical-auth-restart-test-")); + const repairSecretPath = path.join(identityDirectory, "physical-repair-secret"); + await writeFile(repairSecretPath, randomBytes(32).toString("base64url"), { + encoding: "utf8", + mode: 0o600, + flag: "wx", + }); + let first; + let restarted; + try { + first = await launchPhysicalServer( + identityDirectory, + "original", + true, + true, + ["--persist-auth", "--repair-secret-file", repairSecretPath], + ); + assert.equal(first.repairPairingPayload.kind, "aiden-pairing-v1"); + assert.equal(first.repairPairingPayload.bootstrap.endpoint, first.bootstrap.endpoint); + assert.equal(first.repairPairingPayload.bootstrap.serverSpkiSha256, first.bootstrap.serverSpkiSha256); + assert.notEqual(first.repairPairingPayload.bootstrap.secret, first.bootstrap.secret); + const ca = new X509Certificate( + Buffer.from(first.pairingPayload.trust.caCertificateDerBase64, "base64"), + ).toString(); + const exchange = await requestJSON(`${first.bootstrap.endpoint}/pairing/exchange`, { + method: "POST", + ca, + body: { secret: first.bootstrap.secret }, + }); + assert.equal(exchange.status, 200); + const port = new URL(first.bootstrap.endpoint).port; + const originalPin = first.bootstrap.serverSpkiSha256; + await first.stop(); + first = undefined; + const authStatePath = path.join(identityDirectory, "physical-auth-state.json"); + const authStateText = await readFile(authStatePath, "utf8"); + assert.doesNotMatch(authStateText, new RegExp(exchange.body.credential, "u")); + assert.deepEqual( + Object.keys(JSON.parse(authStateText)).sort(), + ["credentialSha256", "revokedCredentialSha256", "version"], + ); + assert.equal(JSON.parse(authStateText).version, 2); + assert.deepEqual(JSON.parse(authStateText).revokedCredentialSha256, []); + assert.equal((await stat(authStatePath)).mode & 0o777, 0o600); + + restarted = await launchPhysicalServer( + identityDirectory, + "original", + false, + true, + ["--persist-auth", "--port", port, "--repair-secret-file", repairSecretPath], + ); + assert.equal(restarted.bootstrap.serverSpkiSha256, originalPin); + const repairPairingPayload = restarted.repairPairingPayload; + const authorized = await requestJSON(`${restarted.bootstrap.endpoint}/server`, { + ca, + headers: { + authorization: `Bearer ${exchange.body.credential}`, + "aiden-protocol-version": "1", + }, + }); + assert.equal(authorized.status, 200); + assert.equal(authorized.body.instanceId, "phase7-physical-device-spike"); + const reopenedPairing = await requestJSON(`${restarted.bootstrap.endpoint}/pairing/exchange`, { + method: "POST", + ca, + body: { secret: restarted.bootstrap.secret }, + }); + assert.equal(reopenedPairing.status, 401); + assert.equal(reopenedPairing.body.error.code, "pairing_closed"); + + const revocationPath = path.join(identityDirectory, "physical-auth-revoked"); + await writeFile(revocationPath, "", { mode: 0o600, flag: "wx" }); + const revoked = await requestJSON(`${restarted.bootstrap.endpoint}/server`, { + ca, + headers: { + authorization: `Bearer ${exchange.body.credential}`, + "aiden-protocol-version": "1", + }, + }); + assert.equal(revoked.status, 403); + assert.equal(revoked.body.error.code, "credential_revoked"); + assert.equal((await stat(revocationPath)).mode & 0o777, 0o600); + + const repairWindowPath = path.join(identityDirectory, "physical-repair-open"); + await writeFile(repairWindowPath, "", { mode: 0o600, flag: "wx" }); + const repairExchange = await requestJSON(`${restarted.bootstrap.endpoint}/pairing/exchange`, { + method: "POST", + ca, + body: { secret: repairPairingPayload.bootstrap.secret }, + }); + assert.equal(repairExchange.status, 200); + assert.notEqual(repairExchange.body.credential, exchange.body.credential); + assert.equal(repairExchange.body.deviceId, "physical-iphone-13-pro-repaired"); + const repairReplay = await requestJSON(`${restarted.bootstrap.endpoint}/pairing/exchange`, { + method: "POST", + ca, + body: { secret: repairPairingPayload.bootstrap.secret }, + }); + assert.equal(repairReplay.status, 401); + assert.equal(repairReplay.body.error.code, "pairing_closed"); + await assert.rejects(access(revocationPath), { code: "ENOENT" }); + await assert.rejects(access(repairWindowPath), { code: "ENOENT" }); + const rotatedAuthState = await readFile(authStatePath, "utf8"); + assert.doesNotMatch(rotatedAuthState, new RegExp(exchange.body.credential, "u")); + assert.doesNotMatch(rotatedAuthState, new RegExp(repairExchange.body.credential, "u")); + assert.equal( + JSON.parse(rotatedAuthState).credentialSha256, + createHash("sha256").update(repairExchange.body.credential).digest("hex"), + ); + assert.deepEqual(JSON.parse(rotatedAuthState).revokedCredentialSha256, [ + createHash("sha256").update(exchange.body.credential).digest("hex"), + ]); + assert.equal((await stat(authStatePath)).mode & 0o777, 0o600); + + const oldAfterRepair = await requestJSON(`${restarted.bootstrap.endpoint}/server`, { + ca, + headers: { + authorization: `Bearer ${exchange.body.credential}`, + "aiden-protocol-version": "1", + }, + }); + assert.equal(oldAfterRepair.status, 403); + assert.equal(oldAfterRepair.body.error.code, "credential_revoked"); + const repaired = await requestJSON(`${restarted.bootstrap.endpoint}/server`, { + ca, + headers: { + authorization: `Bearer ${repairExchange.body.credential}`, + "aiden-protocol-version": "1", + }, + }); + assert.equal(repaired.status, 200); + + await restarted.stop(); + restarted = await launchPhysicalServer( + identityDirectory, + "original", + false, + true, + ["--persist-auth", "--port", port], + ); + const repairedAfterRestart = await requestJSON(`${restarted.bootstrap.endpoint}/server`, { + ca, + headers: { + authorization: `Bearer ${repairExchange.body.credential}`, + "aiden-protocol-version": "1", + }, + }); + assert.equal(repairedAfterRestart.status, 200); + const oldAfterRestart = await requestJSON(`${restarted.bootstrap.endpoint}/server`, { + ca, + headers: { + authorization: `Bearer ${exchange.body.credential}`, + "aiden-protocol-version": "1", + }, + }); + assert.equal(oldAfterRestart.status, 403); + assert.equal(oldAfterRestart.body.error.code, "credential_revoked"); + } finally { + if (first) await first.stop(); + if (restarted) await restarted.stop(); + await rm(identityDirectory, { recursive: true, force: true }); + } +}); diff --git a/scripts/aiden-remote-physical-device-spike-server.mjs b/scripts/aiden-remote-physical-device-spike-server.mjs new file mode 100644 index 00000000..f2f9dd16 --- /dev/null +++ b/scripts/aiden-remote-physical-device-spike-server.mjs @@ -0,0 +1,890 @@ +import { execFile } from "node:child_process"; +import { createHash, randomBytes, X509Certificate } from "node:crypto"; +import { access, mkdir, mkdtemp, readFile, rename, rm, stat, writeFile } from "node:fs/promises"; +import http from "node:http"; +import https from "node:https"; +import os from "node:os"; +import path from "node:path"; +import { promisify } from "node:util"; + +const execFileAsync = promisify(execFile); + +function option(name) { + const index = process.argv.indexOf(name); + return index >= 0 ? process.argv[index + 1] : undefined; +} + +const advertisedHost = option("--host"); +const requestedMode = option("--mode") ?? "original"; +const requestedIdentityDirectory = option("--identity-dir"); +const requestedPortValue = option("--port"); +const requestedRepairSecretFile = option("--repair-secret-file"); +const requestedPublicEndpoint = option("--public-endpoint"); +const requestedPublicSpkiSha256 = option("--public-spki-sha256"); +const initializeRequested = process.argv.includes("--initialize"); +const persistAuthRequested = process.argv.includes("--persist-auth"); +const listenHttpRequested = process.argv.includes("--listen-http"); +const systemTrustRequested = process.argv.includes("--system-trust"); +const phase7Requested = process.argv.includes("--phase7"); +const phase6Requested = process.argv.includes("--phase6") || phase7Requested; +const supportedModes = new Set(["original", "renewed", "rotated", "wrong-host", "expired"]); +const requestedPort = requestedPortValue === undefined ? 0 : Number(requestedPortValue); +const hasCompletePublicTransport = listenHttpRequested && systemTrustRequested && + requestedPublicEndpoint !== undefined && requestedPublicSpkiSha256 !== undefined; +const hasAnyPublicTransport = listenHttpRequested || systemTrustRequested || + requestedPublicEndpoint !== undefined || requestedPublicSpkiSha256 !== undefined; + +let publicEndpoint; +if (requestedPublicEndpoint !== undefined) { + try { + const parsed = new URL(requestedPublicEndpoint); + if (parsed.href !== requestedPublicEndpoint || parsed.protocol !== "https:" || + parsed.username || parsed.password || parsed.port || + parsed.pathname !== "/api/aiden/v1" || parsed.search || parsed.hash) { + throw new Error("invalid"); + } + publicEndpoint = parsed.href; + } catch { + throw new Error("The public endpoint must be a canonical HTTPS Aiden API endpoint on port 443."); + } +} +let publicSpkiSha256; +if (requestedPublicSpkiSha256 !== undefined) { + const encoded = requestedPublicSpkiSha256.startsWith("sha256/") + ? requestedPublicSpkiSha256.slice("sha256/".length) + : ""; + if (!/^[A-Za-z0-9+/]{43}=$/u.test(encoded) || Buffer.from(encoded, "base64").length !== 32) { + throw new Error("The public SPKI pin must be a SHA-256 fingerprint."); + } + publicSpkiSha256 = requestedPublicSpkiSha256; +} + +if (!advertisedHost || !/^[A-Za-z0-9.-]+$/.test(advertisedHost) || !supportedModes.has(requestedMode) || + !Number.isSafeInteger(requestedPort) || requestedPort < 0 || requestedPort > 65_535 || + (requestedRepairSecretFile !== undefined && (!persistAuthRequested || !phase6Requested)) || + (hasAnyPublicTransport && (!hasCompletePublicTransport || !phase6Requested))) { + throw new Error( + "Usage: node scripts/aiden-remote-physical-device-spike-server.mjs --host " + + "[--identity-dir --initialize] " + + "[--mode original|renewed|rotated|wrong-host|expired] [--port <0...65535>] [--persist-auth] " + + "[--repair-secret-file ] " + + "[--listen-http --system-trust --public-endpoint --public-spki-sha256 ]", + ); +} + +const ownsIdentityDirectory = requestedIdentityDirectory === undefined; +const identityDirectory = requestedIdentityDirectory + ? path.resolve(requestedIdentityDirectory) + : await mkdtemp(path.join(os.tmpdir(), "aiden-physical-transport-identity-")); +const shouldInitialize = ownsIdentityDirectory || initializeRequested; +const manifestPath = path.join(identityDirectory, "identity.json"); +const authStatePath = path.join(identityDirectory, "physical-auth-state.json"); +const authRevocationPath = path.join(identityDirectory, "physical-auth-revoked"); +const repairWindowPath = path.join(identityDirectory, "physical-repair-open"); +const caKeyPath = path.join(identityDirectory, "ca-key.pem"); +const caCertificatePath = path.join(identityDirectory, "ca-certificate.pem"); +const stableKeyPath = path.join(identityDirectory, "stable-server-key.pem"); +const rotatedKeyPath = path.join(identityDirectory, "rotated-server-key.pem"); +const subjectAlternativeName = /^\d{1,3}(?:\.\d{1,3}){3}$/.test(advertisedHost) + ? `IP:${advertisedHost}` + : `DNS:${advertisedHost}`; + +async function issueCertificate(mode, keyPath, alternativeName, days) { + const certificatePath = path.join(identityDirectory, `${mode}-certificate.pem`); + const requestPath = path.join(identityDirectory, `${mode}-request.pem`); + await execFileAsync("openssl", [ + "req", "-new", "-key", keyPath, + "-subj", `/CN=Aiden Phase 0 Physical Device Spike/OU=${mode}`, + "-addext", `subjectAltName=${alternativeName}`, + "-addext", "basicConstraints=critical,CA:FALSE", + "-addext", "keyUsage=critical,digitalSignature", + "-addext", "extendedKeyUsage=serverAuth", + "-out", requestPath, + ]); + await execFileAsync("openssl", [ + "x509", "-req", "-in", requestPath, + "-CA", caCertificatePath, "-CAkey", caKeyPath, "-CAcreateserial", + "-days", String(days), "-sha256", "-copy_extensions", "copy", + "-out", certificatePath, + ]); +} + +async function initializeIdentity() { + await mkdir(identityDirectory, { recursive: true, mode: 0o700 }); + try { + await readFile(manifestPath, "utf8"); + throw new Error("Refusing to overwrite an existing Phase 0 transport identity."); + } catch (error) { + if (error?.code !== "ENOENT") throw error; + } + await execFileAsync("openssl", [ + "ecparam", "-name", "prime256v1", "-genkey", "-noout", "-out", stableKeyPath, + ]); + await execFileAsync("openssl", [ + "ecparam", "-name", "prime256v1", "-genkey", "-noout", "-out", rotatedKeyPath, + ]); + await execFileAsync("openssl", [ + "ecparam", "-name", "prime256v1", "-genkey", "-noout", "-out", caKeyPath, + ]); + await execFileAsync("openssl", [ + "req", "-x509", "-new", "-key", caKeyPath, "-sha256", "-days", "3650", + "-subj", "/CN=Aiden Phase 0 Physical Local CA", + "-addext", "basicConstraints=critical,CA:TRUE,pathlen:0", + "-addext", "keyUsage=critical,keyCertSign,cRLSign", + "-out", caCertificatePath, + ]); + await issueCertificate("original", stableKeyPath, subjectAlternativeName, 1); + await issueCertificate("renewed", stableKeyPath, subjectAlternativeName, 2); + await issueCertificate("rotated", rotatedKeyPath, subjectAlternativeName, 1); + await issueCertificate("wrong-host", stableKeyPath, "DNS:wrong-host.aiden.invalid", 1); + await issueCertificate("expired", stableKeyPath, subjectAlternativeName, 0); + await writeFile( + manifestPath, + `${JSON.stringify({ version: 1, advertisedHost }, null, 2)}\n`, + { encoding: "utf8", mode: 0o600, flag: "wx" }, + ); +} + +if (shouldInitialize) await initializeIdentity(); +const manifest = JSON.parse(await readFile(manifestPath, "utf8")); +if (manifest.version !== 1 || manifest.advertisedHost !== advertisedHost) { + throw new Error("The persisted Phase 0 identity is bound to a different advertised host."); +} + +const selectedKeyPath = requestedMode === "rotated" ? rotatedKeyPath : stableKeyPath; +const certificatePath = path.join(identityDirectory, `${requestedMode}-certificate.pem`); +const [privateKey, certificate, caCertificate] = await Promise.all([ + readFile(selectedKeyPath, "utf8"), + readFile(certificatePath, "utf8"), + readFile(caCertificatePath, "utf8"), +]); +const x509 = new X509Certificate(certificate); +const fingerprint = `sha256/${createHash("sha256") + .update(x509.publicKey.export({ type: "spki", format: "der" })) + .digest("base64")}`; +const pairingSecret = randomBytes(32).toString("base64url"); +const deviceCredential = randomBytes(32).toString("base64url"); +const repairSecretPath = requestedRepairSecretFile === undefined + ? undefined + : path.resolve(requestedRepairSecretFile); +if (repairSecretPath !== undefined) { + const metadata = await stat(repairSecretPath); + if (!metadata.isFile() || (metadata.mode & 0o077) !== 0) { + throw new Error("The repair pairing secret file must be a regular owner-only file."); + } +} +const repairPairingSecret = repairSecretPath === undefined + ? undefined + : (await readFile(repairSecretPath, "utf8")).trim(); +if (repairPairingSecret !== undefined && !/^[A-Za-z0-9_-]{43}$/u.test(repairPairingSecret)) { + throw new Error("The repair pairing secret must be exactly 32 bytes of base64url data."); +} +const repairedDeviceCredential = repairPairingSecret === undefined + ? undefined + : randomBytes(32).toString("base64url"); +const instanceId = phase6Requested + ? phase7Requested ? "phase7-physical-device-spike" : "phase6-physical-device-spike" + : "phase0-physical-device-spike"; +let pairingConsumed = false; +let repairPairingConsumed = false; +let authorizedCredentialDigest; +const revokedCredentialDigests = new Set(); +if (persistAuthRequested) { + try { + const persistedAuth = JSON.parse(await readFile(authStatePath, "utf8")); + if (Object.keys(persistedAuth).sort().join(",") !== + "credentialSha256,revokedCredentialSha256,version" || + persistedAuth.version !== 2 || + typeof persistedAuth.credentialSha256 !== "string" || + !/^[a-f0-9]{64}$/u.test(persistedAuth.credentialSha256) || + !Array.isArray(persistedAuth.revokedCredentialSha256) || + persistedAuth.revokedCredentialSha256.length > 16 || + persistedAuth.revokedCredentialSha256.some((value) => + typeof value !== "string" || !/^[a-f0-9]{64}$/u.test(value) || + value === persistedAuth.credentialSha256 + ) || + new Set(persistedAuth.revokedCredentialSha256).size !== + persistedAuth.revokedCredentialSha256.length) { + throw new Error("The persisted physical auth state is invalid."); + } + pairingConsumed = true; + authorizedCredentialDigest = persistedAuth.credentialSha256; + for (const digest of persistedAuth.revokedCredentialSha256) { + revokedCredentialDigests.add(digest); + } + } catch (error) { + if (error?.code !== "ENOENT") throw error; + } +} +let workspaceSequence = 0; +let selectionConsumed = false; +const workspaceLocation = `loc_${randomBytes(32).toString("base64url")}`; +const workspaceSelection = `sel_${randomBytes(32).toString("base64url")}`; +const workspaces = new Map(); +const chats = new Map(); +const streams = new Map(); +const attachments = new Map(); +let chatSequence = 0; +let messageSequence = 0; +let turnSequence = 0; + +const chatCapabilities = ["chat:read", "chat:write", "approval:respond"]; + +function chat(workspaceId) { + chatSequence += 1; + const timestamp = new Date().toISOString(); + return { + id: `chat-${chatSequence}`, + workspaceId, + title: "New Chat", + providerId: "openai", + modelId: "gpt-5.6", + messages: [], + createdAt: timestamp, + updatedAt: timestamp, + revision: `chat-rev-${chatSequence}-1`, + }; +} + +function streamEvent(streamId, sequence, type, terminal, payload) { + return { + protocolVersion: 1, + streamId, + sequence, + timestamp: new Date().toISOString(), + type, + terminal, + payload, + }; +} + +function sendSSE(response, events) { + response.writeHead(200, { + "content-type": "text/event-stream; charset=utf-8", + "cache-control": "no-store", + "aiden-protocol-version": "1", + }); + for (const event of events) { + response.write(`id: ${event.sequence}\nevent: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`); + } + response.end(); +} + +function sendJSON(response, status, value) { + response.writeHead(status, { "content-type": "application/json", "cache-control": "no-store" }); + response.end(JSON.stringify(value)); +} + +function sendError(response, status, code, message) { + sendJSON(response, status, { + error: { code, message, requestId: "physical-device-spike", retryable: false }, + }); +} + +async function isPersistedAuthRevoked() { + if (!persistAuthRequested) return false; + try { + await access(authRevocationPath); + return true; + } catch (error) { + if (error?.code === "ENOENT") return false; + throw error; + } +} + +async function pathExists(filePath) { + try { + await access(filePath); + return true; + } catch (error) { + if (error?.code === "ENOENT") return false; + throw error; + } +} + +async function persistCredentialDigest(credentialSha256, replaceExisting) { + if (replaceExisting && authorizedCredentialDigest !== undefined) { + revokedCredentialDigests.add(authorizedCredentialDigest); + } + const value = `${JSON.stringify({ + version: 2, + credentialSha256, + revokedCredentialSha256: [...revokedCredentialDigests], + }, null, 2)}\n`; + if (!replaceExisting) { + await writeFile(authStatePath, value, { encoding: "utf8", mode: 0o600, flag: "wx" }); + return; + } + const temporaryPath = path.join( + identityDirectory, + `physical-auth-state-${randomBytes(8).toString("hex")}.tmp`, + ); + await writeFile(temporaryPath, value, { encoding: "utf8", mode: 0o600, flag: "wx" }); + await rename(temporaryPath, authStatePath); +} + +async function readJSON(request, maximumBytes = 1_048_576) { + const chunks = []; + let byteCount = 0; + for await (const chunk of request) { + byteCount += chunk.length; + if (byteCount > maximumBytes) throw new Error("Request body is too large."); + chunks.push(chunk); + } + return JSON.parse(Buffer.concat(chunks).toString("utf8")); +} + +const attachmentIdPattern = /^att_[A-Za-z0-9_-]{43}$/u; +const allowedTextMimeTypes = new Set([ + "text/plain", "text/markdown", "text/csv", "application/json", "application/xml", + "application/yaml", "application/x-yaml", "application/javascript", "application/typescript", +]); + +function validAttachmentName(value) { + return typeof value === "string" && value.length > 0 && [...value].length <= 255 && + [...value].every((character) => { + const scalar = character.codePointAt(0); + return scalar > 0x1f && scalar !== 0x7f && character !== "/" && character !== "\\"; + }); +} + +function attachmentReference(body) { + if (!validAttachmentName(body.name) || body.kind !== "text" || + typeof body.mimeType !== "string" || !allowedTextMimeTypes.has(body.mimeType) || + typeof body.text !== "string" || [...body.text].length > 100_000) { + return undefined; + } + const size = Buffer.byteLength(body.text, "utf8"); + if (size > 400_000) return undefined; + return { + id: `att_${randomBytes(32).toString("base64url")}`, + name: body.name, + mimeType: body.mimeType, + kind: "text", + size, + expiresAt: new Date(Date.now() + 10 * 60_000).toISOString(), + }; +} + +function workspace(name, hasFolder) { + workspaceSequence += 1; + const timestamp = new Date().toISOString(); + return { + id: `workspace-${workspaceSequence}`, + name, + permission: "ask", + hasFolder, + isManagedWorktree: false, + createdAt: timestamp, + updatedAt: timestamp, + revision: `rev-${workspaceSequence}-1`, + }; +} + +async function handlePhase6Request(request, response, endpoint, advertisedFingerprint) { + const url = new URL(request.url ?? "/", endpoint); + const pathName = url.pathname; + if (request.method === "POST" && pathName === "/api/aiden/v1/pairing/exchange") { + const body = await readJSON(request); + const isInitialPairing = !pairingConsumed && body.secret === pairingSecret; + const isRepairPairing = repairPairingSecret !== undefined && + repairedDeviceCredential !== undefined && + !repairPairingConsumed && + pairingConsumed && + body.secret === repairPairingSecret && + await pathExists(repairWindowPath) && + await isPersistedAuthRevoked(); + if (!isInitialPairing && !isRepairPairing) { + sendError(response, 401, "pairing_closed", "Pairing is unavailable."); + return; + } + const issuedCredential = isRepairPairing ? repairedDeviceCredential : deviceCredential; + const credentialSha256 = createHash("sha256").update(issuedCredential).digest("hex"); + if (isRepairPairing) repairPairingConsumed = true; + else pairingConsumed = true; + if (persistAuthRequested) { + await persistCredentialDigest(credentialSha256, isRepairPairing); + } + authorizedCredentialDigest = credentialSha256; + if (isRepairPairing) { + await Promise.all([ + rm(authRevocationPath, { force: true }), + rm(repairWindowPath, { force: true }), + ]); + } + sendJSON(response, 200, { + protocolVersion: 1, + instanceId, + deviceId: isRepairPairing ? "physical-iphone-13-pro-repaired" : "physical-iphone-13-pro", + credential: issuedCredential, + capabilities: ["server:read", "workspace:read", "workspace:manage", ...(phase7Requested ? chatCapabilities : [])], + endpoint, + serverSpkiSha256: advertisedFingerprint, + }); + return; + } + const authorization = request.headers.authorization; + const suppliedCredential = typeof authorization === "string" && authorization.startsWith("Bearer ") + ? authorization.slice("Bearer ".length) + : undefined; + const suppliedCredentialDigest = suppliedCredential === undefined + ? undefined + : createHash("sha256").update(suppliedCredential).digest("hex"); + if (request.headers["aiden-protocol-version"] !== "1") { + sendError(response, 400, "invalid_request", "Aiden-Protocol-Version must be 1."); + return; + } + if (authorizedCredentialDigest === undefined || suppliedCredentialDigest === undefined) { + sendError(response, 401, "authentication_required", "Pair this device again."); + return; + } + if (revokedCredentialDigests.has(suppliedCredentialDigest) || + (suppliedCredentialDigest === authorizedCredentialDigest && await isPersistedAuthRevoked())) { + sendError(response, 403, "credential_revoked", "Pair this device again."); + return; + } + if (suppliedCredentialDigest !== authorizedCredentialDigest) { + sendError(response, 401, "authentication_required", "Pair this device again."); + return; + } + if (request.method === "GET" && pathName === "/api/aiden/v1/server") { + sendJSON(response, 200, { + protocolVersion: 1, + instanceId, + name: "Aiden Physical Device Spike", + appVersion: "1.0.0", + capabilities: ["server:read", "workspace:read", "workspace:manage", ...(phase7Requested ? chatCapabilities : [])], + connectionMode: systemTrustRequested ? "tailscale" : "lan", + serverTime: new Date().toISOString(), + }); + return; + } + if (request.method === "GET" && pathName === "/api/aiden/v1/workspaces") { + sendJSON(response, 200, { workspaces: [...workspaces.values()] }); + return; + } + if (request.method === "POST" && pathName === "/api/aiden/v1/workspaces") { + const body = await readJSON(request); + let created; + if (body.mode === "folderless" && typeof body.name === "string") { + created = workspace(body.name, false); + } else if (body.mode === "scratch") { + created = workspace("Scratch Workspace", true); + } else if (body.mode === "selected-folder" && + body.selection === workspaceSelection && !selectionConsumed) { + selectionConsumed = true; + created = workspace(body.name ?? "Approved Project", true); + } else { + sendError(response, 409, "handle_invalid", "The folder selection is invalid or expired."); + return; + } + workspaces.set(created.id, created); + sendJSON(response, 201, created); + return; + } + if (request.method === "GET" && pathName === "/api/aiden/v1/workspace-browser/roots") { + sendJSON(response, 200, { + roots: [{ + id: "root-projects", + label: "Projects", + location: workspaceLocation, + policyRevision: "policy-1", + }], + }); + return; + } + if (request.method === "GET" && pathName === "/api/aiden/v1/workspace-browser/children") { + if (url.searchParams.get("location") !== workspaceLocation) { + sendError(response, 404, "handle_invalid", "The folder location is invalid."); + return; + } + sendJSON(response, 200, { + rootId: "root-projects", + label: "Projects", + breadcrumbs: [{ label: "Projects", location: workspaceLocation }], + entries: [{ id: "entry-aiden", name: "aiden-agent", location: workspaceLocation }], + }); + return; + } + if (request.method === "POST" && pathName === "/api/aiden/v1/workspace-browser/selections") { + const body = await readJSON(request); + if (body.location !== workspaceLocation || selectionConsumed) { + sendError(response, 409, "handle_invalid", "The folder selection is invalid or expired."); + return; + } + sendJSON(response, 201, { + selection: workspaceSelection, + displayName: "aiden-agent", + expiresAt: new Date(Date.now() + 60_000).toISOString(), + }); + return; + } + if (phase7Requested && request.method === "GET" && pathName === "/api/aiden/v1/chats") { + const workspaceId = url.searchParams.get("workspaceId"); + sendJSON(response, 200, { + chats: [...chats.values()].filter((candidate) => !workspaceId || candidate.workspaceId === workspaceId), + }); + return; + } + if (phase7Requested && request.method === "POST" && pathName === "/api/aiden/v1/chats") { + const body = await readJSON(request); + if (!workspaces.has(body.workspaceId) || !request.headers["idempotency-key"]) { + sendError(response, 400, "invalid_request", "A workspace and idempotency key are required."); + return; + } + const created = chat(body.workspaceId); + chats.set(created.id, created); + sendJSON(response, 201, created); + return; + } + if (phase7Requested && request.method === "GET" && pathName === "/api/aiden/v1/models") { + sendJSON(response, 200, { + providers: [{ + id: "openai", + label: "OpenAI", + models: [{ id: "gpt-5.6", label: "GPT-5.6", thinkingLevels: ["high", "max"] }], + }], + defaults: { providerId: "openai", modelId: "gpt-5.6" }, + }); + return; + } + const attachmentCollectionMatch = pathName.match(/^\/api\/aiden\/v1\/chats\/([^/]+)\/attachments$/u); + if (phase7Requested && request.method === "POST" && attachmentCollectionMatch) { + const existing = chats.get(attachmentCollectionMatch[1]); + if (!existing) { + sendError(response, 404, "not_found", "Chat not found."); + return; + } + const body = await readJSON(request, 12 * 1_048_576); + const reference = attachmentReference(body); + if (!reference) { + sendError(response, 400, "invalid_request", "The attachment is invalid."); + return; + } + attachments.set(reference.id, { chatId: existing.id, reference }); + sendJSON(response, 201, reference); + return; + } + const attachmentItemMatch = pathName.match( + /^\/api\/aiden\/v1\/chats\/([^/]+)\/attachments\/(att_[A-Za-z0-9_-]{43})$/u, + ); + if (phase7Requested && request.method === "DELETE" && attachmentItemMatch) { + const [,, chatId, attachmentId] = attachmentItemMatch; + const record = attachments.get(attachmentId); + if (record?.chatId === chatId) attachments.delete(attachmentId); + response.writeHead(204, { "cache-control": "no-store" }).end(); + return; + } + const chatMatch = pathName.match(/^\/api\/aiden\/v1\/chats\/([^/]+)$/u); + if (phase7Requested && chatMatch) { + const existing = chats.get(chatMatch[1]); + if (!existing) { + sendError(response, 404, "not_found", "Chat not found."); + return; + } + if (request.method === "GET") { + sendJSON(response, 200, existing); + return; + } + if (request.headers["if-match"] !== existing.revision) { + sendError(response, 409, "revision_conflict", "The chat changed."); + return; + } + if (request.method === "PATCH") { + const body = await readJSON(request); + const updated = { + ...existing, + title: body.title, + updatedAt: new Date().toISOString(), + revision: `${existing.revision}-next`, + }; + chats.set(updated.id, updated); + sendJSON(response, 200, updated); + return; + } + if (request.method === "DELETE") { + chats.delete(existing.id); + response.writeHead(204, { "cache-control": "no-store" }).end(); + return; + } + } + const turnMatch = pathName.match(/^\/api\/aiden\/v1\/chats\/([^/]+)\/turns$/u); + if (phase7Requested && request.method === "POST" && turnMatch) { + const existing = chats.get(turnMatch[1]); + if (!existing || !request.headers["idempotency-key"]) { + sendError(response, 400, "invalid_request", "The chat and idempotency key are required."); + return; + } + const body = await readJSON(request); + const requestedAttachmentIds = body.attachmentIds === undefined ? [] : body.attachmentIds; + const validAttachmentIds = Array.isArray(requestedAttachmentIds) && + requestedAttachmentIds.length <= 10 && + new Set(requestedAttachmentIds).size === requestedAttachmentIds.length && + requestedAttachmentIds.every((id) => typeof id === "string" && attachmentIdPattern.test(id)); + if (!validAttachmentIds || typeof body.text !== "string" || + (body.text.trim().length === 0 && requestedAttachmentIds.length === 0)) { + sendError(response, 400, "invalid_request", "Text or attachments are required."); + return; + } + const attachmentRecords = requestedAttachmentIds.map((id) => attachments.get(id)); + if (attachmentRecords.some((record) => !record || record.chatId !== existing.id || + Date.parse(record.reference.expiresAt) <= Date.now())) { + sendError(response, 409, "handle_invalid", "An attachment is invalid, expired, or already used."); + return; + } + const messageAttachments = attachmentRecords.map(({ reference }) => ({ + id: reference.id, + name: reference.name, + mimeType: reference.mimeType, + kind: reference.kind, + size: reference.size, + })); + for (const id of requestedAttachmentIds) attachments.delete(id); + turnSequence += 1; + messageSequence += 1; + const turnId = `turn-${turnSequence}`; + const streamId = `stream-${turnSequence}`; + const userMessage = { + id: `message-${messageSequence}`, + role: "user", + text: body.text, + ...(messageAttachments.length > 0 ? { attachments: messageAttachments } : {}), + createdAt: new Date().toISOString(), + }; + existing.messages.push(userMessage); + existing.updatedAt = new Date().toISOString(); + existing.revision = `${existing.revision}-turn-${turnSequence}`; + streams.set(streamId, { + streamId, + chatId: existing.id, + turnId, + kind: body.text.includes("cancel") ? "cancel" : "approval", + state: "running", + approved: false, + cancelled: false, + completed: false, + }); + sendJSON(response, 202, { turnId, streamId, status: "accepted", message: userMessage }); + return; + } + const streamMatch = pathName.match(/^\/api\/aiden\/v1\/streams\/([^/]+)$/u); + if (phase7Requested && request.method === "GET" && streamMatch) { + const stream = streams.get(streamMatch[1]); + if (!stream) { + sendError(response, 404, "not_found", "Stream not found."); + return; + } + sendJSON(response, 200, { + streamId: stream.streamId, + chatId: stream.chatId, + turnId: stream.turnId, + state: stream.cancelled ? "cancelled" : stream.completed ? "done" : stream.approved ? "running" : stream.state, + lastSequence: stream.cancelled ? 2 : stream.completed ? 10 : stream.approved ? 10 : 7, + updatedAt: new Date().toISOString(), + }); + return; + } + const eventMatch = pathName.match(/^\/api\/aiden\/v1\/streams\/([^/]+)\/events$/u); + if (phase7Requested && request.method === "GET" && eventMatch) { + const stream = streams.get(eventMatch[1]); + if (!stream) { + sendError(response, 404, "not_found", "Stream not found."); + return; + } + const after = Number(url.searchParams.get("after") ?? request.headers["last-event-id"] ?? 0); + if (stream.cancelled) { + sendSSE(response, [ + streamEvent(stream.streamId, 1, "status", false, { state: "running" }), + streamEvent(stream.streamId, 2, "cancelled", true, { source: "device" }), + ].filter((event) => event.sequence > after)); + return; + } + if (after < 2) { + sendSSE(response, [ + streamEvent(stream.streamId, 1, "status", false, { state: "running" }), + streamEvent(stream.streamId, 2, "text_delta", false, { text: "Hello " }), + ].filter((event) => event.sequence > after)); + return; + } + if (!stream.approved) { + stream.state = "waiting_for_approval"; + sendSSE(response, [ + streamEvent(stream.streamId, 3, "reasoning_delta", false, { text: "Checking the workspace." }), + streamEvent(stream.streamId, 4, "tool_started", false, { toolId: "tool-1", name: "workspace_read" }), + streamEvent(stream.streamId, 5, "tool_finished", false, { toolId: "tool-1", status: "succeeded" }), + streamEvent(stream.streamId, 6, "timeline", false, { label: "Reviewed workspace" }), + streamEvent(stream.streamId, 7, "approval_required", false, { + approvalId: "approval-1", + summary: "Allow Aiden to finish this physical-device proof?", + expiresAt: new Date(Date.now() + 60_000).toISOString(), + }), + ].filter((event) => event.sequence > after)); + return; + } + const existing = chats.get(stream.chatId); + if (!stream.completed) { + messageSequence += 1; + existing.messages.push({ + id: `message-${messageSequence}`, + role: "assistant", + text: "Hello from Aiden.", + createdAt: new Date().toISOString(), + }); + existing.updatedAt = new Date().toISOString(); + existing.revision = `${existing.revision}-done`; + stream.completed = true; + } + sendSSE(response, [ + streamEvent(stream.streamId, 8, "status", false, { state: "running" }), + streamEvent(stream.streamId, 9, "text_delta", false, { text: "from Aiden." }), + streamEvent(stream.streamId, 10, "done", true, { messageId: existing.messages.at(-1).id }), + ].filter((event) => event.sequence > after)); + return; + } + const cancelMatch = pathName.match(/^\/api\/aiden\/v1\/streams\/([^/]+)\/cancel$/u); + if (phase7Requested && request.method === "POST" && cancelMatch) { + const stream = streams.get(cancelMatch[1]); + if (!stream || !request.headers["idempotency-key"]) { + sendError(response, 404, "not_found", "Stream not found."); + return; + } + stream.cancelled = true; + sendJSON(response, 202, { + streamId: stream.streamId, + chatId: stream.chatId, + turnId: stream.turnId, + state: "cancelled", + lastSequence: 2, + updatedAt: new Date().toISOString(), + }); + return; + } + if (phase7Requested && request.method === "POST" && pathName === "/api/aiden/v1/approvals/approval-1/respond") { + const body = await readJSON(request); + const stream = [...streams.values()].find((candidate) => candidate.state === "waiting_for_approval"); + if (!stream || !["allow", "deny"].includes(body.decision)) { + sendError(response, 409, "approval_expired", "Approval is unavailable."); + return; + } + stream.approved = body.decision === "allow"; + stream.state = stream.approved ? "running" : "cancelled"; + sendJSON(response, 200, { + approvalId: "approval-1", + decision: body.decision, + resolvedAt: new Date().toISOString(), + }); + return; + } + const workspaceMatch = pathName.match(/^\/api\/aiden\/v1\/workspaces\/([^/]+)$/u); + if (workspaceMatch) { + const existing = workspaces.get(workspaceMatch[1]); + if (!existing) { + sendError(response, 404, "not_found", "Workspace not found."); + return; + } + if (request.headers["if-match"] !== existing.revision) { + sendError(response, 409, "revision_conflict", "The workspace changed."); + return; + } + if (request.method === "PATCH") { + const body = await readJSON(request); + const revisionNumber = Number(existing.revision.split("-").at(-1)) + 1; + const updated = { + ...existing, + ...(typeof body.name === "string" ? { name: body.name } : {}), + ...(typeof body.permission === "string" ? { permission: body.permission } : {}), + updatedAt: new Date().toISOString(), + revision: `rev-${workspaceMatch[1].slice("workspace-".length)}-${revisionNumber}`, + }; + workspaces.set(updated.id, updated); + sendJSON(response, 200, updated); + return; + } + if (request.method === "DELETE") { + workspaces.delete(existing.id); + response.writeHead(204, { "cache-control": "no-store" }).end(); + return; + } + } + sendError(response, 404, "not_found", "Route not found."); +} +const requestHandler = async (request, response) => { + if (request.url !== "/api/aiden/v1/health") { + if (phase6Requested) { + try { + await handlePhase6Request( + request, + response, + pairingBootstrap.endpoint, + pairingBootstrap.serverSpkiSha256, + ); + } catch { + sendError(response, 400, "invalid_request", "The request is invalid."); + } + return; + } + response.writeHead(404).end(); + return; + } + response.writeHead(200, { "content-type": "application/json", "cache-control": "no-store" }); + response.end(JSON.stringify({ ok: true, protocolVersion: 1 })); +}; +const server = listenHttpRequested + ? http.createServer(requestHandler) + : https.createServer({ key: privateKey, cert: `${certificate}\n${caCertificate}` }, requestHandler); +await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(requestedPort, "0.0.0.0", resolve); +}); +const address = server.address(); +if (!address || typeof address === "string") throw new Error("Physical-device spike failed to bind TCP."); +const pairingBootstrap = { + protocolVersion: 1, + instanceId, + endpoint: publicEndpoint ?? `https://${advertisedHost}:${address.port}/api/aiden/v1`, + serverSpkiSha256: publicSpkiSha256 ?? fingerprint, + secret: pairingSecret, + expiresAt: new Date(Date.now() + 5 * 60_000).toISOString(), +}; +const pairingTrust = systemTrustRequested + ? { mode: "system" } + : { + mode: "private-ca", + caCertificateDerBase64: new X509Certificate(caCertificate).raw.toString("base64"), + }; +const pairingPayload = { + kind: "aiden-pairing-v1", + bootstrap: pairingBootstrap, + trust: pairingTrust, +}; +const encodedPairingPayload = JSON.stringify(pairingPayload); +if (Buffer.byteLength(encodedPairingPayload, "utf8") > 4_096) { + throw new Error("Physical-device pairing payload is too large."); +} +process.stdout.write(`${encodedPairingPayload}\n`); +if (repairPairingSecret !== undefined) { + const repairPairingPayload = { + kind: "aiden-pairing-v1", + bootstrap: { + ...pairingBootstrap, + secret: repairPairingSecret, + }, + trust: pairingTrust, + }; + const encodedRepairPairingPayload = JSON.stringify(repairPairingPayload); + if (Buffer.byteLength(encodedRepairPairingPayload, "utf8") > 4_096) { + throw new Error("Physical-device repair pairing payload is too large."); + } + process.stdout.write(`${encodedRepairPairingPayload}\n`); +} + +const stop = async () => { + await new Promise((resolve) => server.close(resolve)); + if (ownsIdentityDirectory) await rm(identityDirectory, { recursive: true, force: true }); +}; +for (const signal of ["SIGINT", "SIGTERM"]) { + process.once(signal, async () => { + await stop(); + process.exit(0); + }); +} From ea49159233ad375e0048bdc605c6f97b74744fbd Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Wed, 19 Aug 2026 22:32:20 -0400 Subject: [PATCH 002/266] feat(remote-ui): add Remote Access settings and onboarding Expose pairing, connection status, approved roots, device revocation, and local or Tailscale controls in Electron settings. Introduce Aiden On The Go during onboarding, synchronize appearance tokens, and cover the settings and end-to-end lifecycle. --- .../onboarding/features/aiden-on-the-go.png | Bin 0 -> 792116 bytes renderer/components/onboarding-flow.test.tsx | 8 +- renderer/components/onboarding-flow.tsx | 15 + .../settings/remote-access-settings.test.tsx | 27 ++ .../settings/remote-access-settings.tsx | 391 ++++++++++++++++++ renderer/lib/ipc.ts | 28 ++ renderer/lib/queries.ts | 12 + renderer/lib/settings-section.test.ts | 7 + renderer/main/root-view.tsx | 15 + renderer/main/settings-view.tsx | 4 + renderer/preload-channels.ts | 4 + renderer/shared/appearance.test.ts | 10 +- renderer/shared/appearance.ts | 2 +- renderer/shared/settings-section.ts | 7 + tests/e2e/remote-access-lifecycle.spec.ts | 51 +++ tests/e2e/settings-model-picker.spec.ts | 11 + 16 files changed, 588 insertions(+), 4 deletions(-) create mode 100644 renderer/assets/onboarding/features/aiden-on-the-go.png create mode 100644 renderer/components/settings/remote-access-settings.test.tsx create mode 100644 renderer/components/settings/remote-access-settings.tsx create mode 100644 tests/e2e/remote-access-lifecycle.spec.ts diff --git a/renderer/assets/onboarding/features/aiden-on-the-go.png b/renderer/assets/onboarding/features/aiden-on-the-go.png new file mode 100644 index 0000000000000000000000000000000000000000..94a77e3562a86aa3c47def88bedeb911bdcc1b75 GIT binary patch literal 792116 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

UiXdd)%V@qR-bsBpI&8=&Rra9yaW)M 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 Lg(Npa_X3pDDp(IT(MTdTaoFjNT-7I+RqMl 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

    @f}SoqB#(0sGypM909@|M-GXf>VA;N@IBJD5vc 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 { - assert.equal(featureAssetPaths.length, 23); + assert.equal(featureAssetPaths.length, 24); assert.ok(featureAssetPaths.includes("features/telegram-remote-control.png")); + assert.ok(featureAssetPaths.includes("features/aiden-on-the-go.png")); assert.equal(new Set(featureAssetPaths).size, featureAssetPaths.length); for (const assetPath of featureAssetPaths) { const illustration = readFileSync( diff --git a/renderer/components/onboarding-flow.tsx b/renderer/components/onboarding-flow.tsx index fa592ecf..bedb59f8 100644 --- a/renderer/components/onboarding-flow.tsx +++ b/renderer/components/onboarding-flow.tsx @@ -26,6 +26,7 @@ import { Send, ShieldCheck, SquareTerminal, + Smartphone, UserRound, UsersRound, Wand2, @@ -87,6 +88,8 @@ const FEATURE_ILLUSTRATIONS = { themes: new URL("../assets/onboarding/features/themes-accessibility.png", import.meta.url).href, telegram: new URL("../assets/onboarding/features/telegram-remote-control.png", import.meta.url) .href, + aidenOnTheGo: new URL("../assets/onboarding/features/aiden-on-the-go.png", import.meta.url) + .href, } as const; const providerChoices: Array<{ @@ -337,6 +340,15 @@ const featureBentos: FeatureBento[] = [ imageUrl: FEATURE_ILLUSTRATIONS.telegram, size: "standard", }, + { + id: "aidenOnTheGo", + group: "control", + title: "Aiden On The Go", + description: "Pair your iPhone or iPad over pinned local HTTPS or a private Tailscale route.", + icon: Smartphone, + imageUrl: FEATURE_ILLUSTRATIONS.aidenOnTheGo, + size: "standard", + }, { id: "usage", group: "control", @@ -920,6 +932,9 @@ export function OnboardingFlow() { Explore all {featureBentos.length} shipped features. Scroll, then hover or focus a tile to learn more. + + Phone and iPad access starts off. After setup, opt in from Settings → Remote Access; Aiden must stay running, and Tailscale is optional. +

    { + for (const primitive of ["FieldSet", "Field", "Switch", "Select", "Callout", "Dialog", "AlertDialog"]) { + assert.match(source, new RegExp(`<${primitive}`, "u")); + } + assert.match(source, /onValueChange=/u); + assert.match(source, /aria-label="Enable Aiden Remote Access"/u); + assert.match(source, /max-\[540px\]/u); +}); + +test("pairing renders only the versioned QR envelope and labels its short value as visual verification", () => { + assert.match(source, /QRCode\.toDataURL\(pairing\.qrPayload/u); + assert.doesNotMatch(source, /\{pairing\.secret\}/u); + assert.match(source, /This is a visual check, not a manual pairing password\./u); + assert.match(source, /expires after five minutes/u); +}); + +test("Tailscale and folder controls explain their bounded ownership", () => { + assert.match(source, /never enables Funnel, and never resets unrelated routes/u); + assert.match(source, /Paired devices can explore only these roots/u); + assert.match(source, /Existing Aiden workspaces are unchanged/u); +}); diff --git a/renderer/components/settings/remote-access-settings.tsx b/renderer/components/settings/remote-access-settings.tsx new file mode 100644 index 00000000..974dfd56 --- /dev/null +++ b/renderer/components/settings/remote-access-settings.tsx @@ -0,0 +1,391 @@ +import * as React from "react"; +import { useQueryClient } from "@tanstack/react-query"; +import QRCode from "qrcode"; +import { + CheckCircle2, + Folder, + Loader2, + Network, + Plus, + ShieldCheck, + Smartphone, + Trash2, + TriangleAlert, +} from "lucide-react"; +import { + AlertDialog, + Badge, + Button, + Callout, + Dialog, + Field, + FieldSet, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, + Switch, + Text, + toast, +} from "../ui"; +import { aidenRemoteApi } from "../../lib/ipc"; +import { queryKeys, useAidenRemoteSettings } from "../../lib/queries"; +import type { + AidenRemoteConnectionMode, + AidenRemoteDeviceView, + AidenRemotePairingBootstrapView, + AidenRemoteSettingsSnapshot, +} from "../../shared/aiden-remote"; + +function friendlyDate(timestamp: number): string { + return new Intl.DateTimeFormat(undefined, { + dateStyle: "medium", + timeStyle: "short", + }).format(new Date(timestamp)); +} + +function pairingVerificationCode(pairing: AidenRemotePairingBootstrapView): string { + return pairing.serverSpkiSha256.slice(-9, -1).toUpperCase(); +} + +export function RemoteAccessSettings() { + const queryClient = useQueryClient(); + const settingsQuery = useAidenRemoteSettings(); + const [busy, setBusy] = React.useState(null); + const [pairing, setPairing] = React.useState(null); + const [pairingQr, setPairingQr] = React.useState(null); + const [pairingSeconds, setPairingSeconds] = React.useState(0); + const [revokeDevice, setRevokeDevice] = React.useState(null); + const [removeRootId, setRemoveRootId] = React.useState(null); + + React.useEffect(() => aidenRemoteApi.onChanged(() => { + void queryClient.invalidateQueries({ queryKey: queryKeys.aidenRemote }); + }), [queryClient]); + + React.useEffect(() => { + if (!pairing) { + setPairingQr(null); + setPairingSeconds(0); + return; + } + let current = true; + void QRCode.toDataURL(pairing.qrPayload, { + errorCorrectionLevel: "M", + margin: 2, + width: 512, + color: { dark: "#000000", light: "#ffffff" }, + }).then((url) => current && setPairingQr(url)).catch(() => { + if (current) toast.error("Aiden couldn't draw the pairing code."); + }); + const updateRemaining = () => { + setPairingSeconds(Math.max(0, Math.ceil((Date.parse(pairing.expiresAt) - Date.now()) / 1_000))); + }; + updateRemaining(); + const interval = window.setInterval(updateRemaining, 1_000); + return () => { + current = false; + window.clearInterval(interval); + }; + }, [pairing]); + + const commit = React.useCallback((next: AidenRemoteSettingsSnapshot) => { + queryClient.setQueryData(queryKeys.aidenRemote, next); + }, [queryClient]); + + const mutate = async ( + operation: string, + action: () => Promise, + ) => { + if (busy) return; + setBusy(operation); + try { + commit(await action()); + } catch (error) { + toast.error(error instanceof Error ? error.message : "Remote Access couldn't be updated."); + await queryClient.invalidateQueries({ queryKey: queryKeys.aidenRemote }); + } finally { + setBusy(null); + } + }; + + const beginPairing = async (transport: "lan" | "tailscale") => { + if (busy) return; + setBusy("pairing"); + try { + setPairing(await aidenRemoteApi.beginPairing(transport)); + } catch (error) { + toast.error(error instanceof Error ? error.message : "Aiden couldn't open pairing."); + } finally { + setBusy(null); + } + }; + + const closePairing = (open: boolean) => { + if (open) return; + setPairing(null); + void aidenRemoteApi.closePairing().catch(() => undefined); + }; + + if (settingsQuery.isLoading) { + return ( +
    + + + Checking… + + +
    + ); + } + + if (!settingsQuery.data) { + return ( + + Remote Access is unavailable + + {settingsQuery.error instanceof Error ? settingsQuery.error.message : "Aiden couldn't read its remote settings."} + + + ); + } + + const snapshot = settingsQuery.data; + const { status } = snapshot; + const transportAllowsLan = status.connectionMode === "lan" || status.connectionMode === "both"; + const transportAllowsTailscale = status.connectionMode === "tailscale" || status.connectionMode === "both"; + const activeDevices = snapshot.devices.filter((device) => device.revokedAt === undefined); + + return ( + <> + +
    + +
    + Aiden stays in control + + Aiden On The Go can use only the capabilities and folders approved on this Mac. Provider keys never leave Aiden. The desktop app must be running; Tailscale is optional. + +
    +
    +
    + +
    + +
    + {busy === "enabled" ? : null} + void mutate("enabled", () => aidenRemoteApi.setEnabled(enabled))} + disabled={busy !== null} + aria-label="Enable Aiden Remote Access" + /> +
    +
    + + + + + +
    + {status.running ? ( + + ) : status.error ? ( + + ) : ( + + )} + + {status.running ? "Ready" : status.enabled ? "Needs attention" : "Off"} + + {status.running ? Running : null} +
    + {status.lanEndpoint ? ( + + Local: {status.lanEndpoint} + + ) : null} + {status.tailscaleEndpoint ? ( + + Tailscale: {status.tailscaleEndpoint} + + ) : null} + {status.error ? {status.error} : null} +
    +
    +
    + + {transportAllowsTailscale ? ( +
    + + + + {status.tailscaleRoutePreview ?? "Preparing the loopback route…"} + +
    + + {status.tailscaleConnected ? "Connected" : status.tailscaleInstalled ? "Not connected" : "Tailscale not found"} + + +
    +
    +
    +
    + ) : null} + +
    + +
    + {transportAllowsLan ? ( + + ) : null} + {transportAllowsTailscale ? ( + + ) : null} +
    +
    +
    + +
    + +
    + +
    +
    + {snapshot.approvedRoots.length === 0 ? ( +
    No folders are approved for remote browsing.
    + ) : snapshot.approvedRoots.map((root) => ( +
    + +
    + {root.label} + {root.folderPath} +
    + +
    + ))} +
    + +
    + {activeDevices.length === 0 ? ( +
    No active devices are paired.
    + ) : activeDevices.map((device) => ( +
    + +
    + {device.name} + + {device.type === "ipad" ? "iPad" : "iPhone"} · Last seen {friendlyDate(device.lastSeenAt)} + +
    + +
    + ))} +
    + + + {pairing ? ( +
    + {pairingQr ? ( + One-time Aiden pairing QR code + ) : ( +
    + )} + 0 ? "blue" : "red"}> + {pairingSeconds > 0 ? `Expires in ${Math.floor(pairingSeconds / 60)}:${String(pairingSeconds % 60).padStart(2, "0")}` : "Expired"} + + + Verification {pairingVerificationCode(pairing)} · This is a visual check, not a manual pairing password. + +
    + ) : null} +
    + + !open && setRevokeDevice(null)} + title="Revoke this device?" + description={revokeDevice ? `“${revokeDevice.name}” will immediately lose Remote Access. Pair it again to restore access.` : undefined} + confirmLabel="Revoke" + confirmVariant="destructive" + busy={busy === "revoke"} + keepOpenOnConfirm + onConfirm={async () => { + if (!revokeDevice) return; + await mutate("revoke", () => aidenRemoteApi.revokeDevice(revokeDevice.id)); + setRevokeDevice(null); + }} + /> + + !open && setRemoveRootId(null)} + title="Remove this approved folder?" + description="Paired devices will no longer be able to explore or add workspaces from this root. Existing Aiden workspaces are unchanged." + confirmLabel="Remove" + confirmVariant="destructive" + busy={busy === "removeRoot"} + keepOpenOnConfirm + onConfirm={async () => { + if (!removeRootId) return; + await mutate("removeRoot", () => aidenRemoteApi.removeApprovedRoot(removeRootId)); + setRemoveRootId(null); + }} + /> + + ); +} diff --git a/renderer/lib/ipc.ts b/renderer/lib/ipc.ts index ef589f4c..7adc55fa 100644 --- a/renderer/lib/ipc.ts +++ b/renderer/lib/ipc.ts @@ -104,6 +104,11 @@ import { type SkillCatalogEntry, } from "../shared/slash-commands"; import { rememberAppendReconciliationFailure } from "./append-reconciliation"; +import type { + AidenRemoteConnectionMode, + AidenRemotePairingBootstrapView, + AidenRemoteSettingsSnapshot, +} from "../shared/aiden-remote"; function bridge() { return window.aidenAPI.ipc; @@ -385,6 +390,29 @@ export const telegramApi = { onNotification("telegram:model-selection-changed", handler), }; +export const aidenRemoteApi = { + get: () => invoke("remote:get"), + setEnabled: (enabled: boolean) => + invoke("remote:setEnabled", enabled), + setConnectionMode: (mode: AidenRemoteConnectionMode) => + invoke("remote:setConnectionMode", mode), + connectTailscale: () => + invoke("remote:tailscaleConnect"), + disconnectTailscale: () => + invoke("remote:tailscaleDisconnect"), + beginPairing: (transport: "lan" | "tailscale") => + invoke("remote:beginPairing", transport), + closePairing: () => invoke<{ closed: true }>("remote:closePairing"), + revokeDevice: (deviceId: string) => + invoke("remote:revokeDevice", deviceId), + addApprovedRoot: () => + invoke("remote:addApprovedRoot"), + removeApprovedRoot: (rootId: string) => + invoke("remote:removeApprovedRoot", rootId), + onChanged: (handler: () => void) => + onNotification("remote:changed", handler), +}; + // ── Voice + shortcut ────────────────────────────────────────────────── export const voiceApi = { transcribe: (audioBase64: string, mimeType: string) => diff --git a/renderer/lib/queries.ts b/renderer/lib/queries.ts index 04cc3a28..d5362e81 100644 --- a/renderer/lib/queries.ts +++ b/renderer/lib/queries.ts @@ -9,6 +9,7 @@ import { } from "@tanstack/react-query"; import { assistantApi, + aidenRemoteApi, chatsApi, artificialAnalysisApi, computerUseApi, @@ -60,6 +61,7 @@ export const queryKeys = { mcpPresets: ["mcpPresets"] as const, exa: ["exa"] as const, telegram: ["telegram"] as const, + aidenRemote: ["aidenRemote"] as const, engineStatus: ["engineStatus"] as const, localModels: ["localModels"] as const, workspaces: ["workspaces"] as const, @@ -464,6 +466,16 @@ export function useTelegramSettings() { return useQuery({ queryKey: queryKeys.telegram, queryFn: telegramApi.get }); } +export function useAidenRemoteSettings() { + return useQuery({ + queryKey: queryKeys.aidenRemote, + queryFn: aidenRemoteApi.get, + retry: false, + refetchOnWindowFocus: true, + refetchInterval: 10_000, + }); +} + export function useEngineStatus(enabled = true) { return useQuery({ queryKey: queryKeys.engineStatus, queryFn: localVoiceApi.status, enabled }); } diff --git a/renderer/lib/settings-section.test.ts b/renderer/lib/settings-section.test.ts index 61c8e501..945f85b4 100644 --- a/renderer/lib/settings-section.test.ts +++ b/renderer/lib/settings-section.test.ts @@ -7,6 +7,7 @@ test("accepts known settings deep links and rejects arbitrary search values", () assert.equal(parseSettingsSection("computerUse"), "computerUse"); assert.equal(parseSettingsSection("scheduledTasks"), "scheduledTasks"); assert.equal(parseSettingsSection("assistant"), "assistant"); + assert.equal(parseSettingsSection("remoteAccess"), "remoteAccess"); assert.equal(parseSettingsSection("about"), "about"); assert.equal(parseSettingsSection("unknown"), undefined); assert.equal(parseSettingsSection(["modelData"]), undefined); @@ -16,6 +17,12 @@ test("accepts known settings deep links and rejects arbitrary search values", () assert.deepEqual(parseSettingsSearch({ section: "unknown" }), {}); }); +test("parses the Remote Access settings deep link", () => { + assert.deepEqual(parseSettingsSearch({ section: "remoteAccess" }), { + section: "remoteAccess", + }); +}); + test("parses the Aiden settings deep link", () => { assert.deepEqual(parseSettingsSearch({ section: "assistant" }), { section: "assistant", diff --git a/renderer/main/root-view.tsx b/renderer/main/root-view.tsx index 6513a86e..e0659d26 100644 --- a/renderer/main/root-view.tsx +++ b/renderer/main/root-view.tsx @@ -181,6 +181,21 @@ function RootContent() { }); }, [queryClient]); + React.useEffect(() => { + return onNotification("workspaces:changed", () => { + void Promise.all([ + queryClient.invalidateQueries({ queryKey: queryKeys.workspaces }), + queryClient.invalidateQueries({ queryKey: queryKeys.chats }), + ]); + }); + }, [queryClient]); + + React.useEffect(() => { + return onNotification("chats:changed", () => { + void queryClient.invalidateQueries({ queryKey: queryKeys.chats }); + }); + }, [queryClient]); + React.useEffect( () => subscribeDetachedTerminalChats( diff --git a/renderer/main/settings-view.tsx b/renderer/main/settings-view.tsx index fb2188aa..a82da89c 100644 --- a/renderer/main/settings-view.tsx +++ b/renderer/main/settings-view.tsx @@ -20,6 +20,7 @@ import { Clock3, Sparkles, Send, + Smartphone, } from "lucide-react"; import { ProvidersSettings } from "../components/settings/providers-settings"; import { AppearanceSettings } from "../components/settings/appearance-settings"; @@ -34,6 +35,7 @@ import { ModelDataSettings } from "../components/settings/model-data-settings"; import { AboutSettings } from "../components/settings/about-settings"; import { ScheduledTasksSettings } from "../components/settings/scheduled-tasks-settings"; import { AssistantSettings } from "../components/settings/assistant-settings"; +import { RemoteAccessSettings } from "../components/settings/remote-access-settings"; import { SETTINGS_DESTINATIONS, type SettingsSection } from "../lib/settings-section"; type NavGroup = "Agent" | "App"; @@ -52,6 +54,7 @@ const NAV_ICONS: Record = { skills: , mcp: , telegram: , + remoteAccess: , websearch: , scheduledTasks: , assistant: , @@ -75,6 +78,7 @@ const CONTENT: Record = { modelData: ModelDataSettings, skills: SkillsSettings, telegram: TelegramSettings, + remoteAccess: RemoteAccessSettings, mcp: McpSettings, websearch: WebSearchSettings, computerUse: ComputerUseSettings, diff --git a/renderer/preload-channels.ts b/renderer/preload-channels.ts index 578e066b..6c63f1f6 100644 --- a/renderer/preload-channels.ts +++ b/renderer/preload-channels.ts @@ -22,6 +22,7 @@ export const INVOKE_PREFIXES = [ "models:", "providers:", "profile:", + "remote:", "schedule:", "settings:", "shortcut:", @@ -62,6 +63,7 @@ export const NOTIFICATION_CHANNEL_VALUES = [ "chat:timeline", "chat:tool", "chats:activity-changed", + "chats:changed", "chats:metadata-updated", "chats:settled", "dictation:state", @@ -71,12 +73,14 @@ export const NOTIFICATION_CHANNEL_VALUES = [ "providers:auth:event", "providers:auth:prompt", "providers:auth:status-changed", + "remote:changed", "schedule:updated", "settings:appearance-changed", "shortcut:changed", "terminal:data", "terminal:exit", "telegram:model-selection-changed", + "workspaces:changed", "aiden:theme:changed", ] as const; diff --git a/renderer/shared/appearance.test.ts b/renderer/shared/appearance.test.ts index 28e77160..d54f65a5 100644 --- a/renderer/shared/appearance.test.ts +++ b/renderer/shared/appearance.test.ts @@ -40,6 +40,14 @@ const NORMAL_TEXT_TOKENS = [ "--terminal-white", ] as const; +test("the shared mobile appearance fixture exactly matches Electron presets", () => { + const fixture = JSON.parse( + readFileSync(new URL("../../protocol/aiden-appearance-v1.json", import.meta.url), "utf8"), + ) as { version: number; presets: typeof THEME_PRESETS }; + assert.equal(fixture.version, 1); + assert.deepEqual(fixture.presets, THEME_PRESETS); +}); + function assertSemanticContrast( variant: ThemeVariantConfig, scheme: AppearanceScheme, @@ -118,7 +126,7 @@ test("built-in themes keep light neutrals softer and dark neutrals calmer", () = const darkAccents = { aiden: "#3E97F6", slate: "#21A9BE", - berry: "#22B69B", + berry: "#E8629F", moss: "#42B596", } as const; diff --git a/renderer/shared/appearance.ts b/renderer/shared/appearance.ts index f4dc1a1f..131e937f 100644 --- a/renderer/shared/appearance.ts +++ b/renderer/shared/appearance.ts @@ -145,7 +145,7 @@ export const THEME_PRESETS: ReadonlyArray = [ raised: "#2E2435", foreground: "#D5CFD6", secondary: "#A39AA6", - accent: "#22B69B", + accent: "#E8629F", success: "#32D1B2", warning: "#D9A441", danger: "#F0717A", diff --git a/renderer/shared/settings-section.ts b/renderer/shared/settings-section.ts index 4221a169..2d5108e7 100644 --- a/renderer/shared/settings-section.ts +++ b/renderer/shared/settings-section.ts @@ -5,6 +5,7 @@ export const SETTINGS_SECTIONS = [ "mcp", "websearch", "telegram", + "remoteAccess", "computerUse", "scheduledTasks", "assistant", @@ -42,6 +43,12 @@ export const SETTINGS_DESTINATIONS: ReadonlyArray<{ { id: "mcp", title: "MCP Servers", group: "Agent", keywords: ["connections", "protocol"] }, { id: "websearch", title: "Web Search", group: "Agent", keywords: ["internet", "exa"] }, { id: "telegram", title: "Telegram", group: "Agent", keywords: ["remote", "bot", "phone", "control"] }, + { + id: "remoteAccess", + title: "Remote Access", + group: "Agent", + keywords: ["iphone", "ipad", "aiden on the go", "tailscale", "local network", "pairing"], + }, { id: "scheduledTasks", title: "Scheduled tasks", diff --git a/tests/e2e/remote-access-lifecycle.spec.ts b/tests/e2e/remote-access-lifecycle.spec.ts new file mode 100644 index 00000000..93f17c45 --- /dev/null +++ b/tests/e2e/remote-access-lifecycle.spec.ts @@ -0,0 +1,51 @@ +import https from "node:https"; +import { expect, finishLmStudioOnboarding, test } from "./fixtures"; + +async function remoteHealth(): Promise<{ status: number; body: unknown }> { + return new Promise((resolve, reject) => { + const request = https.get( + "https://127.0.0.1:49220/api/aiden/v1/health", + { rejectUnauthorized: false, timeout: 3_000 }, + (response) => { + const chunks: Buffer[] = []; + response.on("data", (chunk) => chunks.push(Buffer.from(chunk))); + response.on("end", () => { + try { + resolve({ + status: response.statusCode ?? 0, + body: JSON.parse(Buffer.concat(chunks).toString("utf8")), + }); + } catch (error) { + reject(error); + } + }); + }, + ); + request.once("error", reject); + }); +} + +test("Remote Access is opt-in and remains available after the main window closes", async ({ aiden }) => { + const { page } = aiden; + await finishLmStudioOnboarding(page); + await page.getByRole("button", { name: "Settings", exact: true }).click(); + await page.getByRole("navigation", { name: "Settings" }) + .getByRole("button", { name: "Remote Access", exact: true }) + .click(); + + const enabled = page.getByRole("switch", { name: "Enable Aiden Remote Access" }); + await expect(enabled).toHaveAttribute("data-state", "unchecked"); + await expect(remoteHealth()).rejects.toThrow(); + await enabled.click(); + await expect(enabled).toHaveAttribute("data-state", "checked"); + await expect(page.getByText("Ready", { exact: true })).toBeVisible(); + assertHealth(await remoteHealth()); + + await page.close(); + assertHealth(await remoteHealth()); +}); + +function assertHealth(result: { status: number; body: unknown }): void { + expect(result.status).toBe(200); + expect(result.body).toEqual({ ok: true, protocolVersion: 1 }); +} diff --git a/tests/e2e/settings-model-picker.spec.ts b/tests/e2e/settings-model-picker.spec.ts index f2165938..b2f7d658 100644 --- a/tests/e2e/settings-model-picker.spec.ts +++ b/tests/e2e/settings-model-picker.spec.ts @@ -6,6 +6,7 @@ const SETTINGS_SECTIONS = [ "Skills", "MCP Servers", "Web Search", + "Remote Access", "Scheduled tasks", "Aiden", "Computer Use", @@ -43,6 +44,16 @@ async function assertRenderedSettingsDestination( page.getByRole("heading", { level: 2, name: "Web Search (Exa)", exact: true }), ).toBeVisible(); return; + case "Remote Access": + await expect( + page.getByRole("heading", { level: 2, name: "Remote Access", exact: true }), + ).toBeVisible(); + await expect(page.getByRole("switch", { name: "Enable Aiden Remote Access" })).toHaveAttribute( + "data-state", + "unchecked", + ); + await expect(page.getByText("Off", { exact: true })).toBeVisible(); + return; case "Scheduled tasks": await expect( page.getByRole("heading", { level: 2, name: "Scheduled tasks", exact: true }), From 5e416f7c4f9b7fe2a12ef003ac1331d4aa01d434 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Wed, 19 Aug 2026 22:32:34 -0400 Subject: [PATCH 003/266] feat(ios): add the Aiden On The Go native client Add the SwiftUI iPhone and iPad companion with secure pairing, adaptive workspace and chat navigation, resumable SSE streaming, files, Git, scheduled tasks, App Intents, Live Activities, voice input, native Markdown, and Aiden appearance. Include physical-device tests, signing metadata, TestFlight workflows, ASC monitoring, release-policy checks, and required upstream notices. --- .../aiden-on-the-go-external-testflight.yml | 193 ++ .../aiden-on-the-go-internal-testflight.yml | 187 ++ .github/workflows/ci.yml | 15 + .gitignore | 10 + eslint.config.js | 10 + ios/.gitignore | 60 + ios/AGENTS.md | 53 + ios/APP_STORE_METADATA.md | 116 ++ ios/ASC_CLI.md | 167 ++ .../AgentRunLiveActivityWidget.swift | 430 +++++ .../Resources/Info.plist | 31 + ios/AidenOnTheGo.xcodeproj/project.pbxproj | 1034 +++++++++++ .../xcshareddata/swiftpm/Package.resolved | 42 + .../xcschemes/AidenOnTheGo.xcscheme | 93 + ios/AidenOnTheGo/AidenOnTheGoApp.swift | 16 + .../AppIntents/AidenAppIntents.swift | 229 +++ ios/AidenOnTheGo/Auth/KeychainStore.swift | 67 + ios/AidenOnTheGo/Config/AidenAppearance.swift | 521 ++++++ ios/AidenOnTheGo/Config/AppConfig.swift | 21 + ios/AidenOnTheGo/ContentView.swift | 74 + .../Chat/ComposerVoiceInputController.swift | 394 ++++ .../Features/Remote/AidenChatFeature.swift | 1624 +++++++++++++++++ .../Features/Remote/AidenPairingView.swift | 443 +++++ .../Remote/AidenRemoteCoordinator.swift | 377 ++++ .../Remote/AidenScheduledTasksView.swift | 662 +++++++ .../AidenWorkspaceEnvironmentView.swift | 846 +++++++++ .../Remote/AidenWorkspaceShellView.swift | 1505 +++++++++++++++ .../Shared/ThinkingOrbsKit/Core.swift | 142 ++ .../Shared/ThinkingOrbsKit/Lattice.swift | 201 ++ .../Shared/ThinkingOrbsKit/Morph.swift | 136 ++ .../Shared/ThinkingOrbsKit/OrbSpec.swift | 137 ++ .../Shared/ThinkingOrbsKit/Orbits.swift | 75 + .../Shared/ThinkingOrbsKit/Presets.swift | 88 + .../Shared/ThinkingOrbsKit/Snapshot.swift | 51 + .../Shared/ThinkingOrbsKit/Strands.swift | 138 ++ .../Shared/ThinkingOrbsKit/ThinkingOrb.swift | 145 ++ .../Features/Shared/ThinkingOrbsKit/Web.swift | 91 + .../AgentRunActivityAttributes.swift | 457 +++++ .../LiveActivities/AidenDeepLink.swift | 82 + .../AidenRemoteLiveActivityManager.swift | 240 +++ ios/AidenOnTheGo/Models/AidenChat.swift | 247 +++ .../Models/AidenInstallation.swift | 209 +++ .../Models/AidenScheduledTask.swift | 195 ++ .../Models/AidenWorkspaceEnvironment.swift | 250 +++ .../Networking/AidenRemoteClient.swift | 1029 +++++++++++ .../Networking/AidenRemoteContract.swift | 1509 +++++++++++++++ .../Networking/AidenSSEParser.swift | 77 + .../Networking/AidenServerTrust.swift | 215 +++ .../Persistence/AidenChatCache.swift | 135 ++ .../Resources/AidenOnTheGo.entitlements | 10 + .../AppIcon.icon/Assets/aiden-icon-june15.png | Bin 0 -> 962574 bytes .../Resources/AppIcon.icon/icon.json | 45 + .../AccentColor.colorset/Contents.json | 11 + .../AidenAppIcon.imageset/Contents.json | 13 + .../AidenAppIcon.imageset/aiden-app-icon.png | Bin 0 -> 135357 bytes .../AidenSidebarLogo.imageset/Contents.json | 25 + .../aiden-sidebar-logo.png | Bin 0 -> 141352 bytes .../AppIcon.appiconset/Contents.json | 14 + .../AppIcon.appiconset/aiden-icon-june15.png | Bin 0 -> 962574 bytes .../Contents.json | 14 + .../aiden-app-icon-monochrome.png | Bin 0 -> 180290 bytes .../Contents.json | 13 + .../aiden-app-icon-monochrome.png | Bin 0 -> 53942 bytes .../Resources/Assets.xcassets/Contents.json | 6 + ios/AidenOnTheGo/Resources/Info.plist | 77 + .../Resources/Localizable.xcstrings | 6 + .../Resources/PrivacyInfo.xcprivacy | 23 + .../ThirdPartyNotices/Hermex-LICENSE.txt | 21 + .../KeychainAccess-LICENSE.txt | 21 + .../ThirdPartyNotices/MarkdownUI-LICENSE.txt | 21 + .../Resources/ThirdPartyNotices/NOTICE.txt | 17 + .../NetworkImage-LICENSE.txt | 21 + .../ThinkingOrbs-LICENSE.txt | 21 + .../ThirdPartyNotices/swift-cmark-COPYING.txt | 170 ++ ios/AidenOnTheGoTests/AidenChatTests.swift | 529 ++++++ .../AidenNativeIntegrationTests.swift | 464 +++++ .../AidenRemoteClientTests.swift | 1165 ++++++++++++ .../AidenRemotePhase0Tests.swift | 904 +++++++++ .../AidenScheduledTaskTests.swift | 183 ++ .../AidenWorkspaceEnvironmentTests.swift | 268 +++ ios/CHANGELOG.md | 9 + ios/CODE_OF_CONDUCT.md | 131 ++ ios/CONTEXT.md | 9 + ios/CONTRACT_TESTS.md | 5 + ios/CONTRIBUTING.md | 7 + ios/Config/Shared.xcconfig | 17 + ios/DEVELOPMENT.md | 9 + ios/LICENSE | 21 + ios/PROJECT_INTENT.md | 13 + ios/PROJECT_SPEC.md | 122 ++ ios/README.md | 24 + ios/SECURITY.md | 5 + ios/TESTFLIGHT.md | 89 + ios/app-store/MOBILE_PRIVACY_SUPPORT_COPY.md | 43 + ios/app-store/metadata/app-info/en-US.json | 5 + .../metadata/version/0.1.0/en-US.json | 6 + ios/ci/ExternalTestFlightExportOptions.plist | 18 + ios/ci/TestFlightExportOptions.plist | 20 + ios/ci/select_testflight_build_number.rb | 295 +++ ios/ci/select_testflight_build_number_test.rb | 255 +++ ios/scripts/check-swift-file-sizes | 67 + scripts/check-ios-app-store-metadata.test.mjs | 151 ++ scripts/check-ios-shipping-target.test.mjs | 290 +++ scripts/check-ios-testflight-policy.test.mjs | 94 + scripts/ios-asc-monitor.mjs | 268 +++ scripts/ios-asc-monitor.test.mjs | 140 ++ scripts/ios-live-activity-process-proof.mjs | 342 ++++ .../ios-live-activity-process-proof.test.mjs | 102 ++ 108 files changed, 21688 insertions(+) create mode 100644 .github/workflows/aiden-on-the-go-external-testflight.yml create mode 100644 .github/workflows/aiden-on-the-go-internal-testflight.yml create mode 100644 ios/.gitignore create mode 100644 ios/AGENTS.md create mode 100644 ios/APP_STORE_METADATA.md create mode 100644 ios/ASC_CLI.md create mode 100644 ios/AidenLiveActivityWidget/AgentRunLiveActivityWidget.swift create mode 100644 ios/AidenLiveActivityWidget/Resources/Info.plist create mode 100644 ios/AidenOnTheGo.xcodeproj/project.pbxproj create mode 100644 ios/AidenOnTheGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 ios/AidenOnTheGo.xcodeproj/xcshareddata/xcschemes/AidenOnTheGo.xcscheme create mode 100644 ios/AidenOnTheGo/AidenOnTheGoApp.swift create mode 100644 ios/AidenOnTheGo/AppIntents/AidenAppIntents.swift create mode 100644 ios/AidenOnTheGo/Auth/KeychainStore.swift create mode 100644 ios/AidenOnTheGo/Config/AidenAppearance.swift create mode 100644 ios/AidenOnTheGo/Config/AppConfig.swift create mode 100644 ios/AidenOnTheGo/ContentView.swift create mode 100644 ios/AidenOnTheGo/Features/Chat/ComposerVoiceInputController.swift create mode 100644 ios/AidenOnTheGo/Features/Remote/AidenChatFeature.swift create mode 100644 ios/AidenOnTheGo/Features/Remote/AidenPairingView.swift create mode 100644 ios/AidenOnTheGo/Features/Remote/AidenRemoteCoordinator.swift create mode 100644 ios/AidenOnTheGo/Features/Remote/AidenScheduledTasksView.swift create mode 100644 ios/AidenOnTheGo/Features/Remote/AidenWorkspaceEnvironmentView.swift create mode 100644 ios/AidenOnTheGo/Features/Remote/AidenWorkspaceShellView.swift create mode 100644 ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Core.swift create mode 100644 ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Lattice.swift create mode 100644 ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Morph.swift create mode 100644 ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/OrbSpec.swift create mode 100644 ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Orbits.swift create mode 100644 ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Presets.swift create mode 100644 ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Snapshot.swift create mode 100644 ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Strands.swift create mode 100644 ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/ThinkingOrb.swift create mode 100644 ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Web.swift create mode 100644 ios/AidenOnTheGo/LiveActivities/AgentRunActivityAttributes.swift create mode 100644 ios/AidenOnTheGo/LiveActivities/AidenDeepLink.swift create mode 100644 ios/AidenOnTheGo/LiveActivities/AidenRemoteLiveActivityManager.swift create mode 100644 ios/AidenOnTheGo/Models/AidenChat.swift create mode 100644 ios/AidenOnTheGo/Models/AidenInstallation.swift create mode 100644 ios/AidenOnTheGo/Models/AidenScheduledTask.swift create mode 100644 ios/AidenOnTheGo/Models/AidenWorkspaceEnvironment.swift create mode 100644 ios/AidenOnTheGo/Networking/AidenRemoteClient.swift create mode 100644 ios/AidenOnTheGo/Networking/AidenRemoteContract.swift create mode 100644 ios/AidenOnTheGo/Networking/AidenSSEParser.swift create mode 100644 ios/AidenOnTheGo/Networking/AidenServerTrust.swift create mode 100644 ios/AidenOnTheGo/Persistence/AidenChatCache.swift create mode 100644 ios/AidenOnTheGo/Resources/AidenOnTheGo.entitlements create mode 100644 ios/AidenOnTheGo/Resources/AppIcon.icon/Assets/aiden-icon-june15.png create mode 100644 ios/AidenOnTheGo/Resources/AppIcon.icon/icon.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/AidenAppIcon.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/AidenAppIcon.imageset/aiden-app-icon.png create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/AidenSidebarLogo.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/AidenSidebarLogo.imageset/aiden-sidebar-logo.png create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/AppIcon.appiconset/aiden-icon-june15.png create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/AppIconMonochrome.appiconset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/AppIconMonochrome.appiconset/aiden-app-icon-monochrome.png create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/AppIconMonochromePreview.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/AppIconMonochromePreview.imageset/aiden-app-icon-monochrome.png create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Info.plist create mode 100644 ios/AidenOnTheGo/Resources/Localizable.xcstrings create mode 100644 ios/AidenOnTheGo/Resources/PrivacyInfo.xcprivacy create mode 100644 ios/AidenOnTheGo/Resources/ThirdPartyNotices/Hermex-LICENSE.txt create mode 100644 ios/AidenOnTheGo/Resources/ThirdPartyNotices/KeychainAccess-LICENSE.txt create mode 100644 ios/AidenOnTheGo/Resources/ThirdPartyNotices/MarkdownUI-LICENSE.txt create mode 100644 ios/AidenOnTheGo/Resources/ThirdPartyNotices/NOTICE.txt create mode 100644 ios/AidenOnTheGo/Resources/ThirdPartyNotices/NetworkImage-LICENSE.txt create mode 100644 ios/AidenOnTheGo/Resources/ThirdPartyNotices/ThinkingOrbs-LICENSE.txt create mode 100644 ios/AidenOnTheGo/Resources/ThirdPartyNotices/swift-cmark-COPYING.txt create mode 100644 ios/AidenOnTheGoTests/AidenChatTests.swift create mode 100644 ios/AidenOnTheGoTests/AidenNativeIntegrationTests.swift create mode 100644 ios/AidenOnTheGoTests/AidenRemoteClientTests.swift create mode 100644 ios/AidenOnTheGoTests/AidenRemotePhase0Tests.swift create mode 100644 ios/AidenOnTheGoTests/AidenScheduledTaskTests.swift create mode 100644 ios/AidenOnTheGoTests/AidenWorkspaceEnvironmentTests.swift create mode 100644 ios/CHANGELOG.md create mode 100644 ios/CODE_OF_CONDUCT.md create mode 100644 ios/CONTEXT.md create mode 100644 ios/CONTRACT_TESTS.md create mode 100644 ios/CONTRIBUTING.md create mode 100644 ios/Config/Shared.xcconfig create mode 100644 ios/DEVELOPMENT.md create mode 100644 ios/LICENSE create mode 100644 ios/PROJECT_INTENT.md create mode 100644 ios/PROJECT_SPEC.md create mode 100644 ios/README.md create mode 100644 ios/SECURITY.md create mode 100644 ios/TESTFLIGHT.md create mode 100644 ios/app-store/MOBILE_PRIVACY_SUPPORT_COPY.md create mode 100644 ios/app-store/metadata/app-info/en-US.json create mode 100644 ios/app-store/metadata/version/0.1.0/en-US.json create mode 100644 ios/ci/ExternalTestFlightExportOptions.plist create mode 100644 ios/ci/TestFlightExportOptions.plist create mode 100644 ios/ci/select_testflight_build_number.rb create mode 100644 ios/ci/select_testflight_build_number_test.rb create mode 100755 ios/scripts/check-swift-file-sizes create mode 100644 scripts/check-ios-app-store-metadata.test.mjs create mode 100644 scripts/check-ios-shipping-target.test.mjs create mode 100644 scripts/check-ios-testflight-policy.test.mjs create mode 100644 scripts/ios-asc-monitor.mjs create mode 100644 scripts/ios-asc-monitor.test.mjs create mode 100644 scripts/ios-live-activity-process-proof.mjs create mode 100644 scripts/ios-live-activity-process-proof.test.mjs diff --git a/.github/workflows/aiden-on-the-go-external-testflight.yml b/.github/workflows/aiden-on-the-go-external-testflight.yml new file mode 100644 index 00000000..e82c41d7 --- /dev/null +++ b/.github/workflows/aiden-on-the-go-external-testflight.yml @@ -0,0 +1,193 @@ +# Maintainer-only: uploads a build to the Aiden On The Go app's external TestFlight. +# Requires the repo's APP_STORE_CONNECT_* secrets (App Store Connect API key), +# which forks and contributors do not have. Repository CI lives in .github/workflows/ci.yml. + +# Run manually from the Actions tab (workflow_dispatch). +name: Aiden On The Go External TestFlight + +on: + workflow_dispatch: + inputs: + confirm_external_review: + description: "Type EXTERNAL_REVIEW to confirm this upload can be submitted to Beta App Review." + required: true + type: string + build_number: + description: "Optional CFBundleVersion override. Leave blank to use the next App Store Connect build number." + required: false + type: string + +run-name: "Aiden On The Go external TestFlight from ${{ github.ref_name }}" + +permissions: + contents: read + +concurrency: + group: aiden-on-the-go-external-testflight + cancel-in-progress: false + +jobs: + upload: + name: Archive and Upload + runs-on: macos-26 + environment: aiden-on-the-go-external-testflight + timeout-minutes: 60 + + env: + APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }} + APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} + APP_STORE_CONNECT_PRIVATE_KEY: ${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY }} + EXPORT_OPTIONS_PLIST: ios/ci/ExternalTestFlightExportOptions.plist + PROJECT_PATH: ios/AidenOnTheGo.xcodeproj + SCHEME: AidenOnTheGo + TEAM_ID: 5WP229CBB8 + + steps: + - name: Check manual gate + env: + CONFIRM_EXTERNAL_REVIEW: ${{ inputs.confirm_external_review }} + run: | + set -euo pipefail + + if [[ "${GITHUB_REF_NAME}" != "main" ]]; then + echo "External TestFlight uploads must be run from main. Current ref: ${GITHUB_REF_NAME}" >&2 + exit 1 + fi + + if [[ "${CONFIRM_EXTERNAL_REVIEW}" != "EXTERNAL_REVIEW" ]]; then + echo "Re-run with confirm_external_review set to EXTERNAL_REVIEW." >&2 + exit 1 + fi + + - name: Check required secrets + run: | + set -euo pipefail + + missing=0 + for name in APP_STORE_CONNECT_KEY_ID APP_STORE_CONNECT_ISSUER_ID APP_STORE_CONNECT_PRIVATE_KEY; do + if [[ -z "${!name}" ]]; then + echo "Missing required secret: ${name}" >&2 + missing=1 + fi + done + + exit "${missing}" + + - name: Check out repository + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 + + - name: Set output paths + run: | + set -euo pipefail + + { + echo "ARCHIVE_PATH=${RUNNER_TEMP}/AidenOnTheGo.xcarchive" + echo "DERIVED_DATA_PATH=${RUNNER_TEMP}/DerivedData" + echo "EXPORT_PATH=${RUNNER_TEMP}/ExternalTestFlightExport" + } >> "${GITHUB_ENV}" + + - name: Detect marketing version + run: | + set -euo pipefail + + marketing_version="$( + xcodebuild \ + -showBuildSettings \ + -project "${PROJECT_PATH}" \ + -scheme "${SCHEME}" \ + -configuration Release 2>/dev/null | + awk -F'= ' '/MARKETING_VERSION/ { print $2; exit }' + )" + + if [[ -z "${marketing_version}" ]]; then + echo "Could not detect MARKETING_VERSION from Xcode build settings." >&2 + exit 1 + fi + + echo "MARKETING_VERSION=${marketing_version}" >> "${GITHUB_ENV}" + echo "Using marketing version ${marketing_version}" + + - name: Write App Store Connect key + run: | + set -euo pipefail + + key_path="${RUNNER_TEMP}/AuthKey_${APP_STORE_CONNECT_KEY_ID}.p8" + if [[ "${APP_STORE_CONNECT_PRIVATE_KEY}" == *"\\n"* ]]; then + printf "%s" "${APP_STORE_CONNECT_PRIVATE_KEY}" | perl -pe 's/\\n/\n/g' > "${key_path}" + else + printf "%s" "${APP_STORE_CONNECT_PRIVATE_KEY}" > "${key_path}" + fi + chmod 600 "${key_path}" + echo "APP_STORE_CONNECT_KEY_PATH=${key_path}" >> "${GITHUB_ENV}" + + # ENFORCE_OPEN_TRAIN fails this step in seconds — instead of the upload + # step after an expensive archive — when the marketing version's + # pre-release train is already closed by an approved App Store version + # (bitten 2026-06-02 with 1.0 and 2026-08-04 with 1.4). + - name: Check release train and select build number + env: + BUNDLE_ID: sbtbiswas.AidenOnTheGo + REQUESTED_BUILD_NUMBER: ${{ inputs.build_number }} + ENFORCE_OPEN_TRAIN: "1" + run: | + set -euo pipefail + + build_number="$(ruby ios/ci/select_testflight_build_number.rb)" + echo "BUILD_NUMBER=${build_number}" >> "${GITHUB_ENV}" + echo "Using build number ${build_number}" + + - name: Show upload target + run: | + set -euo pipefail + + echo "Uploading external-capable TestFlight build" + echo "Commit SHA: ${GITHUB_SHA}" + echo "Marketing version: ${MARKETING_VERSION}" + echo "Build number: ${BUILD_NUMBER}" + echo "Export options: ${EXPORT_OPTIONS_PLIST}" + echo "This workflow uploads only; App Store Connect external group assignment and Beta App Review submission remain manual." + + - name: Show Xcode version + run: xcodebuild -version + + - name: Resolve Swift packages + run: | + set -euo pipefail + + xcodebuild \ + -resolvePackageDependencies \ + -project "${PROJECT_PATH}" \ + -scheme "${SCHEME}" + + - name: Archive + run: | + set -euo pipefail + + xcodebuild \ + -project "${PROJECT_PATH}" \ + -scheme "${SCHEME}" \ + -configuration Release \ + -destination "generic/platform=iOS" \ + -archivePath "${ARCHIVE_PATH}" \ + -derivedDataPath "${DERIVED_DATA_PATH}" \ + -allowProvisioningUpdates \ + -authenticationKeyPath "${APP_STORE_CONNECT_KEY_PATH}" \ + -authenticationKeyID "${APP_STORE_CONNECT_KEY_ID}" \ + -authenticationKeyIssuerID "${APP_STORE_CONNECT_ISSUER_ID}" \ + DEVELOPMENT_TEAM="${TEAM_ID}" \ + CURRENT_PROJECT_VERSION="${BUILD_NUMBER}" \ + archive + + - name: Upload to App Store Connect + run: | + set -euo pipefail + + xcodebuild \ + -exportArchive \ + -archivePath "${ARCHIVE_PATH}" \ + -exportPath "${EXPORT_PATH}" \ + -exportOptionsPlist "${EXPORT_OPTIONS_PLIST}" \ + -allowProvisioningUpdates \ + -authenticationKeyPath "${APP_STORE_CONNECT_KEY_PATH}" \ + -authenticationKeyID "${APP_STORE_CONNECT_KEY_ID}" \ + -authenticationKeyIssuerID "${APP_STORE_CONNECT_ISSUER_ID}" diff --git a/.github/workflows/aiden-on-the-go-internal-testflight.yml b/.github/workflows/aiden-on-the-go-internal-testflight.yml new file mode 100644 index 00000000..e3a68080 --- /dev/null +++ b/.github/workflows/aiden-on-the-go-internal-testflight.yml @@ -0,0 +1,187 @@ +# Maintainer-only: uploads a build to the Aiden On The Go app's internal TestFlight. +# Requires the repo's APP_STORE_CONNECT_* secrets (App Store Connect API key), +# which forks and contributors do not have. Repository CI lives in .github/workflows/ci.yml. + +# Run manually from the Actions tab (workflow_dispatch). +name: Aiden On The Go Internal TestFlight + +on: + workflow_dispatch: + inputs: + confirm_internal_only: + description: "Type INTERNAL to confirm this upload is for internal TestFlight only." + required: true + type: string + build_number: + description: "Optional CFBundleVersion override. Leave blank to use the next App Store Connect build number." + required: false + type: string + +run-name: "Aiden On The Go internal TestFlight from ${{ github.ref_name }}" + +permissions: + contents: read + +concurrency: + group: aiden-on-the-go-internal-testflight + cancel-in-progress: false + +jobs: + upload: + name: Archive and Upload + runs-on: macos-26 + environment: aiden-on-the-go-internal-testflight + timeout-minutes: 60 + + env: + APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }} + APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} + APP_STORE_CONNECT_PRIVATE_KEY: ${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY }} + EXPORT_OPTIONS_PLIST: ios/ci/TestFlightExportOptions.plist + PROJECT_PATH: ios/AidenOnTheGo.xcodeproj + SCHEME: AidenOnTheGo + TEAM_ID: 5WP229CBB8 + + steps: + - name: Check manual gate + env: + CONFIRM_INTERNAL_ONLY: ${{ inputs.confirm_internal_only }} + run: | + set -euo pipefail + + if [[ "${GITHUB_REF_NAME}" != "main" ]]; then + echo "Internal TestFlight uploads must be run from main. Current ref: ${GITHUB_REF_NAME}" >&2 + exit 1 + fi + + if [[ "${CONFIRM_INTERNAL_ONLY}" != "INTERNAL" ]]; then + echo "Re-run with confirm_internal_only set to INTERNAL." >&2 + exit 1 + fi + + - name: Check required secrets + run: | + set -euo pipefail + + missing=0 + for name in APP_STORE_CONNECT_KEY_ID APP_STORE_CONNECT_ISSUER_ID APP_STORE_CONNECT_PRIVATE_KEY; do + if [[ -z "${!name}" ]]; then + echo "Missing required secret: ${name}" >&2 + missing=1 + fi + done + + exit "${missing}" + + - name: Check out repository + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 + + - name: Set output paths + run: | + set -euo pipefail + + { + echo "ARCHIVE_PATH=${RUNNER_TEMP}/AidenOnTheGo.xcarchive" + echo "DERIVED_DATA_PATH=${RUNNER_TEMP}/DerivedData" + echo "EXPORT_PATH=${RUNNER_TEMP}/TestFlightExport" + } >> "${GITHUB_ENV}" + + - name: Detect marketing version + run: | + set -euo pipefail + + marketing_version="$( + xcodebuild \ + -showBuildSettings \ + -project "${PROJECT_PATH}" \ + -scheme "${SCHEME}" \ + -configuration Release 2>/dev/null | + awk -F'= ' '/MARKETING_VERSION/ { print $2; exit }' + )" + + if [[ -z "${marketing_version}" ]]; then + echo "Could not detect MARKETING_VERSION from Xcode build settings." >&2 + exit 1 + fi + + echo "MARKETING_VERSION=${marketing_version}" >> "${GITHUB_ENV}" + echo "Using marketing version ${marketing_version}" + + - name: Write App Store Connect key + run: | + set -euo pipefail + + key_path="${RUNNER_TEMP}/AuthKey_${APP_STORE_CONNECT_KEY_ID}.p8" + if [[ "${APP_STORE_CONNECT_PRIVATE_KEY}" == *"\\n"* ]]; then + printf "%s" "${APP_STORE_CONNECT_PRIVATE_KEY}" | perl -pe 's/\\n/\n/g' > "${key_path}" + else + printf "%s" "${APP_STORE_CONNECT_PRIVATE_KEY}" > "${key_path}" + fi + chmod 600 "${key_path}" + echo "APP_STORE_CONNECT_KEY_PATH=${key_path}" >> "${GITHUB_ENV}" + + - name: Select build number + env: + BUNDLE_ID: sbtbiswas.AidenOnTheGo + REQUESTED_BUILD_NUMBER: ${{ inputs.build_number }} + run: | + set -euo pipefail + + build_number="$(ruby ios/ci/select_testflight_build_number.rb)" + echo "BUILD_NUMBER=${build_number}" >> "${GITHUB_ENV}" + echo "Using build number ${build_number}" + + - name: Show upload target + run: | + set -euo pipefail + + echo "Uploading internal-only TestFlight build" + echo "Commit SHA: ${GITHUB_SHA}" + echo "Marketing version: ${MARKETING_VERSION}" + echo "Build number: ${BUILD_NUMBER}" + echo "Export options: ${EXPORT_OPTIONS_PLIST}" + + - name: Show Xcode version + run: xcodebuild -version + + - name: Resolve Swift packages + run: | + set -euo pipefail + + xcodebuild \ + -resolvePackageDependencies \ + -project "${PROJECT_PATH}" \ + -scheme "${SCHEME}" + + - name: Archive + run: | + set -euo pipefail + + xcodebuild \ + -project "${PROJECT_PATH}" \ + -scheme "${SCHEME}" \ + -configuration Release \ + -destination "generic/platform=iOS" \ + -archivePath "${ARCHIVE_PATH}" \ + -derivedDataPath "${DERIVED_DATA_PATH}" \ + -allowProvisioningUpdates \ + -authenticationKeyPath "${APP_STORE_CONNECT_KEY_PATH}" \ + -authenticationKeyID "${APP_STORE_CONNECT_KEY_ID}" \ + -authenticationKeyIssuerID "${APP_STORE_CONNECT_ISSUER_ID}" \ + DEVELOPMENT_TEAM="${TEAM_ID}" \ + CURRENT_PROJECT_VERSION="${BUILD_NUMBER}" \ + archive + + - name: Upload to App Store Connect + run: | + set -euo pipefail + + xcodebuild \ + -exportArchive \ + -archivePath "${ARCHIVE_PATH}" \ + -exportPath "${EXPORT_PATH}" \ + -exportOptionsPlist "${EXPORT_OPTIONS_PLIST}" \ + -allowProvisioningUpdates \ + -authenticationKeyPath "${APP_STORE_CONNECT_KEY_PATH}" \ + -authenticationKeyID "${APP_STORE_CONNECT_KEY_ID}" \ + -authenticationKeyIssuerID "${APP_STORE_CONNECT_ISSUER_ID}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efcac39c..45d1b007 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,21 @@ jobs: DEVELOPER_DIR: /Applications/Xcode_26.6.app/Contents/Developer run: npm run test:native + # The owner requires physical-device-only XCTest acceptance. Hosted CI has + # no physical iOS device, so compile the app and test bundle for generic + # hardware here; the complete XCTest suite is recorded from signed devices. + - name: Compile Aiden On The Go tests for generic iOS hardware + env: + DEVELOPER_DIR: /Applications/Xcode_26.6.app/Contents/Developer + run: >- + xcodebuild build-for-testing -quiet + -project ios/AidenOnTheGo.xcodeproj + -scheme AidenOnTheGo + -configuration Debug + -destination 'generic/platform=iOS' + -derivedDataPath '${{ runner.temp }}/AidenOnTheGoDerivedData' + CODE_SIGNING_ALLOWED=NO + - name: Build production bundles run: npm run build diff --git a/.gitignore b/.gitignore index ff13ae20..a0b212e9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,16 @@ build/ release/ native/computer-use-broker/target/ +# Xcode user and build data +DerivedData/ +*.xcuserstate +xcuserdata/ +.build/ + +# App Store Connect CLI credentials (never commit repo-local API/web auth) +.asc/config.json +.asc/sessions/ + # Agent context (auto-generated, not worth versioning) .claude/ .agents/ diff --git a/eslint.config.js b/eslint.config.js index 9747d86d..4c9dec03 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -8,6 +8,16 @@ export default [ ignores: ["build/**", "release/**", "node_modules/**", ".memory/**", ".papercuts/**"], }, js.configs.recommended, + { + files: [ + "scripts/aiden-remote-*.mjs", + "scripts/ios-asc-monitor*.mjs", + "scripts/ios-live-activity-process-proof*.mjs", + ], + languageOptions: { + globals: { ...globals.node }, + }, + }, { files: ["**/*.{ts,tsx}"], languageOptions: { diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 00000000..7dc78a24 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,60 @@ +# Xcode +DerivedData/ +build/ +*.xcworkspace/xcuserdata/ +*.xcodeproj/xcuserdata/ +*.xcuserstate +*.xcscmblueprint + +# Swift Package Manager +.build/ +.swiftpm/ + +# macOS +.DS_Store + +# Local secrets +.env +.env.local +.env.*.local + +# Per-machine signing overrides (see CONTRIBUTING.md) +Config/Local.xcconfig + +# Local reference material +UPDATES.md +.obsidian/ + +# Local agent tooling (per-machine caches, skills, session state) +node_modules/ +.codex-tmp/ +.agents/ +.sc/ +.claude/ +.codex/ +.cursor/ + +# App packaging +*.ipa +*.dSYM.zip +*.dSYM + +# Fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output/ + +# Pi tooling artifacts (review outputs, fix plans, etc.) +.pi/ + +# Editor swap/temp files +*.swp +*.swo +*~ + +# Retired per-session progress log (removed in #224; history lives in git log / merged PRs) +/PROGRESS.md + +# Local session-handoff state (read at session start, overwritten at wrap-up; not tracked) +/CURRENT.md diff --git a/ios/AGENTS.md b/ios/AGENTS.md new file mode 100644 index 00000000..9a1cbd8f --- /dev/null +++ b/ios/AGENTS.md @@ -0,0 +1,53 @@ +# AGENTS.md — Aiden On The Go working agreement + +Aiden On The Go is the native SwiftUI iPhone/iPad companion for the Aiden Agent Electron app in this repository. The desktop app is the execution and persistence authority. The imported Hermex project is an implementation foundation, not a product/API compatibility requirement. + +## Sources of truth + +Read these before changing mobile behavior or contracts: + +1. `../AGENTS.md` for repository-wide requirements. +2. `../docs/plans/aiden-on-the-go-plan.md` for scope, delivery order, and phase gates. +3. `PROJECT_SPEC.md` for the approved mobile product contract. +4. `../docs/aiden-remote-api-v1.md` and `../protocol/aiden-remote/v1/openapi.json` for protocol behavior and exact wire shapes. +5. `../protocol/aiden-remote/v1/fixtures/contract.json` for cross-platform contract fixtures. + +If these disagree, stop at the narrower safety boundary and resolve the documents together. Do not consult or preserve Hermes WebUI endpoints as a fallback. + +## Delivery rules + +- Implement phases 0–12 in order. Do not expose later production endpoints to make an earlier phase easier. +- Add or update focused XCTest and cross-platform contract coverage with every behavioral slice. +- Before advancing a phase, satisfy its evidence gate in `../docs/testing/aiden-on-the-go/`, run the applicable full suites, and clear all P0/P1 findings in a direct source-and-test review. The owner currently prohibits subagent/reviewer delegation; do not use it unless that direction is explicitly reversed. +- Preserve unrelated user changes and imported MIT attribution. +- New third-party dependencies require explicit approval. Prefer Apple frameworks and the dependencies already locked by the imported project. +- Use terminal-based `xcodebuild`/`xcrun` verification against an explicitly selected physical device. The owner currently prohibits simulator use. A `CODE_SIGNING_ALLOWED=NO` build is compile/test-only and is not valid manual Keychain or entitlements evidence. + +## Protocol and security rules + +- Never invent endpoints or JSON shapes in Swift. Change the normative Aiden contract, shared fixture, TypeScript tests, and Swift tests together. +- Decode additive response fields tolerantly, but fail closed when required identity, authentication, capability, ownership, revision, sequence, expiry, or mutation-precondition fields are missing or invalid. +- Do not send or persist Aiden credentials in URLs, App Group data, App Intents, logs, Live Activities, fixtures, or source control. Device credentials belong in Keychain. +- LAN production traffic uses hostname/certificate validation plus the QR-pinned P-256 SPKI SHA-256 fingerprint. A matching pin alone never bypasses normal trust evaluation. +- The client never sends a free-form Mac path. Workspace-browser selections and workspace-file identities use separate opaque, server-issued capabilities. +- TCP/SSE loss never retries a turn creation or abandons server-owned work. Reconcile by stable IDs and sequence. +- Remote controls cannot mint Assistant/unattended authority, enable Computer Use, add a terminal, or widen a workspace's stored permission/tool contract. + +## Product boundaries + +- Retain native Hermex UI/behavior only where `PROJECT_SPEC.md` maps it to Aiden 1:1. +- Remove Kanban and every Hermes-only panel, profile/personality concept, server voice upload path, and Cloudflare-specific onboarding during the designated cleanup phases. +- Workspace permission belongs in Workspace Settings opened from the conversation-toolbar ellipsis, never in the composer. +- App Intents are App-Group-cache-only navigation. Live Activities are bounded last-known state without cloud push. Voice is on-device dictation/read-aloud only. +- Use iPhone navigation stacks and an adaptive iPad `NavigationSplitView`; do not introduce a custom UI system for branding. + +## Approved Apple identity + +- Automatic signing team: `5WP229CBB8` +- Main bundle: `sbtbiswas.AidenOnTheGo` +- Live Activity widget: `sbtbiswas.AidenOnTheGo.LiveActivityWidget` +- App Group: `group.sbtbiswas.AidenOnTheGo` +- Keychain service: `sbtbiswas.AidenOnTheGo.pairing` +- URL scheme: `aiden-otg` + +The shipping project, target, and scheme are `AidenOnTheGo`. Old Hermes project, target, scheme, bundle, service, and asset identifiers are not approved and must not be restored. diff --git a/ios/APP_STORE_METADATA.md b/ios/APP_STORE_METADATA.md new file mode 100644 index 00000000..b79d8f24 --- /dev/null +++ b/ios/APP_STORE_METADATA.md @@ -0,0 +1,116 @@ +# Aiden On The Go — App Store metadata draft + +Status: Release draft. Public identity, category, age-rating answers, privacy-label answers, and screenshot specifications are resolved from the shipping app and current Apple definitions. App Store Connect publication and release-specific owner decisions remain open. + +The executable canonical ASC localization draft lives in `app-store/metadata/`. The first internal TestFlight train is `0.1.0`; build `1` was rejected during upload for an alpha-bearing App Store icon, builds `2`–`5` progressively corrected the icon, compact navigation, native shell, and cold home loading, and build `6` is the current internal candidate with the final scratch/navigation/activity-branding repairs. The version directory and Xcode targets match that decision. Offline validation is safe, but no metadata may be applied until the public mobile privacy/support copy and owner decisions below are complete. + +## Resolved public identity + +- Developer/team name: `Sambit Biswas` +- Apple team: `5WP229CBB8` +- Bundle ID: `sbtbiswas.AidenOnTheGo` +- App Store SKU: `aiden-on-the-go-ios` +- Marketing URL: `https://chatwithaiden.com/` +- Support URL: `https://chatwithaiden.com/` +- Privacy-policy URL: `https://chatwithaiden.com/privacy` +- Feedback/review email: `hey@sambitbiswas.com` +- Copyright: `2026 Sambit Biswas` + +The team/developer identity and feedback email come from the owner's shipped Contact Sheet project and installed Apple profile. Aiden URLs come from the live Aiden website; Contact Sheet's product-specific domain is not reused. The Contact Sheet listing uses its product site for both marketing and support, so Aiden follows the same pattern. Before submission, make the resolved feedback address or another working support contact visible on the Aiden site. The published policy is a valid public URL, but its copy currently describes the macOS app only. `app-store/MOBILE_PRIVACY_SUPPORT_COPY.md` contains ready-to-review replacement/addition copy covering Aiden On The Go's device-local cache, pairing credential, Local Network/Tailscale transport, attachments/providers, permissions, dictation, App Intents, external media, and Live Activity behavior. It is not considered published until the live site is updated and rechecked. + +## Product copy + +- Name: `Aiden On The Go` +- Subtitle: `Your Aiden Agent, anywhere` +- Primary category: Developer Tools +- Secondary category: Productivity +- Keywords draft: `AI assistant,agent,developer,Git,workspace,chat,automation,remote,Tailscale,Swift` + +Developer Tools is the closest current Apple category: Apple describes it as apps for app development, management, coding, workflow management, and code editing. Productivity is the complementary secondary category. Category selection remains an App Store Connect property for this iOS/iPadOS app; do not add the macOS-only Xcode category key just to duplicate it. + +Description draft: + +> Aiden On The Go is the native iPhone and iPad companion for Aiden Agent on your Mac. Pair directly with a Mac you control over your local network or Tailscale, then continue Aiden chats and manage workspaces from your mobile device. +> +> Review conversations, stream responses, handle approval requests, inspect workspace files, work with supported Git flows, and manage scheduled tasks. App Intents provide quick navigation, Live Activities show bounded run status, and optional voice dictation and read-aloud stay on device. +> +> Your Mac remains the execution authority. Remote Access is off by default, each mobile device uses a revocable credential, and provider credentials remain on the Mac. + +Review-notes draft: + +> Aiden On The Go requires the companion Aiden Agent desktop app. In Aiden Agent, open Settings → Remote Access, enable the listener, and open a short-lived pairing session. On the iPhone or iPad, scan the pairing QR code or use the approved manual connection flow. The reviewer must be supplied with a reachable review Mac and any required setup instructions; no production credential is embedded in the app. + +## Age-rating questionnaire draft + +Use the current questionnaire for OS version 26 and later: + +| Questionnaire item | Draft answer | Shipping-app basis | +| --- | --- | --- | +| Parental controls | No | Aiden has no parental-control surface. | +| Age assurance | No | Aiden does not request or infer age. | +| Unrestricted web access | No | The app has no embedded browser or free webpage navigation. Opening a link in the system browser is not in-app unrestricted browsing. | +| User-generated content | No | Chats and files stay private to the paired installation; the app does not broadly distribute user-created content. | +| Social media / disabled under 13 | No / No | There is no feed, discovery, sharing, liking, reposting, or public amplification. | +| Messaging and chat | No | Apple's capability is user-to-user communication. Aiden is private user-to-agent interaction and has no direct/group user messaging or public posting. | +| Advertising | No | No ads or paid promotional placements ship. | +| Mature themes, medical/wellness, sexuality/nudity, violence, and chance-based activities | None for every frequency item; Gambling and Loot Boxes: No | None is authored or promoted as app content. Connected AI output is open-ended and remains subject to provider behavior and user prompts. | +| Made for Kids | Not applicable | The product and public policy are not directed to children. | +| Override | 13+ | The public privacy policy says Aiden is not directed to children under 13. A conservative 13+ override aligns the listing with that product policy even if the questionnaire calculates a lower rating. | +| Age-suitability URL | Leave blank | No dedicated age-suitability page currently exists. | + +Before publishing, compare these answers with the exact final questionnaire wording and distribution candidate. If the release adds an embedded browser, user-to-user communication, public sharing, a hosted social surface, or intentionally supplied mature content, update the answers rather than relying on this draft. + +Current Apple references: + +- `https://developer.apple.com/help/app-store-connect/reference/app-information/age-ratings-values-and-definitions/` +- `https://developer.apple.com/help/app-store-connect/manage-app-information/set-an-app-age-rating/` + +## App Privacy questionnaire draft + +- Privacy Policy URL: `https://chatwithaiden.com/privacy` +- User Privacy Choices URL: leave blank; Aiden has no developer-held account or cloud data for a user to manage. +- Tracking: No. +- “Do you or your third-party partners collect data from this app?”: **No, we do not collect data from this app.** + +Evidence for that answer in the current distribution candidate: + +- The app contains no analytics, advertising, crash-reporting, account, or Aiden-hosted relay SDK. +- Pairing credentials and custom headers stay in Keychain. Cached chats/settings stay on the user's device; authoritative chats/files remain on the Mac the user pairs. +- QR camera frames are processed for pairing and are not uploaded to Aiden's developer. +- Dictation and read-aloud use Apple system frameworks locally; Aiden does not operate a speech collection endpoint. +- Photos/files selected by the user are sent directly to their paired Mac and may then be sent to model providers the user configured on that Mac. Aiden's developer cannot access them. Provider processing remains governed by each selected provider and should be described in the public policy. +- Local Network or Tailscale traffic goes directly to the paired installation. Aiden does not run a central account, synchronization service, analytics endpoint, or proxy. +- Live Activity state is device-local and response excerpts are off by default. +- External transcript media can contact the media host without forwarding Aiden credentials; the public policy should disclose that a remote host can observe an ordinary network request when its media is displayed. +- `PrivacyInfo.xcprivacy` declares no tracking, no collected data types, and only the `UserDefaults` required-reason API (`CA92.1`). + +Apple defines collection for the privacy label as off-device transmission that lets the developer or a third-party partner access the data for longer than needed to service the request in real time. Reconfirm the **No** answer before publishing: it stops being accurate if the release adds developer-accessible telemetry, crash reports, accounts, hosted relay/proxy storage, or another partner that retains app data. The owner must also confirm that the final public policy accurately covers mobile behavior and the configured AI-provider path. + +Current Apple references: + +- `https://developer.apple.com/app-store/app-privacy-details/` +- `https://developer.apple.com/help/app-store-connect/manage-app-information/manage-app-privacy/` + +## Screenshot delivery specification + +The final distribution candidate needs one to ten opaque `.png`, `.jpg`, or `.jpeg` screenshots for each required family. Use real-device captures and do not use a simulator for this project. + +- iPhone: capture the connected iPhone 13 Pro at `1170 × 2532` portrait or `2532 × 1170` landscape. App Store Connect accepts that 6.1-inch size, but a final 6.9-inch capture is preferred when the owner's iPhone 16 Pro Max is available because Apple can scale the highest-resolution set down. +- iPad: a physical iPad capture is mandatory because the app supports iPad. Current 13-inch accepted portrait sizes are `2064 × 2752` or `2048 × 2732`; landscape reverses those dimensions. +- Do not include alpha/transparency. Capture at least pairing/installation selection, workspace/chat navigation, an active transcript with tool status, files/Git, and Scheduled Tasks while excluding real secrets, private paths, personal chats, and live pairing credentials. + +Current Apple reference: `https://developer.apple.com/help/app-store-connect/reference/app-information/screenshot-specifications/`. + +## Owner decisions still required + +- Internal TestFlight starts at marketing version `0.1.0`; build `1` was rejected before processing and build `6` is the current internal candidate. Advance the build number for later uploads in the same train and reserve `1.0.0` for the first public release decision. +- Enter Developer Tools / Productivity in App Store Connect, or deliberately override this documented recommendation. +- Verify and publish the drafted 13+ age-rating answers in the exact current questionnaire. +- Verify and publish the drafted “No data collected” App Privacy answer against the final distributed build and operational services. +- Owner/legal-review and publish `app-store/MOBILE_PRIVACY_SUPPORT_COPY.md` at the resolved privacy URL. +- Make the prepared working support contact visible at the resolved support URL. +- Required physical-iPhone and physical-iPad screenshots captured from the final distribution candidate at accepted dimensions. +- App Review phone number, notes, and a reachable companion-Mac review environment. The name/email are resolved above. +- Availability, price, territories, and release mode. + +Do not replace unresolved values with placeholders in App Store Connect. diff --git a/ios/ASC_CLI.md b/ios/ASC_CLI.md new file mode 100644 index 00000000..c80987b3 --- /dev/null +++ b/ios/ASC_CLI.md @@ -0,0 +1,167 @@ +# App Store Connect CLI runbook + +`asc` is the owner operations and read-only monitoring client for Aiden On The Go. The Hermex-derived GitHub upload workflows remain self-contained and do not install a mutable third-party CLI during an archive. Use `asc` after upload for App Store Connect inspection, deterministic metadata plans, validation, TestFlight operations, and Codex monitoring automations. + +## Local tool and safety policy + +- Audited local binary: Rork `asc` `3.4.0`, installed from the Homebrew stable formula on 2026-08-19. +- Use the stable command contract shown by `asc --help`; do not script experimental screenshot capture/framing commands. +- `asc` command telemetry is enabled by default on this Mac. Aiden operations must set `ASC_TELEMETRY_DISABLED=1` per invocation. The user's global CLI preference was not changed. +- Use `--strict-auth` for API commands so a profile cannot silently mix with partial environment credentials. +- The owner authorizes the existing `Parsely ASC` Keychain profile for Aiden operations within the resources it can actually access. Name it explicitly with `--profile "Parsely ASC"`; never rely on ambient/default credential selection or treat its zero-app result as authority to duplicate the existing public Aiden beta. +- Prefer JSON output for automation and exact App Store Connect IDs after resolving them read-only. +- Credentials belong in macOS Keychain or protected CI secrets. Never create or commit `.asc/config.json`, cached web sessions, `.p8` files, JWTs, passwords, or two-factor codes. +- Run read-only discovery first. App creation, bundle registration, capability edits, profile/certificate creation, metadata application, screenshot upload, tester changes, review submission, pricing, and availability changes require explicit owner authorization and an exact reviewed target. + +## Read-only audit — 2026-08-19 + +The installed CLI has one default System Keychain profile named `Parsely ASC`. The owner authorizes using it for Aiden resources within its visible scope. A cached Apple web session is not authenticated. + +With telemetry disabled and strict authentication: + +- `asc apps list --bundle-id sbtbiswas.AidenOnTheGo` returns zero accessible App Store Connect app records. +- Aiden's live website links to the active public TestFlight invitation `https://testflight.apple.com/join/s3T4T8y3`. On 2026-08-19, Apple's invitation page identified that beta as **Aiden - Quick AI**, available on iOS, with test instructions for the existing macOS product. The page does not expose the app's bundle ID or App Store Connect numeric ID. +- The Developer Portal contains the universal bundle ID `sbtbiswas.AidenOnTheGo` under team `5WP229CBB8`, with App Groups enabled. +- The Developer Portal result does not contain `sbtbiswas.AidenOnTheGo.LiveActivityWidget`. +- No provisioning profile is linked to the Aiden app identifier through this API profile. The accessible team has one iOS Distribution certificate and three unrelated iOS App Store profiles; none is named for Aiden. +- The local keychain still has no Apple Distribution private-key identity, matching the earlier Xcode signing audit. + +These findings prove only what the active API key can access. The public Aiden beta is positive evidence that an Aiden App Store Connect record exists outside this profile's visible scope, although it does not prove whether that record owns `sbtbiswas.AidenOnTheGo`. Before mutation, switch to a dedicated Aiden/release profile and resolve that existing record's numeric ID, platforms, and bundle ID. Zero accessible app records must not be treated as permission to auto-create one. + +Reproduce the non-mutating checks without printing unrelated account data and always select the owner-authorized profile explicitly: + +```sh +ASC_TELEMETRY_DISABLED=1 asc --strict-auth --profile "Parsely ASC" apps list \ + --bundle-id sbtbiswas.AidenOnTheGo \ + --output json --pretty +ASC_TELEMETRY_DISABLED=1 asc --strict-auth --profile "Parsely ASC" bundle-ids list \ + --paginate --output json +ASC_TELEMETRY_DISABLED=1 asc --strict-auth --profile "Parsely ASC" certificates list \ + --certificate-type IOS_DISTRIBUTION,DISTRIBUTION \ + --paginate --output json +ASC_TELEMETRY_DISABLED=1 asc --strict-auth --profile "Parsely ASC" profiles list \ + --profile-type IOS_APP_STORE \ + --paginate --output json +``` + +Filter the bundle/profile responses locally before saving evidence so unrelated apps, identifiers, and signing metadata are not copied into this repository. + +## Resolved internal-TestFlight state — 2026-08-19 + +The earlier zero-record audit is retained above as historical evidence. The owner subsequently created and authorized the distinct **Aiden On The Go** record and provisioned distribution signing: + +- App Store Connect App ID: `6803233275`; bundle ID: `sbtbiswas.AidenOnTheGo`; marketing version: `0.1.0`. +- Live Activity widget bundle ID: `sbtbiswas.AidenOnTheGo.LiveActivityWidget`. +- Internal group: `Internal Testers` (`3f90ffa7-29bb-429e-80a4-88422eb85b6d`). +- Build `1` upload was rejected with `ITMS-90717` because its App Store icon had transparency. +- Build `2` uses the exact opaque RayChat `.icon` package, is Apple Distribution-signed with `get-task-allow=false`, processed as `VALID`, and is `IN_BETA_TESTING` for the internal group. +- Build `3` fixes compact iPhone workspace navigation and preserves the same internal-only distribution contract. +- Build `4` carries the Aiden-native shell refresh but was superseded after a cold-connect home-load edge was found locally. +- Build `5` retries home loading on the connected-state transition; build `6` adds the final scratch/navigation/Live Activity branding repairs. +- Build `7` adds Thinking Orbs, the existing/new/scratch New Agent choices, hopeful server-confirmed mutations, relative-time and landing cleanup, the theme-continuous Liquid Glass composer, explicit keyboard dismissal, and nested per-model thinking-level menus. It is the current `VALID` / `IN_BETA_TESTING` candidate and remains internal-only. +- Current exact build resource ID: `717b6381-4dec-4cce-85d1-72b503c28590` (build `6`: `aa994233-1bf3-4482-86f5-b8b0356eee25`; build `5`: `6173d5e2-0e58-4d0a-92fa-fc804fc82c37`; build `4`: `ee5b7c23-14d6-44e7-a4ba-6a5003018758`; build `3`: `e5f0ae7e-35aa-451e-be87-bc039885b2de`; build `2`: `721aeb9d-2b33-4729-8d10-5bc1783abbef`). + +The account holder is assigned to the internal group. The terminal valid state does not need a processing automation; future uploads may use the checked-in read-only monitor with the exact new build ID. + +## Owner-authorized reconciliation and bootstrap + +The remaining bootstrap has two distinct authorities: + +1. An App Store Connect API key with access to team `5WP229CBB8` can register the missing widget identifier and manage public-API signing/capability resources. +2. The correct Aiden App Store Connect profile or authenticated Apple web session is required to inspect the existing **Aiden - Quick AI** record and decide whether Aiden On The Go belongs in that record or a distinct one. + +First resolve the existing record read-only. Do not join the public beta as an operational shortcut, and do not create a second record merely because the current Parsely-named API key returns zero apps. + +Only after that reconciliation, execute the missing-widget mutation if the owner authorizes it: + +```sh +ASC_TELEMETRY_DISABLED=1 asc --strict-auth --profile "Parsely ASC" bundle-ids create \ + --identifier sbtbiswas.AidenOnTheGo.LiveActivityWidget \ + --name "Aiden On The Go Live Activity Widget" \ + --platform IOS +``` + +If the owner confirms that Aiden On The Go requires a distinct record, initial app-record creation is a web-session workflow in `asc 3.4.0`; `asc apps create` was removed. It requires an authenticated Apple web session and may require an interactive password and two-factor approval. Do not run this shape until the owner also confirms the first marketing version and authorizes the irreversible record: + +```sh +ASC_TELEMETRY_DISABLED=1 asc web apps create \ + --name "Aiden On The Go" \ + --bundle-id sbtbiswas.AidenOnTheGo \ + --sku aiden-on-the-go-ios \ + --platform IOS \ + --primary-locale en-US \ + --version "" +``` + +After creation, record the numeric App ID and App Info ID in protected owner configuration, not source defaults that could target the wrong app. + +## Metadata and validation flow + +Use dry-run/read-only commands before every apply: + +```sh +ASC_TELEMETRY_DISABLED=1 asc metadata validate \ + --dir ios/app-store/metadata \ + --output table + +ASC_TELEMETRY_DISABLED=1 asc --strict-auth --profile "Parsely ASC" app-setup categories set \ + --app "$ASC_APP_ID" \ + --primary DEVELOPER_TOOLS \ + --secondary PRODUCTIVITY + +ASC_TELEMETRY_DISABLED=1 asc --strict-auth --profile "Parsely ASC" age-rating view \ + --app "$ASC_APP_ID" --output json --pretty + +ASC_TELEMETRY_DISABLED=1 asc --strict-auth --profile "Parsely ASC" metadata apply \ + --app "$ASC_APP_ID" \ + --version "$AIDEN_IOS_VERSION" \ + --platform IOS \ + --dir ios/app-store/metadata \ + --dry-run + +ASC_TELEMETRY_DISABLED=1 asc --strict-auth --profile "Parsely ASC" validate \ + --app "$ASC_APP_ID" \ + --version "$AIDEN_IOS_VERSION" \ + --platform IOS \ + --output markdown +``` + +The checked-in canonical localization files contain only fields resolved in `APP_STORE_METADATA.md`; optional `promotionalText`, `whatsNew`, `privacyChoicesUrl`, and `privacyPolicyText` keys are intentionally omitted rather than set to empty strings. The version path matches the owner-approved first internal TestFlight train, `0.1.0`; build `1` was rejected for an alpha-bearing App Store icon, build `2` corrected the icon, and build `3` is the current compact-navigation candidate. The category command mutates immediately, so run it only after the resolved IDs and category decision in `APP_STORE_METADATA.md` are approved. Age-rating edits use `--all-none --age-rating-override-v2 THIRTEEN_PLUS` plus the exact reviewed flags. App Privacy is an Apple web-session surface in this CLI: use `asc web privacy pull` and `plan` first; `apply` and `publish` remain separate owner-confirmed mutations. + +`asc screenshots capture` is not an approved iOS path for this project because its local iOS capture workflow targets a simulator. Capture on physical devices with `xcrun devicectl device capture screenshot`, remove alpha by exporting an opaque JPEG when necessary, then use `asc screenshots validate` before any upload. A provisional physical iPhone 13 Pro capture at `1170 × 2532` converted to opaque JPEG and passed `asc screenshots validate --device-type APP_IPHONE_58`; it is evidence of the pipeline, not a final release asset. + +## Codex automation policy + +Create automations only after `ASC_APP_ID` resolves to the reviewed Aiden record and at least one build exists. Every automation must be read-only, set `ASC_TELEMETRY_DISABLED=1`, use `--strict-auth`, include the exact App ID/platform, and report state changes rather than mutating App Store Connect. + +The registered `npm run ios:asc-monitor` wrapper enforces those rules. It requires an explicit named Keychain profile and therefore cannot silently fall back to ambient credentials. The owner-authorized `Parsely ASC` profile may be supplied once the exact Aiden App ID/build are known. App IDs must be numeric App Store Connect IDs; processing and TestFlight modes additionally require the exact build resource ID. Processing executes `builds info --build-id` instead of selecting the latest build. TestFlight output contains only counts, newest timestamps, and deterministic fingerprints—never tester identity, feedback text, screenshot URLs, or crash content. + +After the correct IDs and profile exist, use these command shapes in Codex automations: + +```sh +npm run ios:asc-monitor -- \ + --mode processing \ + --profile "" \ + --app-id "" \ + --build-id "" + +npm run ios:asc-monitor -- \ + --mode review \ + --profile "" \ + --app-id "" \ + --version "" + +npm run ios:asc-monitor -- \ + --mode testflight \ + --profile "" \ + --app-id "" \ + --build-id "" +``` + +Useful lifecycle cadences: + +- Every 15 minutes after an upload: processing mode until the exact build reaches `VALID`, `FAILED`, or `INVALID`. +- Hourly while a submission is active: review mode, notifying only on state changes or blockers. +- Daily during internal/external testing: TestFlight mode for the exact app/build, notifying when counts, newest timestamps, or fingerprints change; inspect sensitive content only interactively in App Store Connect. + +Do not create a placeholder automation for build `2`: it is already terminal `VALID` and `IN_BETA_TESTING`. For a future upload, create a processing monitor only after that upload has an exact build resource ID, then stop/archive it when the watched state is terminal. diff --git a/ios/AidenLiveActivityWidget/AgentRunLiveActivityWidget.swift b/ios/AidenLiveActivityWidget/AgentRunLiveActivityWidget.swift new file mode 100644 index 00000000..40351580 --- /dev/null +++ b/ios/AidenLiveActivityWidget/AgentRunLiveActivityWidget.swift @@ -0,0 +1,430 @@ +import ActivityKit +import SwiftUI +import WidgetKit + +@main +struct AidenLiveActivityWidgetBundle: WidgetBundle { + var body: some Widget { + AgentRunLiveActivityWidget() + } +} + +struct AgentRunLiveActivityWidget: Widget { + var body: some WidgetConfiguration { + ActivityConfiguration(for: AgentRunActivityAttributes.self) { context in + AgentRunLockScreenView(context: context) + .activityBackgroundTint(AgentRunLiveActivityTheme.background) + .activitySystemActionForegroundColor(AgentRunLiveActivityTheme.primaryText) + .widgetURL(AidenDeepLink.chatURL( + instanceId: context.attributes.instanceID, + chatId: context.state.sessionID + )) + } dynamicIsland: { context in + DynamicIsland { + DynamicIslandExpandedRegion(.leading) { + AgentRunIslandBadge(status: context.state.status) + .padding(.leading, 18) + } + + DynamicIslandExpandedRegion(.trailing) { + AgentRunIslandStatusView(state: context.state) + .padding(.trailing, 18) + } + + DynamicIslandExpandedRegion(.bottom) { + AgentRunExpandedIslandBottomView(state: context.state) + } + } compactLeading: { + AgentRunIslandCompactMark(status: context.state.status) + } compactTrailing: { + Text(context.state.status.compactTitle) + .font(.caption2.weight(.semibold)) + .foregroundStyle(AgentRunStatusStyle.color(for: context.state.status, isStale: context.state.isStale)) + .minimumScaleFactor(0.72) + .lineLimit(1) + } minimal: { + AgentRunIslandCompactMark(status: context.state.status) + } + .widgetURL(AidenDeepLink.chatURL( + instanceId: context.attributes.instanceID, + chatId: context.state.sessionID + )) + .keylineTint(AgentRunStatusStyle.color(for: context.state.status, isStale: context.state.isStale)) + } + } +} + +private struct AgentRunExpandedIslandBottomView: View { + let state: AgentRunActivityAttributes.ContentState + + var body: some View { + VStack(alignment: .leading, spacing: 4) { + AgentRunProgressRail(status: state.status) + + if !state.responseExcerpt.isEmpty { + Text(state.responseExcerpt) + .font(.caption2) + .foregroundStyle(AgentRunLiveActivityTheme.secondaryText) + .lineLimit(2) + .truncationMode(.tail) + } else { + Text(state.currentActivity) + .font(.caption2.weight(.semibold)) + .foregroundStyle(AgentRunLiveActivityTheme.secondaryText) + .lineLimit(1) + .truncationMode(.tail) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.top, 2) + .padding(.horizontal, 12) + .padding(.bottom, 4) + } +} + +private struct AgentRunLockScreenView: View { + let context: ActivityViewContext + + var body: some View { + VStack(alignment: .leading, spacing: 10) { + header + activityProgressRow(progressWidth: 112) + transcriptPanel + } + .frame(maxWidth: .infinity, alignment: .topLeading) + .padding(.horizontal, 16) + .padding(.vertical, 14) + } + + private var activityText: String { + if context.state.isStale { + return "Latest status shown" + } + + if let errorSummary = context.state.errorSummary, !errorSummary.isEmpty { + return errorSummary + } + + return context.state.currentActivity + } + + private var header: some View { + HStack(alignment: .center, spacing: 10) { + AgentRunStatusDot(status: context.state.status, isStale: context.state.isStale, size: 34) + + VStack(alignment: .leading, spacing: 2) { + Text("Aiden") + .font(.caption2.weight(.bold)) + .foregroundStyle(AgentRunLiveActivityTheme.secondaryText) + .textCase(.uppercase) + + Text(context.state.sessionTitle) + .font(.headline.weight(.semibold)) + .foregroundStyle(AgentRunLiveActivityTheme.primaryText) + .lineLimit(1) + .truncationMode(.tail) + .minimumScaleFactor(0.82) + } + .layoutPriority(1) + + Spacer(minLength: 8) + + AgentRunTimerPill(state: context.state) + } + } + + private func activityProgressRow(progressWidth: CGFloat) -> some View { + HStack(alignment: .center, spacing: 8) { + Text(activityText) + .font(.subheadline.weight(.semibold)) + .foregroundStyle(AgentRunLiveActivityTheme.primaryText) + .lineLimit(1) + .minimumScaleFactor(0.82) + .layoutPriority(1) + + Spacer(minLength: 8) + + AgentRunProgressRail(status: context.state.status) + .frame(width: progressWidth) + } + } + + private var transcriptPanel: some View { + VStack(alignment: .leading, spacing: 0) { + Text(excerptText) + .font(.caption) + .foregroundStyle(AgentRunLiveActivityTheme.secondaryText) + .lineLimit(2) + .fixedSize(horizontal: false, vertical: true) + .frame(maxWidth: .infinity, alignment: .topLeading) + } + .padding(.horizontal, 12) + .padding(.vertical, 9) + .background(AgentRunStatusStyle.color(for: context.state.status, isStale: context.state.isStale).opacity(0.14), in: RoundedRectangle(cornerRadius: 8, style: .continuous)) + .overlay( + RoundedRectangle(cornerRadius: 8, style: .continuous) + .stroke(AgentRunLiveActivityTheme.stroke, lineWidth: 1) + ) + } + + private var excerptText: String { + if !context.state.responseExcerpt.isEmpty { + return context.state.responseExcerpt + } + + if context.state.isFinal { + return "Response is ready to review." + } + + return "Waiting for the next agent update." + } +} + +private struct AgentRunIslandBadge: View { + let status: AgentRunActivityStatus + + var body: some View { + HStack(spacing: 6) { + AgentRunStatusDot(status: status, isStale: false) + Text("Aiden") + .font(.caption.weight(.semibold)) + .foregroundStyle(AgentRunLiveActivityTheme.primaryText) + .lineLimit(1) + } + } +} + +private struct AgentRunIslandStatusView: View { + let state: AgentRunActivityAttributes.ContentState + + var body: some View { + VStack(alignment: .trailing, spacing: 3) { + Text(state.status.title) + .font(.caption.weight(.semibold)) + .foregroundStyle(AgentRunStatusStyle.color(for: state.status, isStale: state.isStale)) + .lineLimit(1) + + if state.isFinal { + Text("Ready") + .font(.caption2.weight(.medium)) + .foregroundStyle(AgentRunLiveActivityTheme.secondaryText) + } else if state.isStale { + Text("Latest") + .font(.caption2.weight(.medium)) + .foregroundStyle(AgentRunLiveActivityTheme.secondaryText) + } else { + HStack(spacing: 3) { + Circle() + .fill(AgentRunLiveActivityTheme.liveDot) + .frame(width: 4, height: 4) + AgentRunElapsedTimerText(startedAt: state.startedAt, alignment: .trailing) + } + .font(.caption2.weight(.medium)) + .foregroundStyle(AgentRunLiveActivityTheme.secondaryText) + } + } + } +} + +private struct AgentRunIslandCompactMark: View { + let status: AgentRunActivityStatus + + var body: some View { + ZStack { + Circle() + .fill(AgentRunStatusStyle.color(for: status, isStale: false).opacity(0.25)) + + AgentRunStatusMark(status: status, size: 10) + } + .frame(width: 22, height: 22) + } +} + +private struct AgentRunStatusDot: View { + let status: AgentRunActivityStatus + let isStale: Bool + var size: CGFloat = 22 + + var body: some View { + AgentRunStatusMark(status: status, size: size * 0.46) + .foregroundStyle(color) + .frame(width: size, height: size) + .background(color.opacity(0.18), in: Circle()) + .overlay(Circle().stroke(color.opacity(0.36), lineWidth: 1)) + } + + var color: Color { + AgentRunStatusStyle.color(for: status, isStale: isStale) + } + +} + +private struct AgentRunStatusMark: View { + let status: AgentRunActivityStatus + let size: CGFloat + + @ViewBuilder + var body: some View { + if status == .starting || status == .thinking { + Image("aiden-sidebar-logo") + .renderingMode(.template) + .resizable() + .scaledToFit() + .frame(width: size, height: size) + } else { + Image(systemName: AgentRunStatusStyle.symbolName(for: status)) + .font(.system(size: size, weight: .bold)) + } + } +} + +private struct AgentRunProgressRail: View { + let status: AgentRunActivityStatus + + var body: some View { + GeometryReader { geometry in + ZStack(alignment: .leading) { + Capsule(style: .continuous) + .fill(AgentRunLiveActivityTheme.railBackground) + + Capsule(style: .continuous) + .fill(AgentRunStatusStyle.color(for: status, isStale: false)) + .frame(width: max(12, geometry.size.width * progressFraction)) + } + } + .frame(height: 6) + } + + private var progressFraction: CGFloat { + switch status { + case .starting: + 0.14 + case .thinking: + 0.3 + case .usingTool, .searchingFiles, .readingFiles, .runningCommand: + 0.52 + case .waitingForApproval: + 0.62 + case .responding: + 0.78 + case .complete: + 1 + case .failed, .cancelled: + 1 + } + } +} + +private struct AgentRunTimerPill: View { + let state: AgentRunActivityAttributes.ContentState + + var body: some View { + HStack(spacing: 4) { + Circle() + .fill(state.isFinal ? AgentRunStatusStyle.color(for: state.status, isStale: state.isStale) : AgentRunLiveActivityTheme.liveDot) + .frame(width: 5, height: 5) + + if state.isFinal { + Text("Done") + } else { + AgentRunElapsedTimerText(startedAt: state.startedAt, alignment: .center) + } + } + .font(.caption2.weight(.semibold)) + .foregroundStyle(AgentRunLiveActivityTheme.secondaryText) + .lineLimit(1) + .padding(.horizontal, 8) + .padding(.vertical, 5) + .frame(width: 62, alignment: .center) + .background(AgentRunLiveActivityTheme.pillBackground, in: Capsule(style: .continuous)) + .overlay(Capsule(style: .continuous).stroke(AgentRunLiveActivityTheme.stroke, lineWidth: 1)) + } +} + +private struct AgentRunElapsedTimerText: View { + let startedAt: Date + var alignment: TextAlignment = .trailing + + // `Text(timerInterval:)` reserves layout width for the largest value its range + // could ever show, then draws the shorter live value leading-aligned inside that + // leftover slack — which left-shifted the digits in the Dynamic Island and the + // Lock Screen pill (#247). Bounding the range keeps an MM:SS-sized box, and an + // explicit `multilineTextAlignment` pins the digits to the edge each call site + // wants (trailing under the Dynamic Island status, centered in the pill). + private static let maxDisplayInterval: TimeInterval = 99 * 60 + 59 + + var body: some View { + Text( + timerInterval: startedAt...startedAt.addingTimeInterval(Self.maxDisplayInterval), + countsDown: false, + showsHours: false + ) + .monospacedDigit() + .multilineTextAlignment(alignment) + .lineLimit(1) + } +} + +private enum AgentRunLiveActivityTheme { + static let background = Color(red: 0.025, green: 0.028, blue: 0.038) + static let primaryText = Color.white + static let secondaryText = Color.white.opacity(0.68) + static let stroke = Color.white.opacity(0.13) + static let pillBackground = Color.white.opacity(0.08) + static let railBackground = Color.white.opacity(0.14) + static let liveDot = Color(red: 0.35, green: 0.95, blue: 0.7) +} + +private enum AgentRunStatusStyle { + static func color(for status: AgentRunActivityStatus, isStale: Bool) -> Color { + if isStale { + return Color.white.opacity(0.52) + } + + switch status { + case .starting, .thinking, .responding: + return Color(red: 1.0, green: 0.82, blue: 0.18) + case .usingTool: + return Color(red: 0.50, green: 0.72, blue: 1.0) + case .searchingFiles: + return Color(red: 0.22, green: 0.92, blue: 0.95) + case .readingFiles: + return Color(red: 0.58, green: 0.78, blue: 1.0) + case .runningCommand: + return Color(red: 0.76, green: 0.55, blue: 1.0) + case .waitingForApproval: + return Color(red: 1.0, green: 0.58, blue: 0.24) + case .complete: + return Color(red: 0.35, green: 0.95, blue: 0.55) + case .failed: + return Color(red: 1.0, green: 0.32, blue: 0.32) + case .cancelled: + return Color.white.opacity(0.56) + } + } + + static func symbolName(for status: AgentRunActivityStatus) -> String { + switch status { + case .starting, .thinking: + preconditionFailure("Starting and thinking use the Aiden logo resource") + case .usingTool: + "wrench.and.screwdriver" + case .searchingFiles: + "magnifyingglass" + case .readingFiles: + "doc.text" + case .runningCommand: + "terminal" + case .responding: + "text.bubble" + case .waitingForApproval: + "checkmark.shield" + case .complete: + "checkmark" + case .failed: + "exclamationmark" + case .cancelled: + "xmark" + } + } +} diff --git a/ios/AidenLiveActivityWidget/Resources/Info.plist b/ios/AidenLiveActivityWidget/Resources/Info.plist new file mode 100644 index 00000000..97223c51 --- /dev/null +++ b/ios/AidenLiveActivityWidget/Resources/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + $(APP_DISPLAY_NAME) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + AidenURLScheme + $(APP_URL_SCHEME) + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/ios/AidenOnTheGo.xcodeproj/project.pbxproj b/ios/AidenOnTheGo.xcodeproj/project.pbxproj new file mode 100644 index 00000000..eb299ecf --- /dev/null +++ b/ios/AidenOnTheGo.xcodeproj/project.pbxproj @@ -0,0 +1,1034 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + A0D000000000000000000001 /* AidenRemoteContract.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D000000000000000000011 /* AidenRemoteContract.swift */; }; + A0D000000000000000000002 /* AidenServerTrust.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D000000000000000000012 /* AidenServerTrust.swift */; }; + A0D000000000000000000003 /* AidenRemotePhase0Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D000000000000000000013 /* AidenRemotePhase0Tests.swift */; }; + A0D600000000000000000001 /* AidenRemoteClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D600000000000000000011 /* AidenRemoteClient.swift */; }; + A0D600000000000000000002 /* AidenInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D600000000000000000012 /* AidenInstallation.swift */; }; + A0D600000000000000000003 /* AidenRemoteClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D600000000000000000013 /* AidenRemoteClientTests.swift */; }; + A0D600000000000000000004 /* AidenRemoteCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D600000000000000000014 /* AidenRemoteCoordinator.swift */; }; + A0D600000000000000000005 /* AidenPairingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D600000000000000000015 /* AidenPairingView.swift */; }; + A0D600000000000000000006 /* AidenWorkspaceShellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D600000000000000000016 /* AidenWorkspaceShellView.swift */; }; + A0D700000000000000000001 /* AidenChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D700000000000000000011 /* AidenChat.swift */; }; + A0D700000000000000000002 /* AidenSSEParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D700000000000000000012 /* AidenSSEParser.swift */; }; + A0D700000000000000000003 /* AidenChatCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D700000000000000000013 /* AidenChatCache.swift */; }; + A0D700000000000000000004 /* AidenChatFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D700000000000000000014 /* AidenChatFeature.swift */; }; + A0D700000000000000000005 /* AidenChatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D700000000000000000015 /* AidenChatTests.swift */; }; + A0D800000000000000000001 /* AidenAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D800000000000000000011 /* AidenAppearance.swift */; }; + A0D800000000000000000002 /* AidenAppearanceV1.json in Resources */ = {isa = PBXBuildFile; fileRef = A0D800000000000000000012 /* AidenAppearanceV1.json */; }; + A0D900000000000000000001 /* AidenWorkspaceEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D900000000000000000011 /* AidenWorkspaceEnvironment.swift */; }; + A0D900000000000000000002 /* AidenWorkspaceEnvironmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D900000000000000000012 /* AidenWorkspaceEnvironmentView.swift */; }; + A0D900000000000000000003 /* AidenWorkspaceEnvironmentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D900000000000000000013 /* AidenWorkspaceEnvironmentTests.swift */; }; + A0DA00000000000000000001 /* AidenScheduledTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DA00000000000000000011 /* AidenScheduledTask.swift */; }; + A0DA00000000000000000002 /* AidenScheduledTasksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DA00000000000000000012 /* AidenScheduledTasksView.swift */; }; + A0DA00000000000000000003 /* AidenScheduledTaskTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DA00000000000000000013 /* AidenScheduledTaskTests.swift */; }; + A0D000000000000000000004 /* AidenRemoteContractV1.json in Resources */ = {isa = PBXBuildFile; fileRef = A0D000000000000000000014 /* AidenRemoteContractV1.json */; }; + 1A2B3C4D5E6F700000000001 /* AidenOnTheGoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2B3C4D5E6F700000000011 /* AidenOnTheGoApp.swift */; }; + 1A2B3C4D5E6F700000000002 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2B3C4D5E6F700000000012 /* ContentView.swift */; }; + 1A2B3C4D5E6F700000000338 /* AidenAppIntents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2B3C4D5E6F700000000337 /* AidenAppIntents.swift */; }; + 1A2B3C4D5E6F700000000003 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1A2B3C4D5E6F700000000014 /* Assets.xcassets */; }; + A1DE00000000000000000001 /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = A1DE00000000000000000002 /* AppIcon.icon */; }; + 10CA110C0DE000000000B001 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 10CA110C0DE000000000B002 /* Localizable.xcstrings */; }; + 10CA110C0DE000000000B003 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 10CA110C0DE000000000B002 /* Localizable.xcstrings */; }; + 10CA110C0DE000000000B004 /* AppShortcuts.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 10CA110C0DE000000000B005 /* AppShortcuts.xcstrings */; }; + 1A2B3C4D5E6F700000000008 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 1A2B3C4D5E6F700000000098 /* KeychainAccess */; }; + BADA00000000000000000001 /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = BADA00000000000000000003 /* MarkdownUI */; }; + 1A2B3C4D5E6F7000000000A0 /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2B3C4D5E6F7000000000B0 /* KeychainStore.swift */; }; + 1A2B3C4D5E6F7000000000F8 /* AppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2B3C4D5E6F7000000000F9 /* AppConfig.swift */; }; + 1A2B3C4D5E6F70000000300 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 1A2B3C4D5E6F70000000301 /* PrivacyInfo.xcprivacy */; }; + 9E2A653D968A4ACDA19A8142 /* ThirdPartyNotices in Resources */ = {isa = PBXBuildFile; fileRef = 488791F556304AD2AC59BC5D /* ThirdPartyNotices */; }; + A04500000000000000000001 /* AgentRunActivityAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = A04500000000000000000010 /* AgentRunActivityAttributes.swift */; }; + A04500000000000000000002 /* AidenDeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = A04500000000000000000011 /* AidenDeepLink.swift */; }; + A04500000000000000000004 /* AgentRunActivityAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = A04500000000000000000010 /* AgentRunActivityAttributes.swift */; }; + A04500000000000000000005 /* AidenDeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = A04500000000000000000011 /* AidenDeepLink.swift */; }; + A04500000000000000000006 /* AgentRunLiveActivityWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = A04500000000000000000013 /* AgentRunLiveActivityWidget.swift */; }; + A04500000000000000000007 /* AidenLiveActivityWidget.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A04500000000000000000017 /* AidenLiveActivityWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + A04500000000000000000008 /* aiden-sidebar-logo.png in Resources */ = {isa = PBXBuildFile; fileRef = A04500000000000000000018 /* aiden-sidebar-logo.png */; }; + A0DB00000000000000000001 /* AidenRemoteLiveActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB00000000000000000011 /* AidenRemoteLiveActivityManager.swift */; }; + A0DB00000000000000000002 /* AidenNativeIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB00000000000000000012 /* AidenNativeIntegrationTests.swift */; }; + A710739233F64F4FA4C2B700 /* ComposerVoiceInputController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A710739233F64F4FA4C2B710 /* ComposerVoiceInputController.swift */; }; + A0E000000000000000000001 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E000000000000000000011 /* Core.swift */; }; + A0E000000000000000000002 /* Lattice.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E000000000000000000012 /* Lattice.swift */; }; + A0E000000000000000000003 /* Morph.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E000000000000000000013 /* Morph.swift */; }; + A0E000000000000000000004 /* OrbSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E000000000000000000014 /* OrbSpec.swift */; }; + A0E000000000000000000005 /* Orbits.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E000000000000000000015 /* Orbits.swift */; }; + A0E000000000000000000006 /* Presets.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E000000000000000000016 /* Presets.swift */; }; + A0E000000000000000000007 /* Snapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E000000000000000000017 /* Snapshot.swift */; }; + A0E000000000000000000008 /* Strands.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E000000000000000000018 /* Strands.swift */; }; + A0E000000000000000000009 /* ThinkingOrb.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E000000000000000000019 /* ThinkingOrb.swift */; }; + A0E00000000000000000000A /* Web.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E00000000000000000001A /* Web.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 1A2B3C4D5E6F700000000036 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 1A2B3C4D5E6F700000000070 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 1A2B3C4D5E6F700000000040; + remoteInfo = AidenOnTheGo; + }; + A04500000000000000000030 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 1A2B3C4D5E6F700000000070 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A04500000000000000000040; + remoteInfo = AidenLiveActivityWidget; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + B5A100000000000000000023 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + A04500000000000000000007 /* AidenLiveActivityWidget.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + A0D000000000000000000011 /* AidenRemoteContract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenRemoteContract.swift; sourceTree = ""; }; + A0D000000000000000000012 /* AidenServerTrust.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenServerTrust.swift; sourceTree = ""; }; + A0D000000000000000000013 /* AidenRemotePhase0Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenRemotePhase0Tests.swift; sourceTree = ""; }; + A0D000000000000000000014 /* AidenRemoteContractV1.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = AidenRemoteContractV1.json; path = ../protocol/aiden-remote/v1/fixtures/contract.json; sourceTree = SOURCE_ROOT; }; + A0D600000000000000000011 /* AidenRemoteClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenRemoteClient.swift; sourceTree = ""; }; + A0D600000000000000000012 /* AidenInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenInstallation.swift; sourceTree = ""; }; + A0D600000000000000000013 /* AidenRemoteClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenRemoteClientTests.swift; sourceTree = ""; }; + A0D600000000000000000014 /* AidenRemoteCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenRemoteCoordinator.swift; sourceTree = ""; }; + A0D600000000000000000015 /* AidenPairingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenPairingView.swift; sourceTree = ""; }; + A0D600000000000000000016 /* AidenWorkspaceShellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenWorkspaceShellView.swift; sourceTree = ""; }; + A0D700000000000000000011 /* AidenChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenChat.swift; sourceTree = ""; }; + A0D700000000000000000012 /* AidenSSEParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenSSEParser.swift; sourceTree = ""; }; + A0D700000000000000000013 /* AidenChatCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenChatCache.swift; sourceTree = ""; }; + A0D700000000000000000014 /* AidenChatFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenChatFeature.swift; sourceTree = ""; }; + A0D700000000000000000015 /* AidenChatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenChatTests.swift; sourceTree = ""; }; + A0D800000000000000000011 /* AidenAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenAppearance.swift; sourceTree = ""; }; + A0D800000000000000000012 /* AidenAppearanceV1.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = AidenAppearanceV1.json; path = ../protocol/aiden-appearance-v1.json; sourceTree = SOURCE_ROOT; }; + A0D900000000000000000011 /* AidenWorkspaceEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenWorkspaceEnvironment.swift; sourceTree = ""; }; + A0D900000000000000000012 /* AidenWorkspaceEnvironmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenWorkspaceEnvironmentView.swift; sourceTree = ""; }; + A0D900000000000000000013 /* AidenWorkspaceEnvironmentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenWorkspaceEnvironmentTests.swift; sourceTree = ""; }; + A0DA00000000000000000011 /* AidenScheduledTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenScheduledTask.swift; sourceTree = ""; }; + A0DA00000000000000000012 /* AidenScheduledTasksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenScheduledTasksView.swift; sourceTree = ""; }; + A0DA00000000000000000013 /* AidenScheduledTaskTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenScheduledTaskTests.swift; sourceTree = ""; }; + 351C0F1600000000000000A1 /* Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Shared.xcconfig; path = Config/Shared.xcconfig; sourceTree = ""; }; + 1A2B3C4D5E6F700000000010 /* AidenOnTheGo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AidenOnTheGo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1A2B3C4D5E6F700000000011 /* AidenOnTheGoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenOnTheGoApp.swift; sourceTree = ""; }; + 1A2B3C4D5E6F700000000012 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 1A2B3C4D5E6F700000000337 /* AidenAppIntents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenAppIntents.swift; sourceTree = ""; }; + 1A2B3C4D5E6F700000000013 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1A2B3C4D5E6F700000000014 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + A1DE00000000000000000002 /* AppIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = wrapper.icon; path = AppIcon.icon; sourceTree = ""; }; + 10CA110C0DE000000000B002 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; + 10CA110C0DE000000000B005 /* AppShortcuts.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = AppShortcuts.xcstrings; sourceTree = ""; }; + 1A2B3C4D5E6F700000000015 /* AidenOnTheGoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AidenOnTheGoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 1A2B3C4D5E6F7000000000B0 /* KeychainStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStore.swift; sourceTree = ""; }; + 1A2B3C4D5E6F7000000000F9 /* AppConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfig.swift; sourceTree = ""; }; + 1A2B3C4D5E6F70000000301 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + A710739233F64F4FA4C2B710 /* ComposerVoiceInputController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerVoiceInputController.swift; sourceTree = ""; }; + B5A100000000000000000015 /* AidenOnTheGo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AidenOnTheGo.entitlements; sourceTree = ""; }; + 488791F556304AD2AC59BC5D /* ThirdPartyNotices */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ThirdPartyNotices; sourceTree = ""; }; + A04500000000000000000010 /* AgentRunActivityAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentRunActivityAttributes.swift; sourceTree = ""; }; + A04500000000000000000011 /* AidenDeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenDeepLink.swift; sourceTree = ""; }; + A04500000000000000000013 /* AgentRunLiveActivityWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentRunLiveActivityWidget.swift; sourceTree = ""; }; + A04500000000000000000014 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A04500000000000000000018 /* aiden-sidebar-logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "aiden-sidebar-logo.png"; path = AidenOnTheGo/Resources/Assets.xcassets/AidenSidebarLogo.imageset/aiden-sidebar-logo.png; sourceTree = SOURCE_ROOT; }; + A04500000000000000000017 /* AidenLiveActivityWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AidenLiveActivityWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + A0DB00000000000000000011 /* AidenRemoteLiveActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenRemoteLiveActivityManager.swift; sourceTree = ""; }; + A0DB00000000000000000012 /* AidenNativeIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenNativeIntegrationTests.swift; sourceTree = ""; }; + A0E000000000000000000011 /* Core.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Core.swift; sourceTree = ""; }; + A0E000000000000000000012 /* Lattice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lattice.swift; sourceTree = ""; }; + A0E000000000000000000013 /* Morph.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Morph.swift; sourceTree = ""; }; + A0E000000000000000000014 /* OrbSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrbSpec.swift; sourceTree = ""; }; + A0E000000000000000000015 /* Orbits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Orbits.swift; sourceTree = ""; }; + A0E000000000000000000016 /* Presets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Presets.swift; sourceTree = ""; }; + A0E000000000000000000017 /* Snapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Snapshot.swift; sourceTree = ""; }; + A0E000000000000000000018 /* Strands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strands.swift; sourceTree = ""; }; + A0E000000000000000000019 /* ThinkingOrb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThinkingOrb.swift; sourceTree = ""; }; + A0E00000000000000000001A /* Web.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Web.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1A2B3C4D5E6F700000000020 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1A2B3C4D5E6F700000000008 /* KeychainAccess in Frameworks */, + BADA00000000000000000001 /* MarkdownUI in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1A2B3C4D5E6F700000000021 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A04500000000000000000061 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1A2B3C4D5E6F700000000030 = { + isa = PBXGroup; + children = ( + 351C0F1600000000000000A1 /* Shared.xcconfig */, + 1A2B3C4D5E6F700000000031 /* AidenOnTheGo */, + 1A2B3C4D5E6F700000000034 /* AidenOnTheGoTests */, + A04500000000000000000021 /* AidenLiveActivityWidget */, + 1A2B3C4D5E6F700000000032 /* Products */, + ); + sourceTree = ""; + }; + 1A2B3C4D5E6F700000000031 /* AidenOnTheGo */ = { + isa = PBXGroup; + children = ( + 1A2B3C4D5E6F700000000011 /* AidenOnTheGoApp.swift */, + 1A2B3C4D5E6F700000000012 /* ContentView.swift */, + 1A2B3C4D5E6F7000000000FA /* Config */, + 1A2B3C4D5E6F700000000339 /* AppIntents */, + A04500000000000000000020 /* LiveActivities */, + 1A2B3C4D5E6F7000000000C0 /* Auth */, + 1A2B3C4D5E6F7000000000C1 /* Networking */, + 1A2B3C4D5E6F7000000000C2 /* Models */, + 1A2B3C4D5E6F7000000000F0 /* Persistence */, + 1A2B3C4D5E6F7000000000C3 /* Features */, + 1A2B3C4D5E6F700000000033 /* Resources */, + ); + path = AidenOnTheGo; + sourceTree = ""; + }; + 1A2B3C4D5E6F700000000032 /* Products */ = { + isa = PBXGroup; + children = ( + 1A2B3C4D5E6F700000000010 /* AidenOnTheGo.app */, + 1A2B3C4D5E6F700000000015 /* AidenOnTheGoTests.xctest */, + A04500000000000000000017 /* AidenLiveActivityWidget.appex */, + ); + name = Products; + sourceTree = ""; + }; + 1A2B3C4D5E6F700000000033 /* Resources */ = { + isa = PBXGroup; + children = ( + A1DE00000000000000000002 /* AppIcon.icon */, + 1A2B3C4D5E6F700000000014 /* Assets.xcassets */, + 10CA110C0DE000000000B002 /* Localizable.xcstrings */, + 10CA110C0DE000000000B005 /* AppShortcuts.xcstrings */, + 1A2B3C4D5E6F700000000013 /* Info.plist */, + B5A100000000000000000015 /* AidenOnTheGo.entitlements */, + 1A2B3C4D5E6F70000000301 /* PrivacyInfo.xcprivacy */, + 488791F556304AD2AC59BC5D /* ThirdPartyNotices */, + ); + path = Resources; + sourceTree = ""; + }; + 1A2B3C4D5E6F700000000034 /* AidenOnTheGoTests */ = { + isa = PBXGroup; + children = ( + A0DB00000000000000000012 /* AidenNativeIntegrationTests.swift */, + A0DA00000000000000000013 /* AidenScheduledTaskTests.swift */, + A0D900000000000000000013 /* AidenWorkspaceEnvironmentTests.swift */, + A0D000000000000000000013 /* AidenRemotePhase0Tests.swift */, + A0D600000000000000000013 /* AidenRemoteClientTests.swift */, + A0D700000000000000000015 /* AidenChatTests.swift */, + A0D800000000000000000012 /* AidenAppearanceV1.json */, + A0D000000000000000000014 /* AidenRemoteContractV1.json */, + ); + path = AidenOnTheGoTests; + sourceTree = ""; + }; + 1A2B3C4D5E6F7000000000C0 /* Auth */ = { + isa = PBXGroup; + children = ( + 1A2B3C4D5E6F7000000000B0 /* KeychainStore.swift */, + ); + path = Auth; + sourceTree = ""; + }; + 1A2B3C4D5E6F7000000000C1 /* Networking */ = { + isa = PBXGroup; + children = ( + A0D000000000000000000011 /* AidenRemoteContract.swift */, + A0D000000000000000000012 /* AidenServerTrust.swift */, + A0D600000000000000000011 /* AidenRemoteClient.swift */, + A0D700000000000000000012 /* AidenSSEParser.swift */, + ); + path = Networking; + sourceTree = ""; + }; + 1A2B3C4D5E6F7000000000C2 /* Models */ = { + isa = PBXGroup; + children = ( + A0DA00000000000000000011 /* AidenScheduledTask.swift */, + A0D900000000000000000011 /* AidenWorkspaceEnvironment.swift */, + A0D600000000000000000012 /* AidenInstallation.swift */, + A0D700000000000000000011 /* AidenChat.swift */, + ); + path = Models; + sourceTree = ""; + }; + 1A2B3C4D5E6F7000000000C3 /* Features */ = { + isa = PBXGroup; + children = ( + A0D600000000000000000020 /* Remote */, + 1A2B3C4D5E6F7000000000C4 /* Onboarding */, + C09300000000000000000010 /* Shared */, + 1A2B3C4D5E6F7000000000C5 /* SessionList */, + 1A2B3C4D5E6F7000000000C6 /* Chat */, + 1A2B3C4D5E6F7000000000C7 /* Workspace */, + 1A2B3C4D5E6F7000000000F7 /* Settings */, + 3F91D2A54B9A4F66A2C01020 /* Tasks */, + ); + path = Features; + sourceTree = ""; + }; + A0D600000000000000000020 /* Remote */ = { + isa = PBXGroup; + children = ( + A0DA00000000000000000012 /* AidenScheduledTasksView.swift */, + A0D900000000000000000012 /* AidenWorkspaceEnvironmentView.swift */, + A0D600000000000000000014 /* AidenRemoteCoordinator.swift */, + A0D600000000000000000015 /* AidenPairingView.swift */, + A0D600000000000000000016 /* AidenWorkspaceShellView.swift */, + A0D700000000000000000014 /* AidenChatFeature.swift */, + ); + path = Remote; + sourceTree = ""; + }; + 1A2B3C4D5E6F7000000000C4 /* Onboarding */ = { + isa = PBXGroup; + children = ( + ); + path = Onboarding; + sourceTree = ""; + }; + C09300000000000000000010 /* Shared */ = { + isa = PBXGroup; + children = ( + A0E000000000000000000020 /* ThinkingOrbsKit */, + ); + path = Shared; + sourceTree = ""; + }; + A0E000000000000000000020 /* ThinkingOrbsKit */ = { + isa = PBXGroup; + children = ( + A0E000000000000000000011 /* Core.swift */, + A0E000000000000000000012 /* Lattice.swift */, + A0E000000000000000000013 /* Morph.swift */, + A0E000000000000000000014 /* OrbSpec.swift */, + A0E000000000000000000015 /* Orbits.swift */, + A0E000000000000000000016 /* Presets.swift */, + A0E000000000000000000017 /* Snapshot.swift */, + A0E000000000000000000018 /* Strands.swift */, + A0E000000000000000000019 /* ThinkingOrb.swift */, + A0E00000000000000000001A /* Web.swift */, + ); + path = ThinkingOrbsKit; + sourceTree = ""; + }; + 1A2B3C4D5E6F7000000000C5 /* SessionList */ = { + isa = PBXGroup; + children = ( + ); + path = SessionList; + sourceTree = ""; + }; + 1A2B3C4D5E6F7000000000C6 /* Chat */ = { + isa = PBXGroup; + children = ( + A710739233F64F4FA4C2B710 /* ComposerVoiceInputController.swift */, + ); + path = Chat; + sourceTree = ""; + }; + 1A2B3C4D5E6F7000000000C7 /* Workspace */ = { + isa = PBXGroup; + children = ( + ); + path = Workspace; + sourceTree = ""; + }; + 1A2B3C4D5E6F7000000000F0 /* Persistence */ = { + isa = PBXGroup; + children = ( + A0D700000000000000000013 /* AidenChatCache.swift */, + ); + path = Persistence; + sourceTree = ""; + }; + 1A2B3C4D5E6F7000000000F7 /* Settings */ = { + isa = PBXGroup; + children = ( + ); + path = Settings; + sourceTree = ""; + }; + 1A2B3C4D5E6F7000000000FA /* Config */ = { + isa = PBXGroup; + children = ( + 1A2B3C4D5E6F7000000000F9 /* AppConfig.swift */, + A0D800000000000000000011 /* AidenAppearance.swift */, + ); + path = Config; + sourceTree = ""; + }; + A04500000000000000000020 /* LiveActivities */ = { + isa = PBXGroup; + children = ( + A04500000000000000000010 /* AgentRunActivityAttributes.swift */, + A04500000000000000000011 /* AidenDeepLink.swift */, + A0DB00000000000000000011 /* AidenRemoteLiveActivityManager.swift */, + ); + path = LiveActivities; + sourceTree = ""; + }; + 1A2B3C4D5E6F700000000339 /* AppIntents */ = { + isa = PBXGroup; + children = ( + 1A2B3C4D5E6F700000000337 /* AidenAppIntents.swift */, + ); + path = AppIntents; + sourceTree = ""; + }; + A04500000000000000000021 /* AidenLiveActivityWidget */ = { + isa = PBXGroup; + children = ( + A04500000000000000000013 /* AgentRunLiveActivityWidget.swift */, + A04500000000000000000022 /* Resources */, + ); + path = AidenLiveActivityWidget; + sourceTree = ""; + }; + A04500000000000000000022 /* Resources */ = { + isa = PBXGroup; + children = ( + A04500000000000000000014 /* Info.plist */, + A04500000000000000000018 /* aiden-sidebar-logo.png */, + ); + path = Resources; + sourceTree = ""; + }; + 3F91D2A54B9A4F66A2C01020 /* Tasks */ = { + isa = PBXGroup; + children = ( + ); + path = Tasks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1A2B3C4D5E6F700000000040 /* AidenOnTheGo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1A2B3C4D5E6F700000000041 /* Build configuration list for PBXNativeTarget "AidenOnTheGo" */; + buildPhases = ( + 1A2B3C4D5E6F700000000050 /* Sources */, + 1A2B3C4D5E6F700000000060 /* Resources */, + 1A2B3C4D5E6F700000000020 /* Frameworks */, + B5A100000000000000000023 /* Embed App Extensions */, + ); + buildRules = ( + ); + dependencies = ( + A04500000000000000000031 /* PBXTargetDependency */, + ); + name = AidenOnTheGo; + packageProductDependencies = ( + 1A2B3C4D5E6F700000000098 /* KeychainAccess */, + BADA00000000000000000003 /* MarkdownUI */, + ); + productName = AidenOnTheGo; + productReference = 1A2B3C4D5E6F700000000010 /* AidenOnTheGo.app */; + productType = "com.apple.product-type.application"; + }; + 1A2B3C4D5E6F700000000044 /* AidenOnTheGoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1A2B3C4D5E6F700000000045 /* Build configuration list for PBXNativeTarget "AidenOnTheGoTests" */; + buildPhases = ( + 1A2B3C4D5E6F700000000052 /* Sources */, + A0D000000000000000000060 /* Resources */, + 1A2B3C4D5E6F700000000021 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 1A2B3C4D5E6F700000000037 /* PBXTargetDependency */, + ); + name = AidenOnTheGoTests; + packageProductDependencies = ( + ); + productName = AidenOnTheGoTests; + productReference = 1A2B3C4D5E6F700000000015 /* AidenOnTheGoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + A04500000000000000000040 /* AidenLiveActivityWidget */ = { + isa = PBXNativeTarget; + buildConfigurationList = A04500000000000000000041 /* Build configuration list for PBXNativeTarget "AidenLiveActivityWidget" */; + buildPhases = ( + A04500000000000000000050 /* Sources */, + A04500000000000000000060 /* Resources */, + A04500000000000000000061 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AidenLiveActivityWidget; + productName = AidenLiveActivityWidget; + productReference = A04500000000000000000017 /* AidenLiveActivityWidget.appex */; + productType = "com.apple.product-type.app-extension"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1A2B3C4D5E6F700000000070 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 2640; + LastUpgradeCheck = 2640; + TargetAttributes = { + 1A2B3C4D5E6F700000000040 = { + CreatedOnToolsVersion = 26.4.1; + }; + 1A2B3C4D5E6F700000000044 = { + CreatedOnToolsVersion = 26.4.1; + TestTargetID = 1A2B3C4D5E6F700000000040; + }; + A04500000000000000000040 = { + CreatedOnToolsVersion = 26.4.1; + }; + }; + }; + buildConfigurationList = 1A2B3C4D5E6F700000000071 /* Build configuration list for PBXProject "AidenOnTheGo" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + de, + es, + fr, + it, + pl, + "pt-BR", + nl, + tr, + ru, + ja, + "zh-Hans", + ko, + ar, + he, + ur, + "zh-Hant", + "zh-HK", + ); + mainGroup = 1A2B3C4D5E6F700000000030; + packageReferences = ( + 1A2B3C4D5E6F700000000099 /* XCRemoteSwiftPackageReference "KeychainAccess" */, + BADA00000000000000000002 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */, + ); + productRefGroup = 1A2B3C4D5E6F700000000032 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1A2B3C4D5E6F700000000040 /* AidenOnTheGo */, + 1A2B3C4D5E6F700000000044 /* AidenOnTheGoTests */, + A04500000000000000000040 /* AidenLiveActivityWidget */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A0D000000000000000000060 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A0D000000000000000000004 /* AidenRemoteContractV1.json in Resources */, + A0D800000000000000000002 /* AidenAppearanceV1.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1A2B3C4D5E6F700000000060 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1DE00000000000000000001 /* AppIcon.icon in Resources */, + 1A2B3C4D5E6F700000000003 /* Assets.xcassets in Resources */, + 10CA110C0DE000000000B001 /* Localizable.xcstrings in Resources */, + 1A2B3C4D5E6F70000000300 /* PrivacyInfo.xcprivacy in Resources */, + 9E2A653D968A4ACDA19A8142 /* ThirdPartyNotices in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A04500000000000000000060 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 10CA110C0DE000000000B003 /* Localizable.xcstrings in Resources */, + A04500000000000000000008 /* aiden-sidebar-logo.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1A2B3C4D5E6F700000000050 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A0E000000000000000000001 /* Core.swift in Sources */, + A0E000000000000000000002 /* Lattice.swift in Sources */, + A0E000000000000000000003 /* Morph.swift in Sources */, + A0E000000000000000000004 /* OrbSpec.swift in Sources */, + A0E000000000000000000005 /* Orbits.swift in Sources */, + A0E000000000000000000006 /* Presets.swift in Sources */, + A0E000000000000000000007 /* Snapshot.swift in Sources */, + A0E000000000000000000008 /* Strands.swift in Sources */, + A0E000000000000000000009 /* ThinkingOrb.swift in Sources */, + A0E00000000000000000000A /* Web.swift in Sources */, + 1A2B3C4D5E6F700000000338 /* AidenAppIntents.swift in Sources */, + A04500000000000000000001 /* AgentRunActivityAttributes.swift in Sources */, + A04500000000000000000002 /* AidenDeepLink.swift in Sources */, + A0DB00000000000000000001 /* AidenRemoteLiveActivityManager.swift in Sources */, + A710739233F64F4FA4C2B700 /* ComposerVoiceInputController.swift in Sources */, + A0DA00000000000000000001 /* AidenScheduledTask.swift in Sources */, + A0DA00000000000000000002 /* AidenScheduledTasksView.swift in Sources */, + A0D900000000000000000001 /* AidenWorkspaceEnvironment.swift in Sources */, + A0D900000000000000000002 /* AidenWorkspaceEnvironmentView.swift in Sources */, + A0D000000000000000000001 /* AidenRemoteContract.swift in Sources */, + A0D000000000000000000002 /* AidenServerTrust.swift in Sources */, + A0D600000000000000000001 /* AidenRemoteClient.swift in Sources */, + A0D600000000000000000002 /* AidenInstallation.swift in Sources */, + A0D600000000000000000004 /* AidenRemoteCoordinator.swift in Sources */, + A0D600000000000000000005 /* AidenPairingView.swift in Sources */, + A0D600000000000000000006 /* AidenWorkspaceShellView.swift in Sources */, + A0D700000000000000000001 /* AidenChat.swift in Sources */, + A0D700000000000000000002 /* AidenSSEParser.swift in Sources */, + A0D700000000000000000003 /* AidenChatCache.swift in Sources */, + A0D700000000000000000004 /* AidenChatFeature.swift in Sources */, + A0D800000000000000000001 /* AidenAppearance.swift in Sources */, + 1A2B3C4D5E6F700000000001 /* AidenOnTheGoApp.swift in Sources */, + 1A2B3C4D5E6F700000000002 /* ContentView.swift in Sources */, + 1A2B3C4D5E6F7000000000F8 /* AppConfig.swift in Sources */, + 1A2B3C4D5E6F7000000000A0 /* KeychainStore.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1A2B3C4D5E6F700000000052 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A0DB00000000000000000002 /* AidenNativeIntegrationTests.swift in Sources */, + A0DA00000000000000000003 /* AidenScheduledTaskTests.swift in Sources */, + A0D900000000000000000003 /* AidenWorkspaceEnvironmentTests.swift in Sources */, + A0D000000000000000000003 /* AidenRemotePhase0Tests.swift in Sources */, + A0D600000000000000000003 /* AidenRemoteClientTests.swift in Sources */, + A0D700000000000000000005 /* AidenChatTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A04500000000000000000050 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A04500000000000000000004 /* AgentRunActivityAttributes.swift in Sources */, + A04500000000000000000005 /* AidenDeepLink.swift in Sources */, + A04500000000000000000006 /* AgentRunLiveActivityWidget.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 1A2B3C4D5E6F700000000037 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 1A2B3C4D5E6F700000000040 /* AidenOnTheGo */; + targetProxy = 1A2B3C4D5E6F700000000036 /* PBXContainerItemProxy */; + }; + A04500000000000000000031 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A04500000000000000000040 /* AidenLiveActivityWidget */; + targetProxy = A04500000000000000000030 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 1A2B3C4D5E6F700000000080 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 351C0F1600000000000000A1 /* Shared.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + APP_DISPLAY_NAME = "Aiden On The Go"; + APP_URL_SCHEME_SUFFIX = ""; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_CONCURRENCY = targeted; + }; + name = Debug; + }; + 1A2B3C4D5E6F700000000081 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 351C0F1600000000000000A1 /* Shared.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + APP_DISPLAY_NAME = "Aiden On The Go"; + APP_URL_SCHEME_SUFFIX = ""; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_STRICT_CONCURRENCY = targeted; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 1A2B3C4D5E6F700000000082 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = AppIconMonochrome; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = AidenOnTheGo/Resources/AidenOnTheGo.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 7; + DEVELOPMENT_ASSET_PATHS = ""; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = AidenOnTheGo/Resources/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 0.1.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1A2B3C4D5E6F700000000083 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = AppIconMonochrome; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = AidenOnTheGo/Resources/AidenOnTheGo.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 7; + DEVELOPMENT_ASSET_PATHS = ""; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = AidenOnTheGo/Resources/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 0.1.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 1A2B3C4D5E6F700000000084 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 7; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 0.1.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)Tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AidenOnTheGo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/AidenOnTheGo"; + }; + name = Debug; + }; + 1A2B3C4D5E6F700000000085 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 7; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 0.1.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)Tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AidenOnTheGo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/AidenOnTheGo"; + }; + name = Release; + }; + A04500000000000000000042 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 7; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = AidenLiveActivityWidget/Resources/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 0.1.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).LiveActivityWidget"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + A04500000000000000000043 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 7; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = AidenLiveActivityWidget/Resources/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 0.1.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER).LiveActivityWidget"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1A2B3C4D5E6F700000000041 /* Build configuration list for PBXNativeTarget "AidenOnTheGo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1A2B3C4D5E6F700000000082 /* Debug */, + 1A2B3C4D5E6F700000000083 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1A2B3C4D5E6F700000000045 /* Build configuration list for PBXNativeTarget "AidenOnTheGoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1A2B3C4D5E6F700000000084 /* Debug */, + 1A2B3C4D5E6F700000000085 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1A2B3C4D5E6F700000000071 /* Build configuration list for PBXProject "AidenOnTheGo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1A2B3C4D5E6F700000000080 /* Debug */, + 1A2B3C4D5E6F700000000081 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A04500000000000000000041 /* Build configuration list for PBXNativeTarget "AidenLiveActivityWidget" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A04500000000000000000042 /* Debug */, + A04500000000000000000043 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 1A2B3C4D5E6F700000000099 /* XCRemoteSwiftPackageReference "KeychainAccess" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.2.2; + }; + }; + BADA00000000000000000002 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/gonzalezreal/swift-markdown-ui.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.4.1; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 1A2B3C4D5E6F700000000098 /* KeychainAccess */ = { + isa = XCSwiftPackageProductDependency; + package = 1A2B3C4D5E6F700000000099 /* XCRemoteSwiftPackageReference "KeychainAccess" */; + productName = KeychainAccess; + }; + BADA00000000000000000003 /* MarkdownUI */ = { + isa = XCSwiftPackageProductDependency; + package = BADA00000000000000000002 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */; + productName = MarkdownUI; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 1A2B3C4D5E6F700000000070 /* Project object */; +} diff --git a/ios/AidenOnTheGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/AidenOnTheGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..50dea948 --- /dev/null +++ b/ios/AidenOnTheGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,42 @@ +{ + "originHash" : "fb5fc1072fe083e92c2417e3131383d24de0f43da0c8fb892c71b4aaa6413e88", + "pins" : [ + { + "identity" : "keychainaccess", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kishikawakatsumi/KeychainAccess.git", + "state" : { + "revision" : "84e546727d66f1adc5439debad16270d0fdd04e7", + "version" : "4.2.2" + } + }, + { + "identity" : "networkimage", + "kind" : "remoteSourceControl", + "location" : "https://github.com/gonzalezreal/NetworkImage", + "state" : { + "revision" : "2849f5323265386e200484b0d0f896e73c3411b9", + "version" : "6.0.1" + } + }, + { + "identity" : "swift-cmark", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-cmark", + "state" : { + "revision" : "924936d0427cb25a61169739a7660230bffa6ea6", + "version" : "0.8.0" + } + }, + { + "identity" : "swift-markdown-ui", + "kind" : "remoteSourceControl", + "location" : "https://github.com/gonzalezreal/swift-markdown-ui.git", + "state" : { + "revision" : "5f613358148239d0292c0cef674a3c2314737f9e", + "version" : "2.4.1" + } + } + ], + "version" : 3 +} diff --git a/ios/AidenOnTheGo.xcodeproj/xcshareddata/xcschemes/AidenOnTheGo.xcscheme b/ios/AidenOnTheGo.xcodeproj/xcshareddata/xcschemes/AidenOnTheGo.xcscheme new file mode 100644 index 00000000..fd930180 --- /dev/null +++ b/ios/AidenOnTheGo.xcodeproj/xcshareddata/xcschemes/AidenOnTheGo.xcscheme @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/AidenOnTheGo/AidenOnTheGoApp.swift b/ios/AidenOnTheGo/AidenOnTheGoApp.swift new file mode 100644 index 00000000..a6654769 --- /dev/null +++ b/ios/AidenOnTheGo/AidenOnTheGoApp.swift @@ -0,0 +1,16 @@ +import SwiftUI + +@main +struct AidenOnTheGoApp: App { + @State private var remoteCoordinator = AidenRemoteCoordinator() + @State private var appearance = AidenAppearanceStore() + + var body: some Scene { + WindowGroup { + AidenAppearanceRoot(appearance: appearance) { + ContentView(coordinator: remoteCoordinator) + } + .environment(appearance) + } + } +} diff --git a/ios/AidenOnTheGo/AppIntents/AidenAppIntents.swift b/ios/AidenOnTheGo/AppIntents/AidenAppIntents.swift new file mode 100644 index 00000000..b5cf3f6a --- /dev/null +++ b/ios/AidenOnTheGo/AppIntents/AidenAppIntents.swift @@ -0,0 +1,229 @@ +import AppIntents +import Foundation + +struct AidenIntentInstallationRecord: Codable, Equatable, Sendable { + let id: String + let name: String +} + +struct AidenIntentWorkspaceRecord: Codable, Equatable, Sendable { + let id: String + let instanceId: String + let name: String +} + +struct AidenIntentCatalogSnapshot: Codable, Equatable, Sendable { + let installations: [AidenIntentInstallationRecord] + let workspaces: [AidenIntentWorkspaceRecord] + let activeInstallationId: String? +} + +/// App-Group cache intentionally contains display names and stable IDs only. +/// The intent process never receives endpoints, credentials, pins, permissions, or paths. +struct AidenIntentCatalogStore: @unchecked Sendable { + static let shared = AidenIntentCatalogStore() + private static let key = "aiden.intent-catalog.v1" + private let defaults: UserDefaults? + + init(defaults: UserDefaults? = nil) { + if let defaults { + self.defaults = defaults + } else { + let identifier = Bundle.main.object(forInfoDictionaryKey: "AidenAppGroupIdentifier") as? String + ?? "group.sbtbiswas.AidenOnTheGo" + self.defaults = UserDefaults(suiteName: identifier) + } + } + + func load() -> AidenIntentCatalogSnapshot { + guard let data = defaults?.data(forKey: Self.key), data.count <= 1_048_576, + let value = try? JSONDecoder().decode(AidenIntentCatalogSnapshot.self, from: data) else { + return AidenIntentCatalogSnapshot(installations: [], workspaces: [], activeInstallationId: nil) + } + let installations = unique(value.installations.filter { + Self.safeID($0.id) && Self.safeName($0.name) + }, id: \.id) + let installationIDs = Set(installations.map(\.id)) + let workspaces = uniqueWorkspaces(value.workspaces.filter { + Self.safeID($0.id) && Self.safeID($0.instanceId) + && Self.safeName($0.name) && installationIDs.contains($0.instanceId) + }) + let active = value.activeInstallationId.flatMap { installationIDs.contains($0) ? $0 : nil } + return AidenIntentCatalogSnapshot( + installations: installations, + workspaces: workspaces, + activeInstallationId: active + ) + } + + func update( + installations: [AidenIntentInstallationRecord], + activeInstallationId: String?, + workspaces: [AidenIntentWorkspaceRecord], + for instanceId: String? + ) throws { + let existing = load() + let sanitizedInstallations = unique(installations.filter { + Self.safeID($0.id) && Self.safeName($0.name) + }, id: \.id) + let installationIDs = Set(sanitizedInstallations.map(\.id)) + var retained = existing.workspaces.filter { + installationIDs.contains($0.instanceId) && $0.instanceId != instanceId + } + retained.append(contentsOf: workspaces.filter { + Self.safeID($0.id) && Self.safeID($0.instanceId) + && Self.safeName($0.name) && installationIDs.contains($0.instanceId) + }) + let snapshot = AidenIntentCatalogSnapshot( + installations: sanitizedInstallations, + workspaces: uniqueWorkspaces(retained), + activeInstallationId: activeInstallationId.flatMap { installationIDs.contains($0) ? $0 : nil } + ) + let data = try JSONEncoder().encode(snapshot) + guard data.count <= 1_048_576 else { throw CocoaError(.fileWriteOutOfSpace) } + defaults?.set(data, forKey: Self.key) + } + + private func unique(_ values: [Value], id: KeyPath) -> [Value] { + var seen = Set() + return values.filter { seen.insert($0[keyPath: id]).inserted } + } + + private func uniqueWorkspaces(_ values: [AidenIntentWorkspaceRecord]) -> [AidenIntentWorkspaceRecord] { + var seen = Set() + return values.filter { seen.insert("\($0.instanceId)\u{1F}\($0.id)").inserted } + } + + private static func safeID(_ value: String) -> Bool { + !value.isEmpty && value.count <= 160 + && value.unicodeScalars.allSatisfy { scalar in + CharacterSet.alphanumerics.contains(scalar) || "._:-".unicodeScalars.contains(scalar) + } + } + + private static func safeName(_ value: String) -> Bool { + !value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && value.count <= 256 + } +} + +struct AidenInstallationIntentEntity: AppEntity, Equatable { + static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Aiden Installation") + static let defaultQuery = AidenInstallationIntentQuery() + + let id: String + let name: String + + var displayRepresentation: DisplayRepresentation { DisplayRepresentation(title: "\(name)") } +} + +struct AidenInstallationIntentQuery: EntityQuery { + func entities(for identifiers: [String]) async throws -> [AidenInstallationIntentEntity] { + let wanted = Set(identifiers) + return AidenIntentCatalogStore.shared.load().installations + .filter { wanted.contains($0.id) } + .map { AidenInstallationIntentEntity(id: $0.id, name: $0.name) } + } + + func suggestedEntities() async throws -> [AidenInstallationIntentEntity] { + AidenIntentCatalogStore.shared.load().installations + .map { AidenInstallationIntentEntity(id: $0.id, name: $0.name) } + } +} + +struct AidenWorkspaceIntentEntity: AppEntity, Equatable { + static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Aiden Workspace") + static let defaultQuery = AidenWorkspaceIntentQuery() + + let workspaceId: String + let instanceId: String + let name: String + + var id: String { "\(instanceId)|\(workspaceId)" } + + var displayRepresentation: DisplayRepresentation { DisplayRepresentation(title: "\(name)") } +} + +struct AidenWorkspaceIntentQuery: EntityQuery { + func entities(for identifiers: [String]) async throws -> [AidenWorkspaceIntentEntity] { + let wanted = Set(identifiers) + return AidenIntentCatalogStore.shared.load().workspaces + .map(Self.entity) + .filter { wanted.contains($0.id) } + } + + func suggestedEntities() async throws -> [AidenWorkspaceIntentEntity] { + let snapshot = AidenIntentCatalogStore.shared.load() + return snapshot.workspaces + .filter { snapshot.activeInstallationId == nil || $0.instanceId == snapshot.activeInstallationId } + .map(Self.entity) + } + + private static func entity(_ value: AidenIntentWorkspaceRecord) -> AidenWorkspaceIntentEntity { + AidenWorkspaceIntentEntity(workspaceId: value.id, instanceId: value.instanceId, name: value.name) + } +} + +struct NewChatIntent: AppIntent { + static let title: LocalizedStringResource = "New Chat" + static let description = IntentDescription("Open Aiden On The Go on a new chat.") + + func perform() async throws -> some IntentResult & OpensIntent { + guard let url = AidenDeepLink.newChatURL else { throw AidenIntentError.invalidDestination } + return .result(opensIntent: OpenURLIntent(url)) + } +} + +struct NewChatVoiceIntent: AppIntent { + static let title: LocalizedStringResource = "New Chat with Voice" + static let description = IntentDescription("Open a new Aiden chat and start on-device dictation.") + + func perform() async throws -> some IntentResult & OpensIntent { + guard let url = AidenDeepLink.newChatVoiceURL else { throw AidenIntentError.invalidDestination } + return .result(opensIntent: OpenURLIntent(url)) + } +} + +struct NewChatInWorkspaceIntent: AppIntent { + static let title: LocalizedStringResource = "New Chat in Workspace" + static let description = IntentDescription("Open a new chat in a cached Aiden workspace.") + + @Parameter(title: "Workspace") var workspace: AidenWorkspaceIntentEntity + + static var parameterSummary: some ParameterSummary { + Summary("New chat in \(\.$workspace)") + } + + func perform() async throws -> some IntentResult & OpensIntent { + guard let url = AidenDeepLink.newChatURL( + instanceId: workspace.instanceId, + workspaceId: workspace.workspaceId, + startsVoice: false + ) else { throw AidenIntentError.invalidDestination } + return .result(opensIntent: OpenURLIntent(url)) + } +} + +enum AidenIntentError: Error { case invalidDestination } + +struct AidenShortcuts: AppShortcutsProvider { + static var appShortcuts: [AppShortcut] { + AppShortcut( + intent: NewChatIntent(), + phrases: ["New chat in \(.applicationName)", "Start a new \(.applicationName) chat"], + shortTitle: "New Chat", + systemImageName: "square.and.pencil" + ) + AppShortcut( + intent: NewChatVoiceIntent(), + phrases: ["New voice chat in \(.applicationName)", "Start voice in \(.applicationName)"], + shortTitle: "New Chat with Voice", + systemImageName: "mic.badge.plus" + ) + AppShortcut( + intent: NewChatInWorkspaceIntent(), + phrases: ["New \(.applicationName) chat in \(\.$workspace)"], + shortTitle: "Chat in Workspace", + systemImageName: "folder.badge.plus" + ) + } +} diff --git a/ios/AidenOnTheGo/Auth/KeychainStore.swift b/ios/AidenOnTheGo/Auth/KeychainStore.swift new file mode 100644 index 00000000..dd645be7 --- /dev/null +++ b/ios/AidenOnTheGo/Auth/KeychainStore.swift @@ -0,0 +1,67 @@ +import Foundation +import KeychainAccess + +protocol KeychainStoring { + func save(_ value: String, forKey key: KeychainStore.Key) throws + func load(_ key: KeychainStore.Key) throws -> String? + func delete(_ key: KeychainStore.Key) throws + + // A device credential is scoped to Aiden's stable installation identifier. + // Switching or removing one paired Mac must never read or clear another + // installation's credential. + func save(_ value: String, forKey key: KeychainStore.Key, scope: String) throws + func load(_ key: KeychainStore.Key, scope: String) throws -> String? + func delete(_ key: KeychainStore.Key, scope: String) throws +} + +struct KeychainStore: KeychainStoring { + enum Key: String { + // Aiden Remote installation metadata and the active installation live + // in one Keychain snapshot. Per-device bearer credentials are stored in + // separately scoped entries so switching or removing one installation + // cannot expose or delete another installation's credential. + case remoteInstallations = "aiden_remote_installations" + case remoteCredential = "aiden_remote_credential" + } + + private let keychain: Keychain + + init(service: String? = nil) { + let service = service + ?? Bundle.main.object(forInfoDictionaryKey: "AidenKeychainService") as? String + ?? Bundle.main.bundleIdentifier + ?? "sbtbiswas.AidenOnTheGo.pairing" + self.keychain = Keychain(service: service) + .accessibility(.afterFirstUnlockThisDeviceOnly) + } + + func save(_ value: String, forKey key: Key) throws { + try keychain.set(value, key: key.rawValue) + } + + func load(_ key: Key) throws -> String? { + try keychain.get(key.rawValue) + } + + func delete(_ key: Key) throws { + try keychain.remove(key.rawValue) + } + + func save(_ value: String, forKey key: Key, scope: String) throws { + try keychain.set(value, key: Self.scopedKey(key, scope: scope)) + } + + func load(_ key: Key, scope: String) throws -> String? { + try keychain.get(Self.scopedKey(key, scope: scope)) + } + + func delete(_ key: Key, scope: String) throws { + try keychain.remove(Self.scopedKey(key, scope: scope)) + } + + /// Namespaces a logical key by an installation scope. `::` cannot appear in + /// the fixed lowercase key names, so the representation is unambiguous. + static func scopedKey(_ key: Key, scope: String) -> String { + "\(key.rawValue)::\(scope)" + } +} diff --git a/ios/AidenOnTheGo/Config/AidenAppearance.swift b/ios/AidenOnTheGo/Config/AidenAppearance.swift new file mode 100644 index 00000000..2d402a39 --- /dev/null +++ b/ios/AidenOnTheGo/Config/AidenAppearance.swift @@ -0,0 +1,521 @@ +import Observation +import SwiftUI + +enum AidenAppearanceMode: String, CaseIterable, Identifiable, Codable, Sendable { + case system + case light + case dark + + var id: String { rawValue } + + var title: String { + switch self { + case .system: "System" + case .light: "Light" + case .dark: "Dark" + } + } + + var colorScheme: ColorScheme? { + switch self { + case .system: nil + case .light: .light + case .dark: .dark + } + } +} + +enum AidenThemePresetID: String, CaseIterable, Identifiable, Codable, Sendable { + case aiden + case slate + case berry + case moss + + var id: String { rawValue } + + var title: String { + switch self { + case .aiden: "Aiden" + case .slate: "Slate" + case .berry: "Berry" + case .moss: "Moss" + } + } +} + +enum AidenUIFontID: String, CaseIterable, Identifiable, Codable, Sendable { + case system + case rounded + case humanist + + var id: String { rawValue } + + var title: String { + switch self { + case .system: "System" + case .rounded: "Rounded" + case .humanist: "Humanist" + } + } + + func font(size: CGFloat) -> Font { + switch self { + case .system: .system(size: size) + case .rounded: .system(size: size, design: .rounded) + case .humanist: .custom("Avenir Next", size: size) + } + } +} + +enum AidenCodeFontID: String, CaseIterable, Identifiable, Codable, Sendable { + case sfMono = "sf-mono" + case menlo + case monaco + + var id: String { rawValue } + + var title: String { + switch self { + case .sfMono: "SF Mono" + case .menlo: "Menlo" + case .monaco: "Monaco" + } + } + + func font(size: CGFloat, relativeTo style: Font.TextStyle = .body) -> Font { + let name = switch self { + case .sfMono: "SFMono-Regular" + case .menlo: "Menlo-Regular" + case .monaco: "Monaco" + } + return .custom(name, size: size, relativeTo: style) + } +} + +enum AidenReduceMotionPreference: String, CaseIterable, Identifiable, Codable, Sendable { + case system + case on + case off + + var id: String { rawValue } + + var title: String { + switch self { + case .system: "Follow System" + case .on: "On" + case .off: "Off" + } + } +} + +enum AidenDiffMarkerPreference: String, CaseIterable, Identifiable, Codable, Sendable { + case color + case symbols + + var id: String { rawValue } + var title: String { self == .symbols ? "Symbols and color" : "Color only" } +} + +struct AidenPalette: Equatable, Sendable { + let canvasHex: String + let sidebarHex: String + let raisedHex: String + let foregroundHex: String + let secondaryHex: String + let accentHex: String + let successHex: String + let warningHex: String + let dangerHex: String + + var canvas: Color { Color(aidenHex: canvasHex) } + var sidebar: Color { Color(aidenHex: sidebarHex) } + var raised: Color { Color(aidenHex: raisedHex) } + var foreground: Color { Color(aidenHex: foregroundHex) } + var secondary: Color { Color(aidenHex: secondaryHex) } + var accent: Color { Color(aidenHex: accentHex) } + var success: Color { Color(aidenHex: successHex) } + var warning: Color { Color(aidenHex: warningHex) } + var danger: Color { Color(aidenHex: dangerHex) } + + func applyingContrast(_ contrast: Int, baseline: Int) -> AidenPalette { + guard contrast != baseline else { return self } + let delta = Double(contrast - baseline) + let secondaryTarget = delta > 0 ? foregroundHex : canvasHex + let fraction = min(abs(delta) / 100 * (delta > 0 ? 0.7 : 0.25), 0.7) + return AidenPalette( + canvasHex: canvasHex, + sidebarHex: sidebarHex, + raisedHex: raisedHex, + foregroundHex: foregroundHex, + secondaryHex: Color.mixHex(secondaryHex, secondaryTarget, fraction: fraction), + accentHex: accentHex, + successHex: successHex, + warningHex: warningHex, + dangerHex: dangerHex + ) + } +} + +enum AidenThemeCatalog { + static func palette(preset: AidenThemePresetID, scheme: ColorScheme) -> AidenPalette { + palettes[preset]![scheme == .dark ? 1 : 0] + } + + static let palettes: [AidenThemePresetID: [AidenPalette]] = [ + .aiden: [ + .init(canvasHex: "#F6F7F9", sidebarHex: "#EEF0F3", raisedHex: "#FFFFFF", foregroundHex: "#3D3F41", secondaryHex: "#6B7280", accentHex: "#006AD6", successHex: "#30D158", warningHex: "#FF9F0A", dangerHex: "#FF453A"), + .init(canvasHex: "#181B21", sidebarHex: "#20242C", raisedHex: "#292E37", foregroundHex: "#D1D4DA", secondaryHex: "#9AA3AE", accentHex: "#3E97F6", successHex: "#32D17A", warningHex: "#FFB020", dangerHex: "#FF5E57"), + ], + .slate: [ + .init(canvasHex: "#F2F5F9", sidebarHex: "#E6EBF2", raisedHex: "#FFFFFF", foregroundHex: "#3A434E", secondaryHex: "#637083", accentHex: "#087581", successHex: "#2DB67D", warningHex: "#E0A72E", dangerHex: "#E24D5B"), + .init(canvasHex: "#181E26", sidebarHex: "#202833", raisedHex: "#29323E", foregroundHex: "#D1D6DE", secondaryHex: "#94A3BB", accentHex: "#21A9BE", successHex: "#35C08A", warningHex: "#D4A72C", dangerHex: "#F87171"), + ], + .berry: [ + .init(canvasHex: "#FBF4F7", sidebarHex: "#F1E8EE", raisedHex: "#FFFFFF", foregroundHex: "#443F4A", secondaryHex: "#6E6470", accentHex: "#B42C70", successHex: "#22C7A8", warningHex: "#E3A23C", dangerHex: "#E24C5A"), + .init(canvasHex: "#1D1822", sidebarHex: "#251D2B", raisedHex: "#2E2435", foregroundHex: "#D5CFD6", secondaryHex: "#A39AA6", accentHex: "#E8629F", successHex: "#32D1B2", warningHex: "#D9A441", dangerHex: "#F0717A"), + ], + .moss: [ + .init(canvasHex: "#F3F6F4", sidebarHex: "#E7ECE8", raisedHex: "#FFFFFF", foregroundHex: "#3F4943", secondaryHex: "#65736B", accentHex: "#157862", successHex: "#3DBF7D", warningHex: "#D4A22A", dangerHex: "#E05353"), + .init(canvasHex: "#18201C", sidebarHex: "#202A25", raisedHex: "#29342E", foregroundHex: "#D1D6D3", secondaryHex: "#95A39B", accentHex: "#42B596", successHex: "#47D18C", warningHex: "#D9B43A", dangerHex: "#EB6B6B"), + ], + ] +} + +struct AidenSidebarLogo: View { + var size: CGFloat = 24 + var color: Color? = nil + + var body: some View { + Image("AidenSidebarLogo") + .renderingMode(.template) + .resizable() + .scaledToFit() + .foregroundStyle(color ?? .primary) + .frame(width: size, height: size) + .accessibilityHidden(true) + } +} + +@MainActor +@Observable +final class AidenAppearanceStore { + private enum Key { + static let mode = "aiden.appearance.mode" + static let lightPreset = "aiden.appearance.lightPreset" + static let darkPreset = "aiden.appearance.darkPreset" + static let lightUIFont = "aiden.appearance.lightUIFont" + static let darkUIFont = "aiden.appearance.darkUIFont" + static let lightCodeFont = "aiden.appearance.lightCodeFont" + static let darkCodeFont = "aiden.appearance.darkCodeFont" + static let lightContrast = "aiden.appearance.lightContrast" + static let darkContrast = "aiden.appearance.darkContrast" + static let lightTranslucentSidebar = "aiden.appearance.lightTranslucentSidebar" + static let darkTranslucentSidebar = "aiden.appearance.darkTranslucentSidebar" + static let reduceMotion = "aiden.appearance.reduceMotion" + static let uiFontSize = "aiden.appearance.uiFontSize" + static let codeFontSize = "aiden.appearance.codeFontSize" + static let diffMarkers = "aiden.appearance.diffMarkers" + } + + private let defaults: UserDefaults + var mode: AidenAppearanceMode { didSet { defaults.set(mode.rawValue, forKey: Key.mode) } } + var lightPreset: AidenThemePresetID { didSet { defaults.set(lightPreset.rawValue, forKey: Key.lightPreset) } } + var darkPreset: AidenThemePresetID { didSet { defaults.set(darkPreset.rawValue, forKey: Key.darkPreset) } } + var lightUIFont: AidenUIFontID { didSet { defaults.set(lightUIFont.rawValue, forKey: Key.lightUIFont) } } + var darkUIFont: AidenUIFontID { didSet { defaults.set(darkUIFont.rawValue, forKey: Key.darkUIFont) } } + var lightCodeFont: AidenCodeFontID { didSet { defaults.set(lightCodeFont.rawValue, forKey: Key.lightCodeFont) } } + var darkCodeFont: AidenCodeFontID { didSet { defaults.set(darkCodeFont.rawValue, forKey: Key.darkCodeFont) } } + var lightContrast: Int { didSet { defaults.set(Self.clamp(lightContrast, to: 0...100), forKey: Key.lightContrast) } } + var darkContrast: Int { didSet { defaults.set(Self.clamp(darkContrast, to: 0...100), forKey: Key.darkContrast) } } + var lightTranslucentSidebar: Bool { didSet { defaults.set(lightTranslucentSidebar, forKey: Key.lightTranslucentSidebar) } } + var darkTranslucentSidebar: Bool { didSet { defaults.set(darkTranslucentSidebar, forKey: Key.darkTranslucentSidebar) } } + var reduceMotion: AidenReduceMotionPreference { didSet { defaults.set(reduceMotion.rawValue, forKey: Key.reduceMotion) } } + var uiFontSize: Int { didSet { defaults.set(Self.clamp(uiFontSize, to: 12...18), forKey: Key.uiFontSize) } } + var codeFontSize: Int { didSet { defaults.set(Self.clamp(codeFontSize, to: 10...18), forKey: Key.codeFontSize) } } + var diffMarkers: AidenDiffMarkerPreference { didSet { defaults.set(diffMarkers.rawValue, forKey: Key.diffMarkers) } } + + init(defaults: UserDefaults = .standard) { + self.defaults = defaults + mode = AidenAppearanceMode(rawValue: defaults.string(forKey: Key.mode) ?? "") ?? .system + lightPreset = AidenThemePresetID(rawValue: defaults.string(forKey: Key.lightPreset) ?? "") ?? .aiden + darkPreset = AidenThemePresetID(rawValue: defaults.string(forKey: Key.darkPreset) ?? "") ?? .aiden + lightUIFont = AidenUIFontID(rawValue: defaults.string(forKey: Key.lightUIFont) ?? "") ?? .system + darkUIFont = AidenUIFontID(rawValue: defaults.string(forKey: Key.darkUIFont) ?? "") ?? .system + lightCodeFont = AidenCodeFontID(rawValue: defaults.string(forKey: Key.lightCodeFont) ?? "") ?? .sfMono + darkCodeFont = AidenCodeFontID(rawValue: defaults.string(forKey: Key.darkCodeFont) ?? "") ?? .sfMono + lightContrast = defaults.object(forKey: Key.lightContrast) == nil ? 45 : Self.clamp(defaults.integer(forKey: Key.lightContrast), to: 0...100) + darkContrast = defaults.object(forKey: Key.darkContrast) == nil ? 60 : Self.clamp(defaults.integer(forKey: Key.darkContrast), to: 0...100) + lightTranslucentSidebar = defaults.object(forKey: Key.lightTranslucentSidebar) == nil ? true : defaults.bool(forKey: Key.lightTranslucentSidebar) + darkTranslucentSidebar = defaults.object(forKey: Key.darkTranslucentSidebar) == nil ? true : defaults.bool(forKey: Key.darkTranslucentSidebar) + reduceMotion = AidenReduceMotionPreference(rawValue: defaults.string(forKey: Key.reduceMotion) ?? "") ?? .system + uiFontSize = defaults.object(forKey: Key.uiFontSize) == nil ? 14 : Self.clamp(defaults.integer(forKey: Key.uiFontSize), to: 12...18) + codeFontSize = defaults.object(forKey: Key.codeFontSize) == nil ? 12 : Self.clamp(defaults.integer(forKey: Key.codeFontSize), to: 10...18) + diffMarkers = AidenDiffMarkerPreference(rawValue: defaults.string(forKey: Key.diffMarkers) ?? "") ?? .symbols + } + + func palette(for scheme: ColorScheme, systemHighContrast: Bool = false) -> AidenPalette { + let isDark = scheme == .dark + let baseline = isDark ? 60 : 45 + let requested = (isDark ? darkContrast : lightContrast) + (systemHighContrast ? 20 : 0) + return AidenThemeCatalog + .palette(preset: isDark ? darkPreset : lightPreset, scheme: scheme) + .applyingContrast(Self.clamp(requested, to: 0...100), baseline: baseline) + } + + func uiFont(for scheme: ColorScheme) -> AidenUIFontID { scheme == .dark ? darkUIFont : lightUIFont } + func codeFont(for scheme: ColorScheme) -> AidenCodeFontID { scheme == .dark ? darkCodeFont : lightCodeFont } + func translucentSidebar(for scheme: ColorScheme) -> Bool { scheme == .dark ? darkTranslucentSidebar : lightTranslucentSidebar } + + func resolvedReduceMotion(system: Bool) -> Bool { + switch reduceMotion { + case .system: system + case .on: true + case .off: false + } + } + + private static func clamp(_ value: Int, to range: ClosedRange) -> Int { + min(max(value, range.lowerBound), range.upperBound) + } +} + +private struct AidenPaletteEnvironmentKey: EnvironmentKey { + static let defaultValue = AidenThemeCatalog.palette(preset: .aiden, scheme: .light) +} + +private struct AidenReduceMotionEnvironmentKey: EnvironmentKey { static let defaultValue = false } +private struct AidenDiffMarkerEnvironmentKey: EnvironmentKey { static let defaultValue = AidenDiffMarkerPreference.symbols } +private struct AidenSidebarTranslucencyEnvironmentKey: EnvironmentKey { static let defaultValue = true } +private struct AidenCodeTypographyEnvironmentKey: EnvironmentKey { + static let defaultValue = AidenCodeTypography(font: .sfMono, size: 12) +} + +struct AidenCodeTypography: Equatable, Sendable { + let font: AidenCodeFontID + let size: Int + + func swiftUIFont(relativeTo style: Font.TextStyle = .body) -> Font { + font.font(size: CGFloat(size), relativeTo: style) + } +} + +extension EnvironmentValues { + var aidenPalette: AidenPalette { + get { self[AidenPaletteEnvironmentKey.self] } + set { self[AidenPaletteEnvironmentKey.self] = newValue } + } + + var aidenReduceMotion: Bool { + get { self[AidenReduceMotionEnvironmentKey.self] } + set { self[AidenReduceMotionEnvironmentKey.self] = newValue } + } + + var aidenDiffMarkers: AidenDiffMarkerPreference { + get { self[AidenDiffMarkerEnvironmentKey.self] } + set { self[AidenDiffMarkerEnvironmentKey.self] = newValue } + } + + var aidenSidebarTranslucent: Bool { + get { self[AidenSidebarTranslucencyEnvironmentKey.self] } + set { self[AidenSidebarTranslucencyEnvironmentKey.self] = newValue } + } + + var aidenCodeTypography: AidenCodeTypography { + get { self[AidenCodeTypographyEnvironmentKey.self] } + set { self[AidenCodeTypographyEnvironmentKey.self] = newValue } + } +} + +struct AidenAppearanceRoot: View { + @Bindable var appearance: AidenAppearanceStore + @Environment(\.colorScheme) private var colorScheme + @Environment(\.colorSchemeContrast) private var colorSchemeContrast + @Environment(\.accessibilityReduceMotion) private var systemReduceMotion + @ViewBuilder let content: () -> Content + + var body: some View { + let effectiveScheme: ColorScheme = switch appearance.mode { + case .system: colorScheme + case .light: .light + case .dark: .dark + } + let palette = appearance.palette( + for: effectiveScheme, + systemHighContrast: colorSchemeContrast == .increased + ) + content() + .environment(\.aidenPalette, palette) + .environment(\.aidenReduceMotion, appearance.resolvedReduceMotion(system: systemReduceMotion)) + .environment(\.aidenDiffMarkers, appearance.diffMarkers) + .environment(\.aidenSidebarTranslucent, appearance.translucentSidebar(for: effectiveScheme)) + .environment(\.aidenCodeTypography, AidenCodeTypography(font: appearance.codeFont(for: effectiveScheme), size: appearance.codeFontSize)) + .modifier(AidenUITypographyModifier(font: appearance.uiFont(for: effectiveScheme), size: appearance.uiFontSize)) + .preferredColorScheme(appearance.mode.colorScheme) + .tint(palette.accent) + .background(palette.canvas.ignoresSafeArea()) + } +} + +struct AidenAppearanceSettingsView: View { + @Environment(\.dismiss) private var dismiss + @Bindable var appearance: AidenAppearanceStore + @AppStorage(AidenRemoteLiveActivityManager.responseExcerptPreferenceKey) + private var showsLiveActivityResponseExcerpts = false + + var body: some View { + NavigationStack { + Form { + Section("Appearance") { + Picker("Mode", selection: $appearance.mode) { + ForEach(AidenAppearanceMode.allCases) { mode in + Text(mode.title).tag(mode) + } + } + .pickerStyle(.segmented) + } + + Section("Light Style") { + presetPicker(selection: $appearance.lightPreset, scheme: .light) + variantControls( + uiFont: $appearance.lightUIFont, + codeFont: $appearance.lightCodeFont, + contrast: $appearance.lightContrast, + translucentSidebar: $appearance.lightTranslucentSidebar + ) + } + + Section("Dark Style") { + presetPicker(selection: $appearance.darkPreset, scheme: .dark) + variantControls( + uiFont: $appearance.darkUIFont, + codeFont: $appearance.darkCodeFont, + contrast: $appearance.darkContrast, + translucentSidebar: $appearance.darkTranslucentSidebar + ) + } + + Section("Text and Motion") { + Stepper("UI size: \(appearance.uiFontSize)", value: $appearance.uiFontSize, in: 12...18) + Stepper("Code size: \(appearance.codeFontSize)", value: $appearance.codeFontSize, in: 10...18) + Picker("Reduce Motion", selection: $appearance.reduceMotion) { + ForEach(AidenReduceMotionPreference.allCases) { preference in + Text(preference.title).tag(preference) + } + } + Picker("Diff markers", selection: $appearance.diffMarkers) { + ForEach(AidenDiffMarkerPreference.allCases) { preference in + Text(preference.title).tag(preference) + } + } + } + + Section { + Toggle("Show response excerpts on Lock Screen", isOn: $showsLiveActivityResponseExcerpts) + } header: { + Text("Privacy") + } footer: { + Text("Off by default. Live Activities otherwise show only the chat title and bounded status.") + } + } + .navigationTitle("Appearance") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .confirmationAction) { + Button("Done") { dismiss() } + } + } + } + } + + @ViewBuilder + private func variantControls( + uiFont: Binding, + codeFont: Binding, + contrast: Binding, + translucentSidebar: Binding + ) -> some View { + Picker("UI font", selection: uiFont) { + ForEach(AidenUIFontID.allCases) { font in Text(font.title).tag(font) } + } + Picker("Code font", selection: codeFont) { + ForEach(AidenCodeFontID.allCases) { font in Text(font.title).tag(font) } + } + VStack(alignment: .leading) { + HStack { + Text("Contrast") + Spacer() + Text("\(contrast.wrappedValue)").foregroundStyle(.secondary) + } + Slider( + value: Binding( + get: { Double(contrast.wrappedValue) }, + set: { contrast.wrappedValue = Int($0.rounded()) } + ), + in: 0...100, + step: 1 + ) + .accessibilityLabel("Contrast") + .accessibilityValue("\(contrast.wrappedValue) percent") + } + Toggle("Translucent sidebar", isOn: translucentSidebar) + } + + private func presetPicker( + selection: Binding, + scheme: ColorScheme + ) -> some View { + Picker("Style", selection: selection) { + ForEach(AidenThemePresetID.allCases) { preset in + let palette = AidenThemeCatalog.palette(preset: preset, scheme: scheme) + Label { + Text(preset.title) + } icon: { + Image(systemName: "circle.fill").foregroundStyle(palette.accent) + } + .tag(preset) + } + } + .pickerStyle(.inline) + .labelsHidden() + } +} + +private struct AidenUITypographyModifier: ViewModifier { + let font: AidenUIFontID + @ScaledMetric(relativeTo: .body) private var scaledSize: CGFloat = 14 + + init(font: AidenUIFontID, size: Int) { + self.font = font + _scaledSize = ScaledMetric(wrappedValue: CGFloat(size), relativeTo: .body) + } + + func body(content: Content) -> some View { + content.font(font.font(size: scaledSize)) + } +} + +extension Color { + init(aidenHex: String) { + let hex = aidenHex.hasPrefix("#") ? String(aidenHex.dropFirst()) : aidenHex + let value = UInt64(hex, radix: 16) ?? 0 + self.init( + red: Double((value >> 16) & 0xFF) / 255, + green: Double((value >> 8) & 0xFF) / 255, + blue: Double(value & 0xFF) / 255 + ) + } + + fileprivate static func mixHex(_ from: String, _ to: String, fraction: Double) -> String { + let start = rgb(from) + let end = rgb(to) + let amount = min(max(fraction, 0), 1) + let channels = zip(start, end).map { Int((Double($0.0) + (Double($0.1) - Double($0.0)) * amount).rounded()) } + return String(format: "#%02X%02X%02X", channels[0], channels[1], channels[2]) + } + + private static func rgb(_ hex: String) -> [Int] { + let clean = hex.hasPrefix("#") ? String(hex.dropFirst()) : hex + let value = Int(clean, radix: 16) ?? 0 + return [(value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF] + } +} diff --git a/ios/AidenOnTheGo/Config/AppConfig.swift b/ios/AidenOnTheGo/Config/AppConfig.swift new file mode 100644 index 00000000..9bdb5f1d --- /dev/null +++ b/ios/AidenOnTheGo/Config/AppConfig.swift @@ -0,0 +1,21 @@ +import Foundation + +enum AppConfig { + static var appGroupIdentifier: String { + Bundle.main.object(forInfoDictionaryKey: "AidenAppGroupIdentifier") as? String + ?? "group.sbtbiswas.AidenOnTheGo" + } + + static let privacyPolicyURL = URL(staticString: "https://chatwithaiden.com/privacy") + static let supportURL = URL(staticString: "https://chatwithaiden.com/") +} + +extension URL { + init(staticString string: StaticString) { + let value = string.withUTF8Buffer { String(decoding: $0, as: UTF8.self) } + guard let url = URL(string: value) else { + preconditionFailure("Invalid static URL literal: \(value)") + } + self = url + } +} diff --git a/ios/AidenOnTheGo/ContentView.swift b/ios/AidenOnTheGo/ContentView.swift new file mode 100644 index 00000000..6ee94f35 --- /dev/null +++ b/ios/AidenOnTheGo/ContentView.swift @@ -0,0 +1,74 @@ +import SwiftUI + +struct ContentView: View { + @Bindable var coordinator: AidenRemoteCoordinator + @Environment(\.scenePhase) private var scenePhase + @State private var navigationRequest: AidenNavigationRequest? + + var body: some View { + Group { + switch coordinator.connectionState { + case .needsPairing: + AidenPairingView(coordinator: coordinator) + case .connecting, .connected, .offline: + AidenWorkspaceShellView( + coordinator: coordinator, + navigationRequest: $navigationRequest + ) + } + } + .task { await coordinator.start() } + .onOpenURL { url in + guard let request = AidenDeepLink.request(from: url) else { + coordinator.presentedError = String(localized: "That Aiden link is invalid or no longer supported.") + return + } + Task { await open(request) } + } + .onChange(of: scenePhase) { _, phase in + switch phase { + case .active: + guard coordinator.connectionState != .needsPairing else { return } + Task { + await coordinator.connectActiveInstallation() + if let client = try? coordinator.remoteClient() { + await AidenRemoteLiveActivityManager.shared.reconcile(client: client) + } + } + case .background: + Task { await AidenRemoteLiveActivityManager.shared.markAllStale() } + default: + break + } + } + } + + @MainActor + private func open(_ request: AidenNavigationRequest) async { + if let requestedInstance = request.instanceId { + guard coordinator.installationStore.installations.contains(where: { $0.id == requestedInstance }) else { + coordinator.presentedError = String(localized: "This Aiden installation is no longer paired. Pair it again to continue.") + return + } + if coordinator.activeInstanceId != requestedInstance { + await coordinator.switchInstallation(to: requestedInstance) + } + } else if coordinator.connectionState != .connected { + await coordinator.connectActiveInstallation() + } + + guard coordinator.connectionState == .connected else { + coordinator.presentedError = String(localized: "Connect to Aiden Agent before opening this link.") + return + } + navigationRequest = request + } +} + +#Preview { + let appearance = AidenAppearanceStore() + AidenAppearanceRoot(appearance: appearance) { + ContentView(coordinator: AidenRemoteCoordinator()) + } + .environment(appearance) +} diff --git a/ios/AidenOnTheGo/Features/Chat/ComposerVoiceInputController.swift b/ios/AidenOnTheGo/Features/Chat/ComposerVoiceInputController.swift new file mode 100644 index 00000000..4b83b3c2 --- /dev/null +++ b/ios/AidenOnTheGo/Features/Chat/ComposerVoiceInputController.swift @@ -0,0 +1,394 @@ +import AVFoundation +import Foundation +import Observation +import OSLog +import Speech +import UIKit + +/// On-device-only speech input for the composer. Audio never leaves the device. +@MainActor +@Observable +final class ComposerVoiceInputController { + enum State: Equatable { + case idle + case requestingPermission + case listening + } + + private(set) var state: State = .idle + private(set) var errorMessage: String? + private(set) var liveTranscript = "" + + private let speechRecognizerFactory: () -> SFSpeechRecognizer? + private let audioEngineFactory: () -> AVAudioEngine + private var speechRecognizer: SFSpeechRecognizer? + private var audioEngine: AVAudioEngine? + private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest? + private var recognitionTask: SFSpeechRecognitionTask? + private var draftUpdateSession = ComposerVoiceDraftUpdateSession() + private var updateDraft: ((String) -> Void)? + private var suppressNextRecognitionError = false + private var activatedAudioSessionForRecording = false + private var audioTapInstalled = false + private let logger = Logger.aidenVoiceInput + + @ObservationIgnored var locale = Locale.current + + init( + speechRecognizerFactory: @escaping () -> SFSpeechRecognizer? = { + SFSpeechRecognizer(locale: Locale.current) + }, + audioEngineFactory: @escaping () -> AVAudioEngine = { AVAudioEngine() } + ) { + self.speechRecognizerFactory = speechRecognizerFactory + self.audioEngineFactory = audioEngineFactory + } + + var isListening: Bool { state == .listening } + var isRequestingPermission: Bool { state == .requestingPermission } + + func toggle(currentDraft: String, updateDraft: @escaping (String) -> Void) async { + if isListening { + stopKeepingTranscript() + } else { + await start(currentDraft: currentDraft, updateDraft: updateDraft) + } + } + + func stopKeepingTranscript() { + suppressNextRecognitionError = true + stopAcceptingDraftUpdates() + stopAudio(cancelTask: false) + state = .idle + } + + func stopBeforeSubmittingDraft() { + suppressNextRecognitionError = true + stopAcceptingDraftUpdates() + stopAudio(cancelTask: true) + state = .idle + } + + private func start(currentDraft: String, updateDraft: @escaping (String) -> Void) async { + guard state == .idle else { return } + + errorMessage = nil + liveTranscript = "" + suppressNextRecognitionError = false + draftUpdateSession.begin(baseDraft: currentDraft) + self.updateDraft = updateDraft + state = .requestingPermission + + guard let speechRecognizer = onDeviceSpeechRecognizerForRecording() else { + fail( + String(localized: "On-device speech recognition is not available for the current locale."), + logCategory: .speechUnavailable + ) + return + } + + let speechStatus = await requestSpeechAuthorization() + guard state == .requestingPermission else { return } + guard speechStatus == .authorized else { + fail(Self.speechAuthorizationMessage(for: speechStatus), logCategory: .speechAuthorization) + return + } + + let microphoneGranted = await ComposerVoiceMicrophonePermissionRequester.request() + guard state == .requestingPermission else { return } + guard microphoneGranted else { + fail( + String(localized: "Microphone access is disabled. Enable it in Settings to use voice input."), + logCategory: .microphonePermission + ) + return + } + + guard ComposerVoiceInputStartPolicy.canStart( + appIsActive: UIApplication.shared.applicationState == .active + ) else { + fail(ComposerVoiceInputError.appNotActive.localizedDescription, logCategory: .appNotActive) + return + } + + do { + try startRecognition(speechRecognizer: speechRecognizer) + state = .listening + } catch { + fail(error.localizedDescription, logCategory: Self.logCategory(for: error)) + } + } + + private func startRecognition(speechRecognizer: SFSpeechRecognizer) throws { + stopAudio(cancelTask: true) + + let audioSession = AVAudioSession.sharedInstance() + try audioSession.setCategory( + ComposerVoiceAudioSessionConfiguration.category, + mode: ComposerVoiceAudioSessionConfiguration.mode, + options: ComposerVoiceAudioSessionConfiguration.options + ) + try audioSession.setActive(true, options: .notifyOthersOnDeactivation) + activatedAudioSessionForRecording = true + try ComposerVoiceInputStartPolicy.validateAudioSessionInput( + isInputAvailable: audioSession.isInputAvailable, + sampleRate: audioSession.sampleRate, + inputNumberOfChannels: audioSession.inputNumberOfChannels + ) + + let request = SFSpeechAudioBufferRecognitionRequest() + request.shouldReportPartialResults = true + request.requiresOnDeviceRecognition = true + recognitionRequest = request + + let engine = audioEngineFactory() + audioEngine = engine + try ComposerVoiceInputStartPolicy.validateAudioEngine(isRunning: engine.isRunning) + + let inputNode = engine.inputNode + let recordingFormat = inputNode.outputFormat(forBus: 0) + try ComposerVoiceInputPreflight.validate(recordingFormat: recordingFormat) + inputNode.installTap(onBus: 0, bufferSize: 1_024, format: recordingFormat) { [weak request] buffer, _ in + request?.append(buffer) + } + audioTapInstalled = true + + engine.prepare() + recognitionTask = speechRecognizer.recognitionTask(with: request) { [weak self] result, error in + Task { @MainActor in self?.handleRecognition(result: result, error: error) } + } + try engine.start() + AidenComposerAudioCaptureState.shared.setCapturing(true) + } + + private func handleRecognition(result: SFSpeechRecognitionResult?, error: Error?) { + if let result { + liveTranscript = result.bestTranscription.formattedString + if let draft = draftUpdateSession.composedDraft(for: liveTranscript) { + updateDraft?(draft) + } + } + + if let error { + stopAcceptingDraftUpdates() + stopAudio(cancelTask: false) + state = .idle + if suppressNextRecognitionError { + suppressNextRecognitionError = false + } else { + errorMessage = error.localizedDescription + } + } else if result?.isFinal == true { + stopAcceptingDraftUpdates() + stopAudio(cancelTask: false) + state = .idle + suppressNextRecognitionError = false + } + } + + private func stopAcceptingDraftUpdates() { + draftUpdateSession.stopAcceptingUpdates() + updateDraft = nil + } + + private func stopAudio(cancelTask: Bool) { + AidenComposerAudioCaptureState.shared.setCapturing(false) + if let audioEngine { + if audioEngine.isRunning { audioEngine.stop() } + if audioTapInstalled { + audioEngine.inputNode.removeTap(onBus: 0) + audioTapInstalled = false + } + audioEngine.reset() + } + audioEngine = nil + recognitionRequest?.endAudio() + if cancelTask { recognitionTask?.cancel() } + recognitionTask = nil + recognitionRequest = nil + if activatedAudioSessionForRecording { + try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation) + activatedAudioSessionForRecording = false + } + } + + private func onDeviceSpeechRecognizerForRecording() -> SFSpeechRecognizer? { + if let speechRecognizer { + return speechRecognizer.supportsOnDeviceRecognition ? speechRecognizer : nil + } + + let target = Self.normalizedLocaleIdentifier(locale.identifier) + guard SFSpeechRecognizer.supportedLocales().contains(where: { + Self.normalizedLocaleIdentifier($0.identifier) == target + }) else { return nil } + + let recognizer = speechRecognizerFactory() + guard recognizer?.supportsOnDeviceRecognition == true else { return nil } + speechRecognizer = recognizer + return recognizer + } + + private static func normalizedLocaleIdentifier(_ identifier: String) -> String { + identifier.replacingOccurrences(of: "_", with: "-").lowercased() + } + + private func fail(_ message: String, logCategory: VoiceInputFailureLogCategory) { + logger.error("Voice input failed category=\(logCategory.rawValue, privacy: .public)") + suppressNextRecognitionError = false + stopAcceptingDraftUpdates() + stopAudio(cancelTask: true) + state = .idle + errorMessage = message + } + + private func requestSpeechAuthorization() async -> SFSpeechRecognizerAuthorizationStatus { + await withCheckedContinuation { continuation in + SFSpeechRecognizer.requestAuthorization { continuation.resume(returning: $0) } + } + } + + private static func speechAuthorizationMessage(for status: SFSpeechRecognizerAuthorizationStatus) -> String { + switch status { + case .denied: + return String(localized: "Speech recognition is disabled. Enable it in Settings to use voice input.") + case .restricted: + return String(localized: "Speech recognition is restricted on this device.") + case .notDetermined: + return String(localized: "Speech recognition permission was not granted.") + case .authorized: + return "" + @unknown default: + return String(localized: "Speech recognition is not available right now.") + } + } + + private static func logCategory(for error: Error) -> VoiceInputFailureLogCategory { + guard let voiceError = error as? ComposerVoiceInputError else { return .audioStartup } + switch voiceError { + case .noAudioInput: return .noAudioInput + case .invalidInputFormat: return .invalidInputFormat + case .appNotActive: return .appNotActive + case .audioEngineAlreadyRunning: return .audioEngineAlreadyRunning + } + } +} + +enum ComposerVoiceMicrophonePermissionRequester { + static func request() async -> Bool { + await withCheckedContinuation { continuation in + AVAudioApplication.requestRecordPermission { continuation.resume(returning: $0) } + } + } +} + +enum ComposerVoiceAudioSessionConfiguration { + static let category = AVAudioSession.Category.playAndRecord + static let mode = AVAudioSession.Mode.measurement + static let options: AVAudioSession.CategoryOptions = [.mixWithOthers, .allowBluetoothHFP] +} + +enum ComposerVoiceInputError: LocalizedError { + case noAudioInput + case invalidInputFormat + case appNotActive + case audioEngineAlreadyRunning + + var errorDescription: String? { + switch self { + case .noAudioInput: + return String(localized: "No microphone input is available. Check the device microphone settings.") + case .invalidInputFormat: + return String(localized: "Voice input is not available because the microphone input format is invalid.") + case .appNotActive: + return String(localized: "Voice input can start only while Aiden On The Go is active.") + case .audioEngineAlreadyRunning: + return String(localized: "Voice input is already preparing the microphone. Try again in a moment.") + } + } +} + +enum VoiceInputFailureLogCategory: String { + case speechUnavailable, speechAuthorization, microphonePermission, appNotActive + case noAudioInput, invalidInputFormat, audioEngineAlreadyRunning, audioStartup +} + +enum ComposerVoiceInputStartPolicy { + static func canStart(appIsActive: Bool) -> Bool { appIsActive } + + static func validateAudioSessionInput( + isInputAvailable: Bool, + sampleRate: Double, + inputNumberOfChannels: Int + ) throws { + guard isInputAvailable else { throw ComposerVoiceInputError.noAudioInput } + try ComposerVoiceInputPreflight.validate( + sampleRate: sampleRate, + channelCount: UInt32(max(inputNumberOfChannels, 0)) + ) + } + + static func validateAudioEngine(isRunning: Bool) throws { + guard !isRunning else { throw ComposerVoiceInputError.audioEngineAlreadyRunning } + } +} + +enum ComposerVoiceInputPreflight { + static let validSampleRateRange: ClosedRange = 8_000...192_000 + static let validChannelCountRange: ClosedRange = 1...16 + + static func validate(sampleRate: Double, channelCount: UInt32) throws { + guard sampleRate.isFinite, + validSampleRateRange.contains(sampleRate), + validChannelCountRange.contains(channelCount) + else { throw ComposerVoiceInputError.invalidInputFormat } + } + + static func validate(recordingFormat: AVAudioFormat) throws { + try validate(sampleRate: recordingFormat.sampleRate, channelCount: recordingFormat.channelCount) + } +} + +enum ComposerVoiceDraftComposer { + static func composedDraft(baseDraft: String, transcript: String) -> String { + let transcript = transcript.trimmingCharacters(in: .whitespacesAndNewlines) + guard !transcript.isEmpty else { return baseDraft } + let draft = baseDraft.trimmingCharacters(in: .whitespacesAndNewlines) + return draft.isEmpty ? transcript : "\(draft) \(transcript)" + } +} + +struct ComposerVoiceDraftUpdateSession { + private var baseDraft = "" + private var acceptsUpdates = false + + mutating func begin(baseDraft: String) { + self.baseDraft = baseDraft + acceptsUpdates = true + } + + mutating func stopAcceptingUpdates() { acceptsUpdates = false } + + func composedDraft(for transcript: String) -> String? { + guard acceptsUpdates else { return nil } + return ComposerVoiceDraftComposer.composedDraft(baseDraft: baseDraft, transcript: transcript) + } +} + +private extension Logger { + static let aidenVoiceInput = Logger( + subsystem: Bundle.main.bundleIdentifier ?? "AidenOnTheGo", + category: "VoiceInput" + ) +} + +@MainActor +private final class AidenComposerAudioCaptureState { + static let shared = AidenComposerAudioCaptureState() + private(set) var isCapturing = false + + private init() {} + + func setCapturing(_ capturing: Bool) { + isCapturing = capturing + } +} diff --git a/ios/AidenOnTheGo/Features/Remote/AidenChatFeature.swift b/ios/AidenOnTheGo/Features/Remote/AidenChatFeature.swift new file mode 100644 index 00000000..191d69ae --- /dev/null +++ b/ios/AidenOnTheGo/Features/Remote/AidenChatFeature.swift @@ -0,0 +1,1624 @@ +import AVFoundation +import MarkdownUI +import Observation +import PhotosUI +import SwiftUI +import UniformTypeIdentifiers +import UIKit + +enum AidenAttachmentPreparationError: LocalizedError, Equatable { + case invalidImage + case imageTooLarge + case invalidText + case unsupportedTextType + case fileTooLarge + + var errorDescription: String? { + switch self { + case .invalidImage: "That image could not be read." + case .imageTooLarge: "That image is too large to attach." + case .invalidText: "That file is not valid UTF-8 text." + case .unsupportedTextType: "Choose an image, plain text, Markdown, CSV, JSON, XML, YAML, JavaScript, or TypeScript file." + case .fileTooLarge: "That file is too large to attach." + } + } +} + +enum AidenAttachmentPreparation { + static let maximumSourceImageBytes = 32 * 1_048_576 + static let maximumImageBytes = 8 * 1_048_576 + static let maximumImageDimension: CGFloat = 16_384 + static let maximumImagePixels: CGFloat = 40_000_000 + static let maximumTextBytes = 400_000 + static let maximumTextScalars = 100_000 + + static func imageUpload(data: Data, name: String) throws -> AidenAttachmentUpload { + guard !data.isEmpty, data.count <= maximumSourceImageBytes, let image = UIImage(data: data) else { + throw data.count > maximumSourceImageBytes + ? AidenAttachmentPreparationError.imageTooLarge + : AidenAttachmentPreparationError.invalidImage + } + let pixelWidth = image.size.width * image.scale + let pixelHeight = image.size.height * image.scale + guard pixelWidth.isFinite, pixelHeight.isFinite, pixelWidth > 0, pixelHeight > 0 else { + throw AidenAttachmentPreparationError.invalidImage + } + guard pixelWidth <= maximumImageDimension, + pixelHeight <= maximumImageDimension, + pixelWidth * pixelHeight <= maximumImagePixels + else { + throw AidenAttachmentPreparationError.imageTooLarge + } + for edge in [3_072.0, 2_048.0, 1_536.0] { + let rendered = scaled(image, maximumEdge: edge) + for quality in [0.86, 0.72, 0.58] { + if let encoded = rendered.jpegData(compressionQuality: quality), encoded.count <= maximumImageBytes { + return .image(name: safeImageName(name), mimeType: "image/jpeg", data: encoded) + } + } + } + throw AidenAttachmentPreparationError.imageTooLarge + } + + static func textUpload(data: Data, name: String, mimeType: String) throws -> AidenAttachmentUpload { + guard data.count <= maximumTextBytes else { throw AidenAttachmentPreparationError.fileTooLarge } + guard let text = String(data: data, encoding: .utf8) else { + throw AidenAttachmentPreparationError.invalidText + } + guard text.unicodeScalars.count <= maximumTextScalars else { + throw AidenAttachmentPreparationError.fileTooLarge + } + let canonicalMimeType = try allowedTextMimeType(mimeType, name: name) + return .text(name: safeDisplayName(name), mimeType: canonicalMimeType, text: text) + } + + static func fileUpload(url: URL) throws -> AidenAttachmentUpload { + let accessed = url.startAccessingSecurityScopedResource() + defer { if accessed { url.stopAccessingSecurityScopedResource() } } + let values = try url.resourceValues(forKeys: [.fileSizeKey, .contentTypeKey]) + let isImage = values.contentType?.conforms(to: .image) == true + let readLimit = isImage ? maximumSourceImageBytes : maximumTextBytes + if isImage, let fileSize = values.fileSize, fileSize > readLimit { + throw AidenAttachmentPreparationError.fileTooLarge + } + let handle = try FileHandle(forReadingFrom: url) + defer { try? handle.close() } + let data = try handle.read(upToCount: readLimit + 1) ?? Data() + if isImage { + guard data.count <= readLimit else { throw AidenAttachmentPreparationError.fileTooLarge } + return try imageUpload(data: data, name: url.lastPathComponent) + } + let mimeType = try allowedTextMimeType( + values.contentType?.preferredMIMEType ?? "text/plain", + name: url.lastPathComponent + ) + let readWasTruncated = data.count > maximumTextBytes || (values.fileSize ?? 0) > maximumTextBytes + let prefix = Data(data.prefix(maximumTextBytes)) + guard let decoded = decodedUTF8Prefix(prefix, allowTrailingPartialScalar: readWasTruncated) else { + throw AidenAttachmentPreparationError.invalidText + } + let suffix = "\n… [truncated]" + let scalars = decoded.unicodeScalars + let scalarWasTruncated = scalars.count > maximumTextScalars + let shouldTruncate = readWasTruncated || scalarWasTruncated + let maximumContentScalars = shouldTruncate + ? maximumTextScalars - suffix.unicodeScalars.count + : maximumTextScalars + let bounded = String(String.UnicodeScalarView(scalars.prefix(maximumContentScalars))) + return .text( + name: safeDisplayName(url.lastPathComponent), + mimeType: mimeType, + text: shouldTruncate ? bounded + suffix : bounded + ) + } + + private static func decodedUTF8Prefix(_ data: Data, allowTrailingPartialScalar: Bool) -> String? { + if let exact = String(data: data, encoding: .utf8) { return exact } + guard allowTrailingPartialScalar else { return nil } + for count in 1...3 where data.count >= count { + if let value = String(data: data.dropLast(count), encoding: .utf8) { return value } + } + return nil + } + + private static func scaled(_ image: UIImage, maximumEdge: CGFloat) -> UIImage { + let sourceSize = image.size + let sourceEdge = max(sourceSize.width, sourceSize.height) + guard sourceEdge > maximumEdge, sourceSize.width > 0, sourceSize.height > 0 else { return image } + let scale = maximumEdge / sourceEdge + let target = CGSize( + width: max(1, floor(sourceSize.width * scale)), + height: max(1, floor(sourceSize.height * scale)) + ) + let format = UIGraphicsImageRendererFormat.default() + format.scale = 1 + format.opaque = true + return UIGraphicsImageRenderer(size: target, format: format).image { _ in + image.draw(in: CGRect(origin: .zero, size: target)) + } + } + + private static func allowedTextMimeType(_ value: String, name: String) throws -> String { + let normalized = value.lowercased() + let allowed: Set = [ + "text/plain", "text/markdown", "text/csv", "application/json", "application/xml", + "application/yaml", "application/x-yaml", "application/javascript", "application/typescript", + ] + if allowed.contains(normalized) { return normalized } + switch URL(fileURLWithPath: name).pathExtension.lowercased() { + case "md", "markdown": return "text/markdown" + case "csv": return "text/csv" + case "json": return "application/json" + case "xml": return "application/xml" + case "yaml", "yml": return "application/yaml" + case "js", "jsx": return "application/javascript" + case "ts", "tsx": return "application/typescript" + case "txt", "swift", "m", "mm", "h", "c", "cc", "cpp", "py", "rb", "go", "rs", "java", "kt", "sh": + return "text/plain" + default: throw AidenAttachmentPreparationError.unsupportedTextType + } + } + + private static func safeImageName(_ value: String) -> String { + let base = URL(fileURLWithPath: safeDisplayName(value)).deletingPathExtension().lastPathComponent + return safeDisplayName("\(base.isEmpty ? "Photo" : base).jpg") + } + + private static func safeDisplayName(_ value: String) -> String { + let filtered = value.unicodeScalars.filter { scalar in + scalar.value > 0x1f && scalar.value != 0x7f && scalar != "/" && scalar != "\\" + } + let bounded = String(String.UnicodeScalarView(filtered.prefix(255))).trimmingCharacters(in: .whitespacesAndNewlines) + return bounded.isEmpty ? "Attachment" : bounded + } +} + +struct AidenTurnAttemptTracker { + private var pending: (request: AidenTurnStart, key: UUID)? + + mutating func key(for request: AidenTurnStart) -> UUID { + if let pending, pending.request == request { return pending.key } + let key = UUID() + pending = (request, key) + return key + } + + mutating func reset() { + pending = nil + } +} + +@MainActor +@Observable +final class AidenWorkspaceChatsModel { + private let coordinator: AidenRemoteCoordinator + private let workspaceId: String + private let cache: AidenChatCache + private(set) var chats: [AidenChat] = [] + private(set) var isLoading = false + private(set) var isMutating = false + var presentedError: String? + + init( + coordinator: AidenRemoteCoordinator, + workspaceId: String, + cache: AidenChatCache = .shared + ) { + self.coordinator = coordinator + self.workspaceId = workspaceId + self.cache = cache + } + + var isConnected: Bool { coordinator.connectionState == .connected } + + func load() async { + guard let instanceId = coordinator.activeInstanceId else { return } + if chats.isEmpty, let cached = await cache.loadChats(instanceId: instanceId, workspaceId: workspaceId) { + chats = cached + } + guard !isLoading else { return } + isLoading = true + defer { isLoading = false } + do { + let remote = try await coordinator.remoteClient().chats(workspaceId: workspaceId) + chats = Self.sorted(remote) + try await cache.saveChats(chats, instanceId: instanceId, workspaceId: workspaceId) + } catch { + if chats.isEmpty { presentedError = error.localizedDescription } + } + } + + func create() async -> AidenChat? { + guard !isMutating, let instanceId = coordinator.activeInstanceId else { return nil } + isMutating = true + defer { isMutating = false } + do { + let chat = try await coordinator.remoteClient().createChat(workspaceId: workspaceId) + upsert(chat) + try await persist(chat: chat, instanceId: instanceId) + return chat + } catch { + presentedError = error.localizedDescription + return nil + } + } + + func rename(_ chat: AidenChat, to title: String) async { + let cleaned = title.trimmingCharacters(in: .whitespacesAndNewlines) + guard !cleaned.isEmpty, !isMutating, let instanceId = coordinator.activeInstanceId else { return } + isMutating = true + defer { isMutating = false } + var optimistic = chat + optimistic.title = cleaned + upsert(optimistic) + do { + let updated = try await coordinator.remoteClient().updateChat( + id: chat.id, + revision: chat.revision, + title: cleaned + ) + upsert(updated) + try await persist(chat: updated, instanceId: instanceId) + } catch { + upsert(chat) + presentedError = error.localizedDescription + await load() + } + } + + func remove(_ chat: AidenChat) async { + guard !isMutating, let instanceId = coordinator.activeInstanceId else { return } + isMutating = true + defer { isMutating = false } + chats.removeAll { $0.id == chat.id } + do { + try await coordinator.remoteClient().removeChat(id: chat.id, revision: chat.revision) + await cache.removeChat(instanceId: instanceId, chatId: chat.id) + try await cache.saveChats(chats, instanceId: instanceId, workspaceId: workspaceId) + } catch { + upsert(chat) + presentedError = error.localizedDescription + await load() + } + } + + private func upsert(_ chat: AidenChat) { + chats.removeAll { $0.id == chat.id } + chats.append(chat) + chats = Self.sorted(chats) + } + + private func persist(chat: AidenChat, instanceId: String) async throws { + try await cache.saveChat(chat, instanceId: instanceId) + try await cache.saveChats(chats, instanceId: instanceId, workspaceId: workspaceId) + } + + private static func sorted(_ chats: [AidenChat]) -> [AidenChat] { + chats.sorted { + if $0.updatedAt == $1.updatedAt { return $0.id < $1.id } + return $0.updatedAt > $1.updatedAt + } + } +} + +@MainActor +@Observable +final class AidenChatViewModel { + private let coordinator: AidenRemoteCoordinator + private let instanceId: String + private let cache: AidenChatCache + private let liveActivities: AidenRemoteLiveActivityManager + @ObservationIgnored private var streamTask: Task? + @ObservationIgnored private var turnAttempts = AidenTurnAttemptTracker() + + private(set) var chat: AidenChat + private(set) var catalog: AidenModelCatalog? + private(set) var isLoading = false + private(set) var isStarting = false + private(set) var streamState: AidenStreamState? + private(set) var liveText = "" + private(set) var reasoning = "" + private(set) var tools: [AidenLiveTool] = [] + private(set) var timeline: [String] = [] + private(set) var pendingApproval: AidenPendingApproval? + private(set) var pendingAttachments: [AidenAttachmentReference] = [] + private(set) var isUploadingAttachment = false + var draft = "" + var selectedProviderId: String? + var selectedModelId: String? + var selectedThinkingLevel: String? + var presentedError: String? + + init( + coordinator: AidenRemoteCoordinator, + chat: AidenChat, + cache: AidenChatCache = .shared, + liveActivities: AidenRemoteLiveActivityManager? = nil + ) { + self.coordinator = coordinator + self.chat = chat + instanceId = coordinator.activeInstanceId ?? "" + self.cache = cache + self.liveActivities = liveActivities ?? .shared + selectedProviderId = chat.providerId + selectedModelId = chat.modelId + } + + var isConnected: Bool { coordinator.connectionState == .connected } + var isStreaming: Bool { streamState.map { !$0.isTerminal } ?? false } + var canSend: Bool { + (!draft.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || !pendingAttachments.isEmpty) && + isConnected && !isStarting && !isUploadingAttachment && !isStreaming + } + + var selectedProvider: AidenProvider? { + catalog?.providers.first { $0.id == selectedProviderId } + } + + var selectedModel: AidenModel? { + selectedProvider?.models.first { $0.id == selectedModelId } + } + + func load() async { + guard !instanceId.isEmpty, !isLoading else { return } + isLoading = true + if let cached = await cache.loadChat(instanceId: instanceId, chatId: chat.id) { + chat = cached + } + defer { isLoading = false } + do { + async let chatRequest = coordinator.remoteClient().chat(id: chat.id) + async let catalogRequest = coordinator.remoteClient().modelCatalog() + let (remoteChat, remoteCatalog) = try await (chatRequest, catalogRequest) + chat = remoteChat + catalog = remoteCatalog + resolveModelSelection() + try await cache.saveChat(remoteChat, instanceId: instanceId) + } catch { + if chat.messages.isEmpty { presentedError = error.localizedDescription } + } + await restoreStreamIfNeeded() + } + + func selectProvider(_ providerId: String) { + selectedProviderId = providerId + selectedModelId = catalog?.providers.first { $0.id == providerId }?.models.first?.id + selectedThinkingLevel = selectedModel?.thinkingLevels?.first + } + + func selectModel(_ modelId: String) { + selectedModelId = modelId + selectedThinkingLevel = selectedModel?.thinkingLevels?.first + } + + func send() async { + let text = draft.trimmingCharacters(in: .whitespacesAndNewlines) + guard canSend else { return } + let submittedAttachments = pendingAttachments + let previousUpdatedAt = chat.updatedAt + let optimisticID = "local-\(UUID().uuidString.lowercased())" + let now = Date() + let optimisticMessage = AidenChatMessage( + id: optimisticID, + role: .user, + text: text, + attachments: submittedAttachments.map { + AidenMessageAttachment( + id: $0.id, + name: $0.name, + mimeType: $0.mimeType, + kind: $0.kind, + size: $0.size + ) + }, + createdAt: now + ) + + isStarting = true + defer { isStarting = false } + presentedError = nil + draft = "" + pendingAttachments = [] + chat.messages.append(optimisticMessage) + chat.updatedAt = now + streamState = .queued + let request = AidenTurnStart( + text: text, + providerId: selectedProviderId, + modelId: selectedModelId, + thinkingLevel: selectedThinkingLevel, + attachmentIds: pendingAttachments.isEmpty ? nil : pendingAttachments.map(\.id) + ) + let idempotencyKey = turnAttempts.key(for: request) + do { + let response = try await coordinator.remoteClient().startTurn( + chatId: chat.id, + request: request, + idempotencyKey: idempotencyKey + ) + turnAttempts.reset() + chat.messages.removeAll { $0.id == optimisticID } + if !chat.messages.contains(where: { $0.id == response.message.id }) { + chat.messages.append(response.message) + } + liveText = "" + reasoning = "" + tools = [] + timeline = [] + pendingApproval = nil + streamState = .queued + let stream = AidenChatCache.ActiveStream( + streamId: response.streamId, + turnId: response.turnId, + lastSequence: 0 + ) + try? await cache.saveChat(chat, instanceId: instanceId) + try? await cache.saveActiveStream(stream, instanceId: instanceId, chatId: chat.id) + await liveActivities.start( + instanceID: instanceId, + chatID: chat.id, + title: chat.title, + streamID: response.streamId + ) + startStreaming(stream) + } catch { + chat.messages.removeAll { $0.id == optimisticID } + chat.updatedAt = previousUpdatedAt + if draft.isEmpty { draft = text } + if pendingAttachments.isEmpty { pendingAttachments = submittedAttachments } + streamState = nil + presentedError = error.localizedDescription + } + } + + func upload(_ upload: AidenAttachmentUpload) async { + guard isConnected, !isUploadingAttachment, !isStreaming, pendingAttachments.count < 10 else { return } + isUploadingAttachment = true + presentedError = nil + defer { isUploadingAttachment = false } + do { + let reference = try await coordinator.remoteClient().uploadAttachment(chatId: chat.id, upload: upload) + guard reference.isValid() else { + throw AidenRemoteClientError.invalidResponse + } + pendingAttachments.append(reference) + } catch { + presentedError = error.localizedDescription + } + } + + func removeAttachment(_ attachment: AidenAttachmentReference) async { + pendingAttachments.removeAll { $0.id == attachment.id } + do { + try await coordinator.remoteClient().removeAttachment(chatId: chat.id, attachmentId: attachment.id) + } catch { + // The reference is short lived and server cleanup is automatic. Local removal remains authoritative for the composer. + } + } + + func stop() async { + guard let stream = await cache.loadActiveStream(instanceId: instanceId, chatId: chat.id) else { return } + let previousState = streamState + streamState = .cancelled + do { + streamState = try await coordinator.remoteClient().cancelStream(id: stream.streamId).state + } catch { + streamState = previousState + presentedError = error.localizedDescription + } + } + + func respondToApproval(_ decision: AidenApprovalDecision) async { + guard let approval = pendingApproval, approval.expiresAt > Date() else { + pendingApproval = nil + return + } + let previousState = streamState + pendingApproval = nil + streamState = .running + do { + _ = try await coordinator.remoteClient().respondToApproval(id: approval.id, decision: decision) + } catch { + pendingApproval = approval + streamState = previousState + presentedError = error.localizedDescription + } + } + + private func resolveModelSelection() { + guard let catalog else { return } + if selectedProviderId == nil || !catalog.providers.contains(where: { $0.id == selectedProviderId }) { + selectedProviderId = catalog.defaults["providerId"] ?? catalog.providers.first?.id + } + if selectedModelId == nil || selectedProvider?.models.contains(where: { $0.id == selectedModelId }) != true { + selectedModelId = catalog.defaults["modelId"] ?? selectedProvider?.models.first?.id + } + if selectedThinkingLevel == nil { selectedThinkingLevel = selectedModel?.thinkingLevels?.first } + } + + private func restoreStreamIfNeeded() async { + guard let stream = await cache.loadActiveStream(instanceId: instanceId, chatId: chat.id) else { return } + do { + let status = try await coordinator.remoteClient().streamStatus(id: stream.streamId) + streamState = status.state + if status.state.isTerminal { + await liveActivities.updateStatus(streamID: stream.streamId, state: status.state) + await reconcileChat() + await cache.removeActiveStream(instanceId: instanceId, chatId: chat.id) + } else { + await liveActivities.start( + instanceID: instanceId, + chatID: chat.id, + title: chat.title, + streamID: stream.streamId + ) + await liveActivities.updateStatus(streamID: stream.streamId, state: status.state) + startStreaming(stream) + } + } catch { + presentedError = error.localizedDescription + await liveActivities.markStale(streamID: stream.streamId) + } + } + + private func startStreaming(_ stream: AidenChatCache.ActiveStream) { + streamTask?.cancel() + streamTask = Task { [weak self] in + await self?.consume(stream) + } + } + + private func consume(_ original: AidenChatCache.ActiveStream) async { + var stream = original + while !Task.isCancelled { + do { + let events = try coordinator.remoteClient().streamEvents( + id: stream.streamId, + after: stream.lastSequence + ) + for try await event in events { + try Task.checkCancellation() + guard event.streamId == stream.streamId else { continue } + if event.sequence <= stream.lastSequence { continue } + if event.sequence != stream.lastSequence + 1 { + await reconcileChat() + } + stream.lastSequence = event.sequence + try await cache.saveActiveStream(stream, instanceId: instanceId, chatId: chat.id) + await apply(event) + if event.terminal { return } + } + + let status = try await coordinator.remoteClient().streamStatus(id: stream.streamId) + streamState = status.state + await liveActivities.updateStatus(streamID: stream.streamId, state: status.state) + if status.state.isTerminal { + await finishStream() + return + } + try await Task.sleep(for: .milliseconds(500)) + } catch is CancellationError { + return + } catch { + guard !Task.isCancelled else { return } + do { + let status = try await coordinator.remoteClient().streamStatus(id: stream.streamId) + streamState = status.state + await liveActivities.updateStatus(streamID: stream.streamId, state: status.state) + if status.state.isTerminal { + await finishStream() + return + } + try await Task.sleep(for: .seconds(1)) + } catch is CancellationError { + return + } catch { + presentedError = error.localizedDescription + await liveActivities.markStale(streamID: stream.streamId) + return + } + } + } + } + + private func apply(_ event: AidenRemoteStreamEvent) async { + guard event.shouldApply, let payload = event.payload else { return } + switch event.type { + case .snapshot: + streamState = .reconciling + await reconcileChat() + case .status: + if let value = payload.state, let state = AidenStreamState(rawValue: value) { + streamState = state + await liveActivities.updateStatus(streamID: event.streamId, state: state) + } + case .textDelta: + liveText += payload.text ?? "" + streamState = .running + await liveActivities.appendResponse(payload.text ?? "", streamID: event.streamId) + case .reasoningDelta: + reasoning += payload.text ?? "" + await liveActivities.reasoning(streamID: event.streamId) + case .toolStarted: + if let id = payload.toolId, let name = payload.name { + tools.append(AidenLiveTool(id: id, name: name, status: nil)) + } + await liveActivities.toolStarted(name: payload.name, streamID: event.streamId) + case .toolFinished: + if let id = payload.toolId, let index = tools.firstIndex(where: { $0.id == id }) { + tools[index].status = payload.status + } + await liveActivities.toolFinished(streamID: event.streamId) + case .timeline: + if let label = payload.label, timeline.last != label { timeline.append(label) } + case .approvalRequired: + if let id = payload.approvalId, let summary = payload.summary, let expiresAt = payload.expiresAt { + pendingApproval = AidenPendingApproval(id: id, summary: summary, expiresAt: expiresAt) + streamState = .waitingForApproval + await liveActivities.approvalRequired(streamID: event.streamId) + } + case .error: + presentedError = payload.message ?? "Aiden could not finish this response." + streamState = .error + await liveActivities.finish( + streamID: event.streamId, + status: .failed, + message: String(localized: "Response failed"), + errorSummary: payload.message + ) + await finishStream() + case .cancelled: + streamState = .cancelled + await liveActivities.finish( + streamID: event.streamId, + status: .cancelled, + message: String(localized: "Response cancelled") + ) + await finishStream() + case .done: + streamState = .done + await liveActivities.finish( + streamID: event.streamId, + status: .complete, + message: String(localized: "Response complete") + ) + await finishStream() + case .heartbeat: + break + default: + break + } + } + + private func reconcileChat() async { + do { + let remote = try await coordinator.remoteClient().chat(id: chat.id) + chat = remote + try await cache.saveChat(remote, instanceId: instanceId) + } catch { + presentedError = error.localizedDescription + } + } + + private func finishStream() async { + await reconcileChat() + liveText = "" + reasoning = "" + tools = [] + timeline = [] + pendingApproval = nil + await cache.removeActiveStream(instanceId: instanceId, chatId: chat.id) + } +} + +struct AidenWorkspaceChatsView: View { + @Bindable var coordinator: AidenRemoteCoordinator + @Environment(\.aidenPalette) private var palette + let workspace: AidenWorkspace + @State private var model: AidenWorkspaceChatsModel + @State private var createdChat: AidenChat? + @State private var renameChat: AidenChat? + @State private var renameTitle = "" + @State private var deleteChat: AidenChat? + + init(coordinator: AidenRemoteCoordinator, workspace: AidenWorkspace) { + self.coordinator = coordinator + self.workspace = workspace + _model = State(initialValue: AidenWorkspaceChatsModel(coordinator: coordinator, workspaceId: workspace.id)) + } + + var body: some View { + List { + Section { + VStack(alignment: .leading, spacing: 8) { + Text(workspace.name).font(.title2.bold()) + Label(workspace.permission.detail, systemImage: "checkmark.shield") + .font(.subheadline) + .foregroundStyle(.secondary) + } + .padding(.vertical, 8) + } + + Section("Chats") { + if model.chats.isEmpty, !model.isLoading { + ContentUnavailableView( + "No Chats", + systemImage: "bubble.left.and.bubble.right", + description: Text("Start a chat in this workspace to control Aiden Agent.") + ) + .listRowBackground(Color.clear) + } else { + ForEach(model.chats) { chat in + NavigationLink { + AidenChatDetailView(coordinator: coordinator, chat: chat) + } label: { + VStack(alignment: .leading, spacing: 4) { + Text(chat.title).lineLimit(1) + AidenRelativeTimestampView(date: chat.updatedAt) + .font(.caption) + .foregroundStyle(.secondary) + } + } + .swipeActions(edge: .trailing) { + Button(role: .destructive) { deleteChat = chat } label: { + Label("Delete", systemImage: "trash") + } + Button { beginRename(chat) } label: { + Label("Rename", systemImage: "pencil") + } + .tint(.accentColor) + } + .contextMenu { + Button { beginRename(chat) } label: { Label("Rename", systemImage: "pencil") } + Button(role: .destructive) { deleteChat = chat } label: { Label("Delete", systemImage: "trash") } + } + } + } + } + } + .scrollContentBackground(.hidden) + .background(palette.canvas) + .overlay { if model.isLoading && model.chats.isEmpty { ProgressView() } } + .refreshable { await model.load() } + .task(id: coordinator.activeInstanceId) { await model.load() } + .navigationDestination(isPresented: Binding( + get: { createdChat != nil }, + set: { if !$0 { createdChat = nil } } + )) { + if let createdChat { + AidenChatDetailView(coordinator: coordinator, chat: createdChat) + } + } + .toolbar { + ToolbarItem(placement: .primaryAction) { + Button { + Task { createdChat = await model.create() } + } label: { + Image(systemName: "square.and.pencil") + } + .disabled(!model.isConnected || model.isMutating) + .accessibilityLabel("New chat") + } + } + .alert("Rename Chat", isPresented: Binding( + get: { renameChat != nil }, + set: { if !$0 { renameChat = nil } } + )) { + TextField("Chat title", text: $renameTitle) + Button("Cancel", role: .cancel) { renameChat = nil } + Button("Save") { + guard let chat = renameChat else { return } + renameChat = nil + Task { await model.rename(chat, to: renameTitle) } + } + } + .confirmationDialog("Delete this chat?", isPresented: Binding( + get: { deleteChat != nil }, + set: { if !$0 { deleteChat = nil } } + ), titleVisibility: .visible) { + Button("Delete Chat", role: .destructive) { + guard let chat = deleteChat else { return } + deleteChat = nil + Task { await model.remove(chat) } + } + Button("Cancel", role: .cancel) { deleteChat = nil } + } message: { + Text("This permanently removes the chat from Aiden Agent.") + } + .alert("Aiden On The Go", isPresented: Binding( + get: { model.presentedError != nil }, + set: { if !$0 { model.presentedError = nil } } + )) { + Button("OK", role: .cancel) { model.presentedError = nil } + } message: { + Text(model.presentedError ?? "The operation could not be completed.") + } + } + + private func beginRename(_ chat: AidenChat) { + renameTitle = chat.title + renameChat = chat + } +} + +struct AidenChatDetailView: View { + @Environment(\.aidenReduceMotion) private var reduceMotion + @Environment(\.aidenPalette) private var palette + @State private var model: AidenChatViewModel + @State private var speechPlayback = AidenSpeechPlaybackController() + @State private var composerHeight: CGFloat = 132 + @FocusState private var composerIsFocused: Bool + @Bindable private var coordinator: AidenRemoteCoordinator + let autoStartVoice: Bool + + init(coordinator: AidenRemoteCoordinator, chat: AidenChat, autoStartVoice: Bool = false) { + self.coordinator = coordinator + _model = State(initialValue: AidenChatViewModel(coordinator: coordinator, chat: chat)) + self.autoStartVoice = autoStartVoice + } + + private var workspace: AidenWorkspace? { + coordinator.workspaces.first { $0.id == model.chat.workspaceId } + } + + var body: some View { + ZStack(alignment: .bottom) { + ScrollViewReader { proxy in + ScrollView { + LazyVStack(alignment: .leading, spacing: 18) { + ForEach(model.chat.messages) { message in + AidenMessageView(message: message, speechPlayback: speechPlayback) + } + if model.isStreaming || !model.liveText.isEmpty { + AidenLiveResponseView(model: model) + } + Color.clear + .frame(height: max(96, composerHeight + 12)) + .accessibilityHidden(true) + Color.clear.frame(height: 1).id("chat-bottom") + } + .padding(.horizontal) + .padding(.top, 20) + } + .scrollDismissesKeyboard(.interactively) + .simultaneousGesture( + TapGesture().onEnded { + composerIsFocused = false + } + ) + .onChange(of: model.chat.messages.count) { _, _ in scrollToBottom(proxy) } + .onChange(of: model.liveText) { _, _ in scrollToBottom(proxy) } + } + + AidenComposerView( + model: model, + autoStartVoice: autoStartVoice, + composerFocus: $composerIsFocused + ) + .padding(.horizontal) + .padding(.bottom, 10) + .background { + GeometryReader { proxy in + Color.clear.preference( + key: AidenComposerHeightPreferenceKey.self, + value: proxy.size.height + ) + } + } + } + .background(palette.canvas.ignoresSafeArea()) + .onPreferenceChange(AidenComposerHeightPreferenceKey.self) { height in + guard height > 0 else { return } + composerHeight = height + } + .navigationTitle(model.chat.title) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + if let workspace, workspace.hasFolder { + ToolbarItem(placement: .topBarTrailing) { + NavigationLink { + AidenWorkspaceFilesView(coordinator: coordinator, workspace: workspace) + } label: { + Label("Files", systemImage: "folder") + } + .accessibilityLabel("Workspace files") + } + } + } + .task { await model.load() } + .alert("Aiden On The Go", isPresented: Binding( + get: { model.presentedError != nil }, + set: { if !$0 { model.presentedError = nil } } + )) { + Button("OK", role: .cancel) { model.presentedError = nil } + } message: { + Text(model.presentedError ?? "The operation could not be completed.") + } + } + + private func scrollToBottom(_ proxy: ScrollViewProxy) { + withAnimation(reduceMotion ? nil : .easeOut(duration: 0.2)) { + proxy.scrollTo("chat-bottom", anchor: .bottom) + } + } +} + +private struct AidenComposerHeightPreferenceKey: PreferenceKey { + static var defaultValue: CGFloat = 132 + + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = max(value, nextValue()) + } +} + +private struct AidenMessageView: View { + @Environment(\.aidenPalette) private var palette + let message: AidenChatMessage + let speechPlayback: AidenSpeechPlaybackController + + var body: some View { + HStack(alignment: .top, spacing: 0) { + if message.role == .user { + Spacer(minLength: 48) + messageContent + } else { + messageContent + .frame(maxWidth: .infinity, alignment: .leading) + .layoutPriority(1) + } + } + .frame(maxWidth: .infinity) + .accessibilityElement(children: message.role == .assistant ? .contain : .combine) + .accessibilityLabel(message.role == .user ? "You" : "Aiden") + } + + private var messageContent: some View { + VStack(alignment: .leading, spacing: 8) { + if !message.text.isEmpty { + AidenMessageTextView(role: message.role, content: message.text) + } + if let attachments = message.attachments, !attachments.isEmpty { + ForEach(attachments) { attachment in + Label { + VStack(alignment: .leading, spacing: 1) { + Text(attachment.name).lineLimit(1) + Text(ByteCountFormatter.string(fromByteCount: Int64(attachment.size), countStyle: .file)) + .foregroundStyle(palette.secondary) + } + } icon: { + Image(systemName: attachment.kind == .image ? "photo" : "doc.text") + } + .font(.caption) + .accessibilityElement(children: .combine) + } + } + if message.role == .assistant, !message.text.isEmpty { + Button { + speechPlayback.speak(message.text) + } label: { + Label("Read aloud", systemImage: "speaker.wave.2") + } + .font(.caption) + .buttonStyle(.plain) + .foregroundStyle(palette.secondary) + } + } + .padding(message.role == .user ? 12 : 0) + .background(message.role == .user ? palette.raised : Color.clear) + .clipShape(RoundedRectangle(cornerRadius: 18, style: .continuous)) + } +} + +struct AidenMessageTextView: View { + let role: AidenChatRole + let content: String + + @ViewBuilder + var body: some View { + if role == .assistant { + AidenMarkdownView(content: content) + } else { + Text(verbatim: content) + .font(.body) + .textSelection(.enabled) + } + } +} + +enum AidenMarkdownFallbackReason: Equatable { + case tooManyCharacters + case tooManyLines +} + +enum AidenMarkdownRenderingPolicy { + static let maximumCharacterCount = 80_000 + static let maximumLineCount = 2_000 + + static func fallbackReason(for content: String) -> AidenMarkdownFallbackReason? { + if content.count > maximumCharacterCount { return .tooManyCharacters } + + var lineCount = 1 + var previousWasCarriageReturn = false + for scalar in content.unicodeScalars { + switch scalar.value { + case 0x0A: + if !previousWasCarriageReturn { lineCount += 1 } + previousWasCarriageReturn = false + case 0x0D: + lineCount += 1 + previousWasCarriageReturn = true + case 0x2028, 0x2029: + lineCount += 1 + previousWasCarriageReturn = false + default: + previousWasCarriageReturn = false + } + if lineCount > maximumLineCount { return .tooManyLines } + } + return nil + } +} + +enum AidenMarkdownDocument { + static func plainText(from content: String) -> String { + MarkdownContent(content).renderPlainText() + } +} + +struct AidenMarkdownView: View { + @Environment(\.colorScheme) private var colorScheme + let content: String + + var body: some View { + Group { + if content.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + Text(verbatim: " ") + } else if AidenMarkdownRenderingPolicy.fallbackReason(for: content) != nil { + Text(verbatim: content) + .font(.body) + } else { + MarkdownUI.Markdown(content) + .markdownTheme(.aidenChat(colorScheme: colorScheme)) + .markdownImageProvider(AidenMarkdownNoNetworkImageProvider()) + .markdownTextStyle { + ForegroundColor(.primary) + BackgroundColor(nil) + } + .markdownTextStyle(\.code) { + FontFamilyVariant(.monospaced) + FontSize(.em(0.88)) + BackgroundColor(Color(.tertiarySystemGroupedBackground)) + } + .markdownBlockStyle(\.paragraph) { configuration in + configuration.label + .fixedSize(horizontal: false, vertical: true) + .relativeLineSpacing(.em(0.18)) + .markdownMargin(top: 0, bottom: 8) + } + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .fixedSize(horizontal: false, vertical: true) + .textSelection(.enabled) + } +} + +private struct AidenMarkdownNoNetworkImageProvider: ImageProvider { + func makeImage(url: URL?) -> some View { + EmptyView() + } +} + +private extension MarkdownUI.Theme { + static func aidenChat(colorScheme: ColorScheme) -> MarkdownUI.Theme { + MarkdownUI.Theme.gitHub + .text { + ForegroundColor(.primary) + BackgroundColor(nil) + FontSize(16) + } + .code { + FontFamilyVariant(.monospaced) + FontSize(.em(0.85)) + BackgroundColor( + colorScheme == .dark + ? Color(red: 0.08, green: 0.09, blue: 0.12) + : Color(.tertiarySystemGroupedBackground) + ) + } + } +} + +private struct AidenLiveResponseView: View { + @Environment(\.aidenPalette) private var palette + @Bindable var model: AidenChatViewModel + + private var activity: (label: String, orb: OrbState) { + if model.streamState == .waitingForApproval { + return ("Waiting for approval", .listening) + } + if let tool = model.tools.last(where: { $0.status == nil }) { + let name = tool.name.lowercased() + let isSearch = ["search", "find", "read", "list", "glob", "grep"] + .contains { name.contains($0) } + return (isSearch ? "Searching…" : "Working…", isSearch ? .searching : .working) + } + if !model.liveText.isEmpty { + return ("Responding…", .composing) + } + if model.streamState == .queued { + return ("Preparing…", .shaping) + } + return ("Thinking…", .solving) + } + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + if model.isStreaming { + HStack(spacing: 8) { + ThinkingOrb(state: activity.orb, size: .px20) + Text(model.timeline.last ?? activity.label) + .foregroundStyle(palette.secondary) + } + .font(.callout) + .accessibilityElement(children: .combine) + } + + if !model.reasoning.isEmpty { + AidenReasoningCard(text: model.reasoning) + } + + if !model.tools.isEmpty { + AidenToolActivityCard(tools: model.tools) + } + + if let approval = model.pendingApproval { + VStack(alignment: .leading, spacing: 12) { + Label("Approval needed", systemImage: "hand.raised") + .font(.headline) + Text(approval.summary).font(.callout) + HStack { + Button("Deny", role: .destructive) { + Task { await model.respondToApproval(.deny) } + } + .buttonStyle(.bordered) + Button("Allow") { + Task { await model.respondToApproval(.allow) } + } + .buttonStyle(.borderedProminent) + } + } + .padding() + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16, style: .continuous)) + } + + if !model.liveText.isEmpty { + AidenMarkdownView(content: model.liveText) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + } +} + +private struct AidenReasoningCard: View { + @Environment(\.aidenPalette) private var palette + @Environment(\.aidenReduceMotion) private var reduceMotion + let text: String + @State private var isExpanded = false + + private var summary: String { + let oneLine = text.replacingOccurrences(of: "\n", with: " ") + .trimmingCharacters(in: .whitespacesAndNewlines) + return oneLine.count > 80 ? "\(oneLine.prefix(80))…" : oneLine + } + + var body: some View { + VStack(alignment: .leading, spacing: isExpanded ? 8 : 0) { + Button { + withAnimation(reduceMotion ? nil : .easeInOut(duration: 0.18)) { + isExpanded.toggle() + } + } label: { + HStack(spacing: 8) { + AidenSidebarLogo(size: 18, color: palette.secondary) + Text("Thinking").font(.caption.weight(.semibold)) + Text(summary).font(.caption).foregroundStyle(palette.secondary).lineLimit(1) + Spacer(minLength: 6) + Image(systemName: isExpanded ? "chevron.up" : "chevron.down") + .font(.caption2.weight(.semibold)) + .foregroundStyle(palette.secondary) + } + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + + if isExpanded { + Text(text) + .font(.caption) + .textSelection(.enabled) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + .padding(.horizontal, 10) + .padding(.vertical, 9) + .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 10, style: .continuous)) + .accessibilityElement(children: .contain) + } +} + +private struct AidenToolActivityCard: View { + @Environment(\.aidenPalette) private var palette + @Environment(\.aidenReduceMotion) private var reduceMotion + let tools: [AidenLiveTool] + @State private var isExpanded = false + + private var isComplete: Bool { tools.allSatisfy { $0.status != nil } } + private var summary: String { + let names = Array(Set(tools.map(\.name))).sorted() + let visible = names.prefix(3).joined(separator: ", ") + return names.count > 3 ? "\(visible), +\(names.count - 3)" : visible + } + + var body: some View { + VStack(alignment: .leading, spacing: isExpanded ? 8 : 0) { + Button { + withAnimation(reduceMotion ? nil : .easeInOut(duration: 0.18)) { + isExpanded.toggle() + } + } label: { + HStack(spacing: 8) { + Image(systemName: isComplete ? "checkmark.circle.fill" : "wrench.and.screwdriver.fill") + .font(.system(size: 14, weight: .semibold)) + .foregroundStyle(palette.secondary) + .frame(width: 18, height: 18) + Text(isComplete ? "Tools used" : "Using tools") + .font(.caption.weight(.semibold)) + Text(summary).font(.caption).foregroundStyle(palette.secondary).lineLimit(1) + Spacer(minLength: 6) + Image(systemName: isExpanded ? "chevron.up" : "chevron.down") + .font(.caption2.weight(.semibold)) + .foregroundStyle(palette.secondary) + } + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + + if isExpanded { + VStack(alignment: .leading, spacing: 6) { + ForEach(tools) { tool in + HStack(spacing: 8) { + Image(systemName: tool.status == nil ? "circle.dotted" : "checkmark.circle") + Text(tool.name).lineLimit(1) + Spacer() + Text(tool.status ?? "Running").foregroundStyle(palette.secondary) + } + .font(.caption) + } + } + } + } + .padding(.horizontal, 10) + .padding(.vertical, 9) + .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 10, style: .continuous)) + } +} + +private struct AidenComposerView: View { + @Environment(\.colorScheme) private var colorScheme + @Environment(\.aidenPalette) private var palette + @Bindable var model: AidenChatViewModel + let autoStartVoice: Bool + let composerFocus: FocusState.Binding + @State private var voiceInput = ComposerVoiceInputController() + @State private var didAutoStartVoice = false + @State private var selectedPhoto: PhotosPickerItem? + @State private var isFileImporterPresented = false + + var body: some View { + VStack(alignment: .leading, spacing: 6) { + if !model.pendingAttachments.isEmpty { + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 8) { + ForEach(model.pendingAttachments) { attachment in + HStack(spacing: 6) { + Label(attachment.name, systemImage: attachment.kind == .image ? "photo" : "doc.text") + .lineLimit(1) + Button { + Task { await model.removeAttachment(attachment) } + } label: { + Image(systemName: "xmark.circle.fill") + } + .buttonStyle(.plain) + .accessibilityLabel("Remove \(attachment.name)") + } + .font(.caption) + .padding(.horizontal, 10) + .padding(.vertical, 7) + .background(palette.raised, in: Capsule()) + } + } + } + .accessibilityLabel("Attachments") + } + + TextField("Message Aiden", text: $model.draft, axis: .vertical) + .lineLimit(1...6) + .padding(.horizontal, 4) + .padding(.top, 5) + .focused(composerFocus) + .submitLabel(.send) + .onSubmit { + guard !model.isStreaming else { return } + voiceInput.stopBeforeSubmittingDraft() + Task { await model.send() } + } + + HStack(alignment: .center, spacing: 10) { + Menu { + PhotosPicker(selection: $selectedPhoto, matching: .images) { + Label("Photo Library", systemImage: "photo.on.rectangle") + } + Button { + isFileImporterPresented = true + } label: { + Label("Choose File", systemImage: "doc") + } + } label: { + if model.isUploadingAttachment { + ProgressView().controlSize(.small).frame(width: 44, height: 44) + } else { + Image(systemName: "plus") + .font(.title3.weight(.medium)) + .frame(width: 44, height: 44) + } + } + .disabled(!model.isConnected || model.isStreaming || model.isUploadingAttachment || model.pendingAttachments.count >= 10) + .accessibilityLabel("Add attachment") + .accessibilityHint("Attach an image or bounded text file") + + if let catalog = model.catalog, !catalog.providers.isEmpty { + Menu { + ForEach(catalog.providers) { provider in + Section(provider.label) { + ForEach(provider.models) { candidate in + if let levels = candidate.thinkingLevels, !levels.isEmpty { + Menu { + ForEach(levels, id: \.self) { level in + Button { + select( + candidate, + providerId: provider.id, + thinkingLevel: level + ) + } label: { + if isSelected(candidate, providerId: provider.id, thinkingLevel: level) { + Label(level.capitalized, systemImage: "checkmark") + } else { + Text(level.capitalized) + } + } + } + } label: { + Label { + Text(candidate.label) + } icon: { + AidenSidebarLogo(size: 14, color: palette.secondary) + } + } + } else { + Button { + select(candidate, providerId: provider.id, thinkingLevel: nil) + } label: { + if isSelected(candidate, providerId: provider.id, thinkingLevel: nil) { + Label(candidate.label, systemImage: "checkmark") + } else { + Text(candidate.label) + } + } + } + } + } + } + } label: { + HStack(spacing: 4) { + if model.selectedModel?.thinkingLevels?.isEmpty == false { + AidenSidebarLogo(size: 15, color: palette.secondary) + } + Text(model.selectedModel?.label ?? "Model").lineLimit(1) + if let level = model.selectedThinkingLevel, + model.selectedModel?.thinkingLevels?.isEmpty == false { + Text("· \(level.capitalized)") + .lineLimit(1) + .foregroundStyle(palette.secondary.opacity(0.8)) + } + Image(systemName: "chevron.down").font(.caption2) + } + .font(.caption) + .foregroundStyle(palette.secondary) + .frame(maxWidth: 180, alignment: .leading) + .frame(minHeight: 44) + } + .accessibilityLabel("Model") + .accessibilityValue(selectedModelAccessibilityValue) + } + + Spacer(minLength: 0) + + Button { + Task { + await voiceInput.toggle(currentDraft: model.draft) { model.draft = $0 } + } + } label: { + Image(systemName: voiceInput.isListening ? "waveform" : "mic") + .font(.body.weight(.medium)) + .frame(width: 44, height: 44) + } + .disabled(model.isStreaming || voiceInput.isRequestingPermission) + .accessibilityLabel(voiceInput.isListening ? "Stop voice input" : "Start voice input") + + if model.isStreaming { + Button { Task { await model.stop() } } label: { + Image(systemName: "stop.fill") + .frame(width: 30, height: 30) + .background(palette.foreground, in: Circle()) + .foregroundStyle(palette.canvas) + .frame(width: 44, height: 44) + } + .buttonStyle(.plain) + .accessibilityLabel("Stop response") + } else { + Button { + voiceInput.stopBeforeSubmittingDraft() + Task { await model.send() } + } label: { + Image(systemName: "arrow.up") + .font(.headline.bold()) + .frame(width: 30, height: 30) + .background(sendButtonBackground, in: Circle()) + .foregroundStyle(sendButtonForeground) + .frame(width: 44, height: 44) + } + .buttonStyle(.plain) + .disabled(!model.canSend) + .accessibilityLabel("Send message") + } + } + + if voiceInput.isListening { + Label("Listening on this device", systemImage: "waveform") + .font(.caption) + .foregroundStyle(palette.secondary) + } else if let error = voiceInput.errorMessage { + Text(error) + .font(.caption) + .foregroundStyle(.red) + } + } + .padding(.horizontal, 12) + .padding(.top, 8) + .padding(.bottom, 4) + .aidenComposerGlass() + .overlay { + RoundedRectangle(cornerRadius: 24, style: .continuous) + .stroke(palette.secondary.opacity(0.35), lineWidth: 0.5) + } + .shadow(color: .black.opacity(0.12), radius: 14, y: 6) + .task { + guard autoStartVoice, !didAutoStartVoice else { return } + didAutoStartVoice = true + await voiceInput.toggle(currentDraft: model.draft) { model.draft = $0 } + } + .onChange(of: selectedPhoto) { _, item in + guard let item else { return } + Task { + defer { selectedPhoto = nil } + do { + guard let data = try await item.loadTransferable(type: Data.self) else { + throw AidenAttachmentPreparationError.invalidImage + } + let upload = try await Task.detached(priority: .userInitiated) { + try AidenAttachmentPreparation.imageUpload(data: data, name: "Photo.jpg") + }.value + await model.upload(upload) + } catch { + model.presentedError = error.localizedDescription + } + } + } + .fileImporter( + isPresented: $isFileImporterPresented, + allowedContentTypes: [.image, .plainText, .sourceCode, .json, .xml, .commaSeparatedText], + allowsMultipleSelection: false + ) { result in + Task { + do { + let urls = try result.get() + guard let url = urls.first else { + throw AidenAttachmentPreparationError.unsupportedTextType + } + let upload = try await Task.detached(priority: .userInitiated) { + try AidenAttachmentPreparation.fileUpload(url: url) + }.value + await model.upload(upload) + } catch { + model.presentedError = error.localizedDescription + } + } + } + .onDisappear { voiceInput.stopKeepingTranscript() } + } + + private var sendButtonBackground: Color { + if model.canSend { return palette.accent } + return palette.foreground.opacity(colorScheme == .dark ? 0.18 : 0.12) + } + + private var sendButtonForeground: Color { + model.canSend ? palette.canvas : palette.secondary + } + + private var selectedModelAccessibilityValue: String { + guard let selectedModel = model.selectedModel else { return "Not selected" } + guard let level = model.selectedThinkingLevel, + selectedModel.thinkingLevels?.isEmpty == false + else { return selectedModel.label } + return "\(selectedModel.label), \(level.capitalized) thinking" + } + + private func select(_ candidate: AidenModel, providerId: String, thinkingLevel: String?) { + model.selectProvider(providerId) + model.selectModel(candidate.id) + model.selectedThinkingLevel = thinkingLevel + } + + private func isSelected( + _ candidate: AidenModel, + providerId: String, + thinkingLevel: String? + ) -> Bool { + guard model.selectedProviderId == providerId, + model.selectedModelId == candidate.id + else { return false } + return thinkingLevel == nil || model.selectedThinkingLevel == thinkingLevel + } +} + +private struct AidenComposerGlassModifier: ViewModifier { + @Environment(\.accessibilityReduceTransparency) private var reduceTransparency + @Environment(\.aidenPalette) private var palette + + private let shape = RoundedRectangle(cornerRadius: 24, style: .continuous) + + @ViewBuilder + func body(content: Content) -> some View { + if #available(iOS 26, *), !reduceTransparency { + content.glassEffect(.regular.interactive(), in: shape) + } else if reduceTransparency { + content.background(palette.raised, in: shape) + } else { + content.background(.regularMaterial, in: shape) + } + } +} + +private extension View { + func aidenComposerGlass() -> some View { + modifier(AidenComposerGlassModifier()) + } +} + +@MainActor +final class AidenSpeechPlaybackController { + private let synthesizer = AVSpeechSynthesizer() + + func speak(_ text: String) { + let cleaned = text.trimmingCharacters(in: .whitespacesAndNewlines) + guard !cleaned.isEmpty else { return } + if synthesizer.isSpeaking { + synthesizer.stopSpeaking(at: .immediate) + } + let utterance = AVSpeechUtterance(string: String(cleaned.prefix(8_000))) + utterance.voice = AVSpeechSynthesisVoice(language: Locale.current.language.languageCode?.identifier) + synthesizer.speak(utterance) + } +} diff --git a/ios/AidenOnTheGo/Features/Remote/AidenPairingView.swift b/ios/AidenOnTheGo/Features/Remote/AidenPairingView.swift new file mode 100644 index 00000000..e4d2697a --- /dev/null +++ b/ios/AidenOnTheGo/Features/Remote/AidenPairingView.swift @@ -0,0 +1,443 @@ +import AVFoundation +import SwiftUI + +struct AidenDiscoveredAgent: Identifiable, Equatable { + let id: String + let name: String + let endpoint: String? +} + +enum AidenPairingAlertCopy { + static let title = String(localized: "Aiden On The Go") + static let fallbackMessage = String(localized: "Try again from Aiden Agent Remote Access settings.") +} + +final class AidenDiscoveryModel: NSObject, ObservableObject, NetServiceBrowserDelegate, NetServiceDelegate { + @Published private(set) var agents: [AidenDiscoveredAgent] = [] + @Published private(set) var isSearching = false + + private let browser = NetServiceBrowser() + private var services: [String: NetService] = [:] + + override init() { + super.init() + browser.delegate = self + } + + func start() { + guard !isSearching else { return } + isSearching = true + browser.searchForServices(ofType: "_aiden-agent._tcp.", inDomain: "local.") + } + + func stop() { + browser.stop() + isSearching = false + } + + func netServiceBrowserWillSearch(_ browser: NetServiceBrowser) { + isSearching = true + } + + func netServiceBrowserDidStopSearch(_ browser: NetServiceBrowser) { + isSearching = false + } + + func netServiceBrowser( + _ browser: NetServiceBrowser, + didNotSearch errorDict: [String: NSNumber] + ) { + isSearching = false + } + + func netServiceBrowser( + _ browser: NetServiceBrowser, + didFind service: NetService, + moreComing: Bool + ) { + services[service.name] = service + service.delegate = self + service.resolve(withTimeout: 5) + if !moreComing { publish() } + } + + func netServiceBrowser( + _ browser: NetServiceBrowser, + didRemove service: NetService, + moreComing: Bool + ) { + services[service.name] = nil + if !moreComing { publish() } + } + + func netServiceDidResolveAddress(_ sender: NetService) { + publish() + } + + private func publish() { + agents = services.values.map { service in + let endpoint: String? + if let hostName = service.hostName?.trimmingCharacters(in: CharacterSet(charactersIn: ".")), + !hostName.isEmpty, + service.port > 0 { + endpoint = "https://\(hostName):\(service.port)\(AidenRemoteProtocol.basePath)" + } else { + endpoint = nil + } + return AidenDiscoveredAgent(id: service.name, name: service.name, endpoint: endpoint) + } + .sorted { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending } + } +} + +struct AidenPairingView: View { + @Bindable var coordinator: AidenRemoteCoordinator + @Environment(AidenAppearanceStore.self) private var appearance + @Environment(\.aidenPalette) private var palette + @Environment(\.aidenCodeTypography) private var codeTypography + var onPaired: (() -> Void)? + + @StateObject private var discovery = AidenDiscoveryModel() + @State private var pairingPayload = "" + @State private var isShowingScanner = false + @State private var isShowingAppearance = false + @State private var step: Int + + init(coordinator: AidenRemoteCoordinator, onPaired: (() -> Void)? = nil) { + self.coordinator = coordinator + self.onPaired = onPaired + _step = State(initialValue: onPaired == nil ? 0 : 2) + } + + private var canPair: Bool { + !pairingPayload.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !coordinator.isMutating + } + + var body: some View { + NavigationStack { + Group { + switch step { + case 0: welcomePage + case 1: preparePage + default: pairingPage + } + } + .background(palette.canvas) + .toolbar { + if step > 0 && onPaired == nil { + ToolbarItem(placement: .topBarLeading) { + Button { step -= 1 } label: { Image(systemName: "chevron.left") } + .accessibilityLabel("Back") + } + } + ToolbarItem(placement: .topBarTrailing) { + Button { + isShowingAppearance = true + } label: { + Image(systemName: "circle.lefthalf.filled") + } + .accessibilityLabel("Appearance") + } + } + .overlay { + if coordinator.isMutating { + ProgressView("Pairing…") + .padding() + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12)) + } + } + .task(id: step) { + if step == 2 { + discovery.start() + } else { + discovery.stop() + } + } + .onDisappear { discovery.stop() } + .sheet(isPresented: $isShowingScanner) { + NavigationStack { + AidenQRCodeScanner { payload in + pairingPayload = payload + isShowingScanner = false + Task { + await coordinator.pair(qrPayload: payload) + if coordinator.connectionState == .connected { onPaired?() } + } + } + .ignoresSafeArea(edges: .bottom) + .navigationTitle("Scan Pairing QR") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Cancel") { isShowingScanner = false } + } + } + } + } + .sheet(isPresented: $isShowingAppearance) { + AidenAppearanceSettingsView(appearance: appearance) + } + .alert( + AidenPairingAlertCopy.title, + isPresented: Binding( + get: { coordinator.presentedError != nil }, + set: { if !$0 { coordinator.presentedError = nil } } + ) + ) { + Button("OK", role: .cancel) { coordinator.presentedError = nil } + } message: { + Text(coordinator.presentedError ?? AidenPairingAlertCopy.fallbackMessage) + } + } + } + + private var welcomePage: some View { + VStack(spacing: 0) { + Spacer(minLength: 32) + AidenSidebarLogo(size: 132, color: palette.accent) + .accessibilityElement(children: .ignore) + .accessibilityLabel("Aiden") + Spacer(minLength: 34) + VStack(alignment: .leading, spacing: 12) { + Text("Aiden, wherever you are.") + .font(.system(size: 32, weight: .bold)) + Text("Control Aiden Agent on your Mac from iPhone or iPad—chats, workspaces, files, Git, scheduled tasks, and approvals.") + .font(.subheadline) + .foregroundStyle(palette.secondary) + .fixedSize(horizontal: false, vertical: true) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 28) + Spacer(minLength: 24) + Button("Get Started") { step = 1 } + .buttonStyle(.borderedProminent) + .controlSize(.large) + .frame(maxWidth: .infinity) + .padding(.horizontal, 24) + .padding(.bottom, 20) + } + .navigationBarHidden(true) + } + + private var preparePage: some View { + ScrollView { + VStack(alignment: .leading, spacing: 28) { + VStack(alignment: .leading, spacing: 8) { + Text("Prepare your Mac").font(.largeTitle.bold()) + Text("Aiden Agent remains the server and keeps provider credentials on your Mac.") + .foregroundStyle(palette.secondary) + } + + pairingStep(number: 1, title: "Open Aiden Agent", detail: "On your Mac, go to Settings → Remote Access.") + pairingStep(number: 2, title: "Turn on Remote Access", detail: "Choose Local Network, Tailscale, or both. Tailscale is best when you are away from home.") + pairingStep(number: 3, title: "Create a pairing QR", detail: "Keep the QR visible. It expires after five minutes and can be used once.") + + VStack(alignment: .leading, spacing: 10) { + Label("Per-device credential", systemImage: "key.fill") + Label("Pinned HTTPS identity", systemImage: "lock.shield.fill") + Label("Revocable from your Mac", systemImage: "checkmark.shield") + } + .font(.subheadline) + .foregroundStyle(palette.secondary) + } + .padding(24) + } + .safeAreaInset(edge: .bottom) { + Button("Scan Pairing QR") { step = 2 } + .buttonStyle(.borderedProminent) + .controlSize(.large) + .frame(maxWidth: .infinity) + .padding(20) + .background(.bar) + } + .navigationTitle("Connect") + .navigationBarTitleDisplayMode(.inline) + } + + private func pairingStep(number: Int, title: String, detail: String) -> some View { + HStack(alignment: .top, spacing: 14) { + Text("\(number)") + .font(.caption.bold()) + .foregroundStyle(palette.canvas) + .frame(width: 26, height: 26) + .background(palette.accent, in: Circle()) + VStack(alignment: .leading, spacing: 4) { + Text(title).font(.headline) + Text(detail).font(.subheadline).foregroundStyle(palette.secondary) + } + } + } + + private var pairingPage: some View { + Form { + Section { + Button { isShowingScanner = true } label: { + Label("Scan QR Code", systemImage: "qrcode.viewfinder") + } + + TextEditor(text: $pairingPayload) + .frame(minHeight: 96) + .font(codeTypography.swiftUIFont(relativeTo: .footnote)) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + .accessibilityLabel("Pairing payload") + + Button("Pair with Aiden Agent") { + Task { + await coordinator.pair(qrPayload: pairingPayload.trimmingCharacters(in: .whitespacesAndNewlines)) + if coordinator.connectionState == .connected { onPaired?() } + } + } + .disabled(!canPair) + } header: { + Text("Pairing code") + } footer: { + Text("Scan the one-time QR from Aiden Agent → Settings → Remote Access, or paste its full payload.") + } + + Section { + if discovery.agents.isEmpty { + HStack { + if discovery.isSearching { ProgressView() } + Text(discovery.isSearching ? "Looking for Aiden Agent…" : "No nearby Aiden Agent found") + .foregroundStyle(palette.secondary) + } + } else { + ForEach(discovery.agents) { agent in + VStack(alignment: .leading, spacing: 3) { + Label(agent.name, systemImage: "desktopcomputer") + if let endpoint = agent.endpoint { + Text(endpoint) + .font(codeTypography.swiftUIFont(relativeTo: .caption)) + .foregroundStyle(palette.secondary) + .textSelection(.enabled) + } + } + .accessibilityElement(children: .combine) + } + } + } header: { + Text("Nearby Macs") + } footer: { + Text("Discovery only confirms reachability. The QR verifies your Mac’s certificate and public-key fingerprint.") + } + + Section("Connection") { + Label("Local Network uses pinned HTTPS", systemImage: "wifi") + Label("Tailscale uses the same device credential", systemImage: "network") + Label("Provider keys stay on your Mac", systemImage: "checkmark.shield") + } + } + .scrollContentBackground(.hidden) + .navigationTitle("Pair Aiden") + .navigationBarTitleDisplayMode(.inline) + } +} + +private struct AidenQRCodeScanner: UIViewControllerRepresentable { + let onCode: (String) -> Void + + func makeUIViewController(context: Context) -> AidenQRCodeScannerViewController { + let controller = AidenQRCodeScannerViewController() + controller.onCode = onCode + return controller + } + + func updateUIViewController(_ uiViewController: AidenQRCodeScannerViewController, context: Context) {} +} + +private final class AidenQRCodeScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate { + var onCode: ((String) -> Void)? + + private let session = AVCaptureSession() + private var previewLayer: AVCaptureVideoPreviewLayer? + private var deliveredCode = false + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .black + requestCameraAndConfigure() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + previewLayer?.frame = view.bounds + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + if session.isRunning { + DispatchQueue.global(qos: .userInitiated).async { [session] in session.stopRunning() } + } + } + + private func requestCameraAndConfigure() { + switch AVCaptureDevice.authorizationStatus(for: .video) { + case .authorized: + configureSession() + case .notDetermined: + AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in + guard granted else { return } + DispatchQueue.main.async { self?.configureSession() } + } + default: + showCameraUnavailable() + } + } + + private func configureSession() { + guard let camera = AVCaptureDevice.default(for: .video), + let input = try? AVCaptureDeviceInput(device: camera), + session.canAddInput(input) else { + showCameraUnavailable() + return + } + session.addInput(input) + + let output = AVCaptureMetadataOutput() + guard session.canAddOutput(output) else { + showCameraUnavailable() + return + } + session.addOutput(output) + output.setMetadataObjectsDelegate(self, queue: .main) + output.metadataObjectTypes = [.qr] + + let layer = AVCaptureVideoPreviewLayer(session: session) + layer.videoGravity = .resizeAspectFill + layer.frame = view.bounds + view.layer.addSublayer(layer) + previewLayer = layer + + DispatchQueue.global(qos: .userInitiated).async { [session] in session.startRunning() } + } + + private func showCameraUnavailable() { + let label = UILabel() + label.text = "Camera access is required to scan the pairing QR. You can paste the pairing payload instead." + label.textColor = .white + label.textAlignment = .center + label.numberOfLines = 0 + label.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(label) + NSLayoutConstraint.activate([ + label.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 24), + label.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -24), + label.centerYAnchor.constraint(equalTo: view.centerYAnchor), + ]) + } + + func metadataOutput( + _ output: AVCaptureMetadataOutput, + didOutput metadataObjects: [AVMetadataObject], + from connection: AVCaptureConnection + ) { + guard !deliveredCode, + let object = metadataObjects.first as? AVMetadataMachineReadableCodeObject, + object.type == .qr, + let value = object.stringValue else { return } + deliveredCode = true + session.stopRunning() + onCode?(value) + } +} diff --git a/ios/AidenOnTheGo/Features/Remote/AidenRemoteCoordinator.swift b/ios/AidenOnTheGo/Features/Remote/AidenRemoteCoordinator.swift new file mode 100644 index 00000000..39917b58 --- /dev/null +++ b/ios/AidenOnTheGo/Features/Remote/AidenRemoteCoordinator.swift @@ -0,0 +1,377 @@ +import Foundation +import Observation +import UIKit + +enum AidenRemoteConnectionState: Equatable { + case needsPairing + case connecting + case connected + case offline(message: String) +} + +@MainActor +@Observable +final class AidenRemoteCoordinator { + typealias ClientFactory = (AidenInstallation, String) throws -> AidenRemoteClient + + let installationStore: AidenInstallationStore + private let clientFactory: ClientFactory + private(set) var connectionState: AidenRemoteConnectionState + private(set) var server: AidenServer? + private(set) var workspaces: [AidenWorkspace] = [] + private(set) var isMutating = false + var presentedError: String? + private var pendingManagedWorktreeDeletionKeys: [String: UUID] = [:] + + init() { + let installationStore = AidenInstallationStore() + self.installationStore = installationStore + clientFactory = { try AidenRemoteClient(installation: $0, credential: $1) } + connectionState = installationStore.activeInstallation == nil ? .needsPairing : .connecting + } + + init( + installationStore: AidenInstallationStore, + clientFactory: @escaping ClientFactory = { try AidenRemoteClient(installation: $0, credential: $1) } + ) { + self.installationStore = installationStore + self.clientFactory = clientFactory + connectionState = installationStore.activeInstallation == nil ? .needsPairing : .connecting + } + + func start() async { + guard installationStore.activeInstallation != nil else { + connectionState = .needsPairing + updateIntentCatalog(for: nil) + return + } + await connectActiveInstallation() + } + + func pair(qrPayload: String) async { + guard !isMutating else { return } + isMutating = true + presentedError = nil + defer { isMutating = false } + + do { + guard let data = qrPayload.data(using: .utf8) else { + throw AidenRemoteClientError.invalidResponse + } + let payload = try AidenRemoteJSONDecoder.decodePairingPayload(from: data) + let exchange = try await AidenRemoteClient.pair( + payload: payload, + deviceName: Self.deviceName, + deviceType: Self.deviceType, + clientVersion: Self.clientVersion + ) + let temporaryName = payload.bootstrap.endpoint.host ?? String(localized: "Aiden Agent") + let installation = try installationStore.savePairing( + exchange, + trust: payload.trust, + name: temporaryName + ) + try await load(installation: installation, credential: exchange.credential) + } catch { + connectionState = installationStore.activeInstallation == nil ? .needsPairing : .offline(message: error.localizedDescription) + presentedError = error.localizedDescription + } + } + + func connectActiveInstallation() async { + guard let installation = installationStore.activeInstallation else { + connectionState = .needsPairing + server = nil + workspaces = [] + updateIntentCatalog(for: nil) + return + } + connectionState = .connecting + presentedError = nil + do { + guard let credential = try installationStore.credential(for: installation), + !credential.isEmpty else { + throw AidenRemoteClientError.missingCredential + } + try await load(installation: installation, credential: credential) + } catch { + await handleConnectionError(error, installationId: installation.id) + } + } + + func switchInstallation(to installationId: String) async { + do { + try installationStore.setActive(installationId) + server = nil + workspaces = [] + updateIntentCatalog(for: nil) + await connectActiveInstallation() + } catch { + presentedError = error.localizedDescription + } + } + + func removeInstallation(_ installationId: String) async { + do { + await AidenRemoteLiveActivityManager.shared.endAll(forInstanceID: installationId) + try installationStore.remove(installationId) + server = nil + workspaces = [] + updateIntentCatalog(for: installationId) + await start() + } catch { + presentedError = error.localizedDescription + } + } + + func refreshWorkspaces() async { + guard let client = try? activeClient() else { + connectionState = .needsPairing + return + } + do { + workspaces = try await client.workspaces() + connectionState = .connected + updateIntentCatalog(for: activeInstanceId) + } catch { + await handleConnectionError(error, installationId: installationStore.activeInstallationId) + } + } + + func createWorkspace(_ create: AidenWorkspaceCreate) async -> AidenWorkspace? { + await mutate { client in try await client.createWorkspace(create) } + } + + func updateWorkspace( + _ workspace: AidenWorkspace, + name: String? = nil, + permission: AidenWorkspacePermission? = nil + ) async -> AidenWorkspace? { + guard !isMutating else { return nil } + isMutating = true + presentedError = nil + defer { isMutating = false } + + var optimistic = workspace + if let name { optimistic.name = name } + if let permission { optimistic.permission = permission } + upsert(optimistic) + + do { + let updated = try await activeClient().updateWorkspace( + id: workspace.id, + revision: workspace.revision, + patch: AidenWorkspacePatch(name: name, permission: permission) + ) + upsert(updated) + return updated + } catch { + upsert(workspace) + await handleConnectionError(error, installationId: installationStore.activeInstallationId) + return nil + } + } + + func removeWorkspace(_ workspace: AidenWorkspace) async -> Bool { + guard !isMutating else { return false } + isMutating = true + presentedError = nil + defer { isMutating = false } + workspaces.removeAll { $0.id == workspace.id } + updateIntentCatalog(for: activeInstanceId) + do { + let client = try activeClient() + try await client.removeWorkspace(id: workspace.id, revision: workspace.revision) + return true + } catch { + upsert(workspace) + await handleConnectionError(error, installationId: installationStore.activeInstallationId) + return false + } + } + + func removeManagedWorktree(_ workspace: AidenWorkspace) async -> Bool { + guard workspace.isManagedWorktree, !isMutating else { return false } + isMutating = true + presentedError = nil + defer { isMutating = false } + let scope = "\(installationStore.activeInstallationId ?? ""):\(workspace.id)" + let key = pendingManagedWorktreeDeletionKeys[scope] ?? UUID() + pendingManagedWorktreeDeletionKeys[scope] = key + workspaces.removeAll { $0.id == workspace.id } + updateIntentCatalog(for: activeInstanceId) + do { + let client = try activeClient() + _ = try await client.deleteManagedGitWorktree( + workspaceId: workspace.id, + revision: workspace.revision, + idempotencyKey: key + ) + pendingManagedWorktreeDeletionKeys[scope] = nil + return true + } catch { + upsert(workspace) + if !Self.isAmbiguousMutationError(error) { + pendingManagedWorktreeDeletionKeys[scope] = nil + } + await handleConnectionError(error, installationId: installationStore.activeInstallationId) + return false + } + } + + func browserRoots() async throws -> [AidenBrowserRoot] { + try await activeClient().browserRoots() + } + + func browserChildren(location: String, cursor: String? = nil) async throws -> AidenBrowserPage { + try await activeClient().browserChildren(location: location, cursor: cursor) + } + + func createSelectedFolderWorkspace(location: String, name: String?) async -> AidenWorkspace? { + guard !isMutating else { return nil } + isMutating = true + presentedError = nil + defer { isMutating = false } + do { + let client = try activeClient() + let selection = try await client.createWorkspaceSelection(location: location) + guard selection.expiresAt > Date() else { + throw AidenRemoteClientError.invalidResponse + } + let workspace = try await client.createWorkspace( + .selectedFolder(selection: selection.selection, name: name) + ) + upsert(workspace) + return workspace + } catch { + await handleConnectionError(error, installationId: installationStore.activeInstallationId) + return nil + } + } + + var activeInstanceId: String? { + installationStore.activeInstallationId + } + + func remoteClient() throws -> AidenRemoteClient { + try activeClient() + } + + private func load(installation: AidenInstallation, credential: String) async throws { + let client = try clientFactory(installation, credential) + async let serverRequest = client.server() + async let workspaceRequest = client.workspaces() + let (server, workspaces) = try await (serverRequest, workspaceRequest) + guard server.instanceId == installation.instanceId else { + throw AidenRemoteContractError.invalidPairingExchange + } + try installationStore.updateServer(server) + self.server = server + self.workspaces = workspaces + connectionState = .connected + updateIntentCatalog(for: installation.id) + } + + private func activeClient() throws -> AidenRemoteClient { + guard let installation = installationStore.activeInstallation else { + throw AidenRemoteClientError.missingCredential + } + guard let credential = try installationStore.credential(for: installation), + !credential.isEmpty else { + throw AidenRemoteClientError.missingCredential + } + return try clientFactory(installation, credential) + } + + private func mutate( + operation: (AidenRemoteClient) async throws -> AidenWorkspace + ) async -> AidenWorkspace? { + guard !isMutating else { return nil } + isMutating = true + presentedError = nil + defer { isMutating = false } + do { + let workspace = try await operation(activeClient()) + upsert(workspace) + return workspace + } catch { + await handleConnectionError(error, installationId: installationStore.activeInstallationId) + return nil + } + } + + private func upsert(_ workspace: AidenWorkspace) { + workspaces.removeAll { $0.id == workspace.id } + workspaces.append(workspace) + workspaces.sort { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending } + updateIntentCatalog(for: activeInstanceId) + } + + private func handleConnectionError(_ error: Error, installationId: String?) async { + if let clientError = error as? AidenRemoteClientError, + clientError.isCredentialRevoked, + let installationId { + let revocationMessage = String(localized: "This device was revoked. Pair it with Aiden Agent again.") + await AidenRemoteLiveActivityManager.shared.endAll(forInstanceID: installationId) + try? installationStore.remove(installationId) + server = nil + workspaces = [] + updateIntentCatalog(for: installationId) + connectionState = installationStore.activeInstallation == nil ? .needsPairing : .connecting + if installationStore.activeInstallation != nil { + await connectActiveInstallation() + } + presentedError = revocationMessage + return + } + connectionState = .offline(message: error.localizedDescription) + presentedError = error.localizedDescription + } + + private func updateIntentCatalog(for instanceId: String?) { + let installations = installationStore.installations.map { + AidenIntentInstallationRecord(id: $0.id, name: $0.name) + } + let cachedWorkspaces: [AidenIntentWorkspaceRecord] + if let instanceId, instanceId == activeInstanceId { + cachedWorkspaces = workspaces.map { + AidenIntentWorkspaceRecord(id: $0.id, instanceId: instanceId, name: $0.name) + } + } else { + cachedWorkspaces = [] + } + try? AidenIntentCatalogStore.shared.update( + installations: installations, + activeInstallationId: activeInstanceId, + workspaces: cachedWorkspaces, + for: instanceId + ) + } + + private static var deviceType: AidenDeviceType { + UIDevice.current.userInterfaceIdiom == .pad ? .ipad : .iphone + } + + private static var deviceName: String { + let name = UIDevice.current.name.trimmingCharacters(in: .whitespacesAndNewlines) + return String((name.isEmpty ? "Aiden On The Go" : name).prefix(80)) + } + + private static var clientVersion: String { + let value = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String + return String((value ?? "1.0").prefix(40)) + } + + private static func isAmbiguousMutationError(_ error: Error) -> Bool { + if error is URLError { return true } + guard let clientError = error as? AidenRemoteClientError else { return false } + switch clientError { + case .invalidResponse, .unexpectedStatus: + return true + case .server(_, let body): + return body.code.rawValue == "idempotency_in_flight" || body.code.rawValue == "internal_error" + case .invalidEndpoint, .missingCredential, .missingTrustConfiguration: + return false + } + } +} diff --git a/ios/AidenOnTheGo/Features/Remote/AidenScheduledTasksView.swift b/ios/AidenOnTheGo/Features/Remote/AidenScheduledTasksView.swift new file mode 100644 index 00000000..f81f8755 --- /dev/null +++ b/ios/AidenOnTheGo/Features/Remote/AidenScheduledTasksView.swift @@ -0,0 +1,662 @@ +import CryptoKit +import Foundation +import Observation +import SwiftUI + +actor AidenScheduledTaskCache { + static let shared = AidenScheduledTaskCache() + + struct Snapshot: Codable, Sendable { + let instanceId: String + var tasks: [AidenScheduledTask] + var settings: AidenScheduledSettings? + var runs: [String: [AidenScheduledRun]] + } + + private let root: URL + private let fileManager: FileManager + private let maximumBytes = 10 * 1_024 * 1_024 + + init(root: URL? = nil, fileManager: FileManager = .default) { + self.fileManager = fileManager + if let root { + self.root = root + } else { + let support = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first + ?? fileManager.temporaryDirectory + self.root = support + .appending(path: "AidenOnTheGo", directoryHint: .isDirectory) + .appending(path: "RemoteScheduledTaskCache-v1", directoryHint: .isDirectory) + } + } + + func load(instanceId: String) -> Snapshot? { + let url = file(instanceId: instanceId) + guard let data = try? Data(contentsOf: url), data.count <= maximumBytes, + let value = try? JSONDecoder().decode(Snapshot.self, from: data), + value.instanceId == instanceId else { return nil } + return value + } + + func store( + instanceId: String, + tasks: [AidenScheduledTask], + settings: AidenScheduledSettings? + ) throws { + let retainedRuns = load(instanceId: instanceId)?.runs ?? [:] + try persist(Snapshot(instanceId: instanceId, tasks: tasks, settings: settings, runs: retainedRuns)) + } + + func store(runs: [AidenScheduledRun], taskId: String, instanceId: String) throws { + var snapshot = load(instanceId: instanceId) + ?? Snapshot(instanceId: instanceId, tasks: [], settings: nil, runs: [:]) + snapshot.runs[taskId] = Array(runs.prefix(50)) + try persist(snapshot) + } + + private func persist(_ snapshot: Snapshot) throws { + let data = try JSONEncoder().encode(snapshot) + guard data.count <= maximumBytes else { throw CocoaError(.fileWriteOutOfSpace) } + let url = file(instanceId: snapshot.instanceId) + try fileManager.createDirectory( + at: url.deletingLastPathComponent(), + withIntermediateDirectories: true, + attributes: [.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication] + ) + try data.write(to: url, options: [.atomic, .completeFileProtectionUntilFirstUserAuthentication]) + } + + private func file(instanceId: String) -> URL { + let digest = SHA256.hash(data: Data(instanceId.utf8)) + .map { String(format: "%02x", $0) }.joined() + return root.appending(path: "\(digest).json") + } +} + +@MainActor +@Observable +final class AidenScheduledTasksModel { + private let coordinator: AidenRemoteCoordinator + private let cache: AidenScheduledTaskCache + private var pendingRunKeys: [String: UUID] = [:] + private(set) var tasks: [AidenScheduledTask] = [] + private(set) var settings: AidenScheduledSettings? + private(set) var catalog: AidenModelCatalog? + private(set) var scripts: [AidenScheduledScript] = [] + private(set) var mcpServers: [AidenScheduledMcpServer] = [] + private(set) var isLoading = false + private(set) var isMutating = false + var presentedError: String? + var outcomeMessage: String? + + init( + coordinator: AidenRemoteCoordinator, + cache: AidenScheduledTaskCache = .shared + ) { + self.coordinator = coordinator + self.cache = cache + } + + var isConnected: Bool { coordinator.connectionState == .connected } + var workspaces: [AidenWorkspace] { coordinator.workspaces.filter { $0.permission != .none } } + + func load() async { + guard !isLoading else { return } + isLoading = true + defer { isLoading = false } + guard let instanceId = coordinator.activeInstanceId else { return } + if tasks.isEmpty, let cached = await cache.load(instanceId: instanceId) { + tasks = cached.tasks.sorted(by: Self.sort) + settings = cached.settings + } + guard isConnected else { return } + do { + let client = try coordinator.remoteClient() + async let loadedTasks = client.scheduledTasks() + async let loadedSettings = client.scheduledSettings() + async let loadedCatalog = client.modelCatalog() + async let loadedMcpServers = client.scheduledMcpServers() + let values = try await (loadedTasks, loadedSettings, loadedCatalog, loadedMcpServers) + tasks = values.0.sorted(by: Self.sort) + settings = values.1 + catalog = values.2 + mcpServers = values.3 + try? await cache.store(instanceId: instanceId, tasks: tasks, settings: settings) + presentedError = nil + } catch { + presentedError = error.localizedDescription + } + } + + func loadScripts(workspaceId: String?) async { + do { + scripts = try await coordinator.remoteClient().scheduledScripts(workspaceId: workspaceId) + } catch { + scripts = [] + presentedError = error.localizedDescription + } + } + + func preview(_ draft: AidenScheduledTaskDraft) async throws -> [Date] { + try await coordinator.remoteClient().previewSchedule( + cron: draft.schedule.trimmingCharacters(in: .whitespacesAndNewlines), + timezone: draft.timezone.trimmingCharacters(in: .whitespacesAndNewlines) + ) + } + + func save(_ draft: AidenScheduledTaskDraft, replacing task: AidenScheduledTask?) async -> Bool { + guard draft.validationMessage == nil, !isMutating, isConnected else { return false } + isMutating = true + defer { isMutating = false } + do { + let client = try coordinator.remoteClient() + let saved = if let task { + try await client.updateScheduledTask(id: task.id, revision: task.revision, mutation: draft.mutation) + } else { + try await client.createScheduledTask(draft.mutation) + } + upsert(saved) + outcomeMessage = task == nil ? String(localized: "Scheduled task created.") : String(localized: "Scheduled task updated.") + return true + } catch { + presentedError = error.localizedDescription + await load() + return false + } + } + + func pauseOrResume(_ task: AidenScheduledTask) async { + await mutate { + task.enabled + ? try await $0.pauseScheduledTask(id: task.id, revision: task.revision) + : try await $0.resumeScheduledTask(id: task.id, revision: task.revision) + } + } + + func remove(_ task: AidenScheduledTask) async -> Bool { + guard !isMutating, isConnected else { return false } + isMutating = true + defer { isMutating = false } + do { + try await coordinator.remoteClient().removeScheduledTask(id: task.id, revision: task.revision) + tasks.removeAll { $0.id == task.id } + return true + } catch { + presentedError = error.localizedDescription + await load() + return false + } + } + + func run(_ task: AidenScheduledTask) async { + guard !isMutating, isConnected else { return } + isMutating = true + defer { isMutating = false } + let key = pendingRunKeys[task.id] ?? UUID() + pendingRunKeys[task.id] = key + do { + let accepted = try await coordinator.remoteClient().runScheduledTask(id: task.id, idempotencyKey: key) + pendingRunKeys[task.id] = nil + outcomeMessage = String(localized: "Run accepted (\(accepted.runId.prefix(12))…). It continues on your Mac if this phone disconnects.") + await load() + } catch { + if !Self.isAmbiguous(error) { pendingRunKeys[task.id] = nil } + presentedError = error.localizedDescription + } + } + + func runs(_ task: AidenScheduledTask) async throws -> [AidenScheduledRun] { + guard let instanceId = coordinator.activeInstanceId else { + throw AidenRemoteClientError.missingCredential + } + do { + let values = try await coordinator.remoteClient().scheduledRuns(taskId: task.id) + try? await cache.store(runs: values, taskId: task.id, instanceId: instanceId) + return values + } catch { + if let retained = await cache.load(instanceId: instanceId)?.runs[task.id] { + return retained + } + throw error + } + } + + func updateSettings(_ mutation: AidenScheduledSettingsMutation) async -> Bool { + guard let settings, !isMutating, isConnected else { return false } + isMutating = true + defer { isMutating = false } + do { + self.settings = try await coordinator.remoteClient().updateScheduledSettings( + revision: settings.revision, + mutation: mutation + ) + return true + } catch { + presentedError = error.localizedDescription + await load() + return false + } + } + + private func mutate(_ operation: (AidenRemoteClient) async throws -> AidenScheduledTask) async { + guard !isMutating, isConnected else { return } + isMutating = true + defer { isMutating = false } + do { upsert(try await operation(coordinator.remoteClient())) } + catch { + presentedError = error.localizedDescription + await load() + } + } + + private func upsert(_ task: AidenScheduledTask) { + tasks.removeAll { $0.id == task.id } + tasks.append(task) + tasks.sort(by: Self.sort) + if let instanceId = coordinator.activeInstanceId { + Task { try? await cache.store(instanceId: instanceId, tasks: tasks, settings: settings) } + } + } + + private static func sort(_ left: AidenScheduledTask, _ right: AidenScheduledTask) -> Bool { + if left.enabled != right.enabled { return left.enabled } + if left.nextRunAt != right.nextRunAt { return (left.nextRunAt ?? .distantFuture) < (right.nextRunAt ?? .distantFuture) } + return left.name.localizedCaseInsensitiveCompare(right.name) == .orderedAscending + } + + private static func isAmbiguous(_ error: Error) -> Bool { + if error is URLError { return true } + guard let error = error as? AidenRemoteClientError else { return false } + switch error { + case .invalidResponse, .unexpectedStatus: return true + case .server(_, let body): return body.code.rawValue == "idempotency_in_flight" || body.code.rawValue == "internal_error" + case .invalidEndpoint, .missingCredential, .missingTrustConfiguration: return false + } + } +} + +struct AidenScheduledTasksView: View { + private enum TaskFilter: String, CaseIterable { + case all = "All" + case active = "Active" + case paused = "Paused" + case running = "Running" + } + + @Environment(\.dismiss) private var dismiss + @State private var model: AidenScheduledTasksModel + @State private var editorTask: AidenScheduledTask? + @State private var isCreating = false + @State private var isShowingSettings = false + @State private var searchText = "" + @State private var filter: TaskFilter = .all + + init(coordinator: AidenRemoteCoordinator) { + _model = State(initialValue: AidenScheduledTasksModel(coordinator: coordinator)) + } + + private var visibleTasks: [AidenScheduledTask] { + model.tasks.filter { task in + let matchesFilter = switch filter { + case .all: true + case .active: task.enabled + case .paused: !task.enabled + case .running: task.running + } + let query = searchText.trimmingCharacters(in: .whitespacesAndNewlines) + return matchesFilter && (query.isEmpty + || task.name.localizedCaseInsensitiveContains(query) + || task.schedule.localizedCaseInsensitiveContains(query)) + } + } + + var body: some View { + NavigationStack { + List { + Section { + Picker("Filter", selection: $filter) { + ForEach(TaskFilter.allCases, id: \.self) { Text($0.rawValue).tag($0) } + } + .pickerStyle(.segmented) + } + if let message = model.outcomeMessage { + Section { Text(message).font(.footnote).foregroundStyle(.secondary) } + } + if model.tasks.isEmpty && !model.isLoading { + ContentUnavailableView( + "No Scheduled Tasks", + systemImage: "clock.badge.plus", + description: Text("Create unattended work that Aiden Agent runs on your Mac, even while this phone is disconnected.") + ) + .listRowBackground(Color.clear) + } else if visibleTasks.isEmpty && !model.isLoading { + ContentUnavailableView.search(text: searchText) + .listRowBackground(Color.clear) + } else { + Section("Tasks") { + ForEach(visibleTasks) { task in + NavigationLink { + AidenScheduledTaskDetailView(model: model, taskId: task.id) + } label: { + VStack(alignment: .leading, spacing: 5) { + HStack { + Text(task.name).font(.headline) + if task.running { ProgressView().controlSize(.small) } + Spacer() + Text(task.enabled ? "Active" : "Paused") + .font(.caption).foregroundStyle(.secondary) + } + Text(task.schedule).font(.subheadline).foregroundStyle(.secondary) + if let next = task.nextRunAt { + Text("Next \(next.formatted(date: .abbreviated, time: .shortened))") + .font(.caption).foregroundStyle(.secondary) + } + } + } + .swipeActions(edge: .trailing) { + Button(task.enabled ? "Pause" : "Resume") { + Task { await model.pauseOrResume(task) } + } + .tint(.orange) + } + } + } + } + } + .navigationTitle("Scheduled Tasks") + .searchable(text: $searchText, prompt: "Search tasks") + .toolbar { + ToolbarItem(placement: .cancellationAction) { Button("Done") { dismiss() } } + ToolbarItemGroup(placement: .primaryAction) { + Button { isShowingSettings = true } label: { Label("Settings", systemImage: "gearshape") } + Button { isCreating = true } label: { Label("New Task", systemImage: "plus") } + .disabled(!model.isConnected || model.isMutating) + } + } + .refreshable { await model.load() } + .overlay { if model.isLoading && model.tasks.isEmpty { ProgressView("Loading tasks…") } } + .safeAreaInset(edge: .top) { + if !model.isConnected { + Text("Offline — task history remains visible, but changes are disabled.") + .font(.footnote).frame(maxWidth: .infinity).padding(8).background(.thinMaterial) + } + } + .sheet(isPresented: $isCreating) { + AidenScheduledTaskEditor(model: model, task: nil) { isCreating = false } + } + .sheet(item: $editorTask) { task in + AidenScheduledTaskEditor(model: model, task: task) { editorTask = nil } + } + .sheet(isPresented: $isShowingSettings) { + AidenScheduledSettingsView(model: model) + } + .alert("Scheduled Tasks", isPresented: Binding( + get: { model.presentedError != nil }, + set: { if !$0 { model.presentedError = nil } } + )) { Button("OK") { model.presentedError = nil } } message: { + Text(model.presentedError ?? "") + } + .task { await model.load() } + } + } +} + +private struct AidenScheduledTaskDetailView: View { + @Bindable var model: AidenScheduledTasksModel + let taskId: String + @State private var runs: [AidenScheduledRun] = [] + @State private var isEditing = false + @State private var isConfirmingDelete = false + @State private var isConfirmingRun = false + @Environment(\.dismiss) private var dismiss + + private var task: AidenScheduledTask? { model.tasks.first { $0.id == taskId } } + + var body: some View { + List { + if let task { + Section("Task") { + LabeledContent("Status", value: task.running ? "Running" : (task.enabled ? "Active" : "Paused")) + LabeledContent("Schedule", value: task.schedule) + LabeledContent("Timezone", value: task.timezone) + LabeledContent("Mode", value: task.mode.title) + LabeledContent("Permission", value: task.permission.title) + if let prompt = task.prompt { Text(prompt).textSelection(.enabled) } + } + Section("Actions") { + Button("Run Now", systemImage: "play.fill") { isConfirmingRun = true } + Button(task.enabled ? "Pause" : "Resume", systemImage: task.enabled ? "pause" : "play") { + Task { await model.pauseOrResume(task) } + } + Button("Edit", systemImage: "pencil") { isEditing = true } + Button("Delete", systemImage: "trash", role: .destructive) { isConfirmingDelete = true } + } + .disabled(!model.isConnected || model.isMutating) + Section("Run History") { + if runs.isEmpty { Text("No completed runs yet.").foregroundStyle(.secondary) } + ForEach(runs) { run in + VStack(alignment: .leading, spacing: 5) { + HStack { Text(run.status.capitalized).font(.headline); Spacer(); Text(run.startedAt, style: .relative).font(.caption) } + if let summary = run.summary { Text(summary).font(.body).textSelection(.enabled) } + if let code = run.errorCode { Text(code).font(.caption).foregroundStyle(.red) } + } + } + } + } else { + ContentUnavailableView("Task Removed", systemImage: "trash") + } + } + .navigationTitle(task?.name ?? "Task") + .sheet(isPresented: $isEditing) { + if let task { AidenScheduledTaskEditor(model: model, task: task) { isEditing = false } } + } + .confirmationDialog("Run this task now?", isPresented: $isConfirmingRun, titleVisibility: .visible) { + if let task { Button("Run Now") { Task { await model.run(task); await loadRuns() } } } + Button("Cancel", role: .cancel) {} + } message: { Text("Aiden Agent owns the run. It continues if this phone disconnects.") } + .confirmationDialog("Delete this scheduled task?", isPresented: $isConfirmingDelete, titleVisibility: .visible) { + if let task { Button("Delete", role: .destructive) { Task { if await model.remove(task) { dismiss() } } } } + Button("Cancel", role: .cancel) {} + } + .task { await loadRuns() } + } + + private func loadRuns() async { + guard let task else { return } + do { runs = try await model.runs(task) } catch { model.presentedError = error.localizedDescription } + } +} + +private struct AidenScheduledTaskEditor: View { + @Bindable var model: AidenScheduledTasksModel + let task: AidenScheduledTask? + let onSaved: () -> Void + @State private var draft: AidenScheduledTaskDraft + @State private var preview: [Date] = [] + @State private var isReviewing = false + @Environment(\.dismiss) private var dismiss + + init(model: AidenScheduledTasksModel, task: AidenScheduledTask?, onSaved: @escaping () -> Void) { + self.model = model + self.task = task + self.onSaved = onSaved + _draft = State(initialValue: task.map(AidenScheduledTaskDraft.init(task:)) ?? AidenScheduledTaskDraft()) + } + + private var providers: [AidenProvider] { model.catalog?.providers ?? [] } + private var models: [AidenModel] { providers.first { $0.id == draft.providerId }?.models ?? [] } + + var body: some View { + NavigationStack { + Form { + Section("Task") { + TextField("Name", text: $draft.name) + Picker("Mode", selection: $draft.mode) { + ForEach(AidenScheduledTaskMode.allCases, id: \.self) { Text($0.title).tag($0) } + } + Picker("Workspace", selection: $draft.workspaceId) { + Text("No workspace").tag(String?.none) + ForEach(model.workspaces) { Text($0.name).tag(Optional($0.id)) } + } + } + Section("Schedule") { + TextField("Cron schedule", text: $draft.schedule) + .textInputAutocapitalization(.never).autocorrectionDisabled() + TextField("Timezone", text: $draft.timezone) + .textInputAutocapitalization(.never).autocorrectionDisabled() + Button("Preview Next Runs") { + Task { do { preview = try await model.preview(draft) } catch { model.presentedError = error.localizedDescription } } + } + ForEach(preview, id: \.self) { Text($0.formatted(date: .abbreviated, time: .shortened)).foregroundStyle(.secondary) } + } + if draft.mode == .llm { + Section("Ask Aiden") { + TextEditor(text: $draft.prompt).frame(minHeight: 120) + Picker("Provider", selection: $draft.providerId) { + Text("Default").tag(String?.none) + ForEach(providers) { Text($0.label).tag(Optional($0.id)) } + } + Picker("Model", selection: $draft.modelId) { + Text("Default").tag(String?.none) + ForEach(models) { Text($0.label).tag(Optional($0.id)) } + } + } + if !model.mcpServers.isEmpty { + Section("MCP Access") { + ForEach(model.mcpServers) { server in + Toggle(server.name, isOn: Binding( + get: { draft.mcpServerIds.contains(server.id) }, + set: { selected in + if selected { draft.mcpServerIds.insert(server.id) } + else { draft.mcpServerIds.remove(server.id) } + } + )) + } + Text("Only enabled server names are shown. Connection details and credentials remain on your Mac.") + .font(.footnote).foregroundStyle(.secondary) + } + } + } else { + Section("Script") { + Picker("Aiden script", selection: $draft.scriptId) { + Text("Choose a script").tag(String?.none) + ForEach(model.scripts) { Text($0.name).tag(Optional($0.id)) } + } + Text("Only scripts inventoried by Aiden Agent can be selected. The phone never sends a path.") + .font(.footnote).foregroundStyle(.secondary) + } + } + Section("Unattended Access") { + Picker("Permission", selection: $draft.permission) { + ForEach(AidenScheduledTaskPermission.allCases, id: \.self) { Text($0.title).tag($0) } + } + Toggle("Mac notification", isOn: $draft.notify) + Text("Enabled tasks can run on your Mac while this phone is disconnected. Full permission can edit files and run commands without asking.") + .font(.footnote).foregroundStyle(.secondary) + } + if let validation = draft.validationMessage { Section { Text(validation).foregroundStyle(.red) } } + } + .navigationTitle(task == nil ? "New Task" : "Edit Task") + .toolbar { + ToolbarItem(placement: .cancellationAction) { Button("Cancel") { dismiss() } } + ToolbarItem(placement: .confirmationAction) { Button("Review") { isReviewing = true }.disabled(draft.validationMessage != nil) } + } + .onChange(of: draft.mode) { _, mode in + if mode == .script { draft.permission = .full; Task { await model.loadScripts(workspaceId: draft.workspaceId) } } + } + .onChange(of: draft.providerId) { _, providerId in + guard let providerId, + let provider = providers.first(where: { $0.id == providerId }) else { + draft.modelId = nil + return + } + if !provider.models.contains(where: { $0.id == draft.modelId }) { + draft.modelId = provider.models.first?.id + } + } + .onChange(of: draft.workspaceId) { _, workspaceId in + if draft.mode == .script { draft.scriptId = nil; Task { await model.loadScripts(workspaceId: workspaceId) } } + } + .task { + if draft.providerId == nil { draft.providerId = model.catalog?.defaults["providerId"] } + if draft.modelId == nil { draft.modelId = model.catalog?.defaults["modelId"] } + if draft.mode == .script { await model.loadScripts(workspaceId: draft.workspaceId) } + } + .sheet(isPresented: $isReviewing) { + NavigationStack { + Form { + Section("Final Review") { + LabeledContent("Task", value: draft.name) + LabeledContent("Schedule", value: draft.schedule) + LabeledContent("Timezone", value: draft.timezone) + LabeledContent("Mode", value: draft.mode.title) + LabeledContent("Permission", value: draft.permission.title) + LabeledContent("Notifications", value: draft.notify ? "On" : "Off") + } + Section { Text("Confirm only if this unattended work should run on your Mac while the phone is disconnected.") } + } + .navigationTitle("Review Task") + .toolbar { + ToolbarItem(placement: .cancellationAction) { Button("Back") { isReviewing = false } } + ToolbarItem(placement: .confirmationAction) { + Button(task == nil ? "Create" : "Save") { + Task { if await model.save(draft, replacing: task) { isReviewing = false; onSaved(); dismiss() } } + } + .disabled(model.isMutating) + } + } + } + } + } + } +} + +private struct AidenScheduledSettingsView: View { + @Bindable var model: AidenScheduledTasksModel + @Environment(\.dismiss) private var dismiss + @State private var enabled = true + @State private var mode: AidenScheduledTaskMode = .llm + @State private var permission: AidenScheduledTaskPermission = .readOnly + @State private var mcpEnabled = false + @State private var notify = true + @State private var timezone = TimeZone.current.identifier + + var body: some View { + NavigationStack { + Form { + Section("Global") { + Toggle("Scheduled tasks enabled", isOn: $enabled) + Picker("Default mode", selection: $mode) { ForEach(AidenScheduledTaskMode.allCases, id: \.self) { Text($0.title).tag($0) } } + Picker("Default permission", selection: $permission) { ForEach(AidenScheduledTaskPermission.allCases, id: \.self) { Text($0.title).tag($0) } } + Toggle("Enable MCP by default", isOn: $mcpEnabled) + Toggle("Default notifications", isOn: $notify) + TextField("Default timezone", text: $timezone) + } + Section { Text("Disabling scheduled tasks stops active scheduled work without deleting task definitions.") } + } + .navigationTitle("Task Settings") + .toolbar { + ToolbarItem(placement: .cancellationAction) { Button("Cancel") { dismiss() } } + ToolbarItem(placement: .confirmationAction) { + Button("Save") { + Task { + if await model.updateSettings(AidenScheduledSettingsMutation( + enabled: enabled, defaultMode: mode, defaultPermission: permission, + defaultMcpEnabled: mcpEnabled, defaultNotify: notify, defaultTimezone: timezone + )) { dismiss() } + } + } + .disabled(!model.isConnected || model.isMutating) + } + } + .task { + if let settings = model.settings { + enabled = settings.enabled; mode = settings.defaultMode + permission = settings.defaultPermission; notify = settings.defaultNotify + mcpEnabled = settings.defaultMcpEnabled + timezone = settings.defaultTimezone + } + } + } + } +} diff --git a/ios/AidenOnTheGo/Features/Remote/AidenWorkspaceEnvironmentView.swift b/ios/AidenOnTheGo/Features/Remote/AidenWorkspaceEnvironmentView.swift new file mode 100644 index 00000000..e88c9ff9 --- /dev/null +++ b/ios/AidenOnTheGo/Features/Remote/AidenWorkspaceEnvironmentView.swift @@ -0,0 +1,846 @@ +import CryptoKit +import Foundation +import Observation +import SwiftUI + +actor AidenWorkspaceEnvironmentCache { + struct Snapshot: Codable, Equatable { + var index: AidenWorkspaceFileIndex + var documents: [String: AidenWorkspaceFileDocument] + var updatedAt: Date + } + + static let shared = AidenWorkspaceEnvironmentCache() + private let directory: URL + private let maximumBytes = 8 * 1_048_576 + + init(directory: URL? = nil) { + self.directory = directory ?? FileManager.default.urls( + for: .cachesDirectory, + in: .userDomainMask + )[0].appending(path: "AidenWorkspaceEnvironment", directoryHint: .isDirectory) + } + + func load(instanceId: String, workspaceId: String) -> Snapshot? { + try? JSONDecoder().decode( + Snapshot.self, + from: Data(contentsOf: file(instanceId: instanceId, workspaceId: workspaceId)) + ) + } + + func store( + index: AidenWorkspaceFileIndex, + instanceId: String, + workspaceId: String + ) throws { + let retained = load(instanceId: instanceId, workspaceId: workspaceId)?.documents ?? [:] + let validIDs = Set(index.entries.lazy.filter { $0.kind == .file }.map(\.id)) + try persist( + Snapshot( + index: index, + documents: retained.filter { validIDs.contains($0.key) }, + updatedAt: Date() + ), + instanceId: instanceId, + workspaceId: workspaceId + ) + } + + func store( + document: AidenWorkspaceFileDocument, + instanceId: String, + workspaceId: String + ) throws { + guard var snapshot = load(instanceId: instanceId, workspaceId: workspaceId) else { return } + snapshot.documents[document.id] = document + snapshot.updatedAt = Date() + try persist(snapshot, instanceId: instanceId, workspaceId: workspaceId) + } + + private func persist(_ snapshot: Snapshot, instanceId: String, workspaceId: String) throws { + try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) + var value = snapshot + var data = try JSONEncoder().encode(value) + if data.count > maximumBytes { + value.documents = [:] + data = try JSONEncoder().encode(value) + } + guard data.count <= maximumBytes else { return } + try data.write(to: file(instanceId: instanceId, workspaceId: workspaceId), options: .atomic) + } + + private func file(instanceId: String, workspaceId: String) -> URL { + let digest = SHA256.hash(data: Data("\(instanceId)\u{0}\(workspaceId)".utf8)) + .map { String(format: "%02x", $0) } + .joined() + return directory.appending(path: "\(digest).json") + } +} + +@MainActor +@Observable +final class AidenWorkspaceFilesModel { + var index: AidenWorkspaceFileIndex? + var document: AidenWorkspaceFileDocument? + var draft = "" + var isLoading = false + var isSaving = false + var isOfflineSnapshot = false + var errorMessage: String? + + private let workspace: AidenWorkspace + private let cache: AidenWorkspaceEnvironmentCache + + init(workspace: AidenWorkspace, cache: AidenWorkspaceEnvironmentCache = .shared) { + self.workspace = workspace + self.cache = cache + } + + func load(coordinator: AidenRemoteCoordinator) async { + guard !isLoading, let instanceId = coordinator.activeInstanceId else { return } + isLoading = true + errorMessage = nil + defer { isLoading = false } + if coordinator.connectionState == .connected { + do { + let value = try await coordinator.remoteClient().workspaceFiles(workspaceId: workspace.id) + index = value + isOfflineSnapshot = false + try? await cache.store(index: value, instanceId: instanceId, workspaceId: workspace.id) + return + } catch { + errorMessage = error.localizedDescription + } + } + if let cached = await cache.load(instanceId: instanceId, workspaceId: workspace.id) { + index = cached.index + isOfflineSnapshot = true + } else if errorMessage == nil { + errorMessage = "Connect to Aiden Agent to load workspace files." + } + } + + func open(_ entry: AidenWorkspaceFileEntry, coordinator: AidenRemoteCoordinator) async { + guard entry.kind == .file, let instanceId = coordinator.activeInstanceId else { return } + errorMessage = nil + if coordinator.connectionState == .connected { + do { + let value = try await coordinator.remoteClient().workspaceFile( + workspaceId: workspace.id, + fileId: entry.id + ) + document = value + draft = value.content + isOfflineSnapshot = false + try? await cache.store(document: value, instanceId: instanceId, workspaceId: workspace.id) + return + } catch { + errorMessage = error.localizedDescription + } + } + if let cached = await cache.load(instanceId: instanceId, workspaceId: workspace.id), + let value = cached.documents[entry.id] { + document = value + draft = value.content + isOfflineSnapshot = true + } + } + + func save(coordinator: AidenRemoteCoordinator) async -> Bool { + guard coordinator.connectionState == .connected, + !isSaving, + let document, + let instanceId = coordinator.activeInstanceId else { return false } + isSaving = true + errorMessage = nil + defer { isSaving = false } + do { + let saved = try await coordinator.remoteClient().writeWorkspaceFile( + workspaceId: workspace.id, + fileId: document.id, + content: draft, + expectedVersion: document.version + ) + self.document = saved + draft = saved.content + try? await cache.store(document: saved, instanceId: instanceId, workspaceId: workspace.id) + return true + } catch { + if case AidenRemoteClientError.server(_, let body) = error, + body.code.rawValue == "revision_conflict" { + errorMessage = "This file changed on the Mac. Reload it before saving again." + } else { + errorMessage = error.localizedDescription + } + return false + } + } + + func reloadDocument(coordinator: AidenRemoteCoordinator) async { + guard let document, + let entry = index?.entries.first(where: { $0.id == document.id }) else { return } + await open(entry, coordinator: coordinator) + } +} + +struct AidenWorkspaceFilesView: View { + @Bindable var coordinator: AidenRemoteCoordinator + let workspace: AidenWorkspace + @State private var model: AidenWorkspaceFilesModel + @State private var search = "" + @State private var isShowingDocument = false + + init(coordinator: AidenRemoteCoordinator, workspace: AidenWorkspace) { + self.coordinator = coordinator + self.workspace = workspace + _model = State(initialValue: AidenWorkspaceFilesModel(workspace: workspace)) + } + + private var entries: [AidenWorkspaceFileEntry] { + let values = model.index?.entries ?? [] + guard !search.isEmpty else { return values } + return values.filter { $0.displayPath.localizedCaseInsensitiveContains(search) } + } + + var body: some View { + List { + if model.isOfflineSnapshot { + Section { + Label("Showing the last downloaded snapshot. Editing is disabled.", systemImage: "wifi.slash") + .foregroundStyle(.secondary) + } + } + if let index = model.index, index.truncated { + Section { + Label("This bounded index contains up to 4,000 entries and may be incomplete.", systemImage: "exclamationmark.triangle") + } + } + Section("Files") { + ForEach(entries) { entry in + Button { + guard entry.kind == .file else { return } + Task { + await model.open(entry, coordinator: coordinator) + isShowingDocument = model.document != nil + } + } label: { + Label { + VStack(alignment: .leading, spacing: 2) { + Text(entry.name) + .foregroundStyle(.primary) + Text(entry.displayPath) + .font(.caption) + .foregroundStyle(.secondary) + .lineLimit(1) + } + } icon: { + Image(systemName: entry.kind == .directory ? "folder" : entry.kind == .symlink ? "link" : "doc.text") + } + } + .disabled(entry.kind != .file) + } + } + } + .overlay { + if model.isLoading && model.index == nil { ProgressView("Loading files…") } + } + .navigationTitle("Files") + .searchable(text: $search, prompt: "Find a file") + .refreshable { await model.load(coordinator: coordinator) } + .task { await model.load(coordinator: coordinator) } + .sheet(isPresented: $isShowingDocument) { + AidenWorkspaceFileEditorView(coordinator: coordinator, model: model) + } + .alert("Files", isPresented: Binding( + get: { model.errorMessage != nil && !isShowingDocument }, + set: { if !$0 { model.errorMessage = nil } } + )) { + Button("OK", role: .cancel) { model.errorMessage = nil } + } message: { + Text(model.errorMessage ?? "The file operation failed.") + } + } +} + +private struct AidenWorkspaceFileEditorView: View { + @Environment(\.dismiss) private var dismiss + @Bindable var coordinator: AidenRemoteCoordinator + @Bindable var model: AidenWorkspaceFilesModel + @State private var isConfirmingDiscard = false + + private var isDirty: Bool { model.document.map { $0.content != model.draft } ?? false } + + var body: some View { + NavigationStack { + TextEditor(text: $model.draft) + .font(.system(.body, design: .monospaced)) + .padding(.horizontal, 8) + .navigationTitle(model.document?.displayPath ?? "File") + .navigationBarTitleDisplayMode(.inline) + .safeAreaInset(edge: .bottom) { + if let message = model.errorMessage { + VStack(spacing: 8) { + Text(message).font(.footnote).foregroundStyle(.secondary) + if message.contains("changed on the Mac") { + Button("Reload from Mac") { + Task { await model.reloadDocument(coordinator: coordinator) } + } + } + } + .padding() + .frame(maxWidth: .infinity) + .background(.regularMaterial) + } + } + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Done") { + if isDirty && !model.isOfflineSnapshot { + isConfirmingDiscard = true + } else { + dismiss() + } + } + } + ToolbarItem(placement: .confirmationAction) { + Button("Save") { Task { _ = await model.save(coordinator: coordinator) } } + .disabled( + !isDirty || model.isSaving || model.isOfflineSnapshot || + coordinator.connectionState != .connected || model.document?.truncated == true + ) + } + } + } + .interactiveDismissDisabled(isDirty && !model.isOfflineSnapshot) + .confirmationDialog("Discard unsaved changes?", isPresented: $isConfirmingDiscard) { + Button("Discard Changes", role: .destructive) { dismiss() } + Button("Keep Editing", role: .cancel) {} + } + } +} + +private enum AidenPendingGitMutation { + case commit(UUID, snapshot: String, message: String, stagedOnly: Bool) + case checkout(UUID, snapshot: String, branch: String) + case createBranch(UUID, name: String, startPoint: String) + case push(UUID, snapshot: String, remote: String, branch: String) + case createWorktree(UUID, branch: String, name: String) +} + +@MainActor +@Observable +final class AidenWorkspaceGitModel { + var review: AidenGitReview? + var reviewSnapshotId: String? + var branches: AidenGitBranches? + var branchesSnapshotId: String? + var worktrees: [AidenGitWorktree] = [] + var comparison: AidenGitComparison? + var selectedDiff: AidenGitDiff? + var pushCapability: AidenGitPushCapability? + var pushSnapshotId: String? + var isLoading = false + var errorMessage: String? + var lastMessage: String? + private var pendingMutation: AidenPendingGitMutation? + + var canRetryPendingMutation: Bool { pendingMutation != nil } + + func refresh(client: AidenRemoteClient, workspaceId: String) async { + guard !isLoading else { return } + isLoading = true + errorMessage = nil + defer { isLoading = false } + do { + let reviewResult = try await client.gitReview(workspaceId: workspaceId) + if case .review(let value) = reviewResult.result { + review = value + reviewSnapshotId = reviewResult.snapshotId + } + let branchResult = try await client.gitBranches(workspaceId: workspaceId) + if case .branches(let value) = branchResult.result { + branches = value + branchesSnapshotId = branchResult.snapshotId + } + let worktreeResult = try await client.gitWorktrees(workspaceId: workspaceId) + if case .worktrees(let value) = worktreeResult.result { worktrees = value.worktrees } + } catch { errorMessage = error.localizedDescription } + } + + func diff(client: AidenRemoteClient, workspaceId: String, file: AidenGitFile, comparisonMode: Bool = false) async { + do { + let result = comparisonMode + ? try await client.gitComparisonDiff( + workspaceId: workspaceId, + comparisonId: comparison?.comparisonId ?? "", + fileId: file.id + ) + : try await client.gitDiff( + workspaceId: workspaceId, + snapshotId: reviewSnapshotId ?? "", + fileId: file.id + ) + if case .diff(let value) = result.result { selectedDiff = value } + } catch { errorMessage = error.localizedDescription } + } + + func commit(client: AidenRemoteClient, workspaceId: String, message: String, stagedOnly: Bool) async { + let operation = AidenPendingGitMutation.commit( + UUID(), + snapshot: reviewSnapshotId ?? "", + message: message, + stagedOnly: stagedOnly + ) + if await execute(operation, client: client, workspaceId: workspaceId) { + await refresh(client: client, workspaceId: workspaceId) + } + } + + func checkout(client: AidenRemoteClient, workspaceId: String, branch: String) async { + let operation = AidenPendingGitMutation.checkout( + UUID(), + snapshot: branchesSnapshotId ?? "", + branch: branch + ) + if await execute(operation, client: client, workspaceId: workspaceId) { + await refresh(client: client, workspaceId: workspaceId) + } + } + + func createBranch(client: AidenRemoteClient, workspaceId: String, name: String) async { + let operation = AidenPendingGitMutation.createBranch( + UUID(), + name: name, + startPoint: branches?.current ?? "HEAD" + ) + if await execute(operation, client: client, workspaceId: workspaceId) { + await refresh(client: client, workspaceId: workspaceId) + } + } + + func preparePush(client: AidenRemoteClient, workspaceId: String) async { + do { + let result = try await client.gitPushCapability(workspaceId: workspaceId) + if case .pushCapability(let value) = result.result { + pushCapability = value + pushSnapshotId = result.snapshotId + if !value.allowed { + errorMessage = value.reason ?? "Push is unavailable for this workspace state." + } + } + } catch { errorMessage = error.localizedDescription } + } + + func push(client: AidenRemoteClient, workspaceId: String) async { + guard let pushCapability, let remote = pushCapability.remote, let branch = pushCapability.branch else { return } + _ = await execute( + .push(UUID(), snapshot: pushSnapshotId ?? "", remote: remote, branch: branch), + client: client, + workspaceId: workspaceId + ) + } + + func compare(client: AidenRemoteClient, workspaceId: String, baseRef: String) async { + do { + let result = try await client.compareGit(workspaceId: workspaceId, baseRef: baseRef) + if case .comparison(let value) = result.result { comparison = value } + } catch { errorMessage = error.localizedDescription } + } + + func createWorktree(client: AidenRemoteClient, workspaceId: String, branch: String, name: String) async { + if await execute( + .createWorktree(UUID(), branch: branch, name: name), + client: client, + workspaceId: workspaceId + ) { + await refresh(client: client, workspaceId: workspaceId) + } + } + + func retryPendingMutation(client: AidenRemoteClient, workspaceId: String) async { + guard let pendingMutation else { return } + if await execute(pendingMutation, client: client, workspaceId: workspaceId) { + await refresh(client: client, workspaceId: workspaceId) + } + } + + private func execute( + _ operation: AidenPendingGitMutation, + client: AidenRemoteClient, + workspaceId: String + ) async -> Bool { + isLoading = true + errorMessage = nil + pendingMutation = operation + defer { isLoading = false } + do { + let value: AidenGitResult + switch operation { + case .commit(let key, let snapshot, let message, let stagedOnly): + value = try await client.commitGit( + workspaceId: workspaceId, + snapshotId: snapshot, + message: message, + stagedOnly: stagedOnly, + idempotencyKey: key + ) + case .checkout(let key, let snapshot, let branch): + value = try await client.checkoutGitBranch( + workspaceId: workspaceId, + branch: branch, + snapshotId: snapshot, + idempotencyKey: key + ) + case .createBranch(let key, let name, let startPoint): + value = try await client.createGitBranch( + workspaceId: workspaceId, + name: name, + startPoint: startPoint, + idempotencyKey: key + ) + case .push(let key, let snapshot, let remote, let branch): + value = try await client.pushGit( + workspaceId: workspaceId, + snapshotId: snapshot, + remote: remote, + branch: branch, + idempotencyKey: key + ) + case .createWorktree(let key, let branch, let name): + value = try await client.createGitWorktree( + workspaceId: workspaceId, + branch: branch, + name: name, + idempotencyKey: key + ) + } + if case .mutation(let mutation) = value.result { + lastMessage = mutation.warning.map { "\(mutation.message) \($0)" } ?? mutation.message + } + pendingMutation = nil + return true + } catch { + let retain = shouldRetainForReconciliation(error) + if !retain { pendingMutation = nil } + errorMessage = retain + ? "\(error.localizedDescription) Reconnect, then use Retry Last Git Operation to reconcile safely." + : error.localizedDescription + return false + } + } + + private func shouldRetainForReconciliation(_ error: Error) -> Bool { + if error is URLError { return true } + guard let clientError = error as? AidenRemoteClientError else { return false } + switch clientError { + case .invalidResponse, .unexpectedStatus: + return true + case .server(_, let body): + return body.code.rawValue == "idempotency_in_flight" || body.code.rawValue == "internal_error" + case .invalidEndpoint, .missingCredential, .missingTrustConfiguration: + return false + } + } +} + +struct AidenWorkspaceGitView: View { + @Bindable var coordinator: AidenRemoteCoordinator + let workspace: AidenWorkspace + @State private var model = AidenWorkspaceGitModel() + @State private var commitMessage = "" + @State private var stagedOnly = false + @State private var isShowingCommit = false + @State private var isConfirmingCommit = false + @State private var isConfirmingPush = false + @State private var compareBase = "" + @State private var isShowingCompare = false + @State private var newBranch = "" + @State private var isShowingNewBranch = false + @State private var checkoutBranch: String? + @State private var worktreeBranch = "" + @State private var worktreeName = "" + @State private var isShowingNewWorktree = false + + private var client: AidenRemoteClient? { try? coordinator.remoteClient() } + + var body: some View { + gitConfirmations + .alert("Git", isPresented: Binding( + get: { model.errorMessage != nil }, + set: { if !$0 { model.errorMessage = nil } } + )) { + Button("OK", role: .cancel) { model.errorMessage = nil } + } message: { Text(model.errorMessage ?? "The Git operation failed.") } + } + + private var gitList: some View { + List { + if let review = model.review { + Section("Repository") { + LabeledContent("Branch", value: review.branch) + LabeledContent("Changes", value: "\(review.uncommitted)") + } + Section("Working changes") { + if review.files.isEmpty { + Label("Working tree is clean", systemImage: "checkmark.circle") + } + ForEach(review.files) { file in + gitFileButton(file, comparisonMode: false) + } + } + } + + if let comparison = model.comparison { + Section("Compare \(comparison.base) → \(comparison.head)") { + ForEach(comparison.files) { file in gitFileButton(file, comparisonMode: true) } + } + } + + Section("Actions") { + Button("Commit reviewed changes", systemImage: "checkmark.circle") { isShowingCommit = true } + .disabled(model.review?.files.isEmpty != false) + Button("Push reviewed commit", systemImage: "arrow.up.circle") { + Task { + guard let client else { return } + await model.preparePush(client: client, workspaceId: workspace.id) + isConfirmingPush = model.pushCapability?.allowed == true + } + } + Button("Compare branch", systemImage: "arrow.left.arrow.right") { isShowingCompare = true } + } + .disabled(coordinator.connectionState != .connected || model.isLoading) + + if let branches = model.branches { + Section("Branches") { + ForEach(branches.branches, id: \.self) { branch in + Button { + checkoutBranch = branch + } label: { + HStack { + Text(branch) + Spacer() + if branch == branches.current { Image(systemName: "checkmark") } + } + } + .disabled(branch == branches.current) + } + Button("New Branch", systemImage: "plus") { isShowingNewBranch = true } + } + .disabled(coordinator.connectionState != .connected || model.isLoading) + } + + Section("Managed worktrees") { + ForEach(model.worktrees) { worktree in + LabeledContent(worktree.name, value: worktree.branch) + } + Button("New Managed Worktree", systemImage: "hammer") { isShowingNewWorktree = true } + } + .disabled(coordinator.connectionState != .connected || model.isLoading) + + if let message = model.lastMessage { + Section { Label(message, systemImage: "checkmark.circle") } + } + if model.canRetryPendingMutation { + Section { + Button("Retry Last Git Operation", systemImage: "arrow.clockwise") { + Task { + if let client { + await model.retryPendingMutation(client: client, workspaceId: workspace.id) + } + } + } + } footer: { + Text("Reuses the original idempotency key so reconnecting cannot duplicate the mutation.") + } + .disabled(coordinator.connectionState != .connected || model.isLoading) + } + } + .navigationTitle("Git") + .overlay { if model.isLoading && model.review == nil { ProgressView("Loading Git…") } } + .refreshable { await refresh() } + .task { await refresh() } + .toolbar { + ToolbarItem(placement: .primaryAction) { + Button("Refresh", systemImage: "arrow.clockwise") { Task { await refresh() } } + } + } + } + + private var gitSheets: some View { + gitList + .sheet(isPresented: $isShowingCommit) { + commitSheet + } + .sheet(item: $model.selectedDiff) { diff in + diffSheet(diff) + } + } + + private var gitAlerts: some View { + gitSheets + .alert("Compare Branch", isPresented: $isShowingCompare) { + TextField("Base branch", text: $compareBase) + Button("Compare") { + let base = compareBase.trimmingCharacters(in: .whitespacesAndNewlines) + Task { if let client { await model.compare(client: client, workspaceId: workspace.id, baseRef: base) } } + } + Button("Cancel", role: .cancel) {} + } + .alert("New Branch", isPresented: $isShowingNewBranch) { + TextField("Branch name", text: $newBranch) + Button("Create and Check Out") { + let value = newBranch.trimmingCharacters(in: .whitespacesAndNewlines) + Task { if let client { await model.createBranch(client: client, workspaceId: workspace.id, name: value) } } + } + Button("Cancel", role: .cancel) {} + } + .alert("New Managed Worktree", isPresented: $isShowingNewWorktree) { + TextField("Branch", text: $worktreeBranch) + TextField("Workspace name", text: $worktreeName) + Button("Create") { + let branch = worktreeBranch.trimmingCharacters(in: .whitespacesAndNewlines) + let name = worktreeName.trimmingCharacters(in: .whitespacesAndNewlines) + Task { + if let client { + await model.createWorktree(client: client, workspaceId: workspace.id, branch: branch, name: name) + await coordinator.refreshWorkspaces() + } + } + } + Button("Cancel", role: .cancel) {} + } + } + + private var gitConfirmations: some View { + gitAlerts + .confirmationDialog("Check out \(checkoutBranch ?? "this branch")?", isPresented: Binding( + get: { checkoutBranch != nil }, + set: { if !$0 { checkoutBranch = nil } } + )) { + Button("Check Out Branch") { + let branch = checkoutBranch + checkoutBranch = nil + Task { if let branch, let client { await model.checkout(client: client, workspaceId: workspace.id, branch: branch) } } + } + Button("Cancel", role: .cancel) { checkoutBranch = nil } + } message: { Text("Aiden Agent will switch the workspace to this branch.") } + .confirmationDialog("Push the reviewed commit?", isPresented: $isConfirmingPush) { + Button("Push to \(model.pushCapability?.remote ?? "remote")") { + Task { if let client { await model.push(client: client, workspaceId: workspace.id) } } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("Destination: \(model.pushCapability?.branch ?? "current branch"). Aiden never force-pushes.") + } + } + + private var commitSheet: some View { + NavigationStack { + Form { + Section("Commit message") { + TextField("Describe the change", text: $commitMessage, axis: .vertical) + } + Section { Toggle("Staged changes only", isOn: $stagedOnly) } + } + .navigationTitle("Commit Changes") + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Cancel") { isShowingCommit = false } + } + ToolbarItem(placement: .confirmationAction) { + Button("Review Commit") { isConfirmingCommit = true } + .disabled(commitMessage.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) + } + } + .confirmationDialog("Commit the reviewed snapshot?", isPresented: $isConfirmingCommit) { + Button("Commit Changes") { + let message = commitMessage.trimmingCharacters(in: .whitespacesAndNewlines) + isShowingCommit = false + Task { + guard let client else { return } + await model.commit( + client: client, + workspaceId: workspace.id, + message: message, + stagedOnly: stagedOnly + ) + } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("Aiden Agent will create a Git commit from the exact reviewed snapshot.") + } + } + } + + private func diffSheet(_ diff: AidenGitDiff) -> some View { + NavigationStack { + ScrollView { + Text(diff.diff) + .font(.system(.caption, design: .monospaced)) + .textSelection(.enabled) + .frame(maxWidth: .infinity, alignment: .leading) + .padding() + } + .navigationTitle(diff.displayPath) + .navigationBarTitleDisplayMode(.inline) + .safeAreaInset(edge: .bottom) { + if diff.truncated { + Text("Diff truncated") + .font(.footnote) + .padding() + .frame(maxWidth: .infinity) + .background(.regularMaterial) + } + } + } + } + + private func gitFileButton(_ file: AidenGitFile, comparisonMode: Bool) -> some View { + Button { + Task { + guard let client else { return } + await model.diff(client: client, workspaceId: workspace.id, file: file, comparisonMode: comparisonMode) + } + } label: { + HStack { + Text(file.status.symbol).font(.system(.body, design: .monospaced)).foregroundStyle(file.status.tint) + VStack(alignment: .leading, spacing: 2) { + Text(file.displayPath).foregroundStyle(.primary).lineLimit(1) + if file.additions != nil || file.deletions != nil { + Text("+\(file.additions ?? 0) −\(file.deletions ?? 0)").font(.caption).foregroundStyle(.secondary) + } + } + } + } + } + + private func refresh() async { + guard coordinator.connectionState == .connected, let client else { + model.errorMessage = "Connect to Aiden Agent to use Git." + return + } + await model.refresh(client: client, workspaceId: workspace.id) + } +} + +private extension AidenGitFileStatus { + var symbol: String { + switch self { + case .added: "A" + case .modified: "M" + case .deleted: "D" + case .renamed: "R" + case .untracked: "?" + case .conflicted: "U" + } + } + + var tint: Color { + switch self { + case .added, .untracked: .green + case .deleted, .conflicted: .red + case .modified, .renamed: .orange + } + } +} diff --git a/ios/AidenOnTheGo/Features/Remote/AidenWorkspaceShellView.swift b/ios/AidenOnTheGo/Features/Remote/AidenWorkspaceShellView.swift new file mode 100644 index 00000000..3b2b2387 --- /dev/null +++ b/ios/AidenOnTheGo/Features/Remote/AidenWorkspaceShellView.swift @@ -0,0 +1,1505 @@ +import SwiftUI + +enum AidenRelativeTimestamp { + static func text(for date: Date, now: Date = Date()) -> String { + let elapsed = max(0, now.timeIntervalSince(date)) + if elapsed < 60 { return "just now" } + + let minutes = Int(elapsed / 60) + if minutes < 60 { return minutes == 1 ? "1 min ago" : "\(minutes) mins ago" } + + let hours = Int(elapsed / 3_600) + if hours < 24 { return hours == 1 ? "1 hr ago" : "\(hours) hrs ago" } + + let days = Int(elapsed / 86_400) + return days == 1 ? "1 day ago" : "\(days) days ago" + } +} + +struct AidenRelativeTimestampView: View { + let date: Date + + var body: some View { + TimelineView(.periodic(from: .now, by: 30)) { context in + Text(AidenRelativeTimestamp.text(for: date, now: context.date)) + } + } +} + +private struct AidenLiquidGlassCapsuleModifier: ViewModifier { + @Environment(\.accessibilityReduceTransparency) private var reduceTransparency + let tint: Color + + @ViewBuilder + func body(content: Content) -> some View { + if #available(iOS 26, *), !reduceTransparency { + content.glassEffect(.regular.tint(tint).interactive(), in: Capsule()) + } else { + content.background(tint, in: Capsule()) + } + } +} + +private extension View { + func aidenLiquidGlassCapsule(tint: Color) -> some View { + modifier(AidenLiquidGlassCapsuleModifier(tint: tint)) + } +} + +@MainActor +@Observable +private final class AidenHomeModel { + var chats: [AidenChat] = [] + var scheduledTasks: [AidenScheduledTask] = [] + var usage: AidenUsageSummary? + var isLoading = false + var errorMessage: String? + + func load(coordinator: AidenRemoteCoordinator) async { + guard coordinator.connectionState == .connected, !isLoading else { return } + isLoading = true + errorMessage = nil + defer { isLoading = false } + do { + let client = try coordinator.remoteClient() + async let chatsRequest = client.chats() + async let tasksRequest = client.scheduledTasks() + let (chats, tasks) = try await (chatsRequest, tasksRequest) + self.chats = chats.sorted { $0.updatedAt > $1.updatedAt } + scheduledTasks = tasks.sorted { + ($0.nextRunAt ?? .distantFuture) < ($1.nextRunAt ?? .distantFuture) + } + usage = try? await client.usage() + } catch { + errorMessage = error.localizedDescription + } + } +} + +private struct AidenNavigationResolutionID: Equatable { + let request: AidenNavigationRequest? + let connectionState: AidenRemoteConnectionState +} + +enum AidenWorkspaceNavigation { + static func reconciledSelection(current: String?, workspaceIDs: [String]) -> String? { + guard !workspaceIDs.isEmpty else { return nil } + if let current, workspaceIDs.contains(current) { return current } + return workspaceIDs.first + } + + static func reconciledCompactPath(current: [String], workspaceIDs: [String]) -> [String] { + guard let workspaceID = current.last, workspaceIDs.contains(workspaceID) else { return [] } + return [workspaceID] + } + + static func compactPath( + enteringFromSplit: Bool, + current: [String], + selectedWorkspaceID: String?, + workspaceIDs: [String] + ) -> [String] { + let current = reconciledCompactPath(current: current, workspaceIDs: workspaceIDs) + if !current.isEmpty { return current } + guard enteringFromSplit, + let selectedWorkspaceID, + workspaceIDs.contains(selectedWorkspaceID) else { return [] } + return [selectedWorkspaceID] + } +} + +struct AidenWorkspaceShellView: View { + @Bindable var coordinator: AidenRemoteCoordinator + @Binding private var navigationRequest: AidenNavigationRequest? + @Environment(AidenAppearanceStore.self) private var appearance + @Environment(\.aidenPalette) private var palette + @Environment(\.horizontalSizeClass) private var horizontalSizeClass + + @State private var selectedWorkspaceId: String? + @State private var compactWorkspacePath: [String] = [] + @State private var isShowingPairing = false + @State private var isShowingAppSettings = false + @State private var isShowingScheduledTasks = false + @State private var isShowingUsage = false + @State private var intentChat: AidenChat? + @State private var intentStartsVoice = false + @State private var homeModel = AidenHomeModel() + @State private var searchText = "" + @State private var isSearching = false + @State private var isShowingNewAgentChoices = false + @State private var isShowingExistingWorkspacePicker = false + @State private var isShowingNewAgentWorkspacePrompt = false + @State private var newAgentWorkspaceName = "" + @State private var isCreatingAgent = false + @State private var agentCreationStatus = "" + @FocusState private var searchFieldIsFocused: Bool + @AppStorage("aiden.defaults.workspacePermission") private var defaultWorkspacePermissionRaw = AidenWorkspacePermission.ask.rawValue + + init( + coordinator: AidenRemoteCoordinator, + navigationRequest: Binding = .constant(nil) + ) { + self.coordinator = coordinator + _navigationRequest = navigationRequest + } + + private var usesSplitNavigation: Bool { + horizontalSizeClass == .regular + } + + var body: some View { + Group { + if usesSplitNavigation { + NavigationSplitView { + regularWorkspaceSidebar + .navigationSplitViewColumnWidth(min: 240, ideal: 300, max: 400) + } detail: { + NavigationStack { + workspaceDetail(workspaceID: selectedWorkspaceId) + } + } + .navigationSplitViewStyle(.balanced) + } else { + NavigationStack(path: $compactWorkspacePath) { + compactWorkspaceSidebar + .navigationDestination(for: String.self) { workspaceID in + workspaceDetail(workspaceID: workspaceID) + } + } + } + } + .safeAreaInset(edge: .top, spacing: 0) { + if case .offline(let message) = coordinator.connectionState { + AidenOfflineBanner(message: message) { + Task { await coordinator.connectActiveInstallation() } + } + } + } + .overlay { + if coordinator.connectionState == .connecting { + ProgressView("Connecting to Aiden Agent…") + .padding() + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12)) + } else if isCreatingAgent { + HStack(spacing: 10) { + ProgressView().controlSize(.small) + Text(agentCreationStatus) + } + .font(.callout.weight(.medium)) + .padding(.horizontal, 16) + .frame(height: 48) + .foregroundStyle(palette.canvas) + .aidenLiquidGlassCapsule(tint: palette.foreground) + .accessibilityElement(children: .combine) + } + } + .sheet(isPresented: $isShowingPairing) { + AidenPairingView(coordinator: coordinator) { isShowingPairing = false } + } + .sheet(isPresented: $isShowingAppSettings) { + AidenAppSettingsView( + coordinator: coordinator, + appearance: appearance, + addInstallation: { + isShowingAppSettings = false + isShowingPairing = true + } + ) + } + .sheet(isPresented: $isShowingScheduledTasks) { + AidenScheduledTasksView(coordinator: coordinator) + } + .sheet(isPresented: $isShowingUsage) { + if let usage = homeModel.usage { + AidenUsageView(usage: usage) + } + } + .sheet(isPresented: $isShowingExistingWorkspacePicker) { + NavigationStack { + List(coordinator.workspaces) { workspace in + Button { + isShowingExistingWorkspacePicker = false + Task { await createNewAgent(in: workspace) } + } label: { + AidenWorkspaceRow(workspace: workspace) + } + .buttonStyle(.plain) + } + .navigationTitle("Choose Workspace") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Cancel") { isShowingExistingWorkspacePicker = false } + } + } + } + } + .sheet(item: $intentChat) { chat in + NavigationStack { + AidenChatDetailView( + coordinator: coordinator, + chat: chat, + autoStartVoice: intentStartsVoice + ) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Close") { intentChat = nil } + } + } + } + } + .alert( + "Aiden On The Go", + isPresented: Binding( + get: { coordinator.presentedError != nil }, + set: { if !$0 { coordinator.presentedError = nil } } + ) + ) { + Button("OK", role: .cancel) { coordinator.presentedError = nil } + } message: { + Text(coordinator.presentedError ?? "The operation could not be completed.") + } + .alert("New Workspace", isPresented: $isShowingNewAgentWorkspacePrompt) { + TextField("Workspace name", text: $newAgentWorkspaceName) + Button("Cancel", role: .cancel) { newAgentWorkspaceName = "" } + Button("Create") { + let name = newAgentWorkspaceName.trimmingCharacters(in: .whitespacesAndNewlines) + newAgentWorkspaceName = "" + Task { await createNewAgent(inNewWorkspaceNamed: name) } + } + .disabled(newAgentWorkspaceName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) + } message: { + Text("Aiden will create a reusable workspace, then open a new chat in it.") + } + .confirmationDialog( + "Where should this agent work?", + isPresented: $isShowingNewAgentChoices, + titleVisibility: .visible + ) { + Button("Existing Workspace") { isShowingExistingWorkspacePicker = true } + .disabled(coordinator.workspaces.isEmpty) + Button("New Workspace") { isShowingNewAgentWorkspacePrompt = true } + Button("Managed Scratch Workspace") { + Task { await createNewAgentInScratchWorkspace() } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("Reuse a workspace to avoid creating unnecessary folders, or choose an isolated scratch workspace for temporary work.") + } + .onChange(of: coordinator.workspaces.map(\.id)) { _, ids in + selectedWorkspaceId = AidenWorkspaceNavigation.reconciledSelection( + current: selectedWorkspaceId, + workspaceIDs: ids + ) + compactWorkspacePath = AidenWorkspaceNavigation.reconciledCompactPath( + current: compactWorkspacePath, + workspaceIDs: ids + ) + } + .onChange(of: compactWorkspacePath) { _, path in + guard let workspaceID = path.last, + coordinator.workspaces.contains(where: { $0.id == workspaceID }) else { return } + selectedWorkspaceId = workspaceID + } + .onChange(of: usesSplitNavigation) { wasSplit, isSplit in + let workspaceIDs = coordinator.workspaces.map(\.id) + if isSplit { + if let compactWorkspaceID = compactWorkspacePath.last, + workspaceIDs.contains(compactWorkspaceID) { + selectedWorkspaceId = compactWorkspaceID + } + } else { + compactWorkspacePath = AidenWorkspaceNavigation.compactPath( + enteringFromSplit: wasSplit, + current: compactWorkspacePath, + selectedWorkspaceID: selectedWorkspaceId, + workspaceIDs: workspaceIDs + ) + } + } + .task { + let workspaceIDs = coordinator.workspaces.map(\.id) + selectedWorkspaceId = AidenWorkspaceNavigation.reconciledSelection( + current: selectedWorkspaceId, + workspaceIDs: workspaceIDs + ) + compactWorkspacePath = AidenWorkspaceNavigation.reconciledCompactPath( + current: compactWorkspacePath, + workspaceIDs: workspaceIDs + ) + } + .task(id: AidenNavigationResolutionID( + request: navigationRequest, + connectionState: coordinator.connectionState + )) { + await resolveNavigationRequest() + } + .task(id: coordinator.connectionState) { + await homeModel.load(coordinator: coordinator) + } + } + + private var regularWorkspaceSidebar: some View { + ZStack(alignment: .bottomTrailing) { + homeList { chat in + Button { intentChat = chat } label: { homeChatRow(chat) } + .buttonStyle(.plain) + } + newAgentButton.padding(.trailing, 24).padding(.bottom, 22) + } + } + + private var compactWorkspaceSidebar: some View { + ZStack(alignment: .bottomTrailing) { + homeList { chat in + NavigationLink { + AidenChatDetailView(coordinator: coordinator, chat: chat) + } label: { + homeChatRow(chat) + } + } + newAgentButton.padding(.trailing, 24).padding(.bottom, 22) + } + } + + private var filteredChats: [AidenChat] { + guard !searchText.isEmpty else { return homeModel.chats } + return homeModel.chats.filter { $0.title.localizedCaseInsensitiveContains(searchText) } + } + + private func homeList( + @ViewBuilder chatRow: @escaping (AidenChat) -> ChatRow + ) -> some View { + List { + homeHeader + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 18, trailing: 0)) + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + + if !isSearching { + homeNavigationRows + .padding(.top, 10) + .listRowInsets(EdgeInsets()) + .listRowSeparator(.hidden) + .listRowBackground(palette.canvas) + } + + if !isSearching { + Text("Chats") + .font(.title3.bold()) + .foregroundStyle(palette.foreground) + .padding(.horizontal, 24) + .padding(.top, 26) + .padding(.bottom, 10) + .listRowInsets(EdgeInsets()) + .listRowSeparator(.hidden) + .listRowBackground(palette.canvas) + } + + if filteredChats.isEmpty, !homeModel.isLoading { + ContentUnavailableView( + searchText.isEmpty ? "No Chats Yet" : "No Matching Chats", + systemImage: searchText.isEmpty ? "bubble.left" : "magnifyingglass", + description: Text(searchText.isEmpty + ? "Start a new agent to create your first chat." + : "Try a different search term.") + ) + .listRowInsets(EdgeInsets()) + .listRowSeparator(.hidden) + .listRowBackground(palette.canvas) + } else { + ForEach(filteredChats) { chat in + chatRow(chat) + .listRowInsets(EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12)) + .listRowSeparator(.hidden) + .listRowBackground(palette.canvas) + } + } + + Color.clear.frame(height: 90).listRowSeparator(.hidden) + } + .listStyle(.plain) + .scrollContentBackground(.hidden) + .background(palette.canvas) + .refreshable { + await coordinator.refreshWorkspaces() + await homeModel.load(coordinator: coordinator) + } + .overlay { if homeModel.isLoading && homeModel.chats.isEmpty { ProgressView() } } + } + + private var homeHeader: some View { + HStack(spacing: isSearching ? 0 : 16) { + Image("AidenAppIcon") + .resizable() + .scaledToFit() + .frame(width: isSearching ? 0 : 68, height: 68, alignment: .leading) + .opacity(isSearching ? 0 : 1) + .clipped() + .accessibilityElement(children: .ignore) + .accessibilityLabel("Aiden") + + searchChrome + .frame(maxWidth: .infinity, alignment: .trailing) + } + .padding(.horizontal, 24) + .padding(.top, 28) + .animation(.smooth(duration: 0.24, extraBounce: 0), value: isSearching) + } + + private var searchChrome: some View { + HStack(spacing: isSearching ? 8 : 2) { + Button { + openSearch() + } label: { + Image(systemName: "magnifyingglass") + .font(.system(size: 22, weight: .semibold)) + .frame(width: 44, height: 44) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .accessibilityLabel(isSearching ? "Focus chat search" : "Search chats") + + if isSearching { + TextField("Search chats", text: $searchText) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + .focused($searchFieldIsFocused) + .submitLabel(.done) + + Button { + closeSearch() + } label: { + Image(systemName: "xmark") + .font(.system(size: 21, weight: .medium)) + .frame(width: 44, height: 44) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .accessibilityLabel("Close search") + } else { + Button { isShowingAppSettings = true } label: { + Image(systemName: "person.crop.circle.fill") + .font(.system(size: 28, weight: .medium)) + .frame(width: 44, height: 44) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .accessibilityLabel("Profile and app settings") + } + } + .padding(.vertical, 2) + .frame(maxWidth: isSearching ? .infinity : nil, alignment: .trailing) + .background(.thinMaterial, in: Capsule()) + .clipShape(Capsule()) + .contentShape(Capsule()) + } + + private var homeNavigationRows: some View { + VStack(alignment: .leading, spacing: 2) { + Button { isShowingScheduledTasks = true } label: { + homeNavigationRow( + title: "Scheduled Tasks", + systemImage: "calendar.badge.clock" + ) + } + .buttonStyle(.plain) + .disabled(coordinator.connectionState != .connected) + + Button { isShowingUsage = true } label: { + homeNavigationRow(title: "Usage", systemImage: "chart.bar.xaxis") + } + .buttonStyle(.plain) + .disabled(homeModel.usage == nil) + + NavigationLink { + AidenWorkspacesDirectoryView(coordinator: coordinator) + } label: { + homeNavigationRow( + title: "Workspaces", + systemImage: "folder", + showsChevron: false + ) + } + .buttonStyle(.plain) + } + .padding(.horizontal, 24) + } + + private func homeNavigationRow( + title: String, + systemImage: String, + showsChevron: Bool = true + ) -> some View { + HStack(spacing: 18) { + Image(systemName: systemImage) + .font(.system(size: 21, weight: .medium)) + .foregroundStyle(palette.accent) + .frame(width: 28) + + Text(title) + .font(.body.weight(.semibold)) + .foregroundStyle(palette.foreground) + .lineLimit(1) + + Spacer(minLength: 8) + + if showsChevron { + Image(systemName: "chevron.forward") + .font(.caption.weight(.semibold)) + .foregroundStyle(palette.secondary) + .frame(width: 24) + } + } + .frame(maxWidth: .infinity, minHeight: 48, alignment: .leading) + .contentShape(Rectangle()) + } + + private func homeChatRow(_ chat: AidenChat) -> some View { + HStack(alignment: .top, spacing: 10) { + VStack(alignment: .leading, spacing: 3) { + Text(chat.title) + .font(.headline.weight(.semibold)) + .foregroundStyle(palette.foreground) + .lineLimit(2) + + if let workspace = coordinator.workspaces.first(where: { $0.id == chat.workspaceId }) { + Text(workspace.name) + .font(.caption) + .foregroundStyle(palette.secondary) + .lineLimit(1) + } + } + + Spacer(minLength: 8) + + AidenRelativeTimestampView(date: chat.updatedAt) + .font(.caption) + .foregroundStyle(palette.secondary) + .lineLimit(1) + .fixedSize(horizontal: true, vertical: false) + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + .frame(minHeight: 52) + .contentShape(Rectangle()) + } + + private func openSearch() { + if isSearching { + searchFieldIsFocused = true + return + } + + withAnimation(.smooth(duration: 0.24, extraBounce: 0)) { + isSearching = true + } + Task { @MainActor in + await Task.yield() + searchFieldIsFocused = true + } + } + + private func closeSearch() { + searchText = "" + searchFieldIsFocused = false + withAnimation(.smooth(duration: 0.24, extraBounce: 0)) { + isSearching = false + } + } + + private var newAgentButton: some View { + Button { isShowingNewAgentChoices = true } label: { + Label("New Agent", systemImage: "square.and.pencil") + .font(.headline.weight(.semibold)) + .padding(.horizontal, 20) + .frame(height: 56) + .foregroundStyle(palette.canvas) + .aidenLiquidGlassCapsule(tint: palette.foreground) + .contentShape(Capsule()) + } + .buttonStyle(.plain) + .disabled(coordinator.connectionState != .connected || coordinator.isMutating || isCreatingAgent) + } + + @MainActor + private func createNewAgent(in workspace: AidenWorkspace) async { + await createNewAgent(workspace: workspace, status: "Opening agent…") + } + + @MainActor + private func createNewAgent(inNewWorkspaceNamed name: String) async { + guard !name.isEmpty else { return } + await createNewAgent( + workspaceCreate: .folderless(name: name), + status: "Creating workspace…" + ) + } + + @MainActor + private func createNewAgentInScratchWorkspace() async { + await createNewAgent(workspaceCreate: .scratch, status: "Preparing scratch workspace…") + } + + @MainActor + private func createNewAgent(workspaceCreate: AidenWorkspaceCreate, status: String) async { + guard !isCreatingAgent else { return } + isCreatingAgent = true + agentCreationStatus = status + defer { + isCreatingAgent = false + agentCreationStatus = "" + } + + guard let created = await coordinator.createWorkspace(workspaceCreate) else { return } + let workspace = await applyGlobalDefaults(to: created) + await createNewAgent(workspace: workspace, status: "Opening agent…", managesProgress: false) + } + + @MainActor + private func createNewAgent( + workspace: AidenWorkspace, + status: String, + managesProgress: Bool = true + ) async { + guard !isCreatingAgent || !managesProgress else { return } + if managesProgress { + isCreatingAgent = true + agentCreationStatus = status + } else { + agentCreationStatus = status + } + defer { + if managesProgress { + isCreatingAgent = false + agentCreationStatus = "" + } + } + + do { + let chat = try await coordinator.remoteClient().createChat(workspaceId: workspace.id) + await homeModel.load(coordinator: coordinator) + navigate(to: workspace.id) + intentChat = chat + } catch { + coordinator.presentedError = error.localizedDescription + await coordinator.refreshWorkspaces() + await homeModel.load(coordinator: coordinator) + } + } + + @MainActor + private func applyGlobalDefaults(to workspace: AidenWorkspace) async -> AidenWorkspace { + let permission = AidenWorkspacePermission(rawValue: defaultWorkspacePermissionRaw) ?? .ask + guard permission != workspace.permission else { return workspace } + return await coordinator.updateWorkspace(workspace, permission: permission) ?? workspace + } + + @ViewBuilder + private func workspaceDetail(workspaceID: String?) -> some View { + if let workspaceID, + let workspace = coordinator.workspaces.first(where: { $0.id == workspaceID }) { + AidenWorkspaceDetailView( + coordinator: coordinator, + workspace: workspace, + onRemoved: { removeWorkspaceFromNavigation(workspaceID) } + ) + .id(workspace.revision) + } else { + ContentUnavailableView( + "Choose a Workspace", + systemImage: "bubble.left.and.bubble.right", + description: Text("Select a workspace to see its chats and settings.") + ) + } + } + + private func navigate(to workspaceID: String) { + guard coordinator.workspaces.contains(where: { $0.id == workspaceID }) else { return } + selectedWorkspaceId = workspaceID + if !usesSplitNavigation { + compactWorkspacePath = [workspaceID] + } + } + + private func removeWorkspaceFromNavigation(_ workspaceID: String) { + if selectedWorkspaceId == workspaceID { + selectedWorkspaceId = nil + } + compactWorkspacePath.removeAll { $0 == workspaceID } + } + + @MainActor + private func resolveNavigationRequest() async { + guard let request = navigationRequest, + coordinator.connectionState == .connected else { return } + defer { navigationRequest = nil } + guard request.instanceId == nil || request.instanceId == coordinator.activeInstanceId else { + coordinator.presentedError = String(localized: "The requested Aiden installation is not active.") + return + } + + do { + let chat: AidenChat + switch request.destination { + case .newChat: + let workspaceId = request.workspaceId ?? selectedWorkspaceId ?? coordinator.workspaces.first?.id + guard let workspaceId, + coordinator.workspaces.contains(where: { $0.id == workspaceId }) else { + coordinator.presentedError = String(localized: "The requested workspace is unavailable. Choose or add a workspace first.") + return + } + navigate(to: workspaceId) + chat = try await coordinator.remoteClient().createChat(workspaceId: workspaceId) + case .chat(let chatId): + chat = try await coordinator.remoteClient().chat(id: chatId) + guard coordinator.workspaces.contains(where: { $0.id == chat.workspaceId }) else { + coordinator.presentedError = String(localized: "The chat's workspace is no longer available.") + return + } + navigate(to: chat.workspaceId) + } + intentStartsVoice = request.startsVoice + intentChat = chat + } catch { + coordinator.presentedError = error.localizedDescription + } + } +} + +private struct AidenWorkspacesDirectoryView: View { + @Bindable var coordinator: AidenRemoteCoordinator + @State private var searchText = "" + @State private var isShowingFolderBrowser = false + @State private var isShowingNewWorkspace = false + @State private var isConfirmingScratch = false + @State private var newWorkspaceName = "" + @AppStorage("aiden.defaults.workspacePermission") private var defaultWorkspacePermissionRaw = AidenWorkspacePermission.ask.rawValue + + private var filteredWorkspaces: [AidenWorkspace] { + guard !searchText.isEmpty else { return coordinator.workspaces } + return coordinator.workspaces.filter { workspace in + workspace.name.localizedCaseInsensitiveContains(searchText) + || workspace.repositoryName?.localizedCaseInsensitiveContains(searchText) == true + || workspace.branchName?.localizedCaseInsensitiveContains(searchText) == true + } + } + + var body: some View { + List { + if filteredWorkspaces.isEmpty { + ContentUnavailableView( + searchText.isEmpty ? "No Workspaces" : "No Matching Workspaces", + systemImage: searchText.isEmpty ? "folder" : "magnifyingglass", + description: Text(searchText.isEmpty + ? "Create a workspace or add a Mac folder to get started." + : "Try a different search term.") + ) + .listRowBackground(Color.clear) + } else { + ForEach(filteredWorkspaces) { workspace in + NavigationLink { + AidenDirectoryWorkspaceDetail( + coordinator: coordinator, + workspace: workspace + ) + } label: { + AidenWorkspaceRow(workspace: workspace) + } + } + } + } + .listStyle(.plain) + .navigationTitle("Workspaces") + .navigationBarTitleDisplayMode(.inline) + .searchable(text: $searchText, prompt: "Search workspaces") + .refreshable { + await coordinator.refreshWorkspaces() + } + .toolbar { + ToolbarItem(placement: .primaryAction) { + Menu { + Button { + newWorkspaceName = "" + isShowingNewWorkspace = true + } label: { + Label("New Workspace", systemImage: "folder.badge.plus") + } + + Button { isConfirmingScratch = true } label: { + Label("New Managed Scratch", systemImage: "hammer") + } + + Button { isShowingFolderBrowser = true } label: { + Label("Add Mac Folder", systemImage: "folder.badge.plus") + } + } label: { + Image(systemName: "plus") + } + .accessibilityLabel("Add workspace") + } + } + .sheet(isPresented: $isShowingFolderBrowser) { + AidenFolderBrowserView(coordinator: coordinator) { workspace in + Task { + _ = await applyGlobalDefault(to: workspace) + isShowingFolderBrowser = false + } + } + } + .alert("New Workspace", isPresented: $isShowingNewWorkspace) { + TextField("Workspace name", text: $newWorkspaceName) + Button("Cancel", role: .cancel) {} + Button("Create") { + let name = newWorkspaceName.trimmingCharacters(in: .whitespacesAndNewlines) + Task { + if let workspace = await coordinator.createWorkspace(.folderless(name: name)) { + _ = await applyGlobalDefault(to: workspace) + } + } + } + .disabled(newWorkspaceName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) + } message: { + Text("Creates a workspace registry entry without a Mac folder. You can add a folder later from Aiden Agent.") + } + .confirmationDialog( + "Create a managed scratch workspace?", + isPresented: $isConfirmingScratch, + titleVisibility: .visible + ) { + Button("Create Managed Scratch") { + Task { + if let workspace = await coordinator.createWorkspace(.scratch) { + _ = await applyGlobalDefault(to: workspace) + } + } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("Aiden Agent will create and manage the worktree on your Mac.") + } + } + + @MainActor + private func applyGlobalDefault(to workspace: AidenWorkspace) async -> AidenWorkspace { + let permission = AidenWorkspacePermission(rawValue: defaultWorkspacePermissionRaw) ?? .ask + guard permission != workspace.permission else { return workspace } + return await coordinator.updateWorkspace(workspace, permission: permission) ?? workspace + } +} + +private struct AidenDirectoryWorkspaceDetail: View { + @Environment(\.dismiss) private var dismiss + @Bindable var coordinator: AidenRemoteCoordinator + let workspace: AidenWorkspace + + var body: some View { + AidenWorkspaceDetailView( + coordinator: coordinator, + workspace: workspace, + onRemoved: { dismiss() } + ) + .id(workspace.revision) + } +} + +private struct AidenWorkspaceRow: View { + let workspace: AidenWorkspace + + var body: some View { + HStack(spacing: 12) { + Image(systemName: workspace.isManagedWorktree ? "hammer.fill" : workspace.hasFolder ? "folder.fill" : "folder") + .foregroundStyle(.tint) + .frame(width: 24) + + VStack(alignment: .leading, spacing: 3) { + Text(workspace.name) + .lineLimit(1) + HStack(spacing: 5) { + Text(workspace.permission.title) + if let branch = workspace.branchName ?? workspace.git?.branch { + Text("·") + Text(branch) + } + } + .font(.caption) + .foregroundStyle(.secondary) + .lineLimit(1) + } + } + .accessibilityElement(children: .combine) + } +} + +private struct AidenWorkspaceDetailView: View { + @Bindable var coordinator: AidenRemoteCoordinator + let workspace: AidenWorkspace + let onRemoved: () -> Void + + @State private var isShowingSettings = false + + var body: some View { + AidenWorkspaceChatsView(coordinator: coordinator, workspace: workspace) + .navigationTitle(workspace.name) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .primaryAction) { + Menu { + Button { + isShowingSettings = true + } label: { + Label("Workspace Settings", systemImage: "gearshape") + } + } label: { + Image(systemName: "ellipsis.circle") + } + .accessibilityLabel("Workspace menu") + } + } + .sheet(isPresented: $isShowingSettings) { + AidenWorkspaceSettingsView( + coordinator: coordinator, + workspace: workspace, + onRemoved: { + isShowingSettings = false + onRemoved() + } + ) + } + } +} + +private struct AidenWorkspaceSettingsView: View { + @Environment(\.dismiss) private var dismiss + @Bindable var coordinator: AidenRemoteCoordinator + let workspace: AidenWorkspace + let onRemoved: () -> Void + + @State private var name: String + @State private var permission: AidenWorkspacePermission + @State private var isConfirmingRemoval = false + + init( + coordinator: AidenRemoteCoordinator, + workspace: AidenWorkspace, + onRemoved: @escaping () -> Void + ) { + self.coordinator = coordinator + self.workspace = workspace + self.onRemoved = onRemoved + _name = State(initialValue: workspace.name) + _permission = State(initialValue: workspace.permission) + } + + private var hasChanges: Bool { + name.trimmingCharacters(in: .whitespacesAndNewlines) != workspace.name || permission != workspace.permission + } + + var body: some View { + NavigationStack { + Form { + Section("Workspace") { + TextField("Name", text: $name) + LabeledContent("Folder", value: workspace.hasFolder ? "Connected" : "None") + LabeledContent("Managed worktree", value: workspace.isManagedWorktree ? "Yes" : "No") + if let repository = workspace.repositoryName { + LabeledContent("Repository", value: repository) + } + } + + Section { + Picker("Permission", selection: $permission) { + ForEach(AidenWorkspacePermission.allCases, id: \.self) { permission in + Text(permission.title).tag(permission) + } + } + } header: { + Text("Permission") + } footer: { + Text("\(permission.detail) This setting overrides the app default for this workspace.") + } + + Section("Workspace tools") { + NavigationLink { + AidenWorkspaceFilesView(coordinator: coordinator, workspace: workspace) + } label: { + Label("Files", systemImage: "doc.text") + } + .disabled(!workspace.hasFolder) + + NavigationLink { + AidenWorkspaceGitView(coordinator: coordinator, workspace: workspace) + } label: { + Label("Git", systemImage: "arrow.triangle.branch") + } + .disabled(!workspace.hasFolder) + } + + Section { + Button( + workspace.isManagedWorktree ? "Delete Managed Worktree" : "Unregister Workspace", + role: .destructive + ) { + isConfirmingRemoval = true + } + } footer: { + Text(workspace.isManagedWorktree + ? "Deleting an Aiden-managed worktree removes its checkout and may remove its branch when safe." + : "Unregistering removes this workspace from Aiden. It does not delete its folder.") + } + } + .navigationTitle("Workspace Settings") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Cancel") { dismiss() } + } + ToolbarItem(placement: .confirmationAction) { + Button("Save") { + let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines) + Task { + if await coordinator.updateWorkspace( + workspace, + name: trimmedName == workspace.name ? nil : trimmedName, + permission: permission == workspace.permission ? nil : permission + ) != nil { + dismiss() + } + } + } + .disabled(!hasChanges || name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || coordinator.isMutating) + } + } + .confirmationDialog( + workspace.isManagedWorktree ? "Delete \(workspace.name)?" : "Unregister \(workspace.name)?", + isPresented: $isConfirmingRemoval, + titleVisibility: .visible + ) { + Button(workspace.isManagedWorktree ? "Delete Managed Worktree" : "Unregister Workspace", role: .destructive) { + Task { + let removed = workspace.isManagedWorktree + ? await coordinator.removeManagedWorktree(workspace) + : await coordinator.removeWorkspace(workspace) + if removed { + onRemoved() + } + } + } + Button("Cancel", role: .cancel) {} + } message: { + Text(workspace.isManagedWorktree + ? "This destructive Git operation is performed by Aiden Agent using its persisted worktree ownership record." + : "The folder and its files remain untouched on your Mac.") + } + } + } +} + +private struct AidenUsageView: View { + @Environment(\.dismiss) private var dismiss + let usage: AidenUsageSummary + + var body: some View { + NavigationStack { + List { + Section("Last 30 days") { + LabeledContent("Requests", value: usage.totals.requests.formatted()) + LabeledContent("Total tokens", value: usage.totals.tokens.total.formatted()) + LabeledContent("Input", value: usage.totals.tokens.input.formatted()) + LabeledContent("Output", value: usage.totals.tokens.output.formatted()) + LabeledContent("Reasoning", value: usage.totals.tokens.reasoning.formatted()) + LabeledContent("Cache read", value: usage.totals.tokens.cacheRead.formatted()) + } + + Section("Activity") { + LabeledContent("Active days", value: usage.totals.activeDays.formatted()) + LabeledContent("Current streak", value: "\(usage.totals.currentStreak) days") + LabeledContent("Local requests", value: usage.totals.localRequests.formatted()) + LabeledContent( + "Hosted cost", + value: usage.totals.hostedCostUsd.formatted(.currency(code: "USD")) + ) + } + + Section { + Text("These are privacy-safe aggregates recorded by Aiden Agent on your Mac. Prompts, responses, chat IDs, workspace IDs, and file paths are not part of usage data.") + .font(.footnote) + .foregroundStyle(.secondary) + } + } + .navigationTitle("Usage") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .confirmationAction) { + Button("Done") { dismiss() } + } + } + } + } +} + +private struct AidenAppSettingsView: View { + @Environment(\.dismiss) private var dismiss + @Bindable var coordinator: AidenRemoteCoordinator + @Bindable var appearance: AidenAppearanceStore + let addInstallation: () -> Void + + @State private var isShowingInstallations = false + @State private var isShowingAppearance = false + @AppStorage("aiden.defaults.workspacePermission") private var defaultWorkspacePermissionRaw = AidenWorkspacePermission.ask.rawValue + + var body: some View { + NavigationStack { + Form { + Section("Aiden Agent") { + LabeledContent( + "Connected Mac", + value: coordinator.installationStore.activeInstallation?.name ?? "Not connected" + ) + Button { + isShowingInstallations = true + } label: { + Label("Paired Installations", systemImage: "desktopcomputer") + } + } + + Section { + Button { + isShowingAppearance = true + } label: { + Label("Appearance", systemImage: "circle.lefthalf.filled") + } + Picker("New workspace permission", selection: $defaultWorkspacePermissionRaw) { + ForEach(AidenWorkspacePermission.allCases, id: \.self) { permission in + Text(permission.title).tag(permission.rawValue) + } + } + } header: { + Text("Global Defaults") + } footer: { + Text("These are app-wide defaults. Permission, files, Git, and other workspace-specific options remain in each workspace’s ••• menu.") + } + + Section("About") { + Link(destination: AppConfig.privacyPolicyURL) { + Label("Privacy Policy", systemImage: "hand.raised") + } + Link(destination: AppConfig.supportURL) { + Label("Support", systemImage: "questionmark.circle") + } + LabeledContent("License", value: "MIT") + } + } + .navigationTitle("App Settings") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .confirmationAction) { + Button("Done") { dismiss() } + } + } + } + .sheet(isPresented: $isShowingInstallations) { + AidenInstallationsView( + coordinator: coordinator, + addInstallation: { + isShowingInstallations = false + addInstallation() + } + ) + } + .sheet(isPresented: $isShowingAppearance) { + AidenAppearanceSettingsView(appearance: appearance) + } + } +} + +private struct AidenInstallationsView: View { + @Environment(\.dismiss) private var dismiss + @Bindable var coordinator: AidenRemoteCoordinator + let addInstallation: () -> Void + + @State private var installationToRemove: AidenInstallation? + + var body: some View { + NavigationStack { + List { + Section("Paired installations") { + ForEach(coordinator.installationStore.installations) { installation in + Button { + Task { + await coordinator.switchInstallation(to: installation.id) + dismiss() + } + } label: { + HStack { + VStack(alignment: .leading, spacing: 3) { + Text(installation.name) + .foregroundStyle(.primary) + Text(installation.endpoint.host ?? installation.endpoint.absoluteString) + .font(.caption) + .foregroundStyle(.secondary) + } + Spacer() + if coordinator.installationStore.activeInstallationId == installation.id { + Image(systemName: "checkmark") + } + } + } + .swipeActions { + Button("Forget", role: .destructive) { installationToRemove = installation } + } + } + } + + Section { + Button(action: addInstallation) { + Label("Pair Another Aiden Agent", systemImage: "plus") + } + } + } + .navigationTitle("Aiden Installations") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .confirmationAction) { + Button("Done") { dismiss() } + } + } + .confirmationDialog( + "Forget this Aiden Agent?", + isPresented: Binding( + get: { installationToRemove != nil }, + set: { if !$0 { installationToRemove = nil } } + ), + titleVisibility: .visible + ) { + Button("Forget Installation", role: .destructive) { + guard let installationToRemove else { return } + Task { + await coordinator.removeInstallation(installationToRemove.id) + self.installationToRemove = nil + } + } + Button("Cancel", role: .cancel) { installationToRemove = nil } + } message: { + Text("Its credential will be removed from this device. You can pair again from Aiden Agent settings.") + } + } + } +} + +private struct AidenOfflineBanner: View { + let message: String + let retry: () -> Void + + var body: some View { + HStack(spacing: 10) { + Image(systemName: "wifi.slash") + Text(message) + .font(.footnote) + .lineLimit(2) + Spacer(minLength: 4) + Button("Retry", action: retry) + .font(.footnote.bold()) + } + .padding(.horizontal, 14) + .padding(.vertical, 10) + .background(.regularMaterial) + .accessibilityElement(children: .combine) + } +} + +extension AidenWorkspacePermission { + var title: String { + switch self { + case .full: "Full Access" + case .ask: "Ask Before Actions" + case .none: "No Access" + } + } + + var detail: String { + switch self { + case .full: + "Aiden can use this workspace's approved tools without asking for each ordinary action. Consequential Git actions still require confirmation." + case .ask: + "Aiden asks before actions that need approval in this workspace." + case .none: + "Aiden can show existing chats but cannot use workspace tools." + } + } +} + +private struct AidenFolderLocation: Hashable { + let label: String + let location: String +} + +private struct AidenFolderBrowserView: View { + @Environment(\.dismiss) private var dismiss + @Bindable var coordinator: AidenRemoteCoordinator + let onCreated: (AidenWorkspace) -> Void + + @State private var roots: [AidenBrowserRoot] = [] + @State private var path: [AidenFolderLocation] = [] + @State private var errorMessage: String? + @State private var isLoading = true + + var body: some View { + NavigationStack(path: $path) { + Group { + if isLoading { + ProgressView("Loading approved folders…") + } else if roots.isEmpty { + ContentUnavailableView( + "No Approved Folders", + systemImage: "folder.badge.questionmark", + description: Text("Add an approved root in Aiden Agent → Settings → Remote Access, then try again.") + ) + } else { + List(roots) { root in + NavigationLink(value: AidenFolderLocation(label: root.label, location: root.location)) { + Label(root.label, systemImage: "folder.fill") + } + } + } + } + .navigationTitle("Add Mac Folder") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Cancel") { dismiss() } + } + } + .navigationDestination(for: AidenFolderLocation.self) { location in + AidenFolderPageView( + coordinator: coordinator, + location: location, + onCreated: onCreated + ) + } + .task { await loadRoots() } + .alert("Couldn’t Browse Folders", isPresented: Binding( + get: { errorMessage != nil }, + set: { if !$0 { errorMessage = nil } } + )) { + Button("Retry") { Task { await loadRoots() } } + Button("Cancel", role: .cancel) { errorMessage = nil } + } message: { + Text(errorMessage ?? "Try again.") + } + } + } + + private func loadRoots() async { + isLoading = true + defer { isLoading = false } + do { + roots = try await coordinator.browserRoots() + } catch { + errorMessage = error.localizedDescription + } + } +} + +private struct AidenFolderPageView: View { + @Bindable var coordinator: AidenRemoteCoordinator + let location: AidenFolderLocation + let onCreated: (AidenWorkspace) -> Void + + @State private var page: AidenBrowserPage? + @State private var isLoading = true + @State private var isLoadingMore = false + @State private var errorMessage: String? + + var body: some View { + Group { + if isLoading { + ProgressView("Loading folders…") + } else if let page { + List { + if !page.breadcrumbs.isEmpty { + Section { + Text(page.breadcrumbs.map(\.label).joined(separator: " › ")) + .font(.caption) + .foregroundStyle(.secondary) + } + } + Section("Folders") { + ForEach(page.entries) { entry in + NavigationLink(value: AidenFolderLocation(label: entry.name, location: entry.location)) { + Label(entry.name, systemImage: "folder") + } + } + if let nextCursor = page.nextCursor { + Button { + Task { await loadMore(cursor: nextCursor) } + } label: { + if isLoadingMore { ProgressView() } else { Text("Load More") } + } + .disabled(isLoadingMore) + } + } + } + } else { + ContentUnavailableView("Folder Unavailable", systemImage: "exclamationmark.folder") + } + } + .navigationTitle(location.label) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .confirmationAction) { + Button("Add This Folder") { + Task { + if let workspace = await coordinator.createSelectedFolderWorkspace( + location: location.location, + name: nil + ) { + onCreated(workspace) + } else { + errorMessage = coordinator.presentedError + } + } + } + .disabled(coordinator.isMutating) + } + } + .task(id: location.location) { await load() } + .alert("Couldn’t Add Folder", isPresented: Binding( + get: { errorMessage != nil }, + set: { if !$0 { errorMessage = nil } } + )) { + Button("Retry") { Task { await load() } } + Button("Cancel", role: .cancel) { errorMessage = nil } + } message: { + Text(errorMessage ?? "The selection may have expired. Browse the folder again and retry.") + } + } + + private func load() async { + isLoading = true + defer { isLoading = false } + do { + page = try await coordinator.browserChildren(location: location.location) + } catch { + errorMessage = error.localizedDescription + } + } + + private func loadMore(cursor: String) async { + guard let page else { return } + isLoadingMore = true + defer { isLoadingMore = false } + do { + let next = try await coordinator.browserChildren(location: location.location, cursor: cursor) + self.page = AidenBrowserPage( + rootId: page.rootId, + label: page.label, + breadcrumbs: page.breadcrumbs, + entries: page.entries + next.entries, + nextCursor: next.nextCursor + ) + } catch { + errorMessage = error.localizedDescription + } + } +} diff --git a/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Core.swift b/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Core.swift new file mode 100644 index 00000000..d74f149c --- /dev/null +++ b/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Core.swift @@ -0,0 +1,142 @@ +// Shared primitives for the dotted 3D thought-orbs. +// +// Hand-transcribed from src/engine/core.ts. Every formula here must match +// the TypeScript exactly — OrbGoldenTests compares this package's output +// against spec/orbs-golden.json dot by dot, so a mistyped constant fails a +// test rather than shipping as a subtly wrong animation. +// +// Doubles throughout, deliberately: the golden vectors come from JavaScript +// numbers, which are IEEE-754 doubles. Using Float here would drift past the +// 1e-4 tolerance on the trig-heavy modes. + +import Foundation + +public struct Dot: Sendable { + public var x: Double + public var y: Double + public var z: Double + public var r: Double + /// Ink value: 0 = darkest ink on paper. Mirrored on dark themes. + public var white: Double + public var a: Double + + public init(x: Double, y: Double, z: Double, r: Double, white: Double, a: Double = 1) { + self.x = x; self.y = y; self.z = z; self.r = r; self.white = white; self.a = a + } +} + +/// A stroked edge between two projected points (the `connecting` web). +public struct Line: Sendable { + public var x1: Double + public var y1: Double + public var x2: Double + public var y2: Double + public var white: Double + public var a: Double + public var w: Double +} + +/// One rendered instant: a complete, final set of draw instructions. +/// `dots` is already z-sorted into draw order and radius-clamped; `lines` +/// are drawn first. +public struct OrbFrame: Sendable { + public var dots: [Dot] + public var lines: [Line] +} + +/// Deterministic hash in [0, 1). +@inlinable +func hashD(_ a: Double, _ b: Double) -> Double { + let h = sin(a * 12.9898 + b * 78.233) * 43758.5453 + return h - floor(h) +} + +/// Value noise on a 2D lattice — smooth, deterministic, cheap. +@inlinable +func vnoise(_ x: Double, _ y: Double) -> Double { + let xi = floor(x) + let yi = floor(y) + var fx = x - xi + var fy = y - yi + fx = fx * fx * (3 - 2 * fx) + fy = fy * fy * (3 - 2 * fy) + let a = hashD(xi, yi) + let b = hashD(xi + 1, yi) + let c = hashD(xi, yi + 1) + let d = hashD(xi + 1, yi + 1) + return a + (b - a) * fx + (c - a) * fy + (a - b - c + d) * fx * fy +} + +/// Stable directions on a unit sphere (Fibonacci lattice). +@inlinable +func fibDir(_ i: Int, _ n: Int) -> (Double, Double, Double) { + let golden = Double.pi * (3 - (5.0).squareRoot()) + let y = 1 - (2 * (Double(i) + 0.5)) / Double(n) + let rad = (1 - y * y).squareRoot() + let a = Double(i) * golden + return (rad * cos(a), y, rad * sin(a)) +} + +/// Shortest signed angular distance, wrapped to (-π, π]. +@inlinable +func angleDelta(_ a: Double, _ b: Double) -> Double { + atan2(sin(a - b), cos(a - b)) +} + +@inlinable +func lerp(_ a: Double, _ b: Double, _ f: Double) -> Double { a + (b - a) * f } + +@inlinable +func frac(_ x: Double) -> Double { x - floor(x) } + +/// Shared spin + tilt + orthographic projection. +/// +/// The TS version returns a closure; a struct is the Swift equivalent and +/// avoids an allocation per frame in the inner loops. +struct Projector { + let st: Double, ct: Double, sy: Double, cyw: Double + let cx: Double, cy: Double, scale: Double + + init(yaw: Double, tilt: Double, cx: Double, cy: Double, scale: Double) { + self.st = sin(tilt); self.ct = cos(tilt) + self.sy = sin(yaw); self.cyw = cos(yaw) + self.cx = cx; self.cy = cy; self.scale = scale + } + + @inlinable + func callAsFunction(_ x: Double, _ y: Double, _ z: Double) -> (Double, Double, Double) { + let x1 = x * cyw + z * sy + let z1 = -x * sy + z * cyw + let y1 = y * ct - z1 * st + let z2 = y * st + z1 * ct + return (cx + x1 * scale, cy - y1 * scale, z2) + } +} + +/// Dot radii were tuned for a 300pt frame; sub-linear scaling keeps small +/// spinners legible. Lower pow = radii shrink less with size. +@inlinable +func radiusScale(_ size: Double, pow p: Double) -> Double { + Foundation.pow(size / 300, p) +} + +/// Turn raw mode output into a finished frame: drop invisible marks, clamp +/// radii to the mode's floor, and z-sort far→near into draw order. +/// +/// Sort note: JavaScript's Array.prototype.sort is stable (required since +/// ES2019) and Swift's `sort` is NOT, so ties would be free to reorder and +/// the golden comparison would fail on modes that emit co-planar dots +/// (`shaping` puts every dot at z = 0). Sorting by index as a tiebreaker +/// reproduces JS's stability exactly. +func finalizeFrame(_ dots: [Dot], _ lines: [Line], rMin: Double = 0.3) -> OrbFrame { + var visible: [Dot] = [] + visible.reserveCapacity(dots.count) + for var d in dots where d.a >= 0.02 { + d.r = Swift.max(rMin, d.r) + visible.append(d) + } + let sorted = visible.enumerated() + .sorted { $0.element.z != $1.element.z ? $0.element.z < $1.element.z : $0.offset < $1.offset } + .map(\.element) + return OrbFrame(dots: sorted, lines: lines.filter { $0.a >= 0.02 }) +} diff --git a/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Lattice.swift b/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Lattice.swift new file mode 100644 index 00000000..fab4186a --- /dev/null +++ b/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Lattice.swift @@ -0,0 +1,201 @@ +// The sphere-lattice modes: globe (searching), rubik (solving) and +// wave (listening). Transcribed from src/engine/lattice.ts. + +import Foundation + +// --- the shared solver heartbeat (rubik) ------------------------------ +// Rapid eased moves scramble, then replay in reverse (palindrome) so +// everything clicks back to solved, rests, repeats. + +private struct Move { + let axis: Int + let lo: Double + let hi: Double + let ang: Double +} + +private struct SolveCycle { + var amount: [Double] + var active: Int +} + +private func solveCycle(_ time: Double, _ count: Int, _ slotDur: Double, _ rest: Double) -> SolveCycle { + let cyc = 2 * Double(count) * slotDur + rest + let tc = time.truncatingRemainder(dividingBy: cyc) + var amount = [Double](repeating: 0, count: count) + var active = -1 + if tc < 2 * Double(count) * slotDur { + let slot = Int(floor(tc / slotDur)) + let p = (tc - Double(slot) * slotDur) / slotDur + let cl = Swift.min(1, p / 0.7) + let ep = 1 - pow(1 - cl, 3) // machine ease-out + if slot < count { + for i in 0.. (Double, Double, Double, Bool) { + var (x, y, z) = pt3 + var inActive = false + for i in 0..= mv.hi { continue } + if i == sc.active { inActive = true } + let a = mv.ang * sc.amount[i] + let ca = cos(a) + let sa = sin(a) + if mv.axis == 0 { + let y2 = y * ca - z * sa + z = y * sa + z * ca + y = y2 + } else if mv.axis == 1 { + let x2 = x * ca + z * sa + z = -x * sa + z * ca + x = x2 + } else { + let x2 = x * ca - y * sa + y = x * sa + y * ca + x = x2 + } + } + return (x, y, z, inActive) +} + +private func makeMoves(_ count: Int) -> [Move] { + var moves: [Move] = [] + for i in 0.. OrbFrame { + let spin = 0.5 + let cx = size / 2 + let cy = size / 2 + let radius = (size / 2) * 0.82 + let tilt = 0.4 + 0.06 * sin(t * 0.35) + let pt = Projector(yaw: t * spin, tilt: tilt, cx: cx, cy: cy, scale: radius) + // scan sweeps relative to the spin; scanMul scales that relative rate + let scan = t * (spin + (1.7 - spin) * (o["scanMul"] ?? 1)) + let rs = radiusScale(size, pow: o["rsPow"] ?? 0.6) + let dimBase = o["dimBase"] ?? 1 + + var dots: [Dot] = [] + let latRings = Int(o["latRings"] ?? 17) + let lonDensity = o["lonDensity"] ?? 44 + for li in 0...latRings { + let lat = -Double.pi / 2 + (Double(li) / Double(latRings)) * Double.pi + let cosLat = cos(lat) + let sinLat = sin(lat) + let lonCount = Swift.max(1, Int((abs(cosLat) * lonDensity).rounded(.toNearestOrAwayFromZero))) + for lj in 0.. OrbFrame { + let cx = size / 2 + let cy = size / 2 + let R = (size / 2) * 0.82 + let pt = Projector(yaw: t * 0.55, tilt: 0.35 + 0.1 * sin(t * 0.9), cx: cx, cy: cy, scale: R) + let rs = radiusScale(size, pow: o["rsPow"] ?? 0.6) + let moveCount = Int(o["moveCount"] ?? 14) + let moves = makeMoves(moveCount) + let sc = solveCycle(t, moveCount, 0.42, 1.2) + + var dots: [Dot] = [] + let latRings = Int(o["latRings"] ?? 15) + let lonDensity = o["lonDensity"] ?? 40 + for li in 0...latRings { + let lat = -Double.pi / 2 + (Double(li) / Double(latRings)) * Double.pi + let cosLat = cos(lat) + let sinLat = sin(lat) + let lonCount = Swift.max(1, Int((abs(cosLat) * lonDensity).rounded(.toNearestOrAwayFromZero))) + for lj in 0.. OrbFrame { + let cx = size / 2 + let cy = size / 2 + // 0.76 base × 1.15 — the undulation pulls the sphere inward, so wave read + // ~15% smaller than the other lattice modes; scaled up to match them + let R = (size / 2) * 0.874 + let pt = Projector(yaw: t * 0.18, tilt: 0.38, cx: cx, cy: cy, scale: 1) + let rs = radiusScale(size, pow: o["rsPow"] ?? 0.6) + + var dots: [Dot] = [] + let rings = Int(o["rings"] ?? 15) + let lonDensity = o["lonDensity"] ?? 40 + for ri in 0...rings { + let lat = -Double.pi / 2 + (Double(ri) / Double(rings)) * Double.pi + let cosLat = cos(lat) + let sinLat = sin(lat) + // two waves, different tempi — organic, never quite repeating + let w = 0.62 * sin(t * 2.1 - Double(ri) * 0.52) + 0.38 * sin(t * 1.27 + Double(ri) * 0.83) + let rr = R * (0.88 + 0.105 * w) + let lonCount = Swift.max(1, Int((abs(cosLat) * lonDensity).rounded(.toNearestOrAwayFromZero))) + for lj in 0.. Double { x * x * (3 - 2 * x) } + +private struct PolyPath { + let verts: [(Double, Double)] + let segLengths: [Double] + let total: Double + + init(_ verts: [(Double, Double)]) { + self.verts = verts + var L: [Double] = [] + var sum = 0.0 + for i in 0.. (Double, Double) { + var target = f * total + var i = 0 + while target > segLengths[i] && i < verts.count - 1 { + target -= segLengths[i] + i += 1 + } + let a = verts[i] + let b = verts[(i + 1) % verts.count] + let ff = segLengths[i] != 0 ? Swift.min(1, target / segLengths[i]) : 0 + return (a.0 + (b.0 - a.0) * ff, a.1 + (b.1 - a.1) * ff) + } +} + +private enum Shape { + case circle + case poly(PolyPath) + + func point(_ f: Double) -> (Double, Double) { + switch self { + case .circle: + let a = -Double.pi / 2 + f * 2 * Double.pi + return (cos(a) * 0.24, sin(a) * 0.24) + case .poly(let p): + return p.point(f) + } + } +} + +private let TRIANGLE = PolyPath([(0.0, -0.26), (0.24, 0.16), (-0.24, 0.16)]) +// 5-vertex walk so the path STARTS at top-centre like the other shapes +private let SQUARE = PolyPath([(0, -0.2), (0.2, -0.2), (0.2, 0.2), (-0.2, 0.2), (-0.2, -0.2)]) +private let CYCLE: [Shape] = [.circle, .poly(TRIANGLE), .poly(SQUARE)] + +// low floor keeps sparse outlines possible while never degenerating +private func morphN(_ d: Double) -> Int { + Swift.max(6, Int((34 * d).rounded(.toNearestOrAwayFromZero))) +} + +private let HOLD = 1.4 +private let MORPH = 0.9 +private let SEG = HOLD + MORPH + +func frameMorph(_ size: Double, _ t: Double, _ o: [String: Double]) -> OrbFrame { + let K = CYCLE.count + let tc = t.truncatingRemainder(dividingBy: SEG * Double(K)) + let k = Int(floor(tc / SEG)) + let local = tc - Double(k) * SEG + let m = local > HOLD ? smoothE((local - HOLD) / MORPH) : 0 + let sprd = o["spread"] ?? 1 + + // blend the two shape PATHS at m, then measure the blended outline + let pA = CYCLE[k] + let pB = CYCLE[(k + 1) % K] + let M = 160 + var pts: [(Double, Double)] = [] + pts.reserveCapacity(M) + for i in 0.. OrbFrame { + let cx = size / 2 + let cy = size / 2 + let R = (size / 2) * 0.82 + let pt = Projector(yaw: t * 0.12, tilt: 0.3, cx: cx, cy: cy, scale: 1) + let rs = radiusScale(size, pow: o["rsPow"] ?? 0.6) + + var dots: [Dot] = [] + let orbitN = Int(o["orbitN"] ?? 12) + let ghostN = Int(o["ghostN"] ?? 40) + let particles = Int(o["particles"] ?? 3) + + // orbits: each a tilted circle — a ghost path + running particles + for orb in 0.. 0.5 ? 1 : -1) + + // ghost path + for k in 0.. [String: Double] { + var out = opts + var done = Set() + let rt = scale.squareRoot() + for (a, b) in OrbSpec.countPairs { + if let va = out[a], let vb = out[b], !done.contains(a), !done.contains(b) { + out[a] = Swift.max(2, (va * rt).rounded(.toNearestOrAwayFromZero)) + out[b] = Swift.max(2, (vb * rt).rounded(.toNearestOrAwayFromZero)) + done.insert(a); done.insert(b) + } + } + for k in OrbSpec.countKeys { + // 0 means the mode opted out of that layer entirely (ring has no + // ghost sphere) — scaling must not resurrect it as a single stray dot + if let v = out[k], v != 0, !done.contains(k) { + out[k] = Swift.max(1, (v * scale).rounded(.toNearestOrAwayFromZero)) + } + } + for k in OrbSpec.iconDensityKeys { + if let v = out[k] { out[k] = Swift.max(0.02, v * scale) } + } + return out +} + +private func scaleRadii(_ opts: [String: Double], _ scale: Double) -> [String: Double] { + var out = opts + for k in OrbSpec.radiusKeys { + if let v = out[k] { out[k] = v * scale } + } + out["rSizeMul"] = (out["rSizeMul"] ?? 1) * scale + return out +} + +private let cacheLock = NSLock() +nonisolated(unsafe) private var cache: [String: ResolvedPreset] = [:] + +/// Resolve a (state, size) pair. Cached: the result is identical for the +/// lifetime of the process, and the render loop should never do this work. +public func resolvePreset(_ state: OrbState, _ size: OrbSize) -> ResolvedPreset { + let key = "\(state.rawValue)-\(size.rawValue)" + cacheLock.lock() + defer { cacheLock.unlock() } + if let hit = cache[key] { return hit } + + let mode = state.mode + let preset = OrbSpec.presets[mode]![size]! + var opts = OrbSpec.baseProfiles[mode]! + if preset.count != 1 { opts = scaleCounts(opts, preset.count) } + if preset.size != 1 { opts = scaleRadii(opts, preset.size) } + for (k, v) in preset.extra { opts[k] = v } + + let resolved = ResolvedPreset(mode: mode, speed: preset.speed, opts: opts) + cache[key] = resolved + return resolved +} + +/// Geometry for one instant. Mirrors MODE_FRAMES in the TS engine. +public func orbFrame(_ preset: ResolvedPreset, size: Double, t: Double) -> OrbFrame { + let o = preset.opts + switch preset.mode { + case .orbits: return frameOrbits(size, t, o) + case .globe: return frameGlobe(size, t, o) + case .rubik: return frameRubik(size, t, o) + case .wave: return frameWave(size, t, o) + case .web: return frameWeb(size, t, o) + case .braid: return frameBraid(size, t, o) + // ring shares ribbon's geometry — the `faceOn` profile flag switches it + case .ribbon, .ring: return frameRibbon(size, t, o) + case .morph: return frameMorph(size, t, o) + } +} + +/// Convenience: resolve and build in one call. +public func orbFrame(state: OrbState, size: OrbSize, t: Double) -> OrbFrame { + orbFrame(resolvePreset(state, size), size: size.value, t: t) +} diff --git a/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Snapshot.swift b/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Snapshot.swift new file mode 100644 index 00000000..b6ca3134 --- /dev/null +++ b/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Snapshot.swift @@ -0,0 +1,51 @@ +// Headless frame capture for parity testing. +// +// ImageRenderer runs the real SwiftUI Canvas pipeline without a simulator, +// window or screen-recording permission — but it never fires onAppear and +// never advances a TimelineView, so every capture would otherwise render the +// same t=0 frame. `.orbFrozenTime(_:)` is what makes a capture deterministic +// and comparable against the web at the same instant. + +#if canImport(SwiftUI) && !os(watchOS) +import SwiftUI + +#if canImport(AppKit) +import AppKit +#elseif canImport(UIKit) +import UIKit +#endif + +@available(iOS 16.0, macOS 13.0, *) +public enum OrbSnapshot { + /// Render one orb at a fixed instant. `scale` mirrors the web harness's + /// device pixel ratio, so the two rasters are the same size. + @MainActor + public static func png( + state: OrbState, + size: OrbSize, + dark: Bool, + t: Double, + scale: Double = 2 + ) -> Data? { + let view = ThinkingOrb(state: state, size: size, theme: dark ? .dark : .light) + .orbFrozenTime(t) + .frame(width: size.value, height: size.value) + + let renderer = ImageRenderer(content: view) + renderer.scale = scale + // transparent background, exactly like the web canvas + renderer.isOpaque = false + + #if canImport(AppKit) + guard let image = renderer.nsImage, + let tiff = image.tiffRepresentation, + let rep = NSBitmapImageRep(data: tiff) else { return nil } + return rep.representation(using: .png, properties: [:]) + #elseif canImport(UIKit) + return renderer.uiImage?.pngData() + #else + return nil + #endif + } +} +#endif diff --git a/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Strands.swift b/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Strands.swift new file mode 100644 index 00000000..be26dfa0 --- /dev/null +++ b/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Strands.swift @@ -0,0 +1,138 @@ +// The strand modes: braid (weaving), ribbon (composing) and ring +// (breathing). Transcribed from src/engine/braid.ts and ribbon.ts. + +import Foundation + +// --- Braid: three strands plait around the sphere — weaving ----------- + +func frameBraid(_ size: Double, _ t: Double, _ o: [String: Double]) -> OrbFrame { + let cx = size / 2 + let cy = size / 2 + let R = (size / 2) * 0.76 + let pt = Projector(yaw: t * 0.4, tilt: 0.3, cx: cx, cy: cy, scale: 1) + let rs = radiusScale(size, pow: o["rsPow"] ?? 0.6) + + var dots: [Dot] = [] + let ghostN = Int(o["ghostN"] ?? 150) + for i in 0.. OrbFrame { + let cx = size / 2 + let cy = size / 2 + let R = (size / 2) * 0.78 + // spin scales the 3D tumble; spin=0 freezes the band's orientation, + // leaving only the traveling undulation + let spin = o["spin"] ?? 1 + let camTilt = 0.3 + let faceOn = (o["faceOn"] ?? 0) != 0 + let pt = Projector(yaw: t * 0.1 * spin, tilt: camTilt, cx: cx, cy: cy, scale: 1) + let rs = radiusScale(size, pow: o["rsPow"] ?? 0.6) + + var dots: [Dot] = [] + let ghostN = Int(o["ghostN"] ?? 150) + if ghostN > 0 { + for i in 0.. some View { + Canvas(rendersAsynchronously: false) { context, _ in + var context = context + let zoom = (displaySize ?? size.value) / size.value + if zoom != 1 { context.scaleBy(x: zoom, y: zoom) } + let frame = orbFrame(preset, size: size.value, t: t) + // lines first, so nodes sit on top of their edges + for l in frame.lines { + var path = Path() + path.move(to: CGPoint(x: l.x1, y: l.y1)) + path.addLine(to: CGPoint(x: l.x2, y: l.y2)) + context.stroke( + path, + with: .color(ink(l.white, l.a)), + lineWidth: l.w + ) + } + // dots are already z-sorted into draw order by the engine + for d in frame.dots { + let rect = CGRect(x: d.x - d.r, y: d.y - d.r, width: d.r * 2, height: d.r * 2) + context.fill(Path(ellipseIn: rect), with: .color(ink(d.white, d.a))) + } + } + } + + /// Quantise to 8-bit exactly as the canvas painter does, so the platforms + /// land on identical greys rather than merely close ones. + private func ink(_ white: Double, _ alpha: Double) -> Color { + let w = Swift.min(1, Swift.max(0, white)) + let g = ((isDark ? 1 - w : w) * 255).rounded(.toNearestOrAwayFromZero) / 255 + return Color(.sRGB, red: g, green: g, blue: g, opacity: alpha) + } +} + +// MARK: - Frozen time (snapshot testing) + +private struct OrbFrozenTimeKey: EnvironmentKey { + static let defaultValue: Double? = nil +} + +extension EnvironmentValues { + /// Pins the animation to a fixed instant. Used by the snapshot harness; + /// `ImageRenderer` does not fire `onAppear` or advance `TimelineView`, + /// so without this every capture would render the same t=0 frame. + var orbFrozenTime: Double? { + get { self[OrbFrozenTimeKey.self] } + set { self[OrbFrozenTimeKey.self] = newValue } + } +} + +@available(iOS 15.0, macOS 12.0, *) +extension View { + /// Freeze every ThinkingOrb below this view at `t` seconds. + public func orbFrozenTime(_ t: Double?) -> some View { + environment(\.orbFrozenTime, t) + } +} diff --git a/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Web.swift b/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Web.swift new file mode 100644 index 00000000..f3926db0 --- /dev/null +++ b/ios/AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Web.swift @@ -0,0 +1,91 @@ +// Web: a constellation wires itself — the "connecting" state. Nodes drift +// on the sphere under slow value noise; any pair closer than `thr` grows an +// edge, and bright packets run along randomly re-picked node pairs. +// Transcribed from src/engine/web.ts. + +import Foundation + +func frameWeb(_ size: Double, _ t: Double, _ o: [String: Double]) -> OrbFrame { + let cx = size / 2 + let cy = size / 2 + let R = (size / 2) * 0.8 * (o["spread"] ?? 1) + // note the projector carries the radius as its scale, so node vectors stay + // unit-length and distances below are in unit-sphere space + let pt = Projector(yaw: t * 0.12, tilt: 0.32, cx: cx, cy: cy, scale: R) + let rs = radiusScale(size, pow: o["rsPow"] ?? 0.6) + + let nodeN = Int(o["nodeN"] ?? 30) + let thr = o["thr"] ?? 0.72 + let nodeR = o["nodeR"] ?? 1.4 + let nodeRDepth = o["nodeRDepth"] ?? 1.8 + + // nodes: fib lattice + slow noise wander, renormalised to the surface + var nodes: [(Double, Double, Double)] = [] + nodes.reserveCapacity(nodeN) + for i in 0..= thr { continue } + let (x1, y1, z1) = pt(nodes[i].0, nodes[i].1, nodes[i].2) + let (x2, y2, z2) = pt(nodes[j].0, nodes[j].1, nodes[j].2) + let depth = ((z1 + z2) / 2 + 1) / 2 + lines.append(Line( + x1: x1, y1: y1, x2: x2, y2: y2, + white: 0.42, + a: (1 - dist / thr) * (0.3 + 0.55 * depth), + w: Swift.max(0.6, (o["lineW"] ?? 0.8) * rs) + )) + } + } + + for i in 0.. String { + let normalized = normalizedSingleLine(rawValue) + return trimmed(normalized.isEmpty ? String(localized: "Aiden chat") : normalized, limit: maximumSessionTitleCharacters) + } + + static func activityLine(_ rawValue: String) -> String { + trimmed(normalizedSingleLine(rawValue), limit: maximumActivityCharacters) + } + + static func responseExcerpt(_ rawValue: String) -> String { + let normalized = normalizedSingleLine(rawValue) + return trimmed(normalized, limit: maximumExcerptCharacters) + } + + static func toolKind(name: String?) -> AgentRunActivityToolKind { + let label = toolLabel(name) + let lowercasedName = (name ?? "").lowercased() + let lowercasedLabel = label.lowercased() + let haystack = "\(lowercasedName) \(lowercasedLabel)" + + if haystack.contains("shell") + || haystack.contains("bash") + || haystack.contains("terminal") + || haystack.contains("exec") + || haystack.contains("command") + || haystack.contains("xcodebuild") + || haystack.contains("simctl") { + return .command + } + + if haystack.contains("search") + || haystack.contains("grep") + || haystack.contains("ripgrep") + || haystack.contains("rg") + || haystack.contains("find") { + return .search + } + + if haystack.contains("read") + || haystack.contains("file") + || haystack.contains("list") + || haystack.contains("glob") + || haystack.contains("workspace") { + return .files + } + + return .generic(label) + } + + static func toolLabel(_ rawValue: String?) -> String { + let fallback = String(localized: "tool") + guard let rawValue else { return fallback } + + let noPathSeparators = rawValue + .replacingOccurrences(of: "\\", with: "/") + .split(separator: "/") + .last + .map(String.init) ?? rawValue + let words = noPathSeparators + .replacingOccurrences(of: "_", with: " ") + .replacingOccurrences(of: "-", with: " ") + let normalized = normalizedSingleLine(words) + return trimmed(normalized.isEmpty ? fallback : normalized, limit: maximumToolLabelCharacters) + } + + private static func normalizedSingleLine(_ rawValue: String) -> String { + rawValue + .components(separatedBy: .whitespacesAndNewlines) + .filter { !$0.isEmpty } + .joined(separator: " ") + .trimmingCharacters(in: .whitespacesAndNewlines) + } + + private static func trimmed(_ value: String, limit: Int) -> String { + guard value.count > limit else { return value } + guard limit > 3 else { + return String(value.prefix(limit)) + } + + let endIndex = value.index(value.startIndex, offsetBy: limit - 3) + return String(value[.. String { + let elapsedSeconds = max(0, Int(updatedAt.timeIntervalSince(startedAt).rounded(.down))) + let hours = elapsedSeconds / 3_600 + let minutes = (elapsedSeconds % 3_600) / 60 + let seconds = elapsedSeconds % 60 + + if hours > 0 { + return String(format: "%d:%02d:%02d", hours, minutes, seconds) + } + + return String(format: "%02d:%02d", minutes, seconds) + } +} + +enum AgentLiveActivityReusePolicy { + static func normalizedStreamID(_ streamID: String?) -> String? { + guard let streamID else { return nil } + + let normalized = streamID.trimmingCharacters(in: .whitespacesAndNewlines) + return normalized.isEmpty ? nil : normalized + } + + static func canReuseActivity( + existingSessionID: String, + existingStreamID: String?, + requestedSessionID: String, + requestedStreamID: String? + ) -> Bool { + existingSessionID == requestedSessionID + && normalizedStreamID(existingStreamID) == normalizedStreamID(requestedStreamID) + } +} + +enum AgentRunActivityStateReducer { + static func updatingSessionTitle( + _ title: String, + state: AgentRunActivityAttributes.ContentState, + now: Date = Date() + ) -> AgentRunActivityAttributes.ContentState { + AgentRunActivityAttributes.ContentState( + sessionID: state.sessionID, + sessionTitle: title, + status: state.status, + currentActivity: state.currentActivity, + responseExcerpt: state.responseExcerpt, + startedAt: state.startedAt, + updatedAt: now, + isStale: state.isStale, + isFinal: state.isFinal, + errorSummary: state.errorSummary + ) + } + + static func initialState( + sessionID: String, + sessionTitle: String, + startedAt: Date = Date() + ) -> AgentRunActivityAttributes.ContentState { + AgentRunActivityAttributes.ContentState( + sessionID: sessionID, + sessionTitle: sessionTitle, + status: .starting, + currentActivity: String(localized: "Starting response"), + startedAt: startedAt, + updatedAt: startedAt + ) + } + + static func appendingToken( + _ text: String, + to state: AgentRunActivityAttributes.ContentState, + now: Date = Date() + ) -> AgentRunActivityAttributes.ContentState { + guard !text.isEmpty else { return state } + return AgentRunActivityAttributes.ContentState( + sessionID: state.sessionID, + sessionTitle: state.sessionTitle, + status: .responding, + currentActivity: String(localized: "Writing response"), + responseExcerpt: state.responseExcerpt + text, + startedAt: state.startedAt, + updatedAt: now + ) + } + + static func responding( + state: AgentRunActivityAttributes.ContentState, + now: Date = Date() + ) -> AgentRunActivityAttributes.ContentState { + statusState( + .responding, + activity: String(localized: "Writing response"), + state: state, + now: now + ) + } + + static func settingInterimAssistant( + _ text: String, + on state: AgentRunActivityAttributes.ContentState, + now: Date = Date() + ) -> AgentRunActivityAttributes.ContentState { + let excerpt = AgentRunActivitySanitizer.responseExcerpt(text) + guard !excerpt.isEmpty else { return state } + return AgentRunActivityAttributes.ContentState( + sessionID: state.sessionID, + sessionTitle: state.sessionTitle, + status: .responding, + currentActivity: String(localized: "Writing response"), + responseExcerpt: excerpt, + startedAt: state.startedAt, + updatedAt: now + ) + } + + static func clearingResponseExcerpt( + state: AgentRunActivityAttributes.ContentState, + now: Date = Date() + ) -> AgentRunActivityAttributes.ContentState { + AgentRunActivityAttributes.ContentState( + sessionID: state.sessionID, + sessionTitle: state.sessionTitle, + status: state.status, + currentActivity: state.currentActivity, + responseExcerpt: "", + startedAt: state.startedAt, + updatedAt: now, + isStale: state.isStale, + isFinal: state.isFinal, + errorSummary: state.errorSummary + ) + } + + static func reasoning( + _ text: String, + state: AgentRunActivityAttributes.ContentState, + now: Date = Date() + ) -> AgentRunActivityAttributes.ContentState { + let activity = String(localized: "Thinking") + return statusState(.thinking, activity: activity, state: state, now: now) + } + + static func toolStarted( + name: String?, + state: AgentRunActivityAttributes.ContentState, + now: Date = Date() + ) -> AgentRunActivityAttributes.ContentState { + switch AgentRunActivitySanitizer.toolKind(name: name) { + case .command: + return statusState(.runningCommand, activity: String(localized: "Running command"), state: state, now: now) + case .search: + return statusState(.searchingFiles, activity: String(localized: "Searching files"), state: state, now: now) + case .files: + return statusState(.readingFiles, activity: String(localized: "Reading files"), state: state, now: now) + case .generic(let label): + return statusState(.usingTool, activity: String(localized: "Using \(label)"), state: state, now: now) + } + } + + static func toolCompleted( + state: AgentRunActivityAttributes.ContentState, + now: Date = Date() + ) -> AgentRunActivityAttributes.ContentState { + statusState(.responding, activity: String(localized: "Processing result"), state: state, now: now) + } + + static func waitingForApproval( + state: AgentRunActivityAttributes.ContentState, + now: Date = Date() + ) -> AgentRunActivityAttributes.ContentState { + statusState(.waitingForApproval, activity: String(localized: "Waiting for approval"), state: state, now: now) + } + + static func stale( + state: AgentRunActivityAttributes.ContentState, + now: Date = Date() + ) -> AgentRunActivityAttributes.ContentState { + AgentRunActivityAttributes.ContentState( + sessionID: state.sessionID, + sessionTitle: state.sessionTitle, + status: state.status, + currentActivity: state.currentActivity.isEmpty ? String(localized: "Latest status shown") : state.currentActivity, + responseExcerpt: state.responseExcerpt, + startedAt: state.startedAt, + updatedAt: now, + isStale: true, + isFinal: state.isFinal, + errorSummary: state.errorSummary + ) + } + + static func final( + status: AgentRunActivityStatus, + activity: String, + state: AgentRunActivityAttributes.ContentState, + errorSummary: String? = nil, + now: Date = Date() + ) -> AgentRunActivityAttributes.ContentState { + AgentRunActivityAttributes.ContentState( + sessionID: state.sessionID, + sessionTitle: state.sessionTitle, + status: status, + currentActivity: activity, + responseExcerpt: state.responseExcerpt, + startedAt: state.startedAt, + updatedAt: now, + isStale: false, + isFinal: true, + errorSummary: errorSummary + ) + } + + private static func statusState( + _ status: AgentRunActivityStatus, + activity: String, + state: AgentRunActivityAttributes.ContentState, + now: Date + ) -> AgentRunActivityAttributes.ContentState { + AgentRunActivityAttributes.ContentState( + sessionID: state.sessionID, + sessionTitle: state.sessionTitle, + status: status, + currentActivity: activity, + responseExcerpt: state.responseExcerpt, + startedAt: state.startedAt, + updatedAt: now + ) + } +} diff --git a/ios/AidenOnTheGo/LiveActivities/AidenDeepLink.swift b/ios/AidenOnTheGo/LiveActivities/AidenDeepLink.swift new file mode 100644 index 00000000..97b3ee42 --- /dev/null +++ b/ios/AidenOnTheGo/LiveActivities/AidenDeepLink.swift @@ -0,0 +1,82 @@ +import Foundation + +struct AidenNavigationRequest: Equatable, Sendable { + enum Destination: Equatable, Sendable { case newChat, chat(String) } + let destination: Destination + let instanceId: String? + let workspaceId: String? + let startsVoice: Bool +} + +enum AidenDeepLink { + static var scheme: String { + Bundle.main.object(forInfoDictionaryKey: "AidenURLScheme") as? String ?? "aiden-otg" + } + + static var newChatURL: URL? { newChatURL(instanceId: nil, workspaceId: nil, startsVoice: false) } + static var newChatVoiceURL: URL? { newChatURL(instanceId: nil, workspaceId: nil, startsVoice: true) } + + static func newChatURL(instanceId: String?, workspaceId: String?, startsVoice: Bool) -> URL? { + guard instanceId.map(safeID) ?? true, workspaceId.map(safeID) ?? true else { return nil } + var components = URLComponents() + components.scheme = scheme + components.host = startsVoice ? "new-chat-voice" : "new-chat" + components.queryItems = [ + instanceId.map { URLQueryItem(name: "instance", value: $0) }, + workspaceId.map { URLQueryItem(name: "workspace", value: $0) }, + ].compactMap { $0 } + return components.url + } + + static func chatURL(instanceId: String, chatId: String) -> URL? { + guard safeID(instanceId), safeID(chatId) else { return nil } + var components = URLComponents() + components.scheme = scheme + components.host = "chat" + components.queryItems = [ + URLQueryItem(name: "instance", value: instanceId), + URLQueryItem(name: "chat", value: chatId), + ] + return components.url + } + + static func request(from url: URL) -> AidenNavigationRequest? { + guard url.scheme?.lowercased() == scheme.lowercased(), + url.user == nil, url.password == nil, url.port == nil, + url.fragment == nil, url.path.isEmpty else { return nil } + let items = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems ?? [] + guard Set(items.map(\.name)).count == items.count, + items.allSatisfy({ $0.value != nil }), + items.allSatisfy({ $0.name == "instance" || $0.name == "workspace" || $0.name == "chat" }) else { + return nil + } + func value(_ name: String) -> String? { items.first(where: { $0.name == name })?.value } + let instance = value("instance") + let workspace = value("workspace") + guard instance.map(safeID) ?? true, workspace.map(safeID) ?? true else { return nil } + switch url.host?.lowercased() { + case "new-chat", "new-chat-voice": + guard value("chat") == nil else { return nil } + return AidenNavigationRequest( + destination: .newChat, + instanceId: instance, + workspaceId: workspace, + startsVoice: url.host?.lowercased() == "new-chat-voice" + ) + case "chat": + guard let instance, let chat = value("chat"), safeID(chat), workspace == nil else { return nil } + return AidenNavigationRequest( + destination: .chat(chat), instanceId: instance, workspaceId: nil, startsVoice: false + ) + default: + return nil + } + } + + private static func safeID(_ value: String) -> Bool { + !value.isEmpty && value.count <= 160 + && value.unicodeScalars.allSatisfy { scalar in + CharacterSet.alphanumerics.contains(scalar) || "._:-".unicodeScalars.contains(scalar) + } + } +} diff --git a/ios/AidenOnTheGo/LiveActivities/AidenRemoteLiveActivityManager.swift b/ios/AidenOnTheGo/LiveActivities/AidenRemoteLiveActivityManager.swift new file mode 100644 index 00000000..ce7a64fc --- /dev/null +++ b/ios/AidenOnTheGo/LiveActivities/AidenRemoteLiveActivityManager.swift @@ -0,0 +1,240 @@ +import ActivityKit +import Foundation + +/// Owns the app-side Live Activity lifecycle. The widget renders only the +/// bounded state written here and never receives network credentials. +@MainActor +final class AidenRemoteLiveActivityManager { + static let shared = AidenRemoteLiveActivityManager() + + static let responseExcerptPreferenceKey = "aiden.live-activities.response-excerpts" + + private let defaults: UserDefaults + private var currentActivity: Activity? + private var stateByActivityID: [String: AgentRunActivityAttributes.ContentState] = [:] + + init(defaults: UserDefaults = .standard) { + self.defaults = defaults + } + + var includesResponseExcerpts: Bool { + defaults.bool(forKey: Self.responseExcerptPreferenceKey) + } + + func start(instanceID: String, chatID: String, title: String, streamID: String) async { + guard ActivityAuthorizationInfo().areActivitiesEnabled else { return } + let startedAt = Date() + let attributes = AgentRunActivityAttributes( + instanceID: instanceID, + sessionID: chatID, + sessionTitle: title, + streamID: streamID, + startedAt: startedAt + ) + let state = AgentRunActivityStateReducer.initialState( + sessionID: chatID, + sessionTitle: title, + startedAt: startedAt + ) + + if let existing = activity(streamID: streamID) { + currentActivity = existing + stateByActivityID[existing.id] = state + await existing.update(content(for: state)) + return + } + + do { + let activity = try Activity.request( + attributes: attributes, + content: content(for: state), + pushType: nil + ) + currentActivity = activity + stateByActivityID[activity.id] = state + } catch { + currentActivity = nil + } + } + + func updateStatus(streamID: String, state: AidenStreamState) async { + switch state { + case .queued, .reconciling: + await update(streamID: streamID) { + AgentRunActivityStateReducer.initialState( + sessionID: $0.sessionID, + sessionTitle: $0.sessionTitle, + startedAt: $0.startedAt + ) + } + case .running: + await update(streamID: streamID) { + AgentRunActivityStateReducer.responding(state: $0) + } + case .waitingForApproval: + await approvalRequired(streamID: streamID) + case .done: + await finish(streamID: streamID, status: .complete, message: String(localized: "Response complete")) + case .error, .interrupted: + await finish(streamID: streamID, status: .failed, message: String(localized: "Response failed")) + case .cancelled: + await finish(streamID: streamID, status: .cancelled, message: String(localized: "Response cancelled")) + } + } + + func appendResponse(_ text: String, streamID: String) async { + await update(streamID: streamID) { state in + let updated = AgentRunActivityStateReducer.appendingToken( + includesResponseExcerpts ? text : " ", + to: state + ) + return includesResponseExcerpts + ? updated + : AgentRunActivityStateReducer.clearingResponseExcerpt(state: updated) + } + } + + func reasoning(streamID: String) async { + await update(streamID: streamID) { + AgentRunActivityStateReducer.reasoning("", state: $0) + } + } + + func toolStarted(name: String?, streamID: String) async { + await update(streamID: streamID) { + AgentRunActivityStateReducer.toolStarted(name: name, state: $0) + } + } + + func toolFinished(streamID: String) async { + await update(streamID: streamID) { + AgentRunActivityStateReducer.toolCompleted(state: $0) + } + } + + func approvalRequired(streamID: String) async { + await update(streamID: streamID) { + AgentRunActivityStateReducer.waitingForApproval(state: $0) + } + } + + func markStale(streamID: String) async { + await update(streamID: streamID) { + AgentRunActivityStateReducer.stale(state: $0) + } + } + + func markAllStale() async { + for activity in Activity.activities where isLive(activity) { + let state = AgentRunActivityStateReducer.stale(state: state(for: activity)) + stateByActivityID[activity.id] = state + await activity.update(content(for: state)) + } + } + + func endAll(forInstanceID instanceID: String) async { + for activity in Activity.activities + where activity.attributes.instanceID == instanceID && isLive(activity) { + let state = AgentRunActivityStateReducer.final( + status: .failed, + activity: String(localized: "Connection revoked"), + state: state(for: activity) + ) + stateByActivityID[activity.id] = state + await activity.end(content(for: state, staleDate: nil), dismissalPolicy: .immediate) + stateByActivityID[activity.id] = nil + if currentActivity?.id == activity.id { currentActivity = nil } + } + } + + func finish( + streamID: String, + status: AgentRunActivityStatus, + message: String, + errorSummary: String? = nil + ) async { + guard let activity = activity(streamID: streamID) else { return } + let state = AgentRunActivityStateReducer.final( + status: status, + activity: message, + state: state(for: activity), + errorSummary: errorSummary + ) + stateByActivityID[activity.id] = state + let policy: ActivityUIDismissalPolicy = status == .complete + ? .after(Date().addingTimeInterval(300)) + : .after(Date().addingTimeInterval(30)) + await activity.end(content(for: state, staleDate: nil), dismissalPolicy: policy) + stateByActivityID[activity.id] = nil + if currentActivity?.id == activity.id { currentActivity = nil } + } + + /// Reconciles activities persisted by iOS after relaunch against the pinned, + /// authenticated Aiden client. An unreachable server leaves state stale. + func reconcile(client: AidenRemoteClient) async { + for activity in Activity.activities where isLive(activity) { + guard let streamID = activity.attributes.streamID else { + await activity.end(nil, dismissalPolicy: .immediate) + stateByActivityID[activity.id] = nil + continue + } + if stateByActivityID[activity.id] == nil { + stateByActivityID[activity.id] = activity.content.state + } + do { + let status = try await client.streamStatus(id: streamID) + currentActivity = activity + await updateStatus(streamID: streamID, state: status.state) + } catch { + let state = AgentRunActivityStateReducer.stale(state: state(for: activity)) + stateByActivityID[activity.id] = state + await activity.update(content(for: state)) + } + } + } + + private func update( + streamID: String, + transform: (AgentRunActivityAttributes.ContentState) -> AgentRunActivityAttributes.ContentState + ) async { + guard let activity = activity(streamID: streamID), isLive(activity) else { return } + currentActivity = activity + var state = transform(state(for: activity)) + if !includesResponseExcerpts, !state.responseExcerpt.isEmpty { + state = AgentRunActivityStateReducer.clearingResponseExcerpt(state: state) + } + stateByActivityID[activity.id] = state + await activity.update(content(for: state)) + } + + private func state( + for activity: Activity + ) -> AgentRunActivityAttributes.ContentState { + if let state = stateByActivityID[activity.id] { return state } + let state = activity.content.state + stateByActivityID[activity.id] = state + return state + } + + private func activity(streamID: String) -> Activity? { + if let currentActivity, + currentActivity.attributes.streamID == streamID, + isLive(currentActivity) { + return currentActivity + } + return Activity.activities.first { + $0.attributes.streamID == streamID && isLive($0) + } + } + + private func isLive(_ activity: Activity) -> Bool { + activity.activityState == .active || activity.activityState == .stale + } + + private func content( + for state: AgentRunActivityAttributes.ContentState, + staleDate: Date? = Date().addingTimeInterval(300) + ) -> ActivityContent { + ActivityContent(state: state, staleDate: state.isFinal ? nil : staleDate) + } +} diff --git a/ios/AidenOnTheGo/Models/AidenChat.swift b/ios/AidenOnTheGo/Models/AidenChat.swift new file mode 100644 index 00000000..581c9e76 --- /dev/null +++ b/ios/AidenOnTheGo/Models/AidenChat.swift @@ -0,0 +1,247 @@ +import Foundation + +enum AidenChatRole: String, Codable, Sendable { + case user + case assistant +} + +struct AidenChatMessage: Codable, Identifiable, Equatable, Sendable { + let id: String + let role: AidenChatRole + let text: String + let attachments: [AidenMessageAttachment]? + let createdAt: Date + + init( + id: String, + role: AidenChatRole, + text: String, + attachments: [AidenMessageAttachment]? = nil, + createdAt: Date + ) { + self.id = id + self.role = role + self.text = text + self.attachments = attachments + self.createdAt = createdAt + } +} + +enum AidenAttachmentKind: String, Codable, Sendable { + case image + case text +} + +struct AidenMessageAttachment: Codable, Identifiable, Equatable, Sendable { + let id: String + let name: String + let mimeType: String + let kind: AidenAttachmentKind + let size: Int +} + +struct AidenAttachmentReference: Codable, Identifiable, Equatable, Sendable { + let id: String + let name: String + let mimeType: String + let kind: AidenAttachmentKind + let size: Int + let expiresAt: Date + + func isValid(now: Date = Date()) -> Bool { + guard id.range( + of: #"^att_[A-Za-z0-9_-]{43}$"#, + options: .regularExpression + ) != nil, + expiresAt > now, + size >= 0, + size <= 8 * 1_048_576, + !name.isEmpty, + name.unicodeScalars.count <= 255, + name.unicodeScalars.allSatisfy({ scalar in + scalar.value > 0x1f && scalar.value != 0x7f && scalar != "/" && scalar != "\\" + }), + !mimeType.isEmpty, + mimeType.unicodeScalars.count <= 120 + else { return false } + + switch kind { + case .image: + return mimeType == "image/jpeg" || mimeType == "image/png" + case .text: + return size <= 400_000 && Self.allowedTextMimeTypes.contains(mimeType) + } + } + + private static let allowedTextMimeTypes: Set = [ + "text/plain", "text/markdown", "text/csv", "application/json", "application/xml", + "application/yaml", "application/x-yaml", "application/javascript", "application/typescript", + ] +} + +enum AidenAttachmentUpload: Encodable, Equatable, Sendable { + case image(name: String, mimeType: String, data: Data) + case text(name: String, mimeType: String, text: String) + + private enum CodingKeys: String, CodingKey { + case name, mimeType, kind, data, text + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + switch self { + case .image(let name, let mimeType, let data): + try container.encode(name, forKey: .name) + try container.encode(mimeType, forKey: .mimeType) + try container.encode(AidenAttachmentKind.image.rawValue, forKey: .kind) + try container.encode(data.base64EncodedString(), forKey: .data) + case .text(let name, let mimeType, let text): + try container.encode(name, forKey: .name) + try container.encode(mimeType, forKey: .mimeType) + try container.encode(AidenAttachmentKind.text.rawValue, forKey: .kind) + try container.encode(text, forKey: .text) + } + } +} + +struct AidenChat: Codable, Identifiable, Equatable, Sendable { + let id: String + var workspaceId: String + var title: String + var providerId: String? + var modelId: String? + var messages: [AidenChatMessage] + let createdAt: Date + var updatedAt: Date + var revision: String +} + +struct AidenModel: Codable, Identifiable, Equatable, Sendable { + let id: String + let label: String + let thinkingLevels: [String]? +} + +struct AidenProvider: Codable, Identifiable, Equatable, Sendable { + let id: String + let label: String + let models: [AidenModel] +} + +struct AidenModelCatalog: Codable, Equatable, Sendable { + let providers: [AidenProvider] + let defaults: [String: String] +} + +struct AidenUsageTokens: Codable, Equatable, Sendable { + let input: Int + let output: Int + let cacheRead: Int + let cacheWrite: Int + let cacheWrite1h: Int? + let reasoning: Int + let total: Int +} + +struct AidenUsageTotals: Codable, Equatable, Sendable { + let requests: Int + let completedRequests: Int + let failedRequests: Int + let cancelledRequests: Int + let reportedTokenRequests: Int + let unmeteredRequests: Int + let localRequests: Int + let costedRequests: Int + let unpricedHostedRequests: Int + let hostedCostUsd: Double + let activeDays: Int + let currentStreak: Int + let longestStreak: Int + let tokens: AidenUsageTokens +} + +struct AidenUsageSummary: Codable, Equatable, Sendable { + let range: String + let startDate: String + let endDate: String + let totals: AidenUsageTotals +} + +struct AidenTurnStart: Encodable, Equatable, Sendable { + let text: String + let providerId: String? + let modelId: String? + let thinkingLevel: String? + let attachmentIds: [String]? + + init( + text: String, + providerId: String? = nil, + modelId: String? = nil, + thinkingLevel: String? = nil, + attachmentIds: [String]? = nil + ) { + self.text = text + self.providerId = providerId + self.modelId = modelId + self.thinkingLevel = thinkingLevel + self.attachmentIds = attachmentIds + } +} + +struct AidenTurnStartResponse: Decodable, Equatable, Sendable { + let turnId: String + let streamId: String + let status: String + let message: AidenChatMessage +} + +enum AidenStreamState: String, Codable, Sendable { + case queued + case running + case waitingForApproval = "waiting_for_approval" + case reconciling + case done + case error + case cancelled + case interrupted + + var isTerminal: Bool { + switch self { + case .done, .error, .cancelled, .interrupted: true + default: false + } + } +} + +struct AidenStreamStatus: Codable, Equatable, Sendable { + let streamId: String + let chatId: String + let turnId: String + let state: AidenStreamState + let lastSequence: Int + let updatedAt: Date +} + +enum AidenApprovalDecision: String, Codable, Sendable { + case allow + case deny +} + +struct AidenApprovalResponse: Codable, Equatable, Sendable { + let approvalId: String + let decision: AidenApprovalDecision + let resolvedAt: Date +} + +struct AidenPendingApproval: Identifiable, Equatable, Sendable { + let id: String + let summary: String + let expiresAt: Date +} + +struct AidenLiveTool: Identifiable, Equatable, Sendable { + let id: String + let name: String + var status: String? +} diff --git a/ios/AidenOnTheGo/Models/AidenInstallation.swift b/ios/AidenOnTheGo/Models/AidenInstallation.swift new file mode 100644 index 00000000..23b6e567 --- /dev/null +++ b/ios/AidenOnTheGo/Models/AidenInstallation.swift @@ -0,0 +1,209 @@ +import Foundation +import Observation + +struct AidenInstallation: Codable, Identifiable, Equatable, Sendable { + let instanceId: String + let deviceId: String + var name: String + let endpoint: URL + let serverSpkiSha256: String + let pairingTrust: AidenRemoteContractFixture.PairingTrust? + var capabilities: [AidenRemoteCapability] + let createdAt: Date + var lastConnectedAt: Date? + + var id: String { instanceId } + + init( + exchange: AidenRemoteContractFixture.PairingExchange, + pairingTrust: AidenRemoteContractFixture.PairingTrust, + name: String, + createdAt: Date = Date(), + lastConnectedAt: Date? = nil + ) { + instanceId = exchange.instanceId + deviceId = exchange.deviceId + self.name = name + endpoint = exchange.endpoint + serverSpkiSha256 = exchange.serverSpkiSha256 + self.pairingTrust = pairingTrust + capabilities = exchange.capabilities + self.createdAt = createdAt + self.lastConnectedAt = lastConnectedAt + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + instanceId = try values.decode(String.self, forKey: .instanceId) + deviceId = try values.decode(String.self, forKey: .deviceId) + name = try values.decode(String.self, forKey: .name) + endpoint = try values.decode(URL.self, forKey: .endpoint) + serverSpkiSha256 = try values.decode(String.self, forKey: .serverSpkiSha256) + pairingTrust = try values.decodeIfPresent( + AidenRemoteContractFixture.PairingTrust.self, + forKey: .pairingTrust + ) + capabilities = try values.decode([AidenRemoteCapability].self, forKey: .capabilities) + createdAt = try values.decode(Date.self, forKey: .createdAt) + lastConnectedAt = try values.decodeIfPresent(Date.self, forKey: .lastConnectedAt) + } + + private enum CodingKeys: String, CodingKey { + case instanceId, deviceId, name, endpoint, serverSpkiSha256, pairingTrust + case capabilities, createdAt, lastConnectedAt + } +} + +@MainActor +@Observable +final class AidenInstallationStore { + private struct Snapshot: Codable { + var installations: [AidenInstallation] + var activeInstallationId: String? + } + + private let keychain: any KeychainStoring + private(set) var installations: [AidenInstallation] + private(set) var activeInstallationId: String? + + var activeInstallation: AidenInstallation? { + guard let activeInstallationId else { return nil } + return installations.first { $0.id == activeInstallationId } + } + + init(keychain: any KeychainStoring = KeychainStore()) { + self.keychain = keychain + let snapshot = Self.loadSnapshot(from: keychain) + installations = snapshot.installations + activeInstallationId = snapshot.activeInstallationId.flatMap { candidate in + snapshot.installations.contains(where: { $0.id == candidate }) ? candidate : nil + } + } + + func credential(for installation: AidenInstallation) throws -> String? { + try keychain.load(.remoteCredential, scope: installation.instanceId) + } + + func savePairing( + _ exchange: AidenRemoteContractFixture.PairingExchange, + trust: AidenRemoteContractFixture.PairingTrust, + name: String, + now: Date = Date() + ) throws -> AidenInstallation { + let existing = installations.first { $0.id == exchange.instanceId } + let installation = AidenInstallation( + exchange: exchange, + pairingTrust: trust, + name: name, + createdAt: existing?.createdAt ?? now, + lastConnectedAt: now + ) + + // The scoped credential write must succeed before the installation is + // advertised as usable in the registry. + let previousCredential = try keychain.load(.remoteCredential, scope: exchange.instanceId) + try keychain.save(exchange.credential, forKey: .remoteCredential, scope: exchange.instanceId) + + var updated = installations.filter { $0.id != installation.id } + updated.append(installation) + updated.sort { lhs, rhs in + if lhs.name.localizedCaseInsensitiveCompare(rhs.name) == .orderedSame { + return lhs.id < rhs.id + } + return lhs.name.localizedCaseInsensitiveCompare(rhs.name) == .orderedAscending + } + + let previousInstallations = installations + let previousActiveId = activeInstallationId + installations = updated + activeInstallationId = installation.id + do { + try persist() + } catch { + installations = previousInstallations + activeInstallationId = previousActiveId + if let previousCredential { + try? keychain.save(previousCredential, forKey: .remoteCredential, scope: exchange.instanceId) + } else { + try? keychain.delete(.remoteCredential, scope: exchange.instanceId) + } + throw error + } + return installation + } + + func setActive(_ installationId: String) throws { + guard installations.contains(where: { $0.id == installationId }) else { return } + guard activeInstallationId != installationId else { return } + let previous = activeInstallationId + activeInstallationId = installationId + do { + try persist() + } catch { + activeInstallationId = previous + throw error + } + } + + func updateServer(_ server: AidenServer, connectedAt: Date = Date()) throws { + guard let index = installations.firstIndex(where: { $0.id == server.instanceId }) else { return } + let previous = installations[index] + installations[index].name = server.name + installations[index].capabilities = server.capabilities + installations[index].lastConnectedAt = connectedAt + do { + try persist() + } catch { + installations[index] = previous + throw error + } + } + + func remove(_ installationId: String) throws { + guard installations.contains(where: { $0.id == installationId }) else { return } + let previousInstallations = installations + let previousActiveId = activeInstallationId + installations.removeAll { $0.id == installationId } + if activeInstallationId == installationId { + activeInstallationId = installations.first?.id + } + do { + try persist() + try keychain.delete(.remoteCredential, scope: installationId) + } catch { + installations = previousInstallations + activeInstallationId = previousActiveId + try? persist() + throw error + } + } + + private func persist() throws { + let snapshot = Snapshot( + installations: installations, + activeInstallationId: activeInstallationId + ) + let data = try JSONEncoder().encode(snapshot) + guard let value = String(data: data, encoding: .utf8) else { + throw CocoaError(.fileWriteInapplicableStringEncoding) + } + try keychain.save(value, forKey: .remoteInstallations) + } + + private static func loadSnapshot(from keychain: any KeychainStoring) -> Snapshot { + guard let value = try? keychain.load(.remoteInstallations), + let data = value.data(using: .utf8), + let snapshot = try? JSONDecoder().decode(Snapshot.self, from: data) else { + return Snapshot(installations: [], activeInstallationId: nil) + } + + var seen = Set() + let installations = snapshot.installations.filter { installation in + !installation.instanceId.isEmpty && seen.insert(installation.instanceId).inserted + } + return Snapshot( + installations: installations, + activeInstallationId: snapshot.activeInstallationId + ) + } +} diff --git a/ios/AidenOnTheGo/Models/AidenScheduledTask.swift b/ios/AidenOnTheGo/Models/AidenScheduledTask.swift new file mode 100644 index 00000000..313c3219 --- /dev/null +++ b/ios/AidenOnTheGo/Models/AidenScheduledTask.swift @@ -0,0 +1,195 @@ +import Foundation + +enum AidenScheduledTaskMode: String, Codable, CaseIterable, Sendable { + case llm + case script + + var title: String { self == .llm ? String(localized: "Ask Aiden") : String(localized: "Run Script") } +} + +enum AidenScheduledTaskPermission: String, Codable, CaseIterable, Sendable { + case readOnly = "read-only" + case full + + var title: String { self == .readOnly ? String(localized: "Read Only") : String(localized: "Full") } +} + +enum AidenScheduledTaskResult: String, Codable, Sendable { + case success, error, silent, blocked +} + +struct AidenScheduledTask: Codable, Identifiable, Equatable, Sendable { + let id: String + let revision: String + let name: String + let enabled: Bool + let schedule: String + let timezone: String + let mode: AidenScheduledTaskMode + let permission: AidenScheduledTaskPermission + let workspaceId: String? + let providerId: String? + let modelId: String? + let mcpServerIds: [String]? + let scriptId: String? + let prompt: String? + let notify: Bool + let running: Bool + let nextRunAt: Date? + let lastRunAt: Date? + let lastResult: AidenScheduledTaskResult? + let createdAt: Date + let updatedAt: Date +} + +struct AidenScheduledTaskMutation: Encodable, Equatable, Sendable { + let name: String + let schedule: String + let timezone: String + let mode: AidenScheduledTaskMode + let permission: AidenScheduledTaskPermission + let workspaceId: String? + let providerId: String? + let modelId: String? + let mcpServerIds: [String]? + let scriptId: String? + let prompt: String? + let notify: Bool + let confirmedForeground = true +} + +struct AidenScheduledRunAccepted: Codable, Equatable, Sendable { + let taskId: String + let runId: String + let status: String + let acceptedAt: Date +} + +struct AidenScheduledRun: Codable, Identifiable, Equatable, Sendable { + let id: String + let taskId: String + let status: String + let startedAt: Date + let finishedAt: Date? + let summary: String? + let errorCode: String? +} + +struct AidenScheduledScript: Codable, Identifiable, Equatable, Sendable { + let id: String + let name: String +} + +struct AidenScheduledMcpServer: Codable, Identifiable, Equatable, Sendable { + let id: String + let name: String +} + +struct AidenScheduledPreview: Codable, Equatable, Sendable { + let dates: [Date] +} + +struct AidenScheduledSettings: Codable, Equatable, Sendable { + let revision: String + let enabled: Bool + let defaultMode: AidenScheduledTaskMode + let defaultPermission: AidenScheduledTaskPermission + let defaultMcpEnabled: Bool + let defaultNotify: Bool + let defaultTimezone: String +} + +struct AidenScheduledSettingsMutation: Encodable, Equatable, Sendable { + let enabled: Bool? + let defaultMode: AidenScheduledTaskMode? + let defaultPermission: AidenScheduledTaskPermission? + let defaultMcpEnabled: Bool? + let defaultNotify: Bool? + let defaultTimezone: String? + let confirmedForeground = true +} + +struct AidenScheduledTaskDraft: Equatable, Sendable { + var name = "" + var schedule = "" + var timezone = TimeZone.current.identifier + var mode: AidenScheduledTaskMode = .llm + var permission: AidenScheduledTaskPermission = .readOnly + var workspaceId: String? + var providerId: String? + var modelId: String? + var mcpServerIds = Set() + var scriptId: String? + var prompt = "" + var notify = true + + init() {} + + init(task: AidenScheduledTask) { + name = task.name + schedule = task.schedule + timezone = task.timezone + mode = task.mode + permission = task.permission + workspaceId = task.workspaceId + providerId = task.providerId + modelId = task.modelId + mcpServerIds = Set(task.mcpServerIds ?? []) + scriptId = task.scriptId + prompt = task.prompt ?? "" + notify = task.notify + } + + var validationMessage: String? { + if name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { return String(localized: "Name is required.") } + if schedule.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { return String(localized: "Schedule is required.") } + if timezone.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { return String(localized: "Timezone is required.") } + if mode == .llm && prompt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { return String(localized: "Prompt is required.") } + if mode == .script && scriptId == nil { return String(localized: "Choose a script from Aiden Agent.") } + if mode == .script && permission != .full { return String(localized: "Script tasks require Full permission.") } + return nil + } + + var mutation: AidenScheduledTaskMutation { + AidenScheduledTaskMutation( + name: name.trimmingCharacters(in: .whitespacesAndNewlines), + schedule: schedule.trimmingCharacters(in: .whitespacesAndNewlines), + timezone: timezone.trimmingCharacters(in: .whitespacesAndNewlines), + mode: mode, + permission: permission, + workspaceId: workspaceId, + providerId: mode == .llm ? providerId : nil, + modelId: mode == .llm ? modelId : nil, + mcpServerIds: mode == .llm && !mcpServerIds.isEmpty ? mcpServerIds.sorted() : nil, + scriptId: mode == .script ? scriptId : nil, + prompt: mode == .llm ? prompt.trimmingCharacters(in: .whitespacesAndNewlines) : nil, + notify: notify + ) + } +} + +enum AidenScheduledTaskValidation { + static func tasks(_ tasks: [AidenScheduledTask]) throws -> [AidenScheduledTask] { + guard tasks.count <= 10_000 else { throw AidenRemoteClientError.invalidResponse } + var ids = Set() + for task in tasks { + guard !task.id.isEmpty, task.id.count <= 160, ids.insert(task.id).inserted, + task.revision.hasPrefix("rev_"), !task.name.isEmpty, task.name.count <= 120, + !task.schedule.isEmpty, task.schedule.count <= 500, + !task.timezone.isEmpty, task.timezone.count <= 120, + task.prompt.map({ $0.count <= 32_768 }) ?? true, + task.scriptId.map({ $0.hasPrefix("script_") && $0.count == 50 }) ?? true else { + throw AidenRemoteClientError.invalidResponse + } + } + return tasks + } + + static func runs(_ runs: [AidenScheduledRun], taskId: String) throws -> [AidenScheduledRun] { + guard runs.count <= 50, + runs.allSatisfy({ $0.taskId == taskId && $0.summary.map({ $0.count <= 20_000 }) ?? true }) else { + throw AidenRemoteClientError.invalidResponse + } + return runs + } +} diff --git a/ios/AidenOnTheGo/Models/AidenWorkspaceEnvironment.swift b/ios/AidenOnTheGo/Models/AidenWorkspaceEnvironment.swift new file mode 100644 index 00000000..51fbb9e0 --- /dev/null +++ b/ios/AidenOnTheGo/Models/AidenWorkspaceEnvironment.swift @@ -0,0 +1,250 @@ +import Foundation + +enum AidenWorkspaceFileKind: String, Codable, Sendable { + case file + case directory + case symlink +} + +struct AidenWorkspaceFileEntry: Codable, Identifiable, Equatable, Sendable { + let id: String + let displayPath: String + let name: String + let kind: AidenWorkspaceFileKind + let size: Int? + let language: String? +} + +struct AidenWorkspaceFileIndex: Codable, Equatable, Sendable { + let snapshotId: String + let entries: [AidenWorkspaceFileEntry] + let truncated: Bool + let maxEntries: Int + let maxDepth: Int +} + +struct AidenWorkspaceFileDocument: Codable, Equatable, Sendable { + let id: String + let displayPath: String + let content: String + let version: String + let truncated: Bool + let warning: String? +} + +enum AidenGitFileStatus: String, Codable, Sendable { + case added, modified, deleted, renamed, untracked, conflicted +} + +struct AidenGitFile: Codable, Identifiable, Equatable, Sendable { + let id: String + let displayPath: String + let status: AidenGitFileStatus + let staged: Bool? + let additions: Int? + let deletions: Int? +} + +struct AidenGitCapability: Codable, Equatable, Sendable { + let allowed: Bool + let reason: String? +} + +struct AidenGitReview: Codable, Equatable, Sendable { + let kind: String + let branch: String + let uncommitted: Int + let files: [AidenGitFile] +} + +struct AidenGitDiff: Codable, Identifiable, Equatable, Sendable { + let kind: String + let displayPath: String + let diff: String + let truncated: Bool + + var id: String { displayPath } +} + +struct AidenGitBranches: Codable, Equatable, Sendable { + let kind: String + let current: String + let branches: [String] +} + +struct AidenGitComparison: Codable, Equatable, Sendable { + let kind: String + let comparisonId: String + let base: String + let head: String + let files: [AidenGitFile] +} + +struct AidenGitPushCapability: Codable, Equatable, Sendable { + let kind: String + let allowed: Bool + let reason: String? + let remote: String? + let branch: String? +} + +struct AidenGitWorktree: Codable, Identifiable, Equatable, Sendable { + let id: String + let name: String + let branch: String + let managed: Bool +} + +struct AidenGitWorktrees: Codable, Equatable, Sendable { + let kind: String + let worktrees: [AidenGitWorktree] +} + +struct AidenGitMutation: Codable, Equatable, Sendable { + let kind: String + let message: String + let branch: String? + let commitId: String? + let workspaceId: String? + let warning: String? +} + +enum AidenGitProjection: Decodable, Equatable, Sendable { + case review(AidenGitReview) + case diff(AidenGitDiff) + case branches(AidenGitBranches) + case comparison(AidenGitComparison) + case pushCapability(AidenGitPushCapability) + case worktrees(AidenGitWorktrees) + case mutation(AidenGitMutation) + + private enum CodingKeys: String, CodingKey { case kind } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + switch try values.decode(String.self, forKey: .kind) { + case "review": self = .review(try AidenGitReview(from: decoder)) + case "diff": self = .diff(try AidenGitDiff(from: decoder)) + case "branches": self = .branches(try AidenGitBranches(from: decoder)) + case "comparison": self = .comparison(try AidenGitComparison(from: decoder)) + case "push-capability": self = .pushCapability(try AidenGitPushCapability(from: decoder)) + case "worktrees": self = .worktrees(try AidenGitWorktrees(from: decoder)) + case "mutation": self = .mutation(try AidenGitMutation(from: decoder)) + default: throw AidenRemoteClientError.invalidResponse + } + } +} + +enum AidenGitOperationStatus: String, Codable, Sendable { + case snapshot, accepted, running, succeeded, failed, conflict +} + +struct AidenGitResult: Decodable, Equatable, Sendable { + let operationId: String + let status: AidenGitOperationStatus + let snapshotId: String? + let capability: AidenGitCapability? + let result: AidenGitProjection? +} + +struct AidenGitDiffRequest: Encodable { let snapshotId: String; let fileId: String } +struct AidenGitCreateBranchRequest: Encodable { + let name: String + let startPoint: String + let confirmedForeground = true +} +struct AidenGitCheckoutRequest: Encodable { + let branch: String + let snapshotId: String + let confirmedForeground = true +} +struct AidenGitCommitRequest: Encodable { + let snapshotId: String + let message: String + let scope: String + let confirmedForeground = true +} +struct AidenGitPushRequest: Encodable { + let snapshotId: String + let remote: String + let branch: String + let confirmedForeground = true +} +struct AidenGitCompareRequest: Encodable { let baseRef: String } +struct AidenGitComparisonDiffRequest: Encodable { let comparisonId: String; let fileId: String } +struct AidenGitCreateWorktreeRequest: Encodable { + let branch: String + let name: String + let confirmedForeground = true +} +struct AidenForegroundConfirmation: Encodable { let confirmedForeground = true } +struct AidenWorkspaceFileWriteRequest: Encodable { let content: String; let expectedVersion: String } + +enum AidenWorkspaceEnvironmentValidation { + static func opaqueFileID(_ value: String) -> Bool { + value.range(of: #"^file_[A-Za-z0-9_-]{43}$"#, options: .regularExpression) != nil + } + + static func safeDisplayPath(_ value: String) -> Bool { + !value.isEmpty && !value.hasPrefix("/") && !value.split(separator: "/", omittingEmptySubsequences: false).contains("..") + } + + static func validated(_ index: AidenWorkspaceFileIndex) throws -> AidenWorkspaceFileIndex { + guard index.maxEntries == 4_000, + index.maxDepth == 20, + index.entries.count <= index.maxEntries, + index.entries.allSatisfy({ entry in + opaqueFileID(entry.id) && safeDisplayPath(entry.displayPath) && !entry.name.isEmpty + }) else { + throw AidenRemoteClientError.invalidResponse + } + return index + } + + static func validated(_ document: AidenWorkspaceFileDocument, expectedID: String) throws -> AidenWorkspaceFileDocument { + guard document.id == expectedID, + opaqueFileID(document.id), + safeDisplayPath(document.displayPath), + !document.version.isEmpty, + !document.truncated else { + throw AidenRemoteClientError.invalidResponse + } + return document + } + + static func validated(_ git: AidenGitResult) throws -> AidenGitResult { + guard git.operationId.hasPrefix("op_"), git.operationId.count <= 128 else { + throw AidenRemoteClientError.invalidResponse + } + let validFiles: ([AidenGitFile]) -> Bool = { files in + files.count <= 4_000 && files.allSatisfy { + opaqueFileID($0.id) && safeDisplayPath($0.displayPath) + } + } + switch git.result { + case .review(let review): + guard git.snapshotId != nil, validFiles(review.files), review.uncommitted >= 0 else { + throw AidenRemoteClientError.invalidResponse + } + case .diff(let diff): + guard git.snapshotId != nil, safeDisplayPath(diff.displayPath), diff.diff.count <= 2_000_000 else { + throw AidenRemoteClientError.invalidResponse + } + case .branches: + guard git.snapshotId != nil else { throw AidenRemoteClientError.invalidResponse } + case .comparison(let comparison): + guard git.snapshotId == comparison.comparisonId, validFiles(comparison.files) else { + throw AidenRemoteClientError.invalidResponse + } + case .pushCapability: + guard git.snapshotId != nil else { throw AidenRemoteClientError.invalidResponse } + case .worktrees(let worktrees): + guard worktrees.worktrees.allSatisfy({ !$0.id.isEmpty && !$0.name.isEmpty && !$0.branch.isEmpty }) else { + throw AidenRemoteClientError.invalidResponse + } + case .mutation, .none: + break + } + return git + } +} diff --git a/ios/AidenOnTheGo/Networking/AidenRemoteClient.swift b/ios/AidenOnTheGo/Networking/AidenRemoteClient.swift new file mode 100644 index 00000000..3e2a8a30 --- /dev/null +++ b/ios/AidenOnTheGo/Networking/AidenRemoteClient.swift @@ -0,0 +1,1029 @@ +import Foundation + +enum AidenDeviceType: String, Codable, Sendable { + case iphone + case ipad +} + +enum AidenConnectionMode: String, Codable, Sendable { + case lan + case tailscale + case both +} + +struct AidenServer: Codable, Equatable, Sendable { + let protocolVersion: Int + let instanceId: String + let name: String + let appVersion: String + let capabilities: [AidenRemoteCapability] + let connectionMode: AidenConnectionMode + let minimumClientVersion: String? + let serverTime: Date +} + +enum AidenWorkspacePermission: String, Codable, CaseIterable, Sendable { + case full + case ask + case none +} + +struct AidenWorkspaceGitSummary: Codable, Equatable, Sendable { + let isRepo: Bool + let branch: String? + let uncommitted: Int? +} + +struct AidenWorkspace: Codable, Identifiable, Equatable, Sendable { + let id: String + var name: String + var permission: AidenWorkspacePermission + let hasFolder: Bool + let isManagedWorktree: Bool + let branchName: String? + let repositoryName: String? + let git: AidenWorkspaceGitSummary? + let createdAt: Date + let updatedAt: Date + let revision: String +} + +enum AidenWorkspaceCreate: Encodable, Equatable, Sendable { + case folderless(name: String) + case scratch + case selectedFolder(selection: String, name: String?) + + private enum CodingKeys: String, CodingKey { + case mode, name, selection + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + switch self { + case .folderless(let name): + try container.encode("folderless", forKey: .mode) + try container.encode(name, forKey: .name) + case .scratch: + try container.encode("scratch", forKey: .mode) + case .selectedFolder(let selection, let name): + try container.encode("selected-folder", forKey: .mode) + try container.encode(selection, forKey: .selection) + try container.encodeIfPresent(name, forKey: .name) + } + } +} + +struct AidenWorkspacePatch: Encodable, Equatable, Sendable { + let name: String? + let permission: AidenWorkspacePermission? + let confirmedForeground = true + + init(name: String? = nil, permission: AidenWorkspacePermission? = nil) { + self.name = name + self.permission = permission + } +} + +struct AidenBrowserRoot: Codable, Identifiable, Equatable, Sendable { + let id: String + let label: String + let location: String + let policyRevision: String +} + +struct AidenBrowserBreadcrumb: Codable, Equatable, Sendable { + let label: String + let location: String +} + +struct AidenBrowserEntry: Codable, Identifiable, Equatable, Sendable { + let id: String + let name: String + let location: String +} + +struct AidenBrowserPage: Codable, Equatable, Sendable { + let rootId: String + let label: String + let breadcrumbs: [AidenBrowserBreadcrumb] + let entries: [AidenBrowserEntry] + let nextCursor: String? +} + +struct AidenWorkspaceSelection: Codable, Equatable, Sendable { + let selection: String + let displayName: String + let expiresAt: Date +} + +enum AidenRemoteClientError: Error, LocalizedError { + case invalidEndpoint + case invalidResponse + case unexpectedStatus(Int) + case server(statusCode: Int, body: AidenRemoteErrorEnvelope.Body) + case missingCredential + case missingTrustConfiguration + + var isCredentialRevoked: Bool { + guard case .server(_, let body) = self else { return false } + return body.code.rawValue == "credential_revoked" + } + + var errorDescription: String? { + switch self { + case .invalidEndpoint: + return "The Aiden Agent address is invalid." + case .invalidResponse: + return "Aiden Agent returned an invalid response." + case .unexpectedStatus(let status): + return "Aiden Agent returned HTTP status \(status)." + case .server(_, let body): + return body.message + case .missingCredential: + return "This Aiden installation needs to be paired again." + case .missingTrustConfiguration: + return "This Aiden installation must be paired again to establish secure server trust." + } + } +} + +final class AidenRemoteClient: @unchecked Sendable { + private struct PairingExchangeRequest: Encodable { + let secret: String + let deviceName: String + let deviceType: AidenDeviceType + let clientVersion: String + } + + private struct WorkspaceList: Decodable { + let workspaces: [AidenWorkspace] + } + + private struct BrowserRootList: Decodable { + let roots: [AidenBrowserRoot] + } + + private struct BrowserSelectionRequest: Encodable { + let location: String + } + + private struct ChatList: Decodable { + let chats: [AidenChat] + } + + private struct ChatCreateRequest: Encodable { + let workspaceId: String + let providerId: String? + let modelId: String? + } + + private struct ChatUpdateRequest: Encodable { + let title: String + } + + private struct ChatMoveRequest: Encodable { + let workspaceId: String + let confirmedForeground = true + } + + private struct ApprovalRequest: Encodable { + let decision: AidenApprovalDecision + } + + private struct ScheduledTaskList: Decodable { let tasks: [AidenScheduledTask] } + private struct ScheduledRunList: Decodable { let runs: [AidenScheduledRun] } + private struct ScheduledScriptList: Decodable { let scripts: [AidenScheduledScript] } + private struct ScheduledMcpServerList: Decodable { let servers: [AidenScheduledMcpServer] } + private struct ScheduledPreviewRequest: Encodable { + let cron: String + let timezone: String + let count: Int + } + + private let endpoint: URL + private let credential: String? + private let session: URLSession + + init( + installation: AidenInstallation, + credential: String, + waitsForConnectivity: Bool = true, + requestTimeout: TimeInterval = 30 + ) throws { + guard let pairingTrust = installation.pairingTrust else { + throw AidenRemoteClientError.missingTrustConfiguration + } + endpoint = installation.endpoint + self.credential = credential + session = Self.makePinnedSession( + endpoint: installation.endpoint, + fingerprint: installation.serverSpkiSha256, + trustPolicy: try AidenServerTrustPolicy(pairingTrust: pairingTrust), + waitsForConnectivity: waitsForConnectivity, + requestTimeout: requestTimeout + ) + } + + init(endpoint: URL, credential: String?, session: URLSession) { + self.endpoint = endpoint + self.credential = credential + self.session = session + } + + static func pair( + payload: AidenRemoteContractFixture.PairingPayload, + deviceName: String, + deviceType: AidenDeviceType, + clientVersion: String, + session injectedSession: URLSession? = nil, + now: Date = Date() + ) async throws -> AidenRemoteContractFixture.PairingExchange { + let payload = try payload.validated(at: now) + let bootstrap = payload.bootstrap + let session: URLSession + if let injectedSession { + session = injectedSession + } else { + session = makePinnedSession( + endpoint: bootstrap.endpoint, + fingerprint: bootstrap.serverSpkiSha256, + trustPolicy: try AidenServerTrustPolicy(pairingTrust: payload.trust) + ) + } + let client = AidenRemoteClient(endpoint: bootstrap.endpoint, credential: nil, session: session) + let request = PairingExchangeRequest( + secret: bootstrap.secret, + deviceName: deviceName, + deviceType: deviceType, + clientVersion: clientVersion + ) + let exchange: AidenRemoteContractFixture.PairingExchange = try await client.send( + method: "POST", + path: ["pairing", "exchange"], + body: request, + authenticated: false, + acceptedStatus: [200] + ) + return try exchange.validated(against: bootstrap) + } + + func server() async throws -> AidenServer { + let value: AidenServer = try await send(method: "GET", path: ["server"]) + guard value.protocolVersion == AidenRemoteProtocol.version else { + throw AidenRemoteContractError.invalidProtocolVersion + } + return value + } + + func workspaces() async throws -> [AidenWorkspace] { + let value: WorkspaceList = try await send(method: "GET", path: ["workspaces"]) + return value.workspaces + } + + func workspace(id: String) async throws -> AidenWorkspace { + try await send(method: "GET", path: ["workspaces", id]) + } + + func createWorkspace( + _ create: AidenWorkspaceCreate, + idempotencyKey: UUID = UUID() + ) async throws -> AidenWorkspace { + try await send( + method: "POST", + path: ["workspaces"], + body: create, + headers: ["Idempotency-Key": idempotencyKey.uuidString.lowercased()], + acceptedStatus: [201] + ) + } + + func updateWorkspace( + id: String, + revision: String, + patch: AidenWorkspacePatch + ) async throws -> AidenWorkspace { + guard patch.name != nil || patch.permission != nil else { + throw AidenRemoteClientError.invalidResponse + } + return try await send( + method: "PATCH", + path: ["workspaces", id], + body: patch, + headers: ["If-Match": revision] + ) + } + + func removeWorkspace(id: String, revision: String) async throws { + try await sendWithoutResponse( + method: "DELETE", + path: ["workspaces", id], + headers: ["If-Match": revision], + acceptedStatus: [204] + ) + } + + func browserRoots() async throws -> [AidenBrowserRoot] { + let value: BrowserRootList = try await send( + method: "GET", + path: ["workspace-browser", "roots"] + ) + return value.roots + } + + func browserChildren(location: String, cursor: String? = nil) async throws -> AidenBrowserPage { + var query = [URLQueryItem(name: "location", value: location)] + if let cursor { query.append(URLQueryItem(name: "cursor", value: cursor)) } + return try await send( + method: "GET", + path: ["workspace-browser", "children"], + query: query + ) + } + + func createWorkspaceSelection(location: String) async throws -> AidenWorkspaceSelection { + try await send( + method: "POST", + path: ["workspace-browser", "selections"], + body: BrowserSelectionRequest(location: location), + acceptedStatus: [201] + ) + } + + func chats(workspaceId: String? = nil) async throws -> [AidenChat] { + let query = workspaceId.map { [URLQueryItem(name: "workspaceId", value: $0)] } ?? [] + let value: ChatList = try await send(method: "GET", path: ["chats"], query: query) + return value.chats + } + + func usage(range: String = "30d") async throws -> AidenUsageSummary { + guard ["7d", "30d", "90d", "1y", "all"].contains(range) else { + throw AidenRemoteClientError.invalidResponse + } + return try await send( + method: "GET", + path: ["usage"], + query: [URLQueryItem(name: "range", value: range)] + ) + } + + func chat(id: String) async throws -> AidenChat { + try await send(method: "GET", path: ["chats", id]) + } + + func createChat( + workspaceId: String, + providerId: String? = nil, + modelId: String? = nil, + idempotencyKey: UUID = UUID() + ) async throws -> AidenChat { + try await send( + method: "POST", + path: ["chats"], + body: ChatCreateRequest( + workspaceId: workspaceId, + providerId: providerId, + modelId: modelId + ), + headers: ["Idempotency-Key": idempotencyKey.uuidString.lowercased()], + acceptedStatus: [201] + ) + } + + func updateChat(id: String, revision: String, title: String) async throws -> AidenChat { + try await send( + method: "PATCH", + path: ["chats", id], + body: ChatUpdateRequest(title: title), + headers: ["If-Match": revision] + ) + } + + func removeChat(id: String, revision: String) async throws { + try await sendWithoutResponse( + method: "DELETE", + path: ["chats", id], + headers: ["If-Match": revision], + acceptedStatus: [204] + ) + } + + func uploadAttachment( + chatId: String, + upload: AidenAttachmentUpload + ) async throws -> AidenAttachmentReference { + try await send( + method: "POST", + path: ["chats", chatId, "attachments"], + body: upload, + acceptedStatus: [201] + ) + } + + func removeAttachment(chatId: String, attachmentId: String) async throws { + try await sendWithoutResponse( + method: "DELETE", + path: ["chats", chatId, "attachments", attachmentId], + headers: [:], + acceptedStatus: [204] + ) + } + + func moveChat( + id: String, + revision: String, + workspaceId: String, + idempotencyKey: UUID = UUID() + ) async throws -> AidenChat { + try await send( + method: "POST", + path: ["chats", id, "move"], + body: ChatMoveRequest(workspaceId: workspaceId), + headers: [ + "If-Match": revision, + "Idempotency-Key": idempotencyKey.uuidString.lowercased(), + ] + ) + } + + func modelCatalog() async throws -> AidenModelCatalog { + try await send(method: "GET", path: ["models"]) + } + + func scheduledTasks() async throws -> [AidenScheduledTask] { + let value: ScheduledTaskList = try await send(method: "GET", path: ["scheduled-tasks"]) + return try AidenScheduledTaskValidation.tasks(value.tasks) + } + + func scheduledTask(id: String) async throws -> AidenScheduledTask { + let value: AidenScheduledTask = try await send(method: "GET", path: ["scheduled-tasks", id]) + return try AidenScheduledTaskValidation.tasks([value])[0] + } + + func createScheduledTask( + _ mutation: AidenScheduledTaskMutation, + idempotencyKey: UUID = UUID() + ) async throws -> AidenScheduledTask { + try await send( + method: "POST", path: ["scheduled-tasks"], body: mutation, + headers: idempotencyHeaders(idempotencyKey), acceptedStatus: [201] + ) + } + + func updateScheduledTask( + id: String, + revision: String, + mutation: AidenScheduledTaskMutation + ) async throws -> AidenScheduledTask { + try await send( + method: "PATCH", path: ["scheduled-tasks", id], body: mutation, + headers: ["If-Match": revision] + ) + } + + func removeScheduledTask(id: String, revision: String) async throws { + try await sendWithoutResponse( + method: "DELETE", path: ["scheduled-tasks", id], + headers: ["If-Match": revision], acceptedStatus: [204] + ) + } + + func pauseScheduledTask( + id: String, + revision: String, + idempotencyKey: UUID = UUID() + ) async throws -> AidenScheduledTask { + try await send( + method: "POST", path: ["scheduled-tasks", id, "pause"], + headers: ["If-Match": revision, "Idempotency-Key": idempotencyKey.uuidString.lowercased()], + acceptedStatus: [202] + ) + } + + func resumeScheduledTask( + id: String, + revision: String, + idempotencyKey: UUID = UUID() + ) async throws -> AidenScheduledTask { + try await send( + method: "POST", path: ["scheduled-tasks", id, "resume"], + headers: ["If-Match": revision, "Idempotency-Key": idempotencyKey.uuidString.lowercased()], + acceptedStatus: [202] + ) + } + + func runScheduledTask( + id: String, + idempotencyKey: UUID = UUID() + ) async throws -> AidenScheduledRunAccepted { + try await send( + method: "POST", path: ["scheduled-tasks", id, "run"], + headers: idempotencyHeaders(idempotencyKey), acceptedStatus: [202] + ) + } + + func scheduledRuns(taskId: String) async throws -> [AidenScheduledRun] { + let value: ScheduledRunList = try await send(method: "GET", path: ["scheduled-tasks", taskId, "runs"]) + return try AidenScheduledTaskValidation.runs(value.runs, taskId: taskId) + } + + func previewSchedule(cron: String, timezone: String, count: Int = 3) async throws -> [Date] { + let value: AidenScheduledPreview = try await send( + method: "POST", path: ["scheduled-tasks", "preview"], + body: ScheduledPreviewRequest(cron: cron, timezone: timezone, count: min(max(count, 1), 20)) + ) + guard value.dates.count <= 20 else { throw AidenRemoteClientError.invalidResponse } + return value.dates + } + + func scheduledScripts(workspaceId: String?) async throws -> [AidenScheduledScript] { + let query = workspaceId.map { [URLQueryItem(name: "workspaceId", value: $0)] } ?? [] + let value: ScheduledScriptList = try await send( + method: "GET", path: ["scheduled-tasks", "scripts"], query: query + ) + guard value.scripts.count <= 4_000, + value.scripts.allSatisfy({ $0.id.hasPrefix("script_") && $0.id.count == 50 && !$0.name.isEmpty }) else { + throw AidenRemoteClientError.invalidResponse + } + return value.scripts + } + + func scheduledMcpServers() async throws -> [AidenScheduledMcpServer] { + let value: ScheduledMcpServerList = try await send( + method: "GET", path: ["scheduled-tasks", "mcp-servers"] + ) + guard value.servers.count <= 4_000, + value.servers.allSatisfy({ !$0.id.isEmpty && $0.id.count <= 256 && !$0.name.isEmpty && $0.name.count <= 256 }), + Set(value.servers.map(\.id)).count == value.servers.count else { + throw AidenRemoteClientError.invalidResponse + } + return value.servers + } + + func scheduledSettings() async throws -> AidenScheduledSettings { + try await send(method: "GET", path: ["scheduled-tasks", "settings"]) + } + + func updateScheduledSettings( + revision: String, + mutation: AidenScheduledSettingsMutation + ) async throws -> AidenScheduledSettings { + try await send( + method: "PATCH", path: ["scheduled-tasks", "settings"], body: mutation, + headers: ["If-Match": revision] + ) + } + + func workspaceFiles(workspaceId: String) async throws -> AidenWorkspaceFileIndex { + let value: AidenWorkspaceFileIndex = try await send( + method: "GET", + path: ["workspaces", workspaceId, "files"] + ) + return try AidenWorkspaceEnvironmentValidation.validated(value) + } + + func workspaceFile(workspaceId: String, fileId: String) async throws -> AidenWorkspaceFileDocument { + guard AidenWorkspaceEnvironmentValidation.opaqueFileID(fileId) else { + throw AidenRemoteClientError.invalidResponse + } + let value: AidenWorkspaceFileDocument = try await send( + method: "GET", + path: ["workspaces", workspaceId, "files", fileId], + maximumResponseBytes: AidenRemoteProtocol.maxFileJSONBodyBytes + ) + return try AidenWorkspaceEnvironmentValidation.validated(value, expectedID: fileId) + } + + func writeWorkspaceFile( + workspaceId: String, + fileId: String, + content: String, + expectedVersion: String + ) async throws -> AidenWorkspaceFileDocument { + guard AidenWorkspaceEnvironmentValidation.opaqueFileID(fileId), !expectedVersion.isEmpty else { + throw AidenRemoteClientError.invalidResponse + } + let value: AidenWorkspaceFileDocument = try await send( + method: "PUT", + path: ["workspaces", workspaceId, "files", fileId], + body: AidenWorkspaceFileWriteRequest(content: content, expectedVersion: expectedVersion), + maximumResponseBytes: AidenRemoteProtocol.maxFileJSONBodyBytes + ) + return try AidenWorkspaceEnvironmentValidation.validated(value, expectedID: fileId) + } + + func gitReview(workspaceId: String) async throws -> AidenGitResult { + try validatedGit(await send(method: "GET", path: ["workspaces", workspaceId, "git", "review"])) + } + + func gitDiff(workspaceId: String, snapshotId: String, fileId: String) async throws -> AidenGitResult { + try validatedGit(await send( + method: "POST", + path: ["workspaces", workspaceId, "git", "diff"], + body: AidenGitDiffRequest(snapshotId: snapshotId, fileId: fileId) + )) + } + + func gitBranches(workspaceId: String) async throws -> AidenGitResult { + try validatedGit(await send(method: "GET", path: ["workspaces", workspaceId, "git", "branches"])) + } + + func createGitBranch( + workspaceId: String, + name: String, + startPoint: String, + idempotencyKey: UUID = UUID() + ) async throws -> AidenGitResult { + try validatedGit(await send( + method: "POST", + path: ["workspaces", workspaceId, "git", "branches"], + body: AidenGitCreateBranchRequest(name: name, startPoint: startPoint), + headers: idempotencyHeaders(idempotencyKey), + acceptedStatus: [202] + )) + } + + func checkoutGitBranch( + workspaceId: String, + branch: String, + snapshotId: String, + idempotencyKey: UUID = UUID() + ) async throws -> AidenGitResult { + try validatedGit(await send( + method: "POST", + path: ["workspaces", workspaceId, "git", "checkout"], + body: AidenGitCheckoutRequest(branch: branch, snapshotId: snapshotId), + headers: idempotencyHeaders(idempotencyKey), + acceptedStatus: [202] + )) + } + + func commitGit( + workspaceId: String, + snapshotId: String, + message: String, + stagedOnly: Bool, + idempotencyKey: UUID = UUID() + ) async throws -> AidenGitResult { + try validatedGit(await send( + method: "POST", + path: ["workspaces", workspaceId, "git", "commit"], + body: AidenGitCommitRequest( + snapshotId: snapshotId, + message: message, + scope: stagedOnly ? "staged-reviewed" : "all-reviewed" + ), + headers: idempotencyHeaders(idempotencyKey), + acceptedStatus: [202] + )) + } + + func gitPushCapability(workspaceId: String) async throws -> AidenGitResult { + try validatedGit(await send(method: "GET", path: ["workspaces", workspaceId, "git", "push-capability"])) + } + + func pushGit( + workspaceId: String, + snapshotId: String, + remote: String, + branch: String, + idempotencyKey: UUID = UUID() + ) async throws -> AidenGitResult { + try validatedGit(await send( + method: "POST", + path: ["workspaces", workspaceId, "git", "push"], + body: AidenGitPushRequest(snapshotId: snapshotId, remote: remote, branch: branch), + headers: idempotencyHeaders(idempotencyKey), + acceptedStatus: [202] + )) + } + + func compareGit(workspaceId: String, baseRef: String) async throws -> AidenGitResult { + try validatedGit(await send( + method: "POST", + path: ["workspaces", workspaceId, "git", "compare"], + body: AidenGitCompareRequest(baseRef: baseRef) + )) + } + + func gitComparisonDiff(workspaceId: String, comparisonId: String, fileId: String) async throws -> AidenGitResult { + try validatedGit(await send( + method: "POST", + path: ["workspaces", workspaceId, "git", "comparison-diff"], + body: AidenGitComparisonDiffRequest(comparisonId: comparisonId, fileId: fileId) + )) + } + + func gitWorktrees(workspaceId: String) async throws -> AidenGitResult { + try validatedGit(await send(method: "GET", path: ["workspaces", workspaceId, "git", "worktrees"])) + } + + func createGitWorktree( + workspaceId: String, + branch: String, + name: String, + idempotencyKey: UUID = UUID() + ) async throws -> AidenGitResult { + try validatedGit(await send( + method: "POST", + path: ["workspaces", workspaceId, "git", "worktrees"], + body: AidenGitCreateWorktreeRequest(branch: branch, name: name), + headers: idempotencyHeaders(idempotencyKey), + acceptedStatus: [202] + )) + } + + func deleteManagedGitWorktree( + workspaceId: String, + revision: String, + idempotencyKey: UUID = UUID() + ) async throws -> AidenGitResult { + try validatedGit(await send( + method: "DELETE", + path: ["workspaces", workspaceId, "git", "managed-worktree"], + body: AidenForegroundConfirmation(), + headers: [ + "If-Match": revision, + "Idempotency-Key": idempotencyKey.uuidString.lowercased(), + ], + acceptedStatus: [202] + )) + } + + func startTurn( + chatId: String, + request: AidenTurnStart, + idempotencyKey: UUID = UUID() + ) async throws -> AidenTurnStartResponse { + try await send( + method: "POST", + path: ["chats", chatId, "turns"], + body: request, + headers: ["Idempotency-Key": idempotencyKey.uuidString.lowercased()], + acceptedStatus: [202] + ) + } + + func streamStatus(id: String) async throws -> AidenStreamStatus { + try await send(method: "GET", path: ["streams", id]) + } + + func cancelStream(id: String, idempotencyKey: UUID = UUID()) async throws -> AidenStreamStatus { + try await send( + method: "POST", + path: ["streams", id, "cancel"], + headers: ["Idempotency-Key": idempotencyKey.uuidString.lowercased()], + acceptedStatus: [202] + ) + } + + func respondToApproval( + id: String, + decision: AidenApprovalDecision, + idempotencyKey: UUID = UUID() + ) async throws -> AidenApprovalResponse { + try await send( + method: "POST", + path: ["approvals", id, "respond"], + body: ApprovalRequest(decision: decision), + headers: ["Idempotency-Key": idempotencyKey.uuidString.lowercased()] + ) + } + + func streamEvents( + id: String, + after sequence: Int + ) -> AsyncThrowingStream { + AsyncThrowingStream { continuation in + let task = Task { + do { + let query = sequence > 0 + ? [URLQueryItem(name: "after", value: String(sequence))] + : [] + var request = try makeRequest( + method: "GET", + path: ["streams", id, "events"], + query: query, + body: nil, + headers: sequence > 0 ? ["Last-Event-ID": String(sequence)] : [:], + authenticated: true + ) + request.setValue("text/event-stream", forHTTPHeaderField: "Accept") + let (bytes, response) = try await session.bytes(for: request) + guard let httpResponse = response as? HTTPURLResponse else { + throw AidenRemoteClientError.invalidResponse + } + guard httpResponse.statusCode == 200 else { + var body = Data() + for try await byte in bytes { + guard body.count < AidenRemoteProtocol.maxJSONBodyBytes else { + throw AidenRemoteContractError.payloadTooLarge + } + body.append(byte) + } + try validate(response: response, data: body, acceptedStatus: [200]) + throw AidenRemoteClientError.unexpectedStatus(httpResponse.statusCode) + } + + var parser = AidenSSEParser() + var lineBytes: [UInt8] = [] + lineBytes.reserveCapacity(512) + for try await byte in bytes { + try Task.checkCancellation() + if byte == 0x0A { + if lineBytes.last == 0x0D { lineBytes.removeLast() } + guard let line = String(bytes: lineBytes, encoding: .utf8) else { + throw AidenRemoteClientError.invalidResponse + } + lineBytes.removeAll(keepingCapacity: true) + if let event = try parser.consume(line: line) { + continuation.yield(event) + } + } else { + lineBytes.append(byte) + guard lineBytes.count <= AidenRemoteProtocol.maxSSEFrameBytes else { + throw AidenSSEParserError.frameTooLarge + } + } + } + if !lineBytes.isEmpty { + if lineBytes.last == 0x0D { lineBytes.removeLast() } + guard let line = String(bytes: lineBytes, encoding: .utf8) else { + throw AidenRemoteClientError.invalidResponse + } + if let event = try parser.consume(line: line) { + continuation.yield(event) + } + } + if let event = try parser.finish() { + continuation.yield(event) + } + continuation.finish() + } catch is CancellationError { + continuation.finish() + } catch { + continuation.finish(throwing: error) + } + } + continuation.onTermination = { @Sendable _ in task.cancel() } + } + } + + private static func makePinnedSession( + endpoint: URL, + fingerprint: String, + trustPolicy: AidenServerTrustPolicy, + waitsForConnectivity: Bool = true, + requestTimeout: TimeInterval = 30 + ) -> URLSession { + let delegate = AidenPinnedServerSessionDelegate( + expectedHost: endpoint.host ?? "", + expectedPort: endpoint.port, + expectedFingerprint: fingerprint, + trustPolicy: trustPolicy + ) + let configuration = URLSessionConfiguration.ephemeral + configuration.waitsForConnectivity = waitsForConnectivity + configuration.timeoutIntervalForRequest = requestTimeout + configuration.timeoutIntervalForResource = 60 * 60 + configuration.httpCookieAcceptPolicy = .never + configuration.httpShouldSetCookies = false + return URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil) + } + + private func send( + method: String, + path: [String], + query: [URLQueryItem] = [], + headers: [String: String] = [:], + authenticated: Bool = true, + acceptedStatus: Set = [200], + maximumResponseBytes: Int = AidenRemoteProtocol.maxJSONBodyBytes + ) async throws -> Response { + let request = try makeRequest( + method: method, + path: path, + query: query, + body: nil, + headers: headers, + authenticated: authenticated + ) + let (data, response) = try await session.data(for: request) + try validate(response: response, data: data, acceptedStatus: acceptedStatus) + do { + return try AidenRemoteJSONDecoder.decode(Response.self, from: data, maximumBytes: maximumResponseBytes) + } catch { + throw AidenRemoteClientError.invalidResponse + } + } + + private func send( + method: String, + path: [String], + query: [URLQueryItem] = [], + body: Body, + headers: [String: String] = [:], + authenticated: Bool = true, + acceptedStatus: Set = [200], + maximumResponseBytes: Int = AidenRemoteProtocol.maxJSONBodyBytes + ) async throws -> Response { + let encodedBody = try JSONEncoder().encode(body) + let request = try makeRequest( + method: method, + path: path, + query: query, + body: encodedBody, + headers: headers, + authenticated: authenticated + ) + let (data, response) = try await session.data(for: request) + try validate(response: response, data: data, acceptedStatus: acceptedStatus) + do { + return try AidenRemoteJSONDecoder.decode(Response.self, from: data, maximumBytes: maximumResponseBytes) + } catch { + throw AidenRemoteClientError.invalidResponse + } + } + + private func sendWithoutResponse( + method: String, + path: [String], + headers: [String: String], + acceptedStatus: Set + ) async throws { + let request = try makeRequest( + method: method, + path: path, + query: [], + body: nil, + headers: headers, + authenticated: true + ) + let (data, response) = try await session.data(for: request) + try validate(response: response, data: data, acceptedStatus: acceptedStatus) + } + + private func makeRequest( + method: String, + path: [String], + query: [URLQueryItem], + body: Data?, + headers: [String: String], + authenticated: Bool + ) throws -> URLRequest { + var url = endpoint + for component in path { + url.append(path: component) + } + if !query.isEmpty { + guard var components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { + throw AidenRemoteClientError.invalidEndpoint + } + components.queryItems = query + guard let queryURL = components.url else { throw AidenRemoteClientError.invalidEndpoint } + url = queryURL + } + + var request = URLRequest(url: url) + request.httpMethod = method + request.httpBody = body + request.setValue(String(AidenRemoteProtocol.version), forHTTPHeaderField: "Aiden-Protocol-Version") + request.setValue("application/json", forHTTPHeaderField: "Accept") + if body != nil { + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + } + if authenticated { + guard let credential, !credential.isEmpty else { + throw AidenRemoteClientError.missingCredential + } + request.setValue("Bearer \(credential)", forHTTPHeaderField: "Authorization") + } + for (name, value) in headers { + request.setValue(value, forHTTPHeaderField: name) + } + return request + } + + private func idempotencyHeaders(_ key: UUID) -> [String: String] { + ["Idempotency-Key": key.uuidString.lowercased()] + } + + private func validatedGit(_ result: AidenGitResult) throws -> AidenGitResult { + try AidenWorkspaceEnvironmentValidation.validated(result) + } + + private func validate( + response: URLResponse, + data: Data, + acceptedStatus: Set + ) throws { + guard let response = response as? HTTPURLResponse else { + throw AidenRemoteClientError.invalidResponse + } + guard acceptedStatus.contains(response.statusCode) else { + if let envelope = try? AidenRemoteJSONDecoder.decode(AidenRemoteErrorEnvelope.self, from: data) { + throw AidenRemoteClientError.server(statusCode: response.statusCode, body: envelope.error) + } + throw AidenRemoteClientError.unexpectedStatus(response.statusCode) + } + } +} diff --git a/ios/AidenOnTheGo/Networking/AidenRemoteContract.swift b/ios/AidenOnTheGo/Networking/AidenRemoteContract.swift new file mode 100644 index 00000000..03e74c09 --- /dev/null +++ b/ios/AidenOnTheGo/Networking/AidenRemoteContract.swift @@ -0,0 +1,1509 @@ +import Foundation + +enum AidenRemoteProtocol { + static let version = 1 + static let basePath = "/api/aiden/v1" + static let maxIdentifierLength = 128 + static let maxEndpointLength = 2_048 + static let maxEndpointPort = 65_535 + static let maxEventTypeLength = 80 + static let maxEventPayloadProperties = 32 + // The envelope is additively extensible. Its only key-count ceiling is the + // same whole-document abuse limit enforced by the TypeScript raw scanner; + // the event payload itself remains capped at 32 properties below. + static let maxEventEnvelopeProperties = 16_384 + static let maxJSONTotalObjectKeys = 16_384 + static let maxTextLength = 200_000 + static let maxToolNameLength = 120 + static let maxTimelineLabelLength = 500 + static let maxApprovalSummaryLength = 2_000 + static let maxErrorMessageLength = 2_000 + static let maxJSONBodyBytes = 1_048_576 + static let maxFileJSONBodyBytes = 6 * 1_048_576 + static let maxSSEFrameBytes = maxJSONBodyBytes + static let maxPairingPayloadBytes = 4_096 + static let maxSafeInteger = 9_007_199_254_740_991 + static let maxJSONNestingDepth = 128 + static let forbiddenWireKeys: Set = [ + "authorization", "credentialDigest", "providerFingerprint", "mcpServerBindings", + "folderPath", "repositoryPath", "worktreePath", "worktreeGitDir", + "ownershipToken", "worktreeDevice", "worktreeInode", "createdFromHead", + "canonicalPath", "absolutePath", "scriptPath", "environment", "stdout", "stderr", + ] +} + +enum AidenPairingBootstrapError: Error, Equatable { + case unsupportedProtocol + case invalidInstance + case invalidEndpoint + case invalidFingerprint + case weakSecret + case expired + case excessiveTTL +} + +enum AidenPairingPayloadError: Error, Equatable { + case invalidKind + case invalidTrust + case invalidCACertificateData +} + +enum AidenRemoteContractError: Error, Equatable { + case duplicateJSONKey(String) + case invalidJSON + case unknownTerminalEvent(String) + case unsafePayloadField(String) + case payloadTooLarge + case unknownErrorCode(String) + case invalidTerminalClassification + case invalidProtocolVersion + case invalidStreamIdentity + case invalidSequence + case invalidPairingExchange +} + +private struct AidenDynamicCodingKey: CodingKey { + let stringValue: String + let intValue: Int? = nil + + init?(stringValue: String) { self.stringValue = stringValue } + init?(intValue: Int) { return nil } +} + +private func assertKnownKeys( + _ container: KeyedDecodingContainer, + allowed: Set +) throws { + if let unsupported = container.allKeys.first(where: { !allowed.contains($0.stringValue) }) { + throw AidenRemoteContractError.unsafePayloadField(unsupported.stringValue) + } +} + +/// A JSON object member name after JSON escape processing, retaining its exact +/// Unicode scalar sequence for equality. `String` equality uses canonical +/// equivalence, while JSON member names are compared after unescaping only. +private struct AidenRawJSONKey: Hashable { + let scalars: [UInt32] + let displayValue: String + + init(_ value: String) { + scalars = value.unicodeScalars.map(\.value) + displayValue = value + } + + static func == (lhs: Self, rhs: Self) -> Bool { + lhs.scalars == rhs.scalars + } + + func hash(into hasher: inout Hasher) { + hasher.combine(scalars.count) + for scalar in scalars { + hasher.combine(scalar) + } + } +} + +/// Foundation's JSONDecoder does not provide a duplicate-member policy. Scan +/// the raw JSON first so object names cannot be overwritten by a later member, +/// including when one spelling uses JSON escapes and the other does not. +private struct AidenRawJSONDuplicateKeyScanner { + private let bytes: [UInt8] + private var offset = 0 + private var totalObjectKeys = 0 + + init(data: Data) { + bytes = Array(data) + } + + static func validate(_ data: Data) throws { + var scanner = Self(data: data) + try scanner.parseDocument() + } + + private mutating func parseDocument() throws { + try parseValue(depth: 0) + skipWhitespace() + guard offset == bytes.count else { throw AidenRemoteContractError.invalidJSON } + } + + private mutating func parseValue(depth: Int) throws { + guard depth <= AidenRemoteProtocol.maxJSONNestingDepth else { + throw AidenRemoteContractError.payloadTooLarge + } + skipWhitespace() + guard let byte = peek() else { throw AidenRemoteContractError.invalidJSON } + switch byte { + case 0x7B: // { + try parseObject(depth: depth) + case 0x5B: // [ + try parseArray(depth: depth) + case 0x22: // " + _ = try parseString() + case 0x74: // t + try parseLiteral([0x74, 0x72, 0x75, 0x65]) // true + case 0x66: // f + try parseLiteral([0x66, 0x61, 0x6C, 0x73, 0x65]) // false + case 0x6E: // n + try parseLiteral([0x6E, 0x75, 0x6C, 0x6C]) // null + case 0x2D, 0x30...0x39: // - or digit + try parseNumber() + default: + throw AidenRemoteContractError.invalidJSON + } + } + + private mutating func parseObject(depth: Int) throws { + try consume(0x7B) // { + skipWhitespace() + var keys = Set() + if consumeIf(0x7D) { return } // } + + while true { + skipWhitespace() + guard peek() == 0x22 else { throw AidenRemoteContractError.invalidJSON } + let key = try parseString() + totalObjectKeys += 1 + guard totalObjectKeys <= AidenRemoteProtocol.maxJSONTotalObjectKeys else { + throw AidenRemoteContractError.payloadTooLarge + } + guard keys.insert(key).inserted else { + throw AidenRemoteContractError.duplicateJSONKey(key.displayValue) + } + skipWhitespace() + try consume(0x3A) // : + try parseValue(depth: depth + 1) + skipWhitespace() + if consumeIf(0x2C) { continue } // , + try consume(0x7D) // } + return + } + } + + private mutating func parseArray(depth: Int) throws { + try consume(0x5B) // [ + skipWhitespace() + if consumeIf(0x5D) { return } // ] + + while true { + try parseValue(depth: depth + 1) + skipWhitespace() + if consumeIf(0x2C) { continue } // , + try consume(0x5D) // ] + return + } + } + + private mutating func parseLiteral(_ literal: [UInt8]) throws { + guard bytes.count - offset >= literal.count, + Array(bytes[offset..<(offset + literal.count)]) == literal else { + throw AidenRemoteContractError.invalidJSON + } + offset += literal.count + } + + private mutating func parseNumber() throws { + _ = consumeIf(0x2D) // - + + if consumeIf(0x30) { // 0 + if let next = peek(), next >= 0x30, next <= 0x39 { + throw AidenRemoteContractError.invalidJSON + } + } else { + guard let first = peek(), first >= 0x31, first <= 0x39 else { + throw AidenRemoteContractError.invalidJSON + } + offset += 1 + while let next = peek(), next >= 0x30, next <= 0x39 { offset += 1 } + } + + if consumeIf(0x2E) { // . + guard let first = peek(), first >= 0x30, first <= 0x39 else { + throw AidenRemoteContractError.invalidJSON + } + offset += 1 + while let next = peek(), next >= 0x30, next <= 0x39 { offset += 1 } + } + + if let next = peek(), next == 0x65 || next == 0x45 { // e/E + offset += 1 + _ = consumeIf(0x2B) // + + _ = consumeIf(0x2D) // - + guard let first = peek(), first >= 0x30, first <= 0x39 else { + throw AidenRemoteContractError.invalidJSON + } + offset += 1 + while let digit = peek(), digit >= 0x30, digit <= 0x39 { offset += 1 } + } + } + + private mutating func parseString() throws -> AidenRawJSONKey { + try consume(0x22) // " + var utf8: [UInt8] = [] + + while let byte = peek() { + offset += 1 + switch byte { + case 0x22: // " + guard let value = String(bytes: utf8, encoding: .utf8) else { + throw AidenRemoteContractError.invalidJSON + } + return AidenRawJSONKey(value) + case 0x5C: // \ + guard let escape = peek() else { throw AidenRemoteContractError.invalidJSON } + offset += 1 + switch escape { + case 0x22, 0x5C, 0x2F: // " \\ / + utf8.append(escape) + case 0x62: // b + utf8.append(0x08) + case 0x66: // f + utf8.append(0x0C) + case 0x6E: // n + utf8.append(0x0A) + case 0x72: // r + utf8.append(0x0D) + case 0x74: // t + utf8.append(0x09) + case 0x75: // u + let first = try readHexQuad() + if first >= 0xD800 && first <= 0xDBFF { + guard consumeIf(0x5C), consumeIf(0x75) else { + throw AidenRemoteContractError.invalidJSON + } + let second = try readHexQuad() + guard second >= 0xDC00 && second <= 0xDFFF else { + throw AidenRemoteContractError.invalidJSON + } + let scalar = 0x10000 + ((first - 0xD800) << 10) + (second - 0xDC00) + try appendScalar(scalar, to: &utf8) + } else if first >= 0xDC00 && first <= 0xDFFF { + throw AidenRemoteContractError.invalidJSON + } else { + try appendScalar(first, to: &utf8) + } + default: + throw AidenRemoteContractError.invalidJSON + } + default: + guard byte >= 0x20 else { throw AidenRemoteContractError.invalidJSON } + utf8.append(byte) + } + } + + throw AidenRemoteContractError.invalidJSON + } + + private mutating func readHexQuad() throws -> UInt32 { + guard bytes.count - offset >= 4 else { throw AidenRemoteContractError.invalidJSON } + var value: UInt32 = 0 + for _ in 0..<4 { + guard let digit = Self.hexValue(bytes[offset]) else { + throw AidenRemoteContractError.invalidJSON + } + value = (value << 4) | digit + offset += 1 + } + return value + } + + private func appendScalar(_ value: UInt32, to utf8: inout [UInt8]) throws { + guard let scalar = UnicodeScalar(value) else { + throw AidenRemoteContractError.invalidJSON + } + utf8.append(contentsOf: String(scalar).utf8) + } + + private static func hexValue(_ byte: UInt8) -> UInt32? { + switch byte { + case 0x30...0x39: return UInt32(byte - 0x30) + case 0x41...0x46: return UInt32(byte - 0x41 + 10) + case 0x61...0x66: return UInt32(byte - 0x61 + 10) + default: return nil + } + } + + private func peek() -> UInt8? { + guard offset < bytes.count else { return nil } + return bytes[offset] + } + + private mutating func consume(_ expected: UInt8) throws { + guard consumeIf(expected) else { throw AidenRemoteContractError.invalidJSON } + } + + private mutating func consumeIf(_ expected: UInt8) -> Bool { + guard peek() == expected else { return false } + offset += 1 + return true + } + + private mutating func skipWhitespace() { + while let byte = peek(), byte == 0x20 || byte == 0x09 || byte == 0x0A || byte == 0x0D { + offset += 1 + } + } +} + +private func boundedString( + _ container: KeyedDecodingContainer, + forKey key: Key, + maxLength: Int, + field: String, + required: Bool = false +) throws -> String? { + let value: String? + // `decodeIfPresent` treats an explicitly encoded JSON null the same as an + // absent member. Wire schemas distinguish those cases: null is invalid + // for every bounded string member, while only an absent optional member + // may decode to nil. + if required || container.contains(key) { + value = try container.decode(String.self, forKey: key) + } else { + value = nil + } + guard let value else { return nil } + guard !value.isEmpty, value.unicodeScalars.count <= maxLength else { + throw AidenRemoteContractError.unsafePayloadField(field) + } + return value +} + +private func decodeOptionalNonNull( + _ container: KeyedDecodingContainer, + _ type: Value.Type, + forKey key: Key +) throws -> Value? { + guard container.contains(key) else { return nil } + return try container.decode(type, forKey: key) +} + +private func isAidenASCIIAlphaNumeric(_ scalar: UnicodeScalar) -> Bool { + (scalar.value >= 48 && scalar.value <= 57) || + (scalar.value >= 65 && scalar.value <= 90) || + (scalar.value >= 97 && scalar.value <= 122) +} + +private func isAidenASCIIHex(_ scalar: UnicodeScalar) -> Bool { + (scalar.value >= 48 && scalar.value <= 57) || + (scalar.value >= 65 && scalar.value <= 70) || + (scalar.value >= 97 && scalar.value <= 102) +} + +private func isCanonicalAidenIPv4(_ value: String) -> Bool { + let octets = value.split(separator: ".", omittingEmptySubsequences: false) + guard octets.count == 4 else { return false } + return octets.allSatisfy { octet in + let scalars = Array(octet.unicodeScalars) + guard !scalars.isEmpty, + scalars.count <= 3, + scalars.allSatisfy({ $0.value >= 48 && $0.value <= 57 }) else { + return false + } + if scalars.count > 1, scalars[0].value == 48 { return false } + guard let number = Int(String(octet)) else { return false } + return number <= 255 + } +} + +private func parseAidenIPv6Side(_ value: String) -> Int? { + if value.isEmpty { return 0 } + let groups = value.split(separator: ":", omittingEmptySubsequences: false) + guard groups.allSatisfy({ !$0.isEmpty }) else { return nil } + var count = 0 + for (index, group) in groups.enumerated() { + let value = String(group) + if value.contains(".") { + guard index == groups.count - 1, isCanonicalAidenIPv4(value) else { return nil } + count += 2 + } else { + let scalars = Array(value.unicodeScalars) + guard (1...4).contains(scalars.count), scalars.allSatisfy(isAidenASCIIHex) else { + return nil + } + count += 1 + } + } + return count +} + +private func isCanonicalAidenIPv6(_ value: String) -> Bool { + guard !value.isEmpty, + value.unicodeScalars.allSatisfy({ isAidenASCIIHex($0) || $0.value == 46 || $0.value == 58 }) else { + return false + } + let sides = value.components(separatedBy: "::") + guard sides.count <= 2 else { return false } + if sides.count == 2 { + if sides[0].contains(".") { return false } + guard let left = parseAidenIPv6Side(sides[0]), + let right = parseAidenIPv6Side(sides[1]) else { + return false + } + return left + right < 8 + } + return parseAidenIPv6Side(value) == 8 +} + +private func isCanonicalAidenDNSLabel(_ label: Substring) -> Bool { + let scalars = Array(label.unicodeScalars) + guard !scalars.isEmpty, + scalars.count <= 63, + let first = scalars.first, + let last = scalars.last, + isAidenASCIIAlphaNumeric(first), + isAidenASCIIAlphaNumeric(last) else { + return false + } + return scalars.dropFirst().dropLast().allSatisfy { isAidenASCIIAlphaNumeric($0) || $0.value == 45 } +} + +private func isCanonicalAidenDNSHost(_ value: String) -> Bool { + guard !value.isEmpty, value.utf8.count <= 253 else { return false } + let labels = value.split(separator: ".", omittingEmptySubsequences: false) + guard labels.allSatisfy({ !$0.isEmpty }) else { return false } + if labels.allSatisfy({ !$0.isEmpty && $0.unicodeScalars.allSatisfy({ $0.value >= 48 && $0.value <= 57 }) }) { + // Numeric-only authorities are ambiguous under Foundation URL parsing. + // Keep only canonical dotted-decimal IPv4 instead of letting `123` + // become `0.0.0.123` on one platform and a DNS name on another. + return isCanonicalAidenIPv4(value) + } + // A DNS authority must not end in a numeric-only label. This keeps + // `aiden.123` distinct from canonical IPv4 while retaining numeric labels + // in non-terminal positions. + if labels.last?.unicodeScalars.allSatisfy({ $0.value >= 48 && $0.value <= 57 }) == true { + return false + } + return labels.allSatisfy(isCanonicalAidenDNSLabel) +} + +private func isCanonicalAidenPort(_ value: String) -> Bool { + let scalars = Array(value.unicodeScalars) + guard !scalars.isEmpty, + scalars.count <= 5, + scalars[0].value != 48, + scalars.allSatisfy({ $0.value >= 48 && $0.value <= 57 }), + let port = Int(value) else { + return false + } + return (1...AidenRemoteProtocol.maxEndpointPort).contains(port) +} + +private func isCanonicalAidenAuthority(_ value: String) -> Bool { + // Keep the raw grammar ASCII-only. This rejects C0/DEL, all Unicode + // whitespace and normalization-sensitive host spellings before Foundation + // can decode or normalize them. + guard value.unicodeScalars.allSatisfy({ + $0.value <= 0x7F && $0.value > 0x20 && $0.value != 0x7F + }) else { + return false + } + + let host: String + var rawPort: String? + if value.first == "[" { + guard let closingBracket = value.firstIndex(of: "]"), + closingBracket > value.index(after: value.startIndex) else { + return false + } + let hostStart = value.index(after: value.startIndex) + guard !value[hostStart.. Bool { + guard rawEndpoint.utf8.count <= AidenRemoteProtocol.maxEndpointLength, + rawEndpoint.hasPrefix("https://"), + rawEndpoint.hasSuffix(AidenRemoteProtocol.basePath) else { + return false + } + let authorityStart = rawEndpoint.index(rawEndpoint.startIndex, offsetBy: "https://".count) + let pathStart = rawEndpoint.index(rawEndpoint.endIndex, offsetBy: -AidenRemoteProtocol.basePath.count) + guard pathStart > authorityStart else { return false } + return isCanonicalAidenAuthority(String(rawEndpoint[authorityStart.. Bool { + isCanonicalAidenEndpoint(endpoint.absoluteString) +} + +struct AidenRemoteCapability: RawRepresentable, Codable, Hashable, Sendable { + let rawValue: String + + init(rawValue: String) { + self.rawValue = rawValue + } + + static let serverRead = Self(rawValue: "server:read") + static let chatRead = Self(rawValue: "chat:read") + static let chatWrite = Self(rawValue: "chat:write") + static let approvalRespond = Self(rawValue: "approval:respond") + static let workspaceRead = Self(rawValue: "workspace:read") + static let workspaceBrowse = Self(rawValue: "workspace:browse") + static let workspaceManage = Self(rawValue: "workspace:manage") + static let filesRead = Self(rawValue: "files:read") + static let filesWrite = Self(rawValue: "files:write") + static let gitRead = Self(rawValue: "git:read") + static let gitWrite = Self(rawValue: "git:write") + static let scheduleRead = Self(rawValue: "schedule:read") + static let scheduleWrite = Self(rawValue: "schedule:write") + + static let v1Known: [Self] = [ + .serverRead, .chatRead, .chatWrite, .approvalRespond, + .workspaceRead, .workspaceBrowse, .workspaceManage, + .filesRead, .filesWrite, .gitRead, .gitWrite, + .scheduleRead, .scheduleWrite, + ] + + init(from decoder: Decoder) throws { + let value = try decoder.singleValueContainer().decode(String.self) + guard !value.isEmpty, value.unicodeScalars.count <= AidenRemoteProtocol.maxEventTypeLength else { + throw AidenRemoteContractError.unsafePayloadField("capability") + } + self.init(rawValue: value) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(rawValue) + } +} + +struct AidenRemoteEventType: RawRepresentable, Codable, Hashable, Sendable { + let rawValue: String + + init(rawValue: String) { + self.rawValue = rawValue + } + + static let snapshot = Self(rawValue: "snapshot") + static let status = Self(rawValue: "status") + static let textDelta = Self(rawValue: "text_delta") + static let reasoningDelta = Self(rawValue: "reasoning_delta") + static let toolStarted = Self(rawValue: "tool_started") + static let toolFinished = Self(rawValue: "tool_finished") + static let timeline = Self(rawValue: "timeline") + static let approvalRequired = Self(rawValue: "approval_required") + static let done = Self(rawValue: "done") + static let error = Self(rawValue: "error") + static let cancelled = Self(rawValue: "cancelled") + static let heartbeat = Self(rawValue: "heartbeat") + + static let v1Known: [Self] = [ + .snapshot, .status, .textDelta, .reasoningDelta, + .toolStarted, .toolFinished, .timeline, .approvalRequired, + .done, .error, .cancelled, .heartbeat, + ] + + var isTerminal: Bool { + self == .done || self == .error || self == .cancelled + } +} + +struct AidenRemoteErrorCode: RawRepresentable, Codable, Hashable, Sendable { + let rawValue: String + + init(rawValue: String) { + self.rawValue = rawValue + } + + static let v1Known: [Self] = [ + Self(rawValue: "invalid_request"), + Self(rawValue: "payload_too_large"), + Self(rawValue: "rate_limited"), + Self(rawValue: "authentication_required"), + Self(rawValue: "credential_revoked"), + Self(rawValue: "capability_denied"), + Self(rawValue: "pairing_closed"), + Self(rawValue: "pairing_expired"), + Self(rawValue: "pairing_already_used"), + Self(rawValue: "server_identity_changed"), + Self(rawValue: "not_found"), + Self(rawValue: "already_exists"), + Self(rawValue: "revision_conflict"), + Self(rawValue: "idempotency_conflict"), + Self(rawValue: "idempotency_capacity"), + Self(rawValue: "idempotency_in_flight"), + Self(rawValue: "workspace_unavailable"), + Self(rawValue: "workspace_changing"), + Self(rawValue: "permission_confirmation_required"), + Self(rawValue: "handle_invalid"), + Self(rawValue: "handle_expired"), + Self(rawValue: "handle_wrong_device"), + Self(rawValue: "root_policy_changed"), + Self(rawValue: "filesystem_identity_changed"), + Self(rawValue: "path_outside_root"), + Self(rawValue: "handle_capacity"), + Self(rawValue: "turn_already_active"), + Self(rawValue: "stream_gone"), + Self(rawValue: "approval_already_resolved"), + Self(rawValue: "approval_expired"), + Self(rawValue: "operation_in_progress"), + Self(rawValue: "operation_stale"), + Self(rawValue: "git_capability_denied"), + Self(rawValue: "schedule_disabled"), + Self(rawValue: "schedule_run_in_progress"), + Self(rawValue: "server_interrupted"), + Self(rawValue: "internal_error"), + ] + + init(from decoder: Decoder) throws { + let value = try decoder.singleValueContainer().decode(String.self) + guard Self.v1Known.contains(Self(rawValue: value)) else { + throw AidenRemoteContractError.unknownErrorCode(value) + } + self.init(rawValue: value) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(rawValue) + } +} + +struct AidenRemoteErrorEnvelope: Codable, Equatable, Sendable { + struct Details: Codable, Equatable, Sendable { + let currentRevision: String? + let retryAfterSeconds: Int? + let chatId: String? + let minimumClientVersion: String? + let limit: Int? + let field: String? + + init(from decoder: Decoder) throws { + let dynamic = try decoder.container(keyedBy: AidenDynamicCodingKey.self) + try assertKnownKeys(dynamic, allowed: Set(CodingKeys.allCases.map(\.stringValue))) + let values = try decoder.container(keyedBy: CodingKeys.self) + currentRevision = try boundedString( + values, + forKey: .currentRevision, + maxLength: AidenRemoteProtocol.maxIdentifierLength, + field: "currentRevision" + ) + retryAfterSeconds = try decodeOptionalNonNull( + values, + Int.self, + forKey: .retryAfterSeconds + ) + if let retryAfterSeconds { + guard (0...86_400).contains(retryAfterSeconds) else { + throw AidenRemoteContractError.unsafePayloadField("retryAfterSeconds") + } + } + chatId = try boundedString( + values, + forKey: .chatId, + maxLength: AidenRemoteProtocol.maxIdentifierLength, + field: "chatId" + ) + minimumClientVersion = try boundedString( + values, + forKey: .minimumClientVersion, + maxLength: 40, + field: "minimumClientVersion" + ) + limit = try decodeOptionalNonNull(values, Int.self, forKey: .limit) + if let limit { + guard (0...1_000_000).contains(limit) else { + throw AidenRemoteContractError.unsafePayloadField("limit") + } + } + field = try boundedString( + values, + forKey: .field, + maxLength: 120, + field: "field" + ) + } + + private enum CodingKeys: String, CodingKey, CaseIterable { + case currentRevision, retryAfterSeconds, chatId, minimumClientVersion, limit, field + } + } + + struct Body: Codable, Equatable, Sendable { + let code: AidenRemoteErrorCode + let message: String + let requestId: String + let retryable: Bool + let details: Details? + + init(from decoder: Decoder) throws { + let dynamic = try decoder.container(keyedBy: AidenDynamicCodingKey.self) + try assertKnownKeys(dynamic, allowed: Set(CodingKeys.allCases.map(\.stringValue))) + let values = try decoder.container(keyedBy: CodingKeys.self) + code = try values.decode(AidenRemoteErrorCode.self, forKey: .code) + message = try boundedString( + values, + forKey: .message, + maxLength: AidenRemoteProtocol.maxErrorMessageLength, + field: "message", + required: true + )! + requestId = try boundedString( + values, + forKey: .requestId, + maxLength: AidenRemoteProtocol.maxIdentifierLength, + field: "requestId", + required: true + )! + retryable = try values.decode(Bool.self, forKey: .retryable) + details = try decodeOptionalNonNull(values, Details.self, forKey: .details) + } + + private enum CodingKeys: String, CodingKey, CaseIterable { + case code, message, requestId, retryable, details + } + } + + let error: Body + + init(from decoder: Decoder) throws { + let dynamic = try decoder.container(keyedBy: AidenDynamicCodingKey.self) + try assertKnownKeys(dynamic, allowed: ["error"]) + let values = try decoder.container(keyedBy: CodingKeys.self) + error = try values.decode(Body.self, forKey: .error) + } + + private enum CodingKeys: String, CodingKey { + case error + } +} + +struct AidenRemoteEventPayload: Codable, Equatable, Sendable { + let chatId: String? + let turnId: String? + let nextSequence: Int? + let state: String? + let text: String? + let toolId: String? + let name: String? + let status: String? + let label: String? + let approvalId: String? + let summary: String? + let expiresAt: Date? + let messageId: String? + let code: AidenRemoteErrorCode? + let message: String? + let source: String? + private let wirePresentKeys: Set + + var presentKeys: Set { + wirePresentKeys + } + + init(from decoder: Decoder) throws { + let dynamic = try decoder.container(keyedBy: AidenDynamicCodingKey.self) + guard dynamic.allKeys.count <= AidenRemoteProtocol.maxEventPayloadProperties else { + throw AidenRemoteContractError.payloadTooLarge + } + try assertKnownKeys(dynamic, allowed: Set(CodingKeys.allCases.map(\.stringValue))) + wirePresentKeys = Set(dynamic.allKeys.map(\.stringValue)) + let values = try decoder.container(keyedBy: CodingKeys.self) + chatId = try boundedString( + values, + forKey: .chatId, + maxLength: AidenRemoteProtocol.maxIdentifierLength, + field: "chatId" + ) + turnId = try boundedString( + values, + forKey: .turnId, + maxLength: AidenRemoteProtocol.maxIdentifierLength, + field: "turnId" + ) + nextSequence = try decodeOptionalNonNull(values, Int.self, forKey: .nextSequence) + state = try boundedString(values, forKey: .state, maxLength: 64, field: "state") + text = try boundedString( + values, + forKey: .text, + maxLength: AidenRemoteProtocol.maxTextLength, + field: "text" + ) + toolId = try boundedString( + values, + forKey: .toolId, + maxLength: AidenRemoteProtocol.maxIdentifierLength, + field: "toolId" + ) + name = try boundedString( + values, + forKey: .name, + maxLength: AidenRemoteProtocol.maxToolNameLength, + field: "name" + ) + status = try boundedString(values, forKey: .status, maxLength: 32, field: "status") + label = try boundedString( + values, + forKey: .label, + maxLength: AidenRemoteProtocol.maxTimelineLabelLength, + field: "label" + ) + approvalId = try boundedString( + values, + forKey: .approvalId, + maxLength: AidenRemoteProtocol.maxIdentifierLength, + field: "approvalId" + ) + summary = try boundedString( + values, + forKey: .summary, + maxLength: AidenRemoteProtocol.maxApprovalSummaryLength, + field: "summary" + ) + expiresAt = try decodeOptionalNonNull(values, Date.self, forKey: .expiresAt) + messageId = try boundedString( + values, + forKey: .messageId, + maxLength: AidenRemoteProtocol.maxIdentifierLength, + field: "messageId" + ) + code = try decodeOptionalNonNull(values, AidenRemoteErrorCode.self, forKey: .code) + message = try boundedString( + values, + forKey: .message, + maxLength: AidenRemoteProtocol.maxErrorMessageLength, + field: "message" + ) + source = try boundedString(values, forKey: .source, maxLength: 32, field: "source") + } + + private enum CodingKeys: String, CodingKey, CaseIterable { + case chatId, turnId, nextSequence, state, text, toolId, name, status, label + case approvalId, summary, expiresAt, messageId, code, message, source + } +} + +private indirect enum AidenUnknownJSONValue: Decodable { + case object + case array + case scalar + + init(from decoder: Decoder) throws { + guard decoder.codingPath.count <= AidenRemoteProtocol.maxJSONNestingDepth else { + throw AidenRemoteContractError.payloadTooLarge + } + if let values = try? decoder.container(keyedBy: AidenDynamicCodingKey.self) { + for key in values.allKeys { + if AidenRemoteProtocol.forbiddenWireKeys.contains(key.stringValue) { + throw AidenRemoteContractError.unsafePayloadField(key.stringValue) + } + _ = try values.decode(AidenUnknownJSONValue.self, forKey: key) + } + self = .object + return + } + if var values = try? decoder.unkeyedContainer() { + while !values.isAtEnd { _ = try values.decode(AidenUnknownJSONValue.self) } + self = .array + return + } + let value = try decoder.singleValueContainer() + if value.decodeNil() + || (try? value.decode(Bool.self)) != nil + || (try? value.decode(Int64.self)) != nil + || (try? value.decode(Double.self)) != nil + || (try? value.decode(String.self)) != nil { + self = .scalar + return + } + throw DecodingError.dataCorruptedError(in: value, debugDescription: "Unsupported JSON value.") + } +} + +private struct AidenUnknownEventPayload: Decodable { + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: AidenDynamicCodingKey.self) + guard values.allKeys.count <= AidenRemoteProtocol.maxEventPayloadProperties else { + throw AidenRemoteContractError.payloadTooLarge + } + for key in values.allKeys { + if AidenRemoteProtocol.forbiddenWireKeys.contains(key.stringValue) { + throw AidenRemoteContractError.unsafePayloadField(key.stringValue) + } + _ = try values.decode(AidenUnknownJSONValue.self, forKey: key) + } + } +} + +struct AidenRemoteStreamEvent: Decodable, Equatable, Sendable { + let protocolVersion: Int + let streamId: String + let sequence: Int + let timestamp: Date + let type: AidenRemoteEventType + let terminal: Bool + let payload: AidenRemoteEventPayload? + + var shouldApply: Bool { AidenRemoteEventType.v1Known.contains(type) } + + private enum CodingKeys: String, CodingKey, CaseIterable { + case protocolVersion, streamId, sequence, timestamp, type, terminal, payload + } + + init(from decoder: Decoder) throws { + let dynamic = try decoder.container(keyedBy: AidenDynamicCodingKey.self) + guard dynamic.allKeys.count <= AidenRemoteProtocol.maxEventEnvelopeProperties else { + throw AidenRemoteContractError.payloadTooLarge + } + let envelopeKeys = Set(CodingKeys.allCases.map(\.stringValue)) + for key in dynamic.allKeys where !envelopeKeys.contains(key.stringValue) { + if AidenRemoteProtocol.forbiddenWireKeys.contains(key.stringValue) { + throw AidenRemoteContractError.unsafePayloadField(key.stringValue) + } + _ = try dynamic.decode(AidenUnknownJSONValue.self, forKey: key) + } + let values = try decoder.container(keyedBy: CodingKeys.self) + protocolVersion = try values.decode(Int.self, forKey: .protocolVersion) + guard protocolVersion == AidenRemoteProtocol.version else { + throw AidenRemoteContractError.invalidProtocolVersion + } + streamId = try values.decode(String.self, forKey: .streamId) + guard !streamId.isEmpty, streamId.unicodeScalars.count <= AidenRemoteProtocol.maxIdentifierLength else { + throw AidenRemoteContractError.invalidStreamIdentity + } + sequence = try values.decode(Int.self, forKey: .sequence) + guard (1...AidenRemoteProtocol.maxSafeInteger).contains(sequence) else { + throw AidenRemoteContractError.invalidSequence + } + timestamp = try values.decode(Date.self, forKey: .timestamp) + type = try values.decode(AidenRemoteEventType.self, forKey: .type) + guard !type.rawValue.isEmpty, + type.rawValue.unicodeScalars.count <= AidenRemoteProtocol.maxEventTypeLength else { + throw AidenRemoteContractError.unsafePayloadField("type") + } + terminal = try values.decode(Bool.self, forKey: .terminal) + if !AidenRemoteEventType.v1Known.contains(type) { + guard !terminal else { throw AidenRemoteContractError.unknownTerminalEvent(type.rawValue) } + _ = try values.decode(AidenUnknownEventPayload.self, forKey: .payload) + payload = nil + return + } + guard terminal == type.isTerminal else { + throw AidenRemoteContractError.invalidTerminalClassification + } + let decodedPayload = try values.decode(AidenRemoteEventPayload.self, forKey: .payload) + let allowedKeys: Set + switch type { + case .snapshot: allowedKeys = ["chatId", "turnId", "nextSequence"] + case .status: allowedKeys = ["state"] + case .textDelta, .reasoningDelta: allowedKeys = ["text"] + case .toolStarted: allowedKeys = ["toolId", "name"] + case .toolFinished: allowedKeys = ["toolId", "status"] + case .timeline: allowedKeys = ["label"] + case .approvalRequired: allowedKeys = ["approvalId", "summary", "expiresAt"] + case .done: allowedKeys = ["messageId"] + case .error: allowedKeys = ["code", "message"] + case .cancelled: allowedKeys = ["source"] + case .heartbeat: allowedKeys = [] + default: allowedKeys = [] + } + if let unsupported = decodedPayload.presentKeys.subtracting(allowedKeys).first { + throw AidenRemoteContractError.unsafePayloadField(unsupported) + } + guard decodedPayload.presentKeys == allowedKeys else { + throw AidenRemoteContractError.unsafePayloadField("missing-required-field") + } + if type == .status, + let state = decodedPayload.state, + !["queued", "running", "waiting_for_approval", "reconciling"].contains(state) { + throw AidenRemoteContractError.unsafePayloadField("state") + } + if type == .snapshot, + let nextSequence = decodedPayload.nextSequence, + !(1...AidenRemoteProtocol.maxSafeInteger).contains(nextSequence) { + throw AidenRemoteContractError.unsafePayloadField("nextSequence") + } + if type == .toolFinished, + let status = decodedPayload.status, + !["succeeded", "failed", "cancelled"].contains(status) { + throw AidenRemoteContractError.unsafePayloadField("status") + } + if type == .cancelled, + let source = decodedPayload.source, + !["device", "server"].contains(source) { + throw AidenRemoteContractError.unsafePayloadField("source") + } + payload = decodedPayload + } +} + +struct AidenRemoteContractFixture: Decodable { + struct Health: Decodable { + let ok: Bool + let protocolVersion: Int + + init(from decoder: Decoder) throws { + let dynamic = try decoder.container(keyedBy: AidenDynamicCodingKey.self) + try assertKnownKeys(dynamic, allowed: Set(CodingKeys.allCases.map(\.stringValue))) + let values = try decoder.container(keyedBy: CodingKeys.self) + ok = try values.decode(Bool.self, forKey: .ok) + guard ok else { throw AidenRemoteContractError.unsafePayloadField("ok") } + protocolVersion = try values.decode(Int.self, forKey: .protocolVersion) + guard protocolVersion == AidenRemoteProtocol.version else { + throw AidenRemoteContractError.invalidProtocolVersion + } + } + + private enum CodingKeys: String, CodingKey, CaseIterable { + case ok, protocolVersion + } + } + + struct PairingBootstrap: Codable, Equatable { + let protocolVersion: Int + let instanceId: String + let endpoint: URL + fileprivate let rawEndpoint: String + let serverSpkiSha256: String + let secret: String + let expiresAt: Date + + init( + protocolVersion: Int, + instanceId: String, + endpoint: URL, + serverSpkiSha256: String, + secret: String, + expiresAt: Date + ) { + self.protocolVersion = protocolVersion + self.instanceId = instanceId + self.endpoint = endpoint + self.rawEndpoint = endpoint.absoluteString + self.serverSpkiSha256 = serverSpkiSha256 + self.secret = secret + self.expiresAt = expiresAt + } + + init(from decoder: Decoder) throws { + let dynamic = try decoder.container(keyedBy: AidenDynamicCodingKey.self) + try assertKnownKeys(dynamic, allowed: Set(CodingKeys.allCases.map(\.stringValue))) + let values = try decoder.container(keyedBy: CodingKeys.self) + protocolVersion = try values.decode(Int.self, forKey: .protocolVersion) + instanceId = try boundedString( + values, + forKey: .instanceId, + maxLength: AidenRemoteProtocol.maxIdentifierLength, + field: "instanceId", + required: true + )! + let rawEndpoint = try values.decode(String.self, forKey: .endpoint) + guard isCanonicalAidenEndpoint(rawEndpoint), let endpoint = URL(string: rawEndpoint) else { + throw AidenPairingBootstrapError.invalidEndpoint + } + self.endpoint = endpoint + self.rawEndpoint = rawEndpoint + serverSpkiSha256 = try values.decode(String.self, forKey: .serverSpkiSha256) + secret = try values.decode(String.self, forKey: .secret) + expiresAt = try values.decode(Date.self, forKey: .expiresAt) + } + + @discardableResult + func validated(at now: Date = Date()) throws -> Self { + guard protocolVersion == AidenRemoteProtocol.version else { + throw AidenPairingBootstrapError.unsupportedProtocol + } + guard !instanceId.isEmpty, + instanceId.unicodeScalars.count <= AidenRemoteProtocol.maxIdentifierLength else { + throw AidenPairingBootstrapError.invalidInstance + } + guard isCanonicalAidenEndpoint(rawEndpoint) else { + throw AidenPairingBootstrapError.invalidEndpoint + } + guard serverSpkiSha256.range( + of: "^sha256/[A-Za-z0-9+/]{43}=$", + options: .regularExpression + ) != nil else { + throw AidenPairingBootstrapError.invalidFingerprint + } + let encodedFingerprint = String(serverSpkiSha256.dropFirst("sha256/".count)) + guard serverSpkiSha256.hasPrefix("sha256/"), + let fingerprint = Data(base64Encoded: encodedFingerprint), + fingerprint.count == 32 else { + throw AidenPairingBootstrapError.invalidFingerprint + } + guard secret.base64URLDecoded?.count == 32, + secret.range(of: "^[A-Za-z0-9_-]{43}$", options: .regularExpression) != nil else { + throw AidenPairingBootstrapError.weakSecret + } + guard expiresAt > now else { throw AidenPairingBootstrapError.expired } + guard expiresAt.timeIntervalSince(now) <= 5 * 60 else { + throw AidenPairingBootstrapError.excessiveTTL + } + return self + } + + private enum CodingKeys: String, CodingKey, CaseIterable { + case protocolVersion, instanceId, endpoint, serverSpkiSha256, secret, expiresAt + } + } + + struct PairingTrust: Codable, Equatable, Sendable { + enum Mode: String, Codable, Sendable { + case privateCA = "private-ca" + case system + } + + let mode: Mode + let caCertificateDerBase64: String? + + init(mode: Mode, caCertificateDerBase64: String? = nil) { + self.mode = mode + self.caCertificateDerBase64 = caCertificateDerBase64 + } + + init(from decoder: Decoder) throws { + let dynamic = try decoder.container(keyedBy: AidenDynamicCodingKey.self) + try assertKnownKeys(dynamic, allowed: Set(CodingKeys.allCases.map(\.stringValue))) + let values = try decoder.container(keyedBy: CodingKeys.self) + mode = try values.decode(Mode.self, forKey: .mode) + caCertificateDerBase64 = try values.decodeIfPresent( + String.self, + forKey: .caCertificateDerBase64 + ) + _ = try validated() + } + + @discardableResult + func validated() throws -> Self { + switch mode { + case .system: + guard caCertificateDerBase64 == nil else { + throw AidenPairingPayloadError.invalidTrust + } + case .privateCA: + guard let value = caCertificateDerBase64, + !value.isEmpty, + let data = Data(base64Encoded: value), + data.count <= AidenRemoteProtocol.maxPairingPayloadBytes, + data.base64EncodedString() == value else { + throw AidenPairingPayloadError.invalidCACertificateData + } + } + return self + } + + var caCertificateDER: Data? { + guard mode == .privateCA, + let value = caCertificateDerBase64 else { return nil } + return Data(base64Encoded: value) + } + + private enum CodingKeys: String, CodingKey, CaseIterable { + case mode, caCertificateDerBase64 + } + } + + struct PairingPayload: Codable, Equatable { + static let kindValue = "aiden-pairing-v1" + + let kind: String + let bootstrap: PairingBootstrap + let trust: PairingTrust + + init(bootstrap: PairingBootstrap, trust: PairingTrust) { + kind = Self.kindValue + self.bootstrap = bootstrap + self.trust = trust + } + + init(from decoder: Decoder) throws { + let dynamic = try decoder.container(keyedBy: AidenDynamicCodingKey.self) + try assertKnownKeys(dynamic, allowed: Set(CodingKeys.allCases.map(\.stringValue))) + let values = try decoder.container(keyedBy: CodingKeys.self) + kind = try values.decode(String.self, forKey: .kind) + guard kind == Self.kindValue else { throw AidenPairingPayloadError.invalidKind } + bootstrap = try values.decode(PairingBootstrap.self, forKey: .bootstrap) + trust = try values.decode(PairingTrust.self, forKey: .trust) + } + + @discardableResult + func validated(at now: Date = Date()) throws -> Self { + guard kind == Self.kindValue else { throw AidenPairingPayloadError.invalidKind } + _ = try bootstrap.validated(at: now) + _ = try trust.validated() + return self + } + + private enum CodingKeys: String, CodingKey, CaseIterable { + case kind, bootstrap, trust + } + } + + struct PairingExchange: Codable, Equatable { + let protocolVersion: Int + let instanceId: String + let deviceId: String + let credential: String + let capabilities: [AidenRemoteCapability] + let endpoint: URL + fileprivate let rawEndpoint: String + let serverSpkiSha256: String + + init( + protocolVersion: Int, + instanceId: String, + deviceId: String, + credential: String, + capabilities: [AidenRemoteCapability], + endpoint: URL, + serverSpkiSha256: String + ) { + self.protocolVersion = protocolVersion + self.instanceId = instanceId + self.deviceId = deviceId + self.credential = credential + self.capabilities = capabilities + self.endpoint = endpoint + self.rawEndpoint = endpoint.absoluteString + self.serverSpkiSha256 = serverSpkiSha256 + } + + init(from decoder: Decoder) throws { + let dynamic = try decoder.container(keyedBy: AidenDynamicCodingKey.self) + try assertKnownKeys(dynamic, allowed: Set(CodingKeys.allCases.map(\.stringValue))) + let values = try decoder.container(keyedBy: CodingKeys.self) + protocolVersion = try values.decode(Int.self, forKey: .protocolVersion) + instanceId = try boundedString( + values, + forKey: .instanceId, + maxLength: AidenRemoteProtocol.maxIdentifierLength, + field: "instanceId", + required: true + )! + deviceId = try boundedString( + values, + forKey: .deviceId, + maxLength: AidenRemoteProtocol.maxIdentifierLength, + field: "deviceId", + required: true + )! + credential = try values.decode(String.self, forKey: .credential) + capabilities = try values.decode([AidenRemoteCapability].self, forKey: .capabilities) + let rawEndpoint = try values.decode(String.self, forKey: .endpoint) + guard isCanonicalAidenEndpoint(rawEndpoint), let endpoint = URL(string: rawEndpoint) else { + throw AidenRemoteContractError.invalidPairingExchange + } + self.endpoint = endpoint + self.rawEndpoint = rawEndpoint + serverSpkiSha256 = try values.decode(String.self, forKey: .serverSpkiSha256) + } + + func validated(against bootstrap: PairingBootstrap) throws -> Self { + guard protocolVersion == AidenRemoteProtocol.version, + !instanceId.isEmpty, + instanceId.unicodeScalars.count <= AidenRemoteProtocol.maxIdentifierLength, + !deviceId.isEmpty, + deviceId.unicodeScalars.count <= AidenRemoteProtocol.maxIdentifierLength, + instanceId == bootstrap.instanceId, + isCanonicalAidenEndpoint(rawEndpoint), + rawEndpoint == bootstrap.rawEndpoint, + endpoint == bootstrap.endpoint, + endpoint.absoluteString.utf8.count <= AidenRemoteProtocol.maxEndpointLength, + serverSpkiSha256 == bootstrap.serverSpkiSha256, + credential.base64URLDecoded?.count == 32, + credential.range(of: "^[A-Za-z0-9_-]{43}$", options: .regularExpression) != nil, + Set(capabilities).count == capabilities.count, + Set(capabilities).isSubset(of: Set(AidenRemoteCapability.v1Known)) else { + throw AidenRemoteContractError.invalidPairingExchange + } + return self + } + + private enum CodingKeys: String, CodingKey, CaseIterable { + case protocolVersion, instanceId, deviceId, credential, capabilities, endpoint, serverSpkiSha256 + } + } + + let contractRevision: Int + let protocolVersion: Int + let capabilities: [AidenRemoteCapability] + let health: Health + let pairingBootstrap: PairingBootstrap + let pairingExchange: PairingExchange + let events: [AidenRemoteStreamEvent] + let error: AidenRemoteErrorEnvelope +} + +private extension String { + var base64URLDecoded: Data? { + var value = replacingOccurrences(of: "-", with: "+") + .replacingOccurrences(of: "_", with: "/") + value += String(repeating: "=", count: (4 - value.count % 4) % 4) + return Data(base64Encoded: value) + } +} + +enum AidenRemoteJSONDecoder { + static func decode( + _ type: Value.Type, + from data: Data, + maximumBytes: Int = AidenRemoteProtocol.maxJSONBodyBytes + ) throws -> Value { + try JSONDecoder.aidenRemote().decodeAidenRemote(type, from: data, maximumBytes: maximumBytes) + } + + static func decodeSSEEvent(from data: Data) throws -> AidenRemoteStreamEvent { + try JSONDecoder.aidenRemote().decodeAidenRemoteStreamEvent(from: data) + } + + static func decodePairingBootstrap( + from data: Data + ) throws -> AidenRemoteContractFixture.PairingBootstrap { + try decode(AidenRemoteContractFixture.PairingBootstrap.self, from: data) + } + + static func decodePairingPayload( + from data: Data + ) throws -> AidenRemoteContractFixture.PairingPayload { + guard data.count <= AidenRemoteProtocol.maxPairingPayloadBytes else { + throw AidenRemoteContractError.payloadTooLarge + } + return try decode(AidenRemoteContractFixture.PairingPayload.self, from: data) + } +} + +extension JSONDecoder { + static func aidenRemote() -> JSONDecoder { + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .strictRFC3339 + return decoder + } + + func decodeAidenRemote( + _ type: Value.Type, + from data: Data, + maximumBytes: Int = AidenRemoteProtocol.maxJSONBodyBytes + ) throws -> Value { + guard data.count <= maximumBytes else { + throw AidenRemoteContractError.payloadTooLarge + } + try AidenRawJSONDuplicateKeyScanner.validate(data) + _ = try decode(AidenUnknownJSONValue.self, from: data) + return try decode(type, from: data) + } + + func decodeAidenRemoteStreamEvent(from data: Data) throws -> AidenRemoteStreamEvent { + guard data.count <= AidenRemoteProtocol.maxSSEFrameBytes else { + throw AidenRemoteContractError.payloadTooLarge + } + return try decodeAidenRemote(AidenRemoteStreamEvent.self, from: data) + } +} + +private enum AidenStrictRFC3339Date { + private static let pattern = try! NSRegularExpression( + pattern: #"^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})$"# + ) + + static func date(from value: String) -> Date? { + let range = NSRange(value.startIndex.. String? { + let captureRange = match.range(at: index) + guard captureRange.location != NSNotFound, + let swiftRange = Range(captureRange, in: value) else { + return nil + } + return String(value[swiftRange]) + } + + guard let year = capture(1).flatMap(Int.init), + let month = capture(2).flatMap(Int.init), + let day = capture(3).flatMap(Int.init), + let hour = capture(4).flatMap(Int.init), + let minute = capture(5).flatMap(Int.init), + let second = capture(6).flatMap(Int.init), + let offset = capture(8) else { + return nil + } + + let leapYear = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) + let daysInMonth: [Int] = [ + 31, leapYear ? 29 : 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31, + ] + guard (1...12).contains(month), + (1...daysInMonth[month - 1]).contains(day), + (0...23).contains(hour), + (0...59).contains(minute), + (0...59).contains(second) else { + return nil + } + + let fractionDigits = capture(7).map { String($0.dropFirst()) } ?? "" + let milliseconds = Int(String((fractionDigits + "000").prefix(3))) ?? 0 + let offsetHours: Int + let offsetMinutes: Int + let offsetSign: Int + if offset == "Z" { + offsetHours = 0 + offsetMinutes = 0 + offsetSign = 1 + } else { + offsetHours = Int(offset.dropFirst().prefix(2)) ?? 0 + offsetMinutes = Int(offset.dropFirst(4).prefix(2)) ?? 0 + offsetSign = offset.first == "+" ? 1 : -1 + } + guard offsetHours <= 23, offsetMinutes <= 59 else { return nil } + + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(secondsFromGMT: 0)! + var components = DateComponents() + components.calendar = calendar + components.timeZone = calendar.timeZone + components.year = year + components.month = month + components.day = day + components.hour = hour + components.minute = minute + components.second = second + components.nanosecond = milliseconds * 1_000_000 + guard let date = calendar.date(from: components) else { return nil } + + let offsetSeconds = offsetSign * (offsetHours * 60 + offsetMinutes) * 60 + return date.addingTimeInterval(-TimeInterval(offsetSeconds)) + } +} + +private extension JSONDecoder.DateDecodingStrategy { + static let strictRFC3339 = custom { decoder in + let container = try decoder.singleValueContainer() + let value = try container.decode(String.self) + if let date = AidenStrictRFC3339Date.date(from: value) { return date } + throw DecodingError.dataCorruptedError( + in: container, + debugDescription: "Expected a strict RFC 3339 timestamp." + ) + } +} diff --git a/ios/AidenOnTheGo/Networking/AidenSSEParser.swift b/ios/AidenOnTheGo/Networking/AidenSSEParser.swift new file mode 100644 index 00000000..59b343a5 --- /dev/null +++ b/ios/AidenOnTheGo/Networking/AidenSSEParser.swift @@ -0,0 +1,77 @@ +import Foundation + +enum AidenSSEParserError: Error, Equatable { + case frameTooLarge + case invalidEventID + case eventIDMismatch + case eventNameMismatch + case missingData +} + +struct AidenSSEParser { + private var eventID: String? + private var eventName: String? + private var dataLines: [String] = [] + private var frameBytes = 0 + + mutating func consume(line: String) throws -> AidenRemoteStreamEvent? { + frameBytes += line.utf8.count + 1 + guard frameBytes <= AidenRemoteProtocol.maxSSEFrameBytes else { + throw AidenSSEParserError.frameTooLarge + } + guard !line.isEmpty else { return try finishFrame() } + guard !line.hasPrefix(":") else { return nil } + + let field: Substring + let value: Substring + if let separator = line.firstIndex(of: ":") { + field = line[.. AidenRemoteStreamEvent? { + guard frameBytes > 0 else { return nil } + return try finishFrame() + } + + private mutating func finishFrame() throws -> AidenRemoteStreamEvent? { + defer { reset() } + guard !dataLines.isEmpty else { + if eventID == nil, eventName == nil { return nil } + throw AidenSSEParserError.missingData + } + guard let eventID, let sequence = Int(eventID), sequence > 0 else { + throw AidenSSEParserError.invalidEventID + } + let event = try AidenRemoteJSONDecoder.decodeSSEEvent( + from: Data(dataLines.joined(separator: "\n").utf8) + ) + guard event.sequence == sequence else { throw AidenSSEParserError.eventIDMismatch } + if let eventName, eventName != event.type.rawValue { + throw AidenSSEParserError.eventNameMismatch + } + return event + } + + private mutating func reset() { + eventID = nil + eventName = nil + dataLines.removeAll(keepingCapacity: true) + frameBytes = 0 + } +} diff --git a/ios/AidenOnTheGo/Networking/AidenServerTrust.swift b/ios/AidenOnTheGo/Networking/AidenServerTrust.swift new file mode 100644 index 00000000..30986705 --- /dev/null +++ b/ios/AidenOnTheGo/Networking/AidenServerTrust.swift @@ -0,0 +1,215 @@ +import CryptoKit +import Foundation +import Security + +enum AidenServerTrustError: Error, Equatable { + case missingLeafCertificate + case invalidAnchorCertificate + case missingPublicKey + case unsupportedPublicKey + case invalidPublicKeyRepresentation + case trustConfigurationFailed(OSStatus) + case hostnameOrCertificateInvalid + case publicKeyPinMismatch +} + +enum AidenServerTrustPolicy: Equatable, Sendable { + case system + case privateCA(Data) + + init(pairingTrust: AidenRemoteContractFixture.PairingTrust) throws { + switch pairingTrust.mode { + case .system: + self = .system + case .privateCA: + guard let certificate = pairingTrust.caCertificateDER, + SecCertificateCreateWithData(nil, certificate as CFData) != nil else { + throw AidenServerTrustError.invalidAnchorCertificate + } + self = .privateCA(certificate) + } + } +} + +enum AidenServerTrust { + // ASN.1 SubjectPublicKeyInfo prefix for an uncompressed P-256 public key. + private static let p256SPKIPrefix = Data([ + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, + 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, + 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, + ]) + + static func spkiSHA256(p256ExternalRepresentation: Data) throws -> String { + guard p256ExternalRepresentation.count == 65, + p256ExternalRepresentation.first == 0x04 else { + throw AidenServerTrustError.invalidPublicKeyRepresentation + } + let digest = SHA256.hash(data: p256SPKIPrefix + p256ExternalRepresentation) + return "sha256/\(Data(digest).base64EncodedString())" + } + + static func spkiSHA256(certificate: SecCertificate) throws -> String { + guard let publicKey = SecCertificateCopyKey(certificate) else { + throw AidenServerTrustError.missingPublicKey + } + guard let attributes = SecKeyCopyAttributes(publicKey) as? [CFString: Any], + let keyType = attributes[kSecAttrKeyType] as? String, + keyType == (kSecAttrKeyTypeECSECPrimeRandom as String), + let keySize = attributes[kSecAttrKeySizeInBits] as? Int, + keySize == 256 else { + throw AidenServerTrustError.unsupportedPublicKey + } + var error: Unmanaged? + guard let representation = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else { + throw error?.takeRetainedValue() ?? AidenServerTrustError.invalidPublicKeyRepresentation + } + return try spkiSHA256(p256ExternalRepresentation: representation) + } + + static func evaluate( + serverTrust: SecTrust, + expectedHost: String, + expectedFingerprint: String, + policy: AidenServerTrustPolicy, + verificationDate: Date? = nil + ) throws { + guard let chain = SecTrustCopyCertificateChain(serverTrust) as? [SecCertificate], + let leaf = chain.first else { + throw AidenServerTrustError.missingLeafCertificate + } + let policyStatus = SecTrustSetPolicies( + serverTrust, + SecPolicyCreateSSL(true, expectedHost as CFString) + ) + guard policyStatus == errSecSuccess else { + throw AidenServerTrustError.trustConfigurationFailed(policyStatus) + } + switch policy { + case .system: + let anchorStatus = SecTrustSetAnchorCertificates(serverTrust, nil) + guard anchorStatus == errSecSuccess else { + throw AidenServerTrustError.trustConfigurationFailed(anchorStatus) + } + let anchorsOnlyStatus = SecTrustSetAnchorCertificatesOnly(serverTrust, false) + guard anchorsOnlyStatus == errSecSuccess else { + throw AidenServerTrustError.trustConfigurationFailed(anchorsOnlyStatus) + } + case .privateCA(let certificateData): + guard let trustAnchor = SecCertificateCreateWithData(nil, certificateData as CFData) else { + throw AidenServerTrustError.invalidAnchorCertificate + } + let anchorStatus = SecTrustSetAnchorCertificates(serverTrust, [trustAnchor] as CFArray) + guard anchorStatus == errSecSuccess else { + throw AidenServerTrustError.trustConfigurationFailed(anchorStatus) + } + let anchorsOnlyStatus = SecTrustSetAnchorCertificatesOnly(serverTrust, true) + guard anchorsOnlyStatus == errSecSuccess else { + throw AidenServerTrustError.trustConfigurationFailed(anchorsOnlyStatus) + } + } + if let verificationDate { + let dateStatus = SecTrustSetVerifyDate(serverTrust, verificationDate as CFDate) + guard dateStatus == errSecSuccess else { + throw AidenServerTrustError.trustConfigurationFailed(dateStatus) + } + } + var evaluationError: CFError? + guard SecTrustEvaluateWithError(serverTrust, &evaluationError) else { + throw AidenServerTrustError.hostnameOrCertificateInvalid + } + let actualFingerprint = try spkiSHA256(certificate: leaf) + guard constantTimeEqual(actualFingerprint, expectedFingerprint) else { + throw AidenServerTrustError.publicKeyPinMismatch + } + } + + private static func constantTimeEqual(_ left: String, _ right: String) -> Bool { + let lhs = Array(left.utf8) + let rhs = Array(right.utf8) + var difference = lhs.count ^ rhs.count + for index in 0.. Bool { + destination.scheme?.lowercased() == "https" && + destination.host == expectedHost && + (destination.port ?? 443) == expectedPort + } + + func urlSession( + _ session: URLSession, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: @escaping @Sendable (URLSession.AuthChallengeDisposition, URLCredential?) -> Void + ) { + guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust, + challenge.protectionSpace.host == expectedHost, + challenge.protectionSpace.port == expectedPort, + let serverTrust = challenge.protectionSpace.serverTrust else { + completionHandler(.cancelAuthenticationChallenge, nil) + return + } + + do { + try AidenServerTrust.evaluate( + serverTrust: serverTrust, + expectedHost: expectedHost, + expectedFingerprint: expectedFingerprint, + policy: trustPolicy + ) + completionHandler(.useCredential, URLCredential(trust: serverTrust)) + } catch let error as AidenServerTrustError { + failureLock.lock() + recordedTrustError = error + failureLock.unlock() + completionHandler(.cancelAuthenticationChallenge, nil) + } catch { + completionHandler(.cancelAuthenticationChallenge, nil) + } + } + + func urlSession( + _ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest, + completionHandler: @escaping @Sendable (URLRequest?) -> Void + ) { + guard let destination = request.url, allowsRedirect(to: destination) else { + completionHandler(nil) + return + } + completionHandler(request) + } +} diff --git a/ios/AidenOnTheGo/Persistence/AidenChatCache.swift b/ios/AidenOnTheGo/Persistence/AidenChatCache.swift new file mode 100644 index 00000000..ad0804fb --- /dev/null +++ b/ios/AidenOnTheGo/Persistence/AidenChatCache.swift @@ -0,0 +1,135 @@ +import CryptoKit +import Foundation + +actor AidenChatCache { + static let shared = AidenChatCache() + + struct ActiveStream: Codable, Equatable, Sendable { + let streamId: String + let turnId: String + var lastSequence: Int + } + + private struct ChatListEnvelope: Codable { + let instanceId: String + let workspaceId: String + let chats: [AidenChat] + } + + private struct ChatEnvelope: Codable { + let instanceId: String + let chat: AidenChat + } + + private struct StreamEnvelope: Codable { + let instanceId: String + let chatId: String + let stream: ActiveStream + } + + private let root: URL + private let fileManager: FileManager + private let maxCacheFileBytes = 10 * 1_024 * 1_024 + + init(root: URL? = nil, fileManager: FileManager = .default) { + self.fileManager = fileManager + if let root { + self.root = root + } else { + let applicationSupport = fileManager.urls( + for: .applicationSupportDirectory, + in: .userDomainMask + ).first ?? fileManager.temporaryDirectory + self.root = applicationSupport + .appending(path: "AidenOnTheGo", directoryHint: .isDirectory) + .appending(path: "RemoteChatCache-v1", directoryHint: .isDirectory) + } + } + + func loadChats(instanceId: String, workspaceId: String) -> [AidenChat]? { + guard let envelope: ChatListEnvelope = load( + ChatListEnvelope.self, + from: fileURL(kind: "lists", instanceId, workspaceId) + ), envelope.instanceId == instanceId, envelope.workspaceId == workspaceId else { + return nil + } + return envelope.chats + } + + func saveChats(_ chats: [AidenChat], instanceId: String, workspaceId: String) throws { + try save( + ChatListEnvelope(instanceId: instanceId, workspaceId: workspaceId, chats: chats), + to: fileURL(kind: "lists", instanceId, workspaceId) + ) + } + + func loadChat(instanceId: String, chatId: String) -> AidenChat? { + guard let envelope: ChatEnvelope = load( + ChatEnvelope.self, + from: fileURL(kind: "chats", instanceId, chatId) + ), envelope.instanceId == instanceId, envelope.chat.id == chatId else { + return nil + } + return envelope.chat + } + + func saveChat(_ chat: AidenChat, instanceId: String) throws { + try save( + ChatEnvelope(instanceId: instanceId, chat: chat), + to: fileURL(kind: "chats", instanceId, chat.id) + ) + } + + func loadActiveStream(instanceId: String, chatId: String) -> ActiveStream? { + guard let envelope: StreamEnvelope = load( + StreamEnvelope.self, + from: fileURL(kind: "streams", instanceId, chatId) + ), envelope.instanceId == instanceId, envelope.chatId == chatId else { + return nil + } + return envelope.stream + } + + func saveActiveStream(_ stream: ActiveStream, instanceId: String, chatId: String) throws { + try save( + StreamEnvelope(instanceId: instanceId, chatId: chatId, stream: stream), + to: fileURL(kind: "streams", instanceId, chatId) + ) + } + + func removeActiveStream(instanceId: String, chatId: String) { + try? fileManager.removeItem(at: fileURL(kind: "streams", instanceId, chatId)) + } + + func removeChat(instanceId: String, chatId: String) { + try? fileManager.removeItem(at: fileURL(kind: "chats", instanceId, chatId)) + removeActiveStream(instanceId: instanceId, chatId: chatId) + } + + private func fileURL(kind: String, _ parts: String...) -> URL { + let digest = SHA256.hash(data: Data(parts.joined(separator: "\u{1f}").utf8)) + let name = digest.map { String(format: "%02x", $0) }.joined() + return root + .appending(path: kind, directoryHint: .isDirectory) + .appending(path: "\(name).json") + } + + private func load(_ type: Value.Type, from url: URL) -> Value? { + guard let data = try? Data(contentsOf: url), + data.count <= maxCacheFileBytes else { return nil } + return try? JSONDecoder().decode(type, from: data) + } + + private func save(_ value: Value, to url: URL) throws { + let data = try JSONEncoder().encode(value) + guard data.count <= maxCacheFileBytes else { + throw CocoaError(.fileWriteOutOfSpace) + } + try fileManager.createDirectory( + at: url.deletingLastPathComponent(), + withIntermediateDirectories: true, + attributes: [.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication] + ) + try data.write(to: url, options: [.atomic, .completeFileProtectionUntilFirstUserAuthentication]) + } +} diff --git a/ios/AidenOnTheGo/Resources/AidenOnTheGo.entitlements b/ios/AidenOnTheGo/Resources/AidenOnTheGo.entitlements new file mode 100644 index 00000000..d9849a81 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/AidenOnTheGo.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + $(APP_GROUP_IDENTIFIER) + + + diff --git a/ios/AidenOnTheGo/Resources/AppIcon.icon/Assets/aiden-icon-june15.png b/ios/AidenOnTheGo/Resources/AppIcon.icon/Assets/aiden-icon-june15.png new file mode 100644 index 0000000000000000000000000000000000000000..6c4d3adc7e455947b3f35c19a8f60f7611f980c5 GIT binary patch literal 962574 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
    vTzh4 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
    kJg8ege1-A_*+3QX@_|24%#KMAK@4K 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

    $;Gri38-YJOXOnMjHwZ(UWlE^G5 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

    ZVrZszo7~V3iFrc~O(?ML;eY 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�gZv%WI7 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%0DSeUyJn+#ZWt#FNp`VDhviVxg< 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

      (i-T+3M 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 {-Lma< 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[Sd}WtLf#X5$uZ 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

      +^l)cPxL?aWZK2o38v% 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 z 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%0D5K#Ns(sXaGY-H!th$;qp5Aa*k|~c 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 U*2L$@+SLgzPVUDjN6ugWFS?T6S^xk5 literal 0 HcmV?d00001 diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..2d274073 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "aiden-icon-june15.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/AppIcon.appiconset/aiden-icon-june15.png b/ios/AidenOnTheGo/Resources/Assets.xcassets/AppIcon.appiconset/aiden-icon-june15.png new file mode 100644 index 0000000000000000000000000000000000000000..6c4d3adc7e455947b3f35c19a8f60f7611f980c5 GIT binary patch literal 962574 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
      kJg8ege1-A_*+3QX@_|24%#KMAK@4K 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

      $;Gri38-YJOXOnMjHwZ(UWlE^G5 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

      ZVrZszo7~V3iFrc~O(?ML;eY zY8*^>EyaD$MxF6;F)AoJ45h`AsU_=JD!PQaTiK>pqQc%fz7>@v+Q!idOdk0SW$Esy zL{lb_zKqnl$Ha(MT&9!50Z=5RjVe|$K4T(@gdDRano*gmX1ui%Uw4)Ew)r{5ke`3t zT9x+sRQ_Yna!F>VdlFa*=i>mQB=RHzj8YAt&^yovjgwS1_5#yJxzbT!a*ID8Dl>`j zF6a*lNw8G3NWLKu!DnS+$pR{5ST2*hg?`QCe3Ua4*auH$?*OWR znvXe%70pE0IFIUNvO7!g#v}9YdTH9n7WNe55As=vVmzsVY7SMAZ4-GRQ!t>Uni z^aq5qIaN6-Wk0!v3xgHkCdV@l;LxTvkylw(h#@@Qvxf=;VfIN{sMPQyvd!4t=mL@7 zGnY>mWyTCUW@%vNFC5!N6UZ`1aRY)ep7fPaj-C8j4UI0K&NVW-p=xaVq+l#%)Z>j3 zj}sEp??n;xP*)NZ>Z9gS-a;mxJzOc=f-cF*)AbsX>@sl#ScX#BlrfSVY9$|lWMP02 zytY%_+gXyt#H8JE4!*8LtoIdg&;I+O^W(l^I5(;?1dSR{8eqLks80$ng%1wnTY6Pucr8f`UTmB9OM2qW;^2r7((YkM+B z1A|Nmloaze&giZGgdOQQY}cetnKG53H4DHqK?Ch;Kd1y!R4Z*K=ah*S2yN8?_SDjF zwo3N-48ZUyU*`yfNP^@o+L0$fuJCSBHp@BD2?0}ecl@Gk#Llz3`g10L7|SX7aB2R( zD5fB~sQHXT%DfSA~N~s~$Yz z&&K2jMJ58Da(_{R_g3H+ebTf!pvEU_jq)#!A9vm~2g&YR3IpQS4GK;<8L`REo$AZ7 zw4`(o$%PPh($QRmmSiUz1S9#PTH<*1?gt<0GymeB{KLQhv&Z8jy6Wgvy12USOn4Ml zKlAwR)A4wfOJ2pYq^fyP*;Ybv=u{qOK(R=-g|V_2a4UB;F`nFl>_W}rhxl;du*F=f z(^@oZ*+!O;SX9!;5kqP09N2Dm-uVgV?KT<)S1wefXG&q(6pgdf44zD(D=AcKJjnPT zfPqieA4!Y?8l+hIHjZEmfT=j3@ZsDZ1O_Py-9&%3QB|B78xU@eB95oHZU0!kRgJpO)mwaEUF1&8KHp^z?Q|v9 zGjrKiVvH_`1o^vCo8FTTk&h&4dqx42@LcyK9deK^wYhievE_*Dz?^#aLPeog#tP3A z7Dd{Q%iVYw$RPgxk!p{l$_^9$pk_WNt#dsv4!n! z59P9=meF=>fJ-%^%DKXi8_*yTX7_^R8Yau*j!&-`n6fCynAnE%*OvO2bw@F}Zu1CWM)d?;O0CDn0XN6z4eD zByB=5F9JwIZl9GK+*Z*WdV{N!Eaqess$_Knr9k$LkW`h$Bp_@X#rK^|O17o*W6H*3 z^ltDq$XF8FKLv5tH@y8(7_{!Jae5dF@9W^IN|bX(bdcY`1_s&MMmW)M)OI?&DCbym z@Un+w@VJXQ44S?j7ViASW}_k!=y^d-T7@LGfMh3;8t%`>bf4inz{nZ$y)8M>a76@}zY5FIEN>mSD|a3sa|7D$Yd1ya0+OhzO2#$s0?`^mR0Rqa zH%oBDL`hIVS-47NtFlE3xqFW;oFTi6P8tvt*p^5VR7I68%hz|`{m?gl;NN`apa0#T z{>kI<6}}=bm9+Yrog2z3ujj{petq?|FI4%Wl9k(|MICZyH(iy@LNDRi6HCw~7PG7G z7Fd!j2vdR~P!@)TW|NkZl`vtDRY@>%r;Sjrm`vlnScm&<=)DIDU`{bjMGzJf&`r6W z@TP`@FKXxF7CSm>0}?Q~cu8E}vCZAwVXshuM8 zDpBejg6C4BWTPaMilqvTmsiGbFw+W3(=a&Y#ObV1cy~u|v2VA6>NIpox>HnhgW?)4 zT4m1$;dHr8Rwfoa!FBNH+Mk|vp`>j7QO?q1ysJe?toW=ulPnRaZXtv5$Vs+u!)PU;Vr! z)kF4b3H3#;D>Pl*SnknL>)rUqiY%zC`Xe8dDQrU#s1mwLeYnAlmV&}ifs{K93{_(6ky`R z#CPxZVa3*eOfDvG8*r{vmq#Oz&7=YC7}`vIlluNNHgSb`^VYJ3vxkts^?|!vfNt2O zdt8;W3`%fU{0BfRYg#yw5d%fsD!;zGkR9;y)PStvja*5P&R&MhJ(bF2CNfX2NGw>K zH@?6*(;lTd`Il}%PDT=Psce($v>72Oc|2Ajl|&nvgM_uRTP_x}6XGlzYH%M;Z$E!m z3R*Wc3muyh&{jF;;D);-yA+Bx!)}2v=YgU5lytfP6XhOHQxi+dZq|+0_eJ!mbwm=S zbYIG^zWU|w`i?*QE5G#FkN?p3KfiiMzE;?xab#;V7A3V3_*+Xtr95^6o%?|b*_1YOD%#{FNoA$Mc8n_g35}WLX6HK~$DkqxBPcMK zyWJwkJyoJkY6ODPVT-&eJu^vVW#9AMmKTKS)v_kG6|{-um+W$O2Ylj~#(h+d)K#2# z2x!O+9D_Q!JIb-()9rEdCV)nDge(;zy&=lej8}^`4MuY??l9z{RF6=EK*Y=dB@7Sm z(gvxVt0?zZFJfsKhZ-p)487Gjtc7IZ9mElCDw&1xWLH#aFkpsOq!;CP%Vil?4JETj zB(PMHQAx^OyebLgMqqj=js(rdT^G`_kYwTR!6_tVq!yh#kl^RU`N zO$k8mLRfk5CtDS&5_!SpaXept;QK%N-GAXP{>RV#>eJ)ns<#{#ct@QQSv;FlM{%o^ z=hzG=s{tasR-?qENJ2D{hjWpJle$|sS0mumB_qkU8D*IuDln5Q1-z7b0LGt7@qu7U zDv)RD3m9jYAcH|=xd4Gce!mP0jeD3$0jC=#0S`*kLrN2vID(vyDyQUo&IIEorb^)n z7{wb+e`V9^?wTiZi$@d4jI~Sp;6_VA)6jr$r z!VbFOPt<2d8cT|@3u5gah$A%^>yn0tIoKrvM%w94jx&c&%wi8xCIE5rHxB9sGDr94 zt}5C_RLQE!_|O#rZ@?RB$SC2s3{TBmp4)vK@0pQhL0g1Pb~ndbNzKT{PS_zSqzmSv zdNRRks&ON{jtmsqzc4_RhRJ}Ki;Xqn3n?A31j+QgeM5|Gq|?Jq!okq^p5=|Euzx&0 zgJuA2-SUhIDqFF)GT^zr9yB5x5DNUUFaTFb4yb{^8znWV9y|zBcC7^(NJK%Catu4A zNF?P%!*Lbc3kA{5M+nfF2xkKAFEx@92zJ+!Bor>>dfSPmZh%8V61Ed7&~8@M60r=D zi=ON|#dSAujz#zwLgMI-(n4MFe*${n0(4XNfglL_#8fqtCEJU{?Fp_RtHzKrFL<28 zJ#JIBY~HMlFu|*;?WMuWHYzxUxUn7SL&yPw!3ld~J|r;kMvkdTDj*!UEM|*mxjEMZ zv%@|kP~)Eatb&Y0wiU}gKgMF)OCmS?9P?gGRw1jqoh%`ko@JZEsUL&0LT$BRj#Z5c zhk1agm|!xG(V6jF(<>6H+y^l#x+#jA3zT!uBfcI>xpY(4C%t~>(a%~csQt?tE*XoiY%K?mC6JLj zuNn7}DmS^Vc1?(pWUNAEx_|~FV*`>=Q!Uyuc&pe1Wi7XNQjsEx>I|>=!i3@MS)YM{ z0HQd;e3p!cY)o)63Bt39M2dowY=q zlvrhD&1Rhoh)52QE|x86D^yXiFtT9ucMrirZZ8#$U|(5{LY)Mh<)!NwNWmU(ltHG( zm;kcs60~i(uyY>w3>K~|c?=$pZdSIb9pmjdFwFdA-jrhdD#u zAcF2MQmc_bsx@=PuLzaojq!4%1ddP>{;(Tt=cYRY zftX^(K+p()-~ohFyXPFwd_HjvDYA#Avv)2#J4q3$a*OPr%m!GvbPgDuKm_-h+L>BJ ze7lHyRGDGK%x}7K;+Z-Pk-MbbSRmb0&Dwg|h;Uiz9WBwRQ&jj2Rn+EL0hUU$96)NU zUTp^?p%E)SD^G|73!B1urUooo8sJq~E@E6ad}8V$B?tWu-LkVxR)V}4$KJQw5w$yiB@TNd?I*vcq`Hj z&)KbEw0-}7p)-gg#1z7vU>8uCNQh{ZlJg5TlUd}4nA<>CqE|^)TfpSnX08Zy8Bi$Y zWJPGkM1?^&JO^(pYZ9r-AxdC_Mq|U|0L;vQJc*sa7V_SUKG&v!G&9Y&C=sI=a3H!5 zDZrXZa!^sSu1~N29$H-@`KscfIG0tWUL zQ&cz83b8Q@e})lf=~5x758KYIhZO>&EP#EtF^_{oe4%3+7405-$*c;$14P=x5Jtna zjpvl29Yrk?x^Kva6T9EkdX9wMC>LYOBxDxI%vCX(B|5xRCwfNQYFH|h;1xRaa$TI+ zx@FnquJd>?L!1Cc(AA{~anLeaSm8&3Bpqt1n07=%(W1Jls#dlMiV9gOQC(f`dhX}z z^(xloaw+#gR&)x#RIBL-T=opZkFy{P^cT|M@R}`AhZiKI|6m zgYI@qHYL-hj3|SbN6?8LhiuSFdLfc~)e0>fM)^Zg1mgY@GzdSK^e83zAcW*-T8via z&LlMq*FFirNTMw#>jJs2vzbkd3Q7_*t)@}soSoEdrLGZetibKL@Nyy<4KcSY(WCn# z)Il#_!3|<0(YN3&G!`PAI_+sSNRlm;!R11hEH7J@X}fS!RxmR#A!?`^{6JY9dGgKR#}4(Sv64IqI^5}7__3wZnj041MNA3iB}lEkXT z2*IFS=mIQo3exQ2!#F^(xt^neJqPKQ=l_E5jf%T+V`+YpLl}3sJ)|anNupUrfvuLi zlaJ4%Mwei*s$eC-l5JIZv3W}z)}XV|o~9`h&fp;}b|k5?IF=+W6a~<@7ElyWgb^7W zcxfcHw{nkw$?Gu&Ro(Q!K#&RK9Xv#B>sBUp{$2ygMdQWuI)(ZG_$| zd;F&M&0?~G%)A0~_M9q#x1i$_!~g&w07*naRIO^pkd|>0=`z*U7`d?l8k1!o(zaE= zAvOB7Y0GCN0UBYAJ%$G7`d!9`-)IljfD`B_55}cJ)@I!L;M;#?QwTN@+}MzSxjj*W zgg*^~B=V$!D-RtHc9zSCEM*3xT`UasvXTNT5gZMBWyr-gCFgChDNs@I8e2|ZSEkOx z6y4Z1m77Bl9LMgH)lu3GeQ;4&Phq?_L=JOtd=R*8T&r|SIn zeDAsy<126!clZpsW&DPsYs4!Z)33Cz>B~Jh08tr%j0^SHi`28Yc6gu>A0bzU(zwBp z=tehb=4&U-4WfmTvP!B>-)>4-3`7?M5PK6*p@FK=A4USnMa)*Sb9lZygHB{#me6Z% zOq}oDJ@WL0b4bLPB6oBXRR*+)D>5DZQXRHTwHiPQFT*vA?4s-v_*m}D11v015^RgG z&hceYB9cH#xo^XkIj)uk2@t=R=D_{A_x?CKo7)j(73McEyNN)I&ISc{mm z!%TuI3hsBMIc1;5f(bs8H0=N97v)flH{|cGRA} zl*R{v3~g8_5Bx*Nw+Lfd*_U-MpR1Wr7nQxkH0AzyPNX?f{=Ybms|?EmXgBxGh#{KqLk|+J_l4N{zsl)FC1n%_d=F zkYIpgctc5gMGSE__w{Eq-Xno3MF4Fi9HpwTZ*x2IYEJIHax638gq)7j)Z7MWG)*TC zR_Zb?-5O`tsE|wK1(Py`pp3n@!s+gA?^q-$nL`7eiO{M6eTTF5jJK+NqOrCiq9X<6 zodts`&M)68fIu~LdP@*cazqu9VoDhg*bIL%=Z5PWAHa6i3L^#OmRmf3?aP1slRy4r zzxdgI`@vQ1>$2CSE{nG65@9Twr*~dIzj`jO#Ra1>47ZK#Tz`q5)h6B~1VUGd9^ID8 zc8yn7fW<-`x8rS!Gl`%N6nSF3Y?^tuPG-V=)Z8}0XPmSX>T?KsSQO~J&Rt_vO| z8eJH=0SP%eS}})VTy!T*(yb%rrqKcos|}dUO%u1!)_oEI@M6(}(B6Pap9#k>H%O4# zAZr^$_NPH~GW*(6FG4$DW^X~1?aB>j))CnVQ7y`K1l2|YH$i?7mJF7>8a8jCs*r8v z>M*KsTwocM%XW*^Qc=na4~&E=Ze?9Ah}CN9>FKH64{u$B4qH{KVWBt*W09-uZGx#? z*hj1C`Ss(6|ID|3>XSeIoB#E{{Qe*O;pMFd$*7OxP&K|dX};Z1YFR3l+fF81jO(LZ zDyw`EYMR2Pa8aekWl9?F9~S8}qFv&ed`K0BXzQ35EkXfsKkC#8K#jG8kfVAQxl|u1 z6rj08gpSC!iRMS&1F9sZMk9on-VG%Si+I}6%pj4IH>&SY2JBdcL1cp%1MN1df+ey? zVeBJ8$B;W3huF#`La6;7eyPA!>a2Vus>`z^h-q#isKj0s+X%@@>LB7CrfeqcP@ksF zt4%Z_`jB2en|FtO5^Dt2+$9G}gRs^L`#{Ly5ySNKGRQ`XX^?L~O81Z_DF|yVSsu{| z@BloEfN_c?l%=bh{3rKcpvJkBI0FiLA!*$FC}~A7hA5jXgQ_mJRaBL`RIQ_nDx6IT zDhZBG>^2Ygj2hyAaO*s|Yk1GH`4=r3m;m@(=NO_h_tGEvy*xJ2u1WJkHjV*l`bkmi zF{>fLph5^IsZEQgdHUVv%<87gwPKNy|Kgy-iS9%xgV z0~MmM2dPbAO{RVV2&V(2fStC#QDP&Tm@OK!M{leJLd?Da0EL_#0x4XHsaWAYfNZZ1 zy#0|)Tz~|TvRg`O8)v1`!ZK}7joCRa#->`fnA~i-pu}*Q-s$K`)0@XrYK*^WSWqe} zsO&7Orqjs_V0bfJN#j&-aw^hF_nWP$#oW#;AS?6W596I{v@3I(1FTu@D~{YO3Q2t_ zt=NRsiIEs;$p|Y6=%702I0)qQ4(-;izZs3S&u%S}lv{d&x5cm|UB>Dh-XP%yNWG1b z8D$`Yk!)kj-bhIXXSY^1@`ZlqPrmdsKlKy;>NB5yxUA(an#JXky9c^ha&hg~@4WW2 z%Ubm?Y}v25s2bk1uTOBtn~2~`aHwvr7lNo#^q z{%<{s3Z5{cl))gCGAigBu+7sv1`$A6X5A;EC8ZH3 zg3kuuLyTCjSEZbaroBR;+VN2yC zrw#bBMH`m;!a{A!eeI*K=5l#!t&-$C1!_x$WXd9!nl$=Ytz}mkEj>Ryed{;9{fQs> z#DD+o-}&MfzxdX}YO7YYtGh3y=5R&j!^(!PQgg9ao<1#07o%=tGl@S!)T*ny7EG4m zrnZlnure=~lTb%rs$?Z=Y81^tfrv!Btk_ya6cQ+w6)_>ZOrEAKORAP^dPS84$;(DZ z9Lps6e0UC$6+0RW80Cv-0!Ivxo35X5_qjxL%%kth$QeDHJA_1@VZ~C`tVLo7)GBvg z)24Bk!6rs(RW_ec%Pl!pwNXp>l;WVMrafS~DJe?=Ni7T1H`=thKB&A=l-*o`WS?Ee zNo2LI5Gw`&LE9a_7G5ghyvjb5@GAsX_Ffyg_U<@J`y9)SJ1h8^2rrA(0_-e0+-TS~ zq7>m?rP?>B1BKo>Rjdjzj>lAk8dfpa8SSc-6ktFXF(FypyvGD@M8Q>=c1ROarUo$Y z$@4eM*=Hu6v#pi8Ibj3{mmpIQ(J$xy|NKdS<)~euY@=;7_l&_j0W9W(4N_&-bM7_G zvIme7j0xO;U;`^z(Qb(4Z~PBN_olaqgT2vF)j>0uo4NoLLeUQZ zeBW1_d-0u<(}-w;YGL^b$De-bkN?ik{OzCn*Z*?S%Za0gB0{@oRUo=3cY!;%{$wsIkq4lJk^zLSp{`>8A#PKu z^MkRlAQZ<6WWQgtVD~odzzMtE)Rt)lYB!=WPG&9JVZ6y_j01xzGTL1w! zmGyuaWt}!Bq|A`{Fd7$5_)J#EYL#q_14@Ts6ocE00!c{&MUb*9I!)ohcd$TMjA89+ z5HCFNS&D<)jU%X@J!Z%qk60#ot3W<)r3BMk>Fn9^PYCr0+(p?4V;nDNg;Nf-VNECj z+^S({#ji;Ubv{YhRNP3j?THM7z&XxQ1Q5*Sw=oPGNwG~8Ak;XDi^D$H!YjpnykO$Y z1%WeFU?@SE$@tS)f-Q!@E`wp4m~WnY$^hPZOJ2u9Q|h*m@~cw9mQiTQY9uJw!l(+% z*p?O?*#;uW8iA872cfT1p!+ha_+E{H8q1juCXoT#h>~`5%8k`CTB$sXL5^0ZbsA%p zZ}j#Zl=C%ho0{EvqLfy_@YzVrL@BAxW5Xu`VZg1|3;27|Y1tAS&g@#oxgr!@2MQ50 zvQ)LYyVgXaGtB8t?$`#jLjZt%w;H__Lr~fl$5h-Ohet{3Bqh)zvUoiEWSEgEUTolO z{k>xXC|nmpZfE{d*}g3R1Xxg^CA21njN3W&N=3%Yl;UnOn{cGtE}@M>fjj7)=gOSJ ziPP{DJ5nr-%5+1HAa3p??N)S`5GeT$oiuzZvRc_YfZf>kth{UFIL;AvI4KIEB;|P* z4HjCf7#BnnnT-vHas_=QRHtY@R%`Zr`!?Ey2NGQJ#Xp@KSgeG|$ zJ+YfUErQtYco-zNaqT-wOQk|8oZNeJcH|^8k_rL{A$NDp)>OYGzxtCe|Gl67Tc7## zr`3nt3mzmd+f)g$TE|nre)U|f2d}y$8M124ZWRhA1?sxUZH{W&Wzn>pG%frJeiF$( zL>Jx6yNsO6%Yt(iAcrR=oW8Y16$4tE$g}KuQf0Wauam{z$4dJ)@8o1Q2T>UzlYof= zDxhOM3Sfg~8Y?I`@kC~pp9=SFue#Vt?q{ifhdvCzvpQJ9pe#dW87pj$GpPWd%8Zku zj@yXOPf2OF;#vlEWfl?nk_KsWIyj`9IN#N5wuXR!1MYTs?(oO}-ykX0GpXf`1zVyJ z<0^=Oq+S7IkGII*fHu;oD_F{U=mf^>_jA+Jl<@hk%5i}e-i1gIg-XZ>tMkZ!a&@s# zUZ~f7v`VtO;rTeOhosBp0?f@iSg5VssW^EBhs8P!B@W8+<>`3*#`nMf;~)RnZ~u?q z{ev%lu`Um~EU7XlMoV;;RpP3!%Oss7UQ@X(s=R6tUE7tltZtVsR+ZLh5(+tzqM+-tM{A6;+vbK8=gcm1AGYoB(XZg+!{HnI>npQam<4o$%sAgqE$7)m5SSz@s*B!HpIaC`MJwLm>796Et$YU7xuk|1%R zHuUHLLW~SGt~7X3GB`rs%pdNa;|>%sF%d3I;GtKL^1KP-{458`d<$#*&2}Wcw-_lAZZCEeRIhWbF`G@T6u`Yc#(V zH6f*>Gd~aqY7If9pibXI$t*P+a>xkOw2`1psZchNYW5d0QNf6ykhMy7Q?$TVKKNhJ zHqy)iLvz?xhzKQ;f>eq9PdU>_?n|0sB}*d}Ig7`*J3%ih!5dUnS-+9Bn}l#1*v3X$ zCGrRlgVi)Nt#i z&;6U5^9qAJWMu#WwyGf8camgW#8N}t30|=@2w~C6lT9>A-Xjfyq3O-M3x}?I)nVmU zxyPBx-Eto%q5y>&kbF;7KuIAodu((uw@qqV6s$s4Ekg~!u=Ap$WLrrZ_?^#XcWXsRl>Sq01& zAu(&DCdY@CY=+Jwm65>w7ZKjL$A|DC<}f1xVmWcpKYvm-bW@ zG6^=6#5#h8B$}X^nH*xR@pLehfy(4MD)JbE6y^j0M`Wf`9419HpdG-+9#mC!nJdxT zgQJLoq#+tB6I;4%vZ%u($%LHe22AB{Hu4!8?H8$yq)}pZIw9D?WOfpmEwju@NGNLy zJLf+t-NN%B0AySf)++P|EI<)dx-t@kneC#YH4yGnxiI9+W0i1ml8TL443!v0jf41* z^%OzOGOz&RSKaZbIbGSa;=0Y-gjustngi#nCPLXyMBH2men3MxRfeG-SX5Vh-x!04S zb_sK-kP+!|W9p2uYc_sG9jSVHwSv&oCBdZA28l%o(-_%+w6m#5ZOCD&=I$^8P(y%3 zJXTN8ln`VCak3#S_sY_G+(}T9+mMG#L*o|2@DfNkh_t@)qu)&Zus$EoQ6q-0h^fM( zZON7D(b*&cin(V@d6@6JGBW-gV<2(nZ5!nLM}>@?Zsyxx1fu5jwol?;Vm2o|CETV8zzGS((@~zB3+dL1%v-yIC zNn^s#t!!6$+i*uQEeGJ-Z>dR;kWT`O23={;ZaN}{j3x-9X#2Ye0>>)ec#OmjVtNxJ z&m7@ByPc+6*`d`^B#a^E!gHowW3DUlWrAHHPIje4po|%bYEv~35CKTT*)BJbx73|m z6Puq%cA$6Xv+yBe&{NKOx(Yr7A(wtFHuU2+G7ORT|6s?&dunVB5t7Uhs;=;mS z_zE8(c>)Y>86tbZ@e~;AvG=*}5KkzXO*ma+rDe|tDtQ!8rS5KN?{}1Tm#kwY$nyy= zQHau+CQ45t2%%ER-M0qE9X09Ajk`9Lr^M-$c9syzWM&CT1?5uXp+B>~ zoP$OiKM2`I%F|sW$&@NXfP@sRZC|C8Xf)$T{$esDj2QLc&ACw2Mn(jYNC{FI6;*l& zRn{}p%ju9mBiXp5jy8|`h7-n5i*H6Jb=?S@)HyP)bRq$FK~sm<*1xG+84u zG?7$b7Z08NFKQ12T1wAx3~}rgvP(l~k9&$xcqT!!0g$q*gK@%puXvluPH9zHS9A~x zL%kGUdhpAZB?~L-2SGHIuEa7YWH3G*E67zdy4j{LC3uk`^JgJ>BK1z@kP()Nz~pR_ zZU?ln10XD@q;OVlrC^Z8s0dZsPv!(r*=SpiZIogpA_$b7vU7(_8C!MYMFI7JooSYd z5E+6n3D<_u?aIaTFPymS$UZbm8iD3M)WI}^#MlAkt2@`1&u0Jve%fOO(;>A9r;TnS z8KeZE(7;G+#F6yih86hywmZa2Q!=|TTqspB_-arr#rYm;foCC*k{M}J9F3DieHskj zO@RwVK2+x-w$p?qgNQVZa@|Bm2I5MUjw+DyFkFv#7xXy!^b*3(*NQ0T2KR3}%m5lW zO5w5Mw|8e|=n1&DFru)Cdm5jw zf|(U|zx{R)y|M6(WOq>^#>Utv_c+=OgDEAmFyj28oK5Hrz>F*))-mM)Q__p(KK?$6sF#-1k7Jjo$e56|Ft2bfQT=AX6oDQr=y)yD1X z^(y5@t59VxXuP^0d)FPJ8iWcL59#%@Pk;2QU-`~={?c##*8lX6|MBmv$48w{Cby|l z8#S#ZvWY4KZY7BZO(d>LkdjdXnS1BnnIs^LKv**ni@OQlK*TD=3Xx<{WmuGyoNZyV z1o<)?#s5M1lb!TEqK$@K%JYqCB1pO_HUJ1kIkR{Gl?){fvlihmROR4l87jy5)2nbM znayTs;>Kx6Y~v9F(#$Eavq*qQE@rV^a`TAoQbk6DB!Lc8-c-U4JwrOXC^V)Q<6e=6 z7^wjmSriJr6SDSHEl~z%t$>LlORLoq(pei>Lzh98<^foQCk0tj+(a3|1<4?5ZVv}@ zAbI1aS2&_luj zNVOm$3=xR<61fGCM$4kKqtTX34UTz};{DFVUa+Y@0x^)><`{8ExXrd86^M!hAmuR? zj0tQF4)pfN3_X<)d07%k)&;TO2p$neXB8BI!p830+euw;YMhlGcoe6|L!;c&|CDpP zFEUQq2F_2p)gF+^-zWmicLzU#sHQKV%{wpN(6er+EX+h^Z%Ycb^d#vIz#ceao zPn~SYQ^cEg+;YZi^uR{mqQg+~$Z#9$YAC2LmF zeB0Uc(L{Q{(gTjh3-VB2^6bVNMY9JjAW5Q^6L+fyWFLd?Z4+{PR`H44FC$YD$XQ8o!J z<#lzxUe~HsOIBsy2$ou1&|DNqSOz_ctfZErU8|E@Q^^tqNXp418!e;4a!HrUnvIwR$@k*n0!tR1 zxy++|+l5#ZZIPoWX9wCQpIPrxOtA*tkrs5hBR-LcCzYH#nQQVMC_NBY9&xy}DszYo zDmq(IrA$q&*)(O0C8iNnGxT|z3IJJw61bf0V+^IpkHjlgw*IqXag5OrSD5}At%R{c zgSHEX*#HE16OnS708OZg;AP-KI!uU3a#eW*Ajwcrw(_(livW6bUX3f=6+@O`W**67 z05pVpMUtBJ4<<94m_QGN3Fm8b0Z&V~-GY#+WJ_hCd6XSglY$P{9ql5NPhnWv3?q$H zVvjz<=K{tK0ZHR~65P1OpdyE0AUu0M@sTHE0+kn1t4wS-GxA0bvxgcph=Ru;LW9g9 zJ>*s>l`|`%JY2^SMCfg0DjimbB~g7@lx!KPtmc|$XBjfkn;w8Xvs1}dU|>%v0JnWx zZ=wRC8C8aoN~)svN=TwqrQM_yN-CVrHF`vZRUs+s&{DolY7eoMBzH81Vs9uyLXzwl zXpKU~Mi4G_)21oNWauER%ff$hH!!IB?oPa1sUxFEw`9yXX1Dg9|YbZ%@~6V&4P^1 z*yl1!QZ)%8WqM2}v1W5~Py=KC`}TukrfJCf5Nr&=!2>b>83rffxZ>i%y|UF*WqYwA zHA;zSo1+FHx;{IGS|43vAd0OILHWJYoDC3voKJBYAt8qN1%6W z?W}mtbO=lBc~EI9;x4d#Ol;gz;+V=2q;1LM%QJ(rOW0Hk>3k&E#L*Im$1M?ZOR;MJ zA+Qqi&M0c-@r6M-8{rDU5dc7sJGL?+$=C|Q$$-(>x1Ig4+D+OCC@9)U@C!CQ831Ag z<8SHn`63d3oOR1cVfR0ROyyfF!xuu@)ML$|;F4@=PzapKF3%q3ELNhBMOk!Gg{0d^ z`9NGtZY$>xy3m^8^Vq6$ZR~CI%J6&dP202pJTKcnN=3fE*6Ara63=(lifpHi%{iII zkwY9V$P#FG+gnj>4a_)^+UU)mdV?fsch&%djEKQbixD$zSm1~X$RjMfAHozJ$~T>( zun&v0b=~CToDi`oBQX!AWCM7(Xy^U?M zgXhTP*w5}4b3^si>a^t5$vvH_@NsJoU(*K2+g5KO*h8&>r>mw3ZM`_+X;9)YwiLC@ zAVYt3o-PSXwrvq*&w&CM^&zQ7RBi8}=513^662ncLLuWx`*5@tLg$U6`lsBoXz)P` z>GAezcpp^rLtv?NRXKYyE{M;eXn;mg;(}6s20}o~D+sH$+d2aj$XLlUu}KIGSF;F) z_(G%cRv~ejd=&H!;NHHrS3dEw_MQFtT>2JY*n{#j?JrUNVa9QFM1-ILw&2` zjcQd5agC~2MH}3Wx(k(Y0oD1+1PirL%j>GPumnQok9haV_kQo^e)=!`$}j!Wm)=#s zysqlCmQbOGs;KKTfA+~|YD$l#LT5I&)_Bm?THH%5B?ydq)JXZw`%#Pl9JRzE1xDj} z12@M#2g{VGV!P08^J(3Bd)rO>MKI>@K{-|lI^MXId*EXSY-5uWM;$?=mxsn&ZIit7 zu`wR1cipK8cek!{m-azqfNW-vHj%g|RVL=C2MUlfwMWbwL&z0IM7a)u^EOI*vUq0& z(1{uV0>^p z*~=gK=f3{!Kl|;!{~Q19_y6Ggk9Y4HBMQ05BrQ6LzjKyeSZYB&ARz4d7FCeA+WEOM zB-sc{OLDv5Q7l}DYUgW@w3!yvQoC7s7ND^|D3+PowlCKzX9*(O*i?y}MploC3zGR4 zE7P$6WECo1RlWpv)dJ|E8akrKL=&@%O@jv^C6Au4DK2ddE7WScR%a`7s})Z00)(n? zvS8ED7IDS8(g*f^q0VnRp~gGQ!6wTT(|OTIm)~)v#lDAUbxQ zq3X&&Z-tOwnvqGiB12h_yf7UE7u#;79Xp?dW(0wxEIBj8QOZDUpt;zsjsrlL%@Apo zA!d2|ap&C#1a}|=v;@uzfLa3DDkneoZpuE|c$!2G<%Mj`qQj8Gh9kt(wlkWl?3RX< zVO#+^WiIo!>(i%4f?Gg0(3{OXy9!ue`P#Rfo#2jqD#mGsk(9{3XU}9JgXqexxIi;_ z-a1A+raVYaD-66gXxzp^`=>Wzl-pS46|ID#!$#hQnR9=C-vG}psrUeerPyGGc{XgI zcK@Zc@G%xHN||Y(DzoSlt-w2;7qWUzxd~ZRXYCK7OvG4-UaBn3Y?S#! zD#Zi^Mj$*X!eXbz4c(T;m3l!hj7lYJN_2b6ra^mtFPVq<{_p<3Kl@XE{=fXS|Kj5> zebm>>yN4dNN_wYyS0&Z^AAa`o!_OXiS6Y?C-<~C~z_I*rT@HY@G&W&@80#y6C*W#Y z;~KjwCkL%cZgU3OS`NCo;S9@mw0{U_2_%FaoDo*C)z^b1a^j`bP;1e z&v69oNcp)UW(>|iyZQz{Qp~s#k^=0+ETg1RLy0yD0NZJma9br}-%{LNiXt=}fRVEy zzz10xPua?NQwH6}`L;6=;TQEwujw9^4-uLc-Q*}3M;q}F5o!o#eEm7neN1J9RNZjS z)e!H&nYL5H_r2kX>Ip@74O^$2Ax4SqIK4BH1&_5<_Qhw{tLZO2zC`z{BrM74Au0hR zT}#z8;98~kKm6p!e(aC_xj*->|N7tiyWju*57y(OzKoVu(x{3FmrBZ);JIpvN(8ek zs*GE0EUg7CmDJgO9V72-Fvc~A-;T)q+_;oVbV-oJI2#xdM^tE4<$Me) zkiGMU@Z1t@If}+8R$$qxWI`5dh5CZFjVS0@ydj`Rmb3c{hMGAIZ(b~Xgat&JLdSXX zAu_~KtOuEGOCH;aqyqL7l+Fu^kkH^ZH62hw`j$4JO_xRqccKL&zbI*jKe30bwL#ge zF$im)EN7S5vlp_oH5l<&D47*|fukKJz&2&KONFn|i5~F|fWAs~VH-Qo7X`R*hs)Aa zHNt^3RTXJKGgHefXh(vN>={+I*Rp~Zed6L>pAGN zsfa1SsB?rVfMj7}3Ae}RHgw3yOl?R!vL|nmne?!f)qP1-lNiUOARq>l_U)W~6^A&Qa{5FDJr6sm4|5A8Ed)bP3TVO9Fy<@kIL?V9E^IZ`(v6{Is6 zhFj$uGBC9;l)=S95%y8*a_cDR@SuuvR5j|_uqyzB;u|li~K+3LC2Hu1Wl6^I1EH>=!j?jeKTji|? zPF&0NcB^Q7u9RrJi7m~G2lWN@5|J0JV{ zUU!P5#NBDV0lk&R2fA=XGQRVaPg?MS-yKG2yZ6Svb@qhx9xE2)pu7Q`kp%jnYS-Ux z2H#Z9NJkeL=S?5`Mi7vYz)qGhl0#Fsfmi!#Y1LY#t0lqe$C8*Sxh$1m0i)8R45l9I z{ipAL{KvlWlRx>*|N8Iz{g=KTAH91;WmDyD3YEQz%O#fT6-)UD3Cgs!0L!YvHrVb& zeo?ZTE2D)RWxhNNd$R+I(M|*0ln92BR1($r0bsH4MGi*} zWvg21l41H(by->2OsGjvozDR@6@Bi_y+he%=U7bcHR=YC*iHqKMTCgR;=-sfn#sdd z@{k56Di$(*cZ)>wtioglooD2^WeYLJdUqCsBqS_kWv3?J22;#)B!OutK~pkVjZo$$ zmMk#R%=dZFEG>G+bA+qjNoqCPUo%aQWB}BSbU~pUvyN8b$#@ux1L@{!ETRyx85$qmR^S(_W<9Q(SQc_{s#wrS{efffg zgvx4HR4|ei66<BOYNZu#7<-Jz zjzpCBYHY125CrlwM+~BqY6};}9`muZC1VW&8Ljx80Ne%Rh0~OTWdB%LZPIEC4~fRk%+jb>}4Wd zpc3~aDJH~705m3(2p}=6PjdjJ13x9qiO`r~*2iD_Nn~9m9lXyADC3>Hh7(hYM1oWd z*QK*)!5~LKgJ_^5^U;}~3;{9@Ug=FllhE!0%!uB|otnc|IC)cY`?m58Z3n=x=^1>} z8iOUIluCN-`6UoVG@Igqi%#b&@vKMT0=XO`3bGOsCG||^#Rxs&)PPzntLN)cYyWCR zbT1(mPAaY;JiBX!6H#qPRoU=TQO=L&7NTDbD!Ep`;3Fq0@Zm_V}D@JVz zjx-16t;3HLlKN-+e*n`PJq;N|<|ZHti?)ZfJ;WIEt%Sr-8g5EQ z@p6qSZ$|L;>W1-yqEHn8OH7WGwl|!l+&Pudj=IyQz&2HiD)S_-@zO&!)XU3ziFY5p zD`-=ty&DBc#_FYdbh#~69QILudH?<={`kN2$Nt2h_`84S*I&CI@7^UmTNZ#QR;#2- zl*;95=@OEwsztdXLp!6DK=IZvWNMY9DwKt1#nz}mAe6*AY_mmj?q@Yy(9pHYxEm@5 zQlVsvnouN-1bewyVy$J1zCxBQ=a?nVx?~TU^dw1QFb7v@8+Cr@3>XRv1TMJz1-ebi zHq998h$}(Pb_Pq^L^+2Rh&s}(4pfRE#u_1n%3wJ#;q34yg%3DJs!UkOu{U+uMo4%o zC%D>RS3O7|SW6fCeCa^FlS?IEoiA*v?n^>6)nCbP@p#I3|NyHkP0yquCSibHo{3(A~<`KQ<+brMOtp-Q6+Qsz$Ji z{=0oVRG+I(^$t~OP7&b4fWvhlT7wvWT>>eg^isF4os*^|6JzwFl^Jd zCzmKeOIh=YIFLH*>wcn4<9Ng(mDdXjUvZAAQZ%$voBWADhQ*m)5kf*%o2WSCMF^vg zQ%VwA%=Z^g)7^6J(46Yv+HLZ{o%b<8(7u`;Wmq47{H?`2jgW`KPOt&65zGk}W7EnK zAbf-@v7`pnOh=xy&F$2TXR(P^w3(~(I@JPE-bx0>@^>KLM5M2IQ?RPUWqn&#lV`?Wy_vxD2bOC8j?L8hp2I2lU`F z2tR=oLp&9EGZ0Za-_^2`HWa8 z)jI;4Y|=C?nIe;-K-asqK7IfGPyVSt{*7<^k-zurzo8}8QiH0rC{>q%wO-=q0vC!Q z(OE#MiVLaGvBR8$0TaV3V|U^vF4qp9drPEqz%r9Sg+IWF8|yKNy5+1t6ob4#Dw|7M zMvF?0*dP|H*=A~CtZJL81~2H@wD9FBgxe})Y_f`8-9$+vf57dL0$|#6`BW^M0JhyY ztZlo{N`PM_T zTFfJ611_r21(vbPD!K&}Z5EBxb|WnrFohai1<`t@i*cEXpSeXF3S|OhNyMN4Y(SI0 zjN;NnsM>@JSxHjznKxl&QP_}#LIDFE{e>(d-n@Csd5R*TsSwJtBPKby_gk2aacZYX zR<_1V)s)3iQ$UT>qOFB5)@&Xd;wv%kl0@Zef*TRbV6UfgHyUqd{aT6)O*39!g}Z%~ z${uITcXsX4`7qd`Z5vvW$mk-j5TS>N02sw-vsoYE%!RNkBgD=M%62PlSjwNI*i{cU z3AKO?904O+tpM0TD>Yjp(1|)=yJfbEB?{OeXQd0a=cCb0hHzH~b(fh(Ud@t(7LuqP zqy$(%X$kU7OsI(5&Yufq-6=2@iKk)!($XaOhOzqRj zBt4J#rs@nMAU2`79q$h0CN;!syMCab97#s2Y-3}D3?=o>RI}SaLQUC2m2I3!x4;;S zSDf$gF)rkXhqt1jO8LGmK$K`ywB!q9Q_BTQ1>WW?v}jY%vgYG16^0{22N7NA*~@aa z|3Ou%9BINYpZws*zwx!d{j0zHr~c$m^k+X@xT=K%!z=GUdEv6UWHD!Du~m}kj5YcT z7m}p0p(Fm$fIR;hE009S?jY45#ZD4TkVr-}KoZWv0HmkJfy^F&snW54TZsZ<3-`v< zSWn`f?SYStBpQXMjeTBDmxFcN)V{wL0mpXdDvT(#+C*i(G<#$t+4($8L_K zB(^bm6AGZLYBQ#Ut*pI<6{hV*|3L(B6Z}(T#A`O#;pMzO`{z0~9sZ}*@bqiPJl>%A zdK15r2nP}h?UKZ8#L+j>S_-*=ZjLKvhIfgdVXoq41UPGe;+m$pAuPte{5VG06B+_H`qRdr1H^*b_9LCBQ*S zhR=s4a=jy%+*$Hf;t8tSX!O8X;Lbin9tSOBa%|s{EkcIJ);d{K8^KaHBDs&)VJdH> zRT%6XM>p}s89(Hw7$mpzSb`4AAkkQYD`$g1NX|k}F)|XO!A)&Zo@4+aSER?#dg$pxkg`*x%)C_uHs226M{#>W_Xa)i50;A(=};_M}Ux zO{WYc5OI6#?YO%ra53TOb{l(NoVF)eC^pf(Z^Lfs`WjS-vDKu$yXG zwyD*oCe&nxZ^~*{=Dc}9KqIj}78NF<>AXsS%rtFr zQjjSm$xRb5NkVXRdTeYP$-cI1R~06cbeuWkO_#iFRJXdAQU)2C(r_pq=so7s8sA#q z6Y;sf&l$J!rZ(;&W*MX#0TNl>42@EzSO6{?sj9%WAtSOo`p8P-DtSM0obbAba?Q_0 zPVu}PdbcZkbDP(ZgP3$Kx4U&No;cp{e^Bwf*jP6+2}Ey4-sK5}NotXxmSzomNLvJg zcnFh)4SmwH-x~zEybU|8y9EHdq-vI- zE=#njCCzR)6kwAtHn5W{%J_r>B#ENkYO2tZFdd(waT*kUO1}h{9T(gT8tnN4)+1_T81YJ@Kx2jgO z7>=`kla*~sdY_0w!fkPtC=hY2BP35hVJt1}Jy690@@7R=P1e&GB|@NxRK%a83S($& z+cr&kLd;XC#7>kqfHEk`0L{L1)G|O4m{kuDj=5n_LW^ap92^D8LjV#`7>kV*GzbM{ zv$s_Qj8z2lkdrbb&)pWFg{W-yA0sBz28{wK;Ty~W92C3mdG$p_!20+r-;{J54tU-P zG(&-*wv)ZZDXb%WjTG9} zQY!!eAOJ~3K~%FwF!?~BQ#FGd5Vb}86p;|&Sb2Vlr4l+&Fxfv)Qp;84?tLs4L=|q9 z-0ij8l985+d{ddE(nYqoWJOIXky9Qm3naa?}KGTa=t72;*znV zx>-sFfOu1+9D=q~J-#|c64@>SaHLQnHhpik5hrR_YC|X3eG6^iTOE1t_J^UKy2iL?IsEnEzbQx@_au{6Ha84 ziRcb>5C(kL={YYv%S~)=hB-K%blkfhw843rhWvZY9slNQt4;YYA&ENu6d@@U8LV)2 zB$S!d=H>O%sax|K$Jr?SJ&Yt&cu(JuFN6 zx#g;IzKFkNWg`+5`%yetmXk@Sz}dYvn6`aR&r8|i6$u+sHVXvt`5Xp9F=YW&I==&0 zl&oU>{^nSsEM1~o)kV$2ZX<-0+1zE>1sKkcR#n?H6fGB0m4wkMCC947h9Ik|<9ie$UppA42$dw{f(d0eo)FlH zWD5(M+LJ39*7r9)eP~FK<4UwP^%>x|dW!hz5x^bcE?#c&* zO=-{CW>T^V1Bds%bv&YD!Is?>yp&;_`AlCAiH8LtcTiA6^u z;S-S!k&tmn8ah5Frw#b#KD|DyO<7<5(Qg(SO;tt?Aw2|4cV$4xHq;QYnb*yL_5}eu z)bG|OJ*R%GQ$RIm5((~v7os|d%MEb@wNY*=#xg(P+&Z!}c?rOm!H&tzy!DUL7KZpVbCiVm%y^~Rc&!N)kv>iflTBG3em89^+#)|sO$B5|Goe4Kltl^{onsd%o0Yjf-CCKpe>wySM*>C zyYle8i%@AM>GU@!bKVVzP0JZ0*bh*89~xp4zE|8Rgpfa7GSnV$EarB{?xqZUtDh2^ z-ckdG*T5+6hB$7@p2(~2Q#xQtRdL2$wu7;qImCxfMN5Z$qtX`hre`-D4s`GYeeM8dhYi^wEtt;kstA;(Q=Sw&0>-uwPX$3QxO>WJes-7eFR zTN$a`&4rv0XCZ-96H<|u@)KN75rV+ZcC`~6j&j6A3q6Mwc1e^hmCmykSyw5Gva7j- zHS<|b#~`+qqxbb7d3|@UOjn{PFOX?@2}vc&vycNRWZ6^+PiB%4r1BLml!^=-N1H@W z6ml;l8fc8XKPwwlMD$7i3UoB#t6P$+wBF|b8F7e887%z9x(ASp@@cqht2H%OBaDM-jNpR5PQyA?2(Y723tlJGu-L)fc^MK zYtr!r5|(jd$au}B4A9nfX;a!@X`wuPoB_7@*gBllsN8gRPh(?Y8Vd=YT*0aS%5MBnToBL;{3z10olo#38nj0V8q6e*~D2b^sZ; z;)Wm-IlyI#c04`hnwtSC~p6Tsg0I=2?^|A5`*7U4Kjx2bQ7#G z)(zpNeHk(PC5ee^NwR9?K)oa=X!5ifbsUy0EvmH^=%C#l3e^<5&o3>h5alH=sc|(v zoO^!N2GLE^F>!lN^7DEe2WmUN=#ErfK(}6@=%OFc>zxjD`S|eFkN@?5^hsSf9)e<7?`m=I;q&*8uCC}dVD&m9~jdvwo z&GL6qO+EtTQd@)p7$+pH6zq^jgX;Xsl~E$GeL4BmjWnCu4O{q_LP?sc;cpZl*6!&l zIHkn82n@lkFe_9}<~>LwS`@g+oB$^qzgOD9;90Jd$FMS$0l2~t5dS!ddzU2j20fXX z0zquP-6rTqXSZ~+N31NyPqf|agmj#CH)v;m7IqV+Hj66Rw%teDc@wDAEY&-eUhAvR zzWVC3*NlkkC=2MKbN9bD);bO~vSmL&$A{nl={Nt2U;WEJ`_uoBKYT8~v_;#k(mO0D z(gm>dJ6KI#Fnsthl%y(3rx*pug>6>?VvdQB?iuy%**BgYpy4m2`TYXgxRDQNXdS*< zx|PEvgQzH7J{Tlea8wG4EO*!Dp&)l3GnMSl9@P#0#Dr17Pc(+Q5BtT#hg80J|L_V zpvum;Oo7J1z``TH6bFbx0WDG4wc}DzX$-du3#`6(lZ__oHoF}lk`0|dxns^rBwYoZYe!#|d zqycQhOwv7t+JuMA_@pC*avfnf^*0jR3A0Z05ZNy^8B`d8b3JM2+>KaN=x+R@IP3({ zc+YKov!c?C=QLAKU!C%)%UZ`|_7AF>87yMlD!$fNg2%OCyo|IDBLmA~?re0-3-s*Vq@!izdS^y^n2^jd9Bve|#=@&(H0iN$1hd-R`vPHr;jvNwXRe6+5J2;CH%8 z&YBCiL3>?y!J4qJRdA+f-0_!Oj1&08aRbVCxE~p=+PN`SNW>Fri%5HN5qGzZ2pdhl zx7G~WJ^ALbA|g$#hLuU_B|#M;`AbR*ZR8v9tcO!m8_UVS8)XngbA5N4sXZ^hHVxE3 zD29?KXAQL-=R$JBd%JQboZNJ9aTSO`k3bS4T{30QFk8y4Zh_NBZtTpZf-voclnpEZ z3Xv5xmotFn36W_j$>jj#L_5OlUMN???Utia`!LS^5r!-ajI z5__bw+FaZDiBvYd&=s|u=~`L2#WBT03fr}WA|~S}v1m3dgtf3nKQgL97RETwI9Y~v z9Ls44DhaZLkSdNUq`*ZE4=qxVX-!G_1+Q!{Nffe-SgX=Dv&o=97T96tQ z6xM;&(0zn8I;bM>e4BKA)iQ=Y&yS^&h`~&R+jAy9x-&Rq<@6hjp(3EpAX?E239l#7 zO+#hHH?+~lsX<6*U>vqahXuw6fp+NI_1IF}AP`6y2?xp|?K7T(s9L$8qTGU#Q{P}6 zF4y!j7zfDrCZ8G@vYwhO&5K|aR%KQsMVk^3M+-lF;oKMLUvrza1>4Om#?5TV=|G5g zR0cIn=fr^MEF?-|rtj%4(V{QqTSBZYNv~hM5H@ju$5Sy(WV=Kd@e1PePV;&9qT@d60ZAfoAn_uN( z<~p~uGh2uEx-0-7cW*Ms+DZU4#-1Ee!*kj^oSopT5MXV>#i;W6|Lr5KV%f<66QVrI z+!cx+yh#exSnI-lQ}>A=Tn)$x5O2RRG&G*1lQ88Tr`Adj$w7R8riABKcrVPYxPlv#!CqmN-(l5oXyvo&@0nQ@d_Ka z)eo+!S}Nf$KmXA${js0FJAQOs!~U!QS7J_GM7kK z-&&NE;}>VSfUHY&xxq{zIGMNbSINCbCttyKWd#!`E|K5RONn7~6M&j-fi&~CfefHRe>KTD@yfvSo5_Lt4s%y@gPbBSkI1 z53O^;LWBT3lbj56a-(WF`W_wF_EHr&*JCi`CjzL<>uA=pRi%t_GC2WI)y{1BOad~O zI0Z_%Aq2(R~A zV$nAUf?fw0g`^j?7&hnI|Cm>s^-ehY82 z=U`)o_=`rcfi^cugK76r-l)z7q};7=cg0&AIqHSbi2 zF1FePHWuC{N*Yo!I=j=bFlGkJLg^J#b^jizWUPpLrXijW&BqjNWn8tBbiqouwNL?__~!3$*%EOFv>osN+qny!GBnJ`6q=M zN^)zhOfkSS_RPJINFH#g+>;UkCq&rs|xj_)NU&142!g8?8>h#d(9Ss=x^He3Tr5QKc$uAwa9 z&|64oSxazw8lxp+7TNW;Jj`yPC#5(T1d!+;v%jD{)_74}eRw)=#uTyJp_ zocEt7P07H_-$=C64Pl5(@y(X(1;ADU5yp`(F$9=<;gixAINRyxwBZSw~-etR?`l#%N?!0`ad{Vr(-C8to1@fsp4UwBoI)r<< zsoZ14riPr2<;G}E$lC%7z0_gPYGyha=2vL#FDnOTypI=Yu$-SOR zp4dIp;0xt&IFz5j(ENWwAhvwz9WWw>&07*rGl(brGaw6iaxmd}Cu0lR0d$v(H+oVq@-6=2-z}xVXs+*j%Pq{G*(iQW!tyq#fBeM13 z^#iK4))+323mH+Jle~kgD1`CrW7To=Fa6T@zWnlw|Mh?WKOW!srd@5XD#RSrUfKPp zJkialkHfQ7(UMBb4KgJo)Y(|7^Fi$*wkU&LiZG?B0$Vjhzw*MO0;gxE+D3YDln3eVm&f#1Y#xdag-`n+MBp-m2`P>kiztfO4r+^Hb_1?lPbG2t56A# z-4)iN@EP(l%`R|N&aZ>CZ@q|&cnKM(RPC0n#q0)W3DcpW9nZGU-jeOoN^Rwa(ZYEr zXsnYv$>`{=c&@gwT~4=#L_UFt!cgheK`QT;ZI23RVi$~<|4i}1Z6aV!@Iyh{*g zKlW%d*;wWxGpaMF5M_|6ing;DtF(&i8Ia{QEyo>`1fFg7$>z%;veQ!rU<63JTa}=& zn_ij|J(6a=oQ^TfTLLMEaJEs5lot(+hX}Dj0f>r%_eOO5!zcpZQ&uda2!3l{DS|q(1Tu^R%1iOI&3eBMCZ%v&`KpPNZSNhfd;zB6#Y)nS zmAm}T>Y3GGxyo<#iZ|S9fX(@-hC6iw#6mYceasuC$65O{Gb->-v1;h=^ z1lE9DVRRVOU=17^B+V6ZCB?}h#vI7uZ-{JcCw7A*r@@WspdIol+1bFH*`3ogIvK*_ zR5zOWx`K}cLZ|v}_@2}7cQbmT;E1=S8?nAW}YN;)i%gZj*HpY~Tf`*nTbR3rZAODj-_&@)zzxDt8 z?f-9m@{PQ)tldRFR7=honDH02(lT>kVr}9f2yBcpXh>3FiM&(DImCFBEK7&5?1$Ra z3Ot&bdtRpuR%QbYE3@NOkU}Ga6SvTa+W4xd5Sdz6=@#x_}+H0)IdnF~ejU!Z2K7;|lqW{)JcmPfAp-|oW@ zB^hNd1@01Lyq6ZCTh7#iC~y-&wuFI^)3aGUS~D#JEw+ z;9R(h%x#dQQHf${yCu6bDYb3sG*X=$%NCHS62Ub1EVTC{q*x&Sg^;Q+sB`gpplWhOCf1TQ7jH>fvng(y|LXZB=mIAPajV-8O;l^4_vQjfFQHpn-Wx zo>F1fPkj3Ga#@s&%TlfKYkpRWO(VQi_AcglX!KuYsnASIRkXEGZr9m3`yN>eSSN#~ zMP*1RV-_?cPI{9iWX;S#n{$9zbjLS^(Mc(|Tz1s(wrZOY5of?yWtqO#J0}!7XQX@x z9DqU^AF*W>mve=Dz(uvr3BM*z01^`25_lm&oFxy&$Q)I9mcXh7=@;EdrF0xgs?Msf zfl_{4adXD95^_y;8$@Fojt9qw&;H(@|2O}Y|L`yVd!K*y<9Dp6{ZUJ;51+m7_s65& zWpgDdo$$(DCQx)Nws4#VfDNYG#r}#hb?Wj$dBgs64Inah>>O;fUEMZr6f=`paPfXd zk}FBUv4O(v8?v|j_J^sMZ>8!ineaEcE#sB$I(%CuL##~v9uV>g^M=@PzCmwAX*TCP z-Y|Qn<~ z0XK(ulcM9)Mt4uuAGQ2CGbUK=TvFi1)t=bDcdQg8|Abje`)y%?C{@OaTlWlsd=wbf zK52kh@85r@wN|}TH7rXAcP6h0=#rH4_C}@VczO4YcYpc^fBHB6`Vas9kN-iv`-a0? z_-S-9BEKv7;n32M2D-6v|evGfiRouaH&kVkNX@z^o;z-O#pFKn|2(ngSvT zCCtHL%W~PO3@K!#Q<>re&Gab1A{#FWrY&48FO#iT+_RjCUF0xk!IHq4)OL%iq6&`< zlo=lel$jCPkV}%{0^8M;A|@~jAlrwLLlRWCoy!fm8InC5$0UU)%JC$iJQuB3F; zYixZ=ao{jqEUINSk3#J(Q=er08gnjaq?qLaT2AOJ~3K~x8o zX6489is(~9n(n<-F}m7W&?&s%)C`%#(pz!ryo-j3Th&>hGZ&_^^Nm4b_wQ>gj!b6j zd(ybg8#3o99Uo+F+*v3MVB(@*l;vd(dnB_`BC-q`QQ5||I9D`+F$)oKYVy! z?e5o=oAh4C7kzx_rIiJ(L}h#)<&5fXYC2O)5;$K0bc5>znY6D$W|{&#^@dGzJHriF zFr9KY-1LGg@Wc3-#vGG*u6vt?wUeK_Kn~uR{!CwkO*h7q&(|3s2Z@4rlh~nkck8J% zs;z(7gMKp>zJ|MDG#hq1-5As@my6J<>_r)n802|~E`fR-bk0Nh{zgIemz}P#n4e2QizeRO&bYm_2oO(;m`FZ-m39mwz zY+si&ABN2x=?JIa1wn#G%#mi%p9HN?$*)}5$Kl5+Jsw>RqOlJinys`2c1ehmC8^5C z@sq#rJ3sf2{n7vazx|CvTD7vvqgGdwS@zM$5~Uh9rIyt~<(?aok+u@?fYu>{-Lz!6 zTc-WU-C>)YJ8zmsqELe5YALZZQYU;Om!zr(n39DUi^N@>$W+6MmX_5^F0BkSs`-Ic zCBTZ~P>FOm7t#nL*)|l7r=wMIbVIufRqrjdyZt4(E$Oz~t*9}Ul}44ZX|>JF*@>Nv zXhDlrg+rhSrXfQ{txSiZ<|s=j8qLs~J{r0R82l&8ra&CIsU)<{sevN-l0>7jyOiJY zEmzB8&jo{jRz$WW%TUp4DelVI@4#CDo9E1yCisTeyIRvX7#U01kz{*7z{v)`%vL?~ z6+W-RXj`P8TBpA9;m`>`0+j0v?7?!P7xPC2G@ zB6)}~9Sb1Sf_wN6kEh0>t*b6u*D!u4=$27wZXkqZQDYpsjki-H z8;ov^jB^WMMw21IsMvS65C&&esPcFR2urgQRf8L(%+_uqvIzvjl5{s9RKz@+%*E(T zh&2Tju8^S$cNrsDdN?y&cgZ`JPIKYbn!AP4+Bbp*spct^0+Aa76ga$!h%qC| zWx8OuEjuh-a-*%qogMd&Ncl@mAk#ACH>K{3h=EeR4IKXCg1e$zPW%nkG%{=&LGly? zpagk@z9iX-#DHr-@&Prg$ql1yD+!EwtP+;6$Zm^T+CM9nAIm@(ttxQa@^Y49epQ}c z^YQZK=Rfn^Km6bP*T4RaPh`KAk@T*XSTA3^fBA3}%CRH~>~!P1%7)ET#gjJs*YzpU zF1oc)+%-xZRa~`MrOw~JF2={ho2ISMbgwae=ne}5e|Fiyd9OA+b@}U33Rl}34D1!% z6n&J^`ou8~Dd~(=+iirT*^W6`8f7R%r&w{IV7vKdFz==Ti07Ov&X>tVW2@+vHravO zX@_os$*OZIa9hImJ{r3_Q37z%GscUPt&kJu6lDWkIHu&L&Zi{aK!@Vjq_^##@^71r z+AqQdX)U~`u=Bs_78va8{hfiZ~iZOymPJI9!;4eKPQZ=MIJRM z-19?WBCpQe{23)Mb$GQ=B9UkOwvVT~hTt#t=zIp-O-i4|Urq5jopKVcqm2 z;2LkN&KM&!ij&FFCQNv930g9m;b`G(7FMPtE7KCd>_=S%-dmeHO_$At#biO>-IWEl z@~1iIgqNur79)l7GiemqrQA18k38j5NfM@3HuxB6LtB}X?OFH!)3k;OnL3}yJx(xABqU186jtRD?zw}GR>>yL5GyH> z*uY--?Al>i|(T9cWPJTp#BmAVY;>!%c+ebmdRIDG^?_>&BE$WeMZp zCAKh(xifP#fGy$SFIqZr0jJ!I3a1=oP)XAx2$r)k2@SI&tS~0*p|V{dg~~&kPf3Y9 zJL64vGr%TysdmISGm(`Q>+Y8Gf7EqtmZ=ank_^&miQHS%DcVUI7>(Y_b(T$%LB175 z%4aM)#YpR0-}%0>XAe1!w1voHHv&^e!ZO-nR{LPnAgk!C-~bgW$)cpL61MPgf3ve> zD>39I)pjMRks0!5Ye5TJNfp5X1!VLMA`4LK5+#@C_544L$~a6=41kcMq0L4vfnK*^ z2T)dflBDQsEZNyWo6*2k(5BjQHt7w}P-UuCOCTeoE=np1m9u%&R!)=&#L=!YExAlV z)+7gu+-qjuK-7__k1kQmg^%|izWvSmFMs8if9_|$d%S#58}aZ1_v=@@e0eOYSi8G| zWZ1(>O7a)nOpJM|-WR)7TG+%5r*K~>TC?tEmrgTpPzS>x&h=(unoJkig>U$hFQQMC z8V`J~I(_t(Lhn|e%O^(ejU;9ClEx2_qRuUpZY_q1)M_00jxoUTMZ}P*u|$H%#vfw>>?n|o=Huf3EDsO6_~Z24;Mp{%3KE@9 zppX+pVliPj#ZS(_0F41a39pjIIqn=hd6{BS;3OkJGgo`t;ACf4pNNyPdt~rk8qpf5 zB^vi0GiSHyq_$}#F72o(FqUOu`T+Oq>&tr7quv$By%Kj>$j~KI9HvyZ5BCFJU;XpH z@V(#po&Wc5{r3O;$tT}%7YoaZU_A#q0M8E^HNw=wLIp+z=ceTDcamUyQFi7piEoEW zAaylbOUQG0-KYcyY;9PjHm>C|Mze!lAQe%A_H;OJcCLkuvf;1xA}1gHU^RLOH>FiB z=ip0`iAR~J6$3I<+)8b7?kaLcyx7l-v>=qB2AH&|~mP$)l%wix!HRLBMpkT6Y z8N^uH`|n~at)PjS^DhFXT_$d(L)|d_)Dvq=ZbX4&jQlBbh7(F&VOT`pke+$*fK79> zu#~M%5&@<8P$oyURe4G!*}0Wz*;s{tH+fUAwZlz8nSRYIEdL|25D4dNMi@!U!67VH zsWW0G=Cu&{k@dtO2XG1*;%A#0EQr`cd;SXotKXmg4 zV56);7r;77(4Dhaa_|9=-vJPC^is8~xs57l?YjlZS5@l}vRvZ8s~WrW7#!^am#WdB zwvYDl^2`76fAlZ>v;Xw_FTYySLp&Z*>-GKd^7)5{9;JtwU1S6~{A5l(=L0QrlmMIz(0({^V;MyQiQfI=f+_E>P5lQdmBByQzjmpOs1)@SY4q+q_B z3r-l_m{f|lil#ISZ-9Hc1?PL!Gt@95%H$(&5 z5T}ylZI0diq7`BhDnMs(5KIHY$Y6e(o~*sE2jiZ#q@Qeb8ut&s#Ber(bGJe;OSqz4 zkM(-A8oD2!JXUr4m_317+hpCpx%*b#}cx0a& zVwu|@Mk|KhFRJVl;+z$1-f(5wmQm~UehtD(=_qLIkzN@B&St%_sg@BOEfpn7d30lxflLmEq48BrWG*_R;zoysoaxSRiHt3>HC02QWmmE2cHQb%itYoVpmIlsg;uB6`Va|YLze-QBxi|MA;CXZSitZ zU_0Yj8N+l2rw7xX6BZ@ey! zvGiUyjiO1Zc1r>4Ti^KwNv2%eWmI5QUT6xC=$fEnH*Knb%Em?l_#`2b;Vf^!SLU-e zQF!BV*NFZsrZGrl-59GDE9r^071vwRfzm66d_pEMxJp&?1esk%wA1^nmeg|PF-xgd9u9VKxS^U}XtiV; zElSAQ_aeHoSPxOcy|7E3(A>6ov{@K$c=q#2?9GjZa#;QA|zAd2v$YshyV!TTVy}@vM*L9Gc2&dkl za}8F46kN@%C_#ghem)9Xzgb=P|OV~VkMLEBy zsM(;3+UAoIP|kQ`HwM$)WszaI$fQ%5l4vy`dliC~aL)i(HdS&P(W=_Yn(-S8nu02s z^MPQMY*b}C6Itkj46bnqfWeZ>I7-gPC=^c;C5l#sIa>a`9RFb zVQ@(jtqkcZTHWSA(j#H4B6=Z9%eGVr*{2>&<|7zz_}`*bWZP;7VZmu(x8Qs1~ zir%g)tqYI0hIg$hsP!r!2TfEn`%^xF3P00EQ7ST;+2agqoK+OXJX7D0+w?+~^1OwF zAaf9j!9Q-(qPJUzNua6@+#V^6QPi0~zJa<}TnUBgf z3X6CWBrpvs0Hu?a93JLVEFqN6Ru_wMeOiI2ic@Bc?p^D{_@o15&)6K1}r>|`_9=em@{X*Y;&7`IDq!3TcYY^bezr5|~)5b7zJW*aq| z`2G(A`~-8R)0EE_l9aJG4ONhhoZY$SD<9Fa5Hlf2wTSpTvs*--Gi_Vlt$B%dGPbX7 zUpId}(*=LO1a{^ux@qrLs!?|euQ6lW%ySjirfU8_B?UN;O8#p=fROXPqgarwvXl#_ zvxMSBOXtVi?^}&QavV_t3ffu{ynJ{SOW#N|H;qaQ3v0H2EiZ~k8{!hj;W@3CH#(%-hb+C>EwcKAOy$?%u2p5+ zrN_JmQ-;RbVMi@hHj*NPWQ4SmbCqbra?NXMsBRgna=OxPSaup4Ox(Zlp@YO+OUQ}e zbY?erynkyO03kE;rI^w&AP8;y0__a(>CcztQZ0?~l`YXtNMba(777a`4GzN|2?k%T z({#|!fa592hSJ&MO2)fe8VZauJD%kl#uC0wFI^-JcFlHRG}z*hQ71+ z8xw~NP8sbN14&HpG#agBWw(>GCE?U*T1;}Fc6c>0(HpthbVe8?X}n8l8X7wlqi%)$ z-Ev35jXe?&OG(H(HxS0mS{*XW0!>V)+z(3?d1IGFL~73k$ZS_el5Hdi?ftp2z|0q7 z%K}43g$~BYKnsTPbP^<@dWn$4P!TB_8Ok8epFM)J9U`NQTp*{PxKaeflP~(Jx$7hK zXlYha77%CAL94Piuqh?11kssp3Ss8)5+q)Rz4_FXUnQHVu)J;Put{c;0@v;~bV>jZ z(Xki5YZ3%%AU6g>)f!W7OjC>YwvkJgOjWpeGl4!EEgXyYFeYeh@@QJv}O%r!5Fl&S2p^;U@7bR-S^64&5!OlwFVDsA>n3Syi6M=7e%Tpha*A)2qQI{+dyv3`LWN ztILupBiSxoDs_5Pl}uNs)7zpcXEZcz)30BB_ox2Qum9Sw{lTC7)Gsd&w{m#%sF$x^ z`p{B|{FLyeMhiqap=awe>d7_N#aR%claID9B?m5+8UPI#O5ZxQFU~-}k&9c2rrlY4 zTKr0bcvo18Gd@}Ug5Ah(g-^Nde@g4gp2D8cC*nx4MQ4KW*QaP@R6Y6A5{antja&(w zU5KsPvv&BLqlzu+Su!Qc3r=z=k9syyl|0WT#;<`46vJBnkXF^)x0lRYayLnG4c6JQ zbW;2{X(aN5@X2d4@sh}bhbeQoP6q5l)Mz*c_K8GlfCPi zp==R`tr$dx>;PoOPmTib>!>&}+ms=Ho4tGiknPDar$wPiQ?jjMm5!!rbsw!-kF^$! zkZcJa(xEP`BN}n5)+)97`0(NT-~aw^|MuVc+rRTW>yvM|R*?shpjtr;45Z! zp@KH97IIYyJ&J>pY}I3BhF{J)uGVACPyb>(E-n!#1JUkMW=j;PRCtw2=hR@C8GnKC zkFaTYqfVxHgqv(y`U)EH&P(1f_y&<3e@#xOJu-eHP}Kqq*$`HUU1(G)G<&FkE_J3~ zhtD+F?7H*Jvl8jri6J-&#tThB?%RkW^TJGMUq{THYJfe25k{BCA=9M z<<^WJB33J+eq|Xhfu!EgX$MVUVw`|5o03wuq*six2v^2}ILYAK3`*s6S#~l;sBE~M zs`o%B4jZ@}kVwo_E)YR(5vl_}$br!Ul88+&>bFaUuwdAg!nxDvgx%Dm%M?eSXb1*u z*!eg)%%Z9z|*e<>_iIAfDk6OhLV>N5{ib*#E&X<80a63#Ob~A;EyTh4H z04ADjw$Aj1t24W?O?!=pj5u#v4J?9th&ih+4Lr4#Odi`#<+O3Mc9x#VGdk>kE07W> zVapfa2+JO)PAb@>FguJ3<{C+ZB*?t~-wl(RNaA>pHs4|6WO+u6bI1)>gJxemWdpgR zvvgKmW6l7*ZLh1_6ljJ4#zo~aTCafQl?6qo{KHOiuysXD354}^p&SvToA}7DcTN-7zT)4f~ z`jbESC;s|x{_yYq=(Bh0lRYI6s0D464tv!qCFZqNm05tR3JrX8X73OqK_3Wm&L2<- zi-jBLV*+eTI)F-mbZlzJ>6o54Ckc?C`Rl;;x7HYdxhS>!--HYt+%V_V4f>33e z>eB9AWcDYzPqXy4iV(S{ogNy&&c1OOFC8a!SFxRqdP&$6VA(Ne37Q_o88TC{RWTnk zM-1ZN6oE~t1>UpQsoAjFO#ljzvYQ~~`Z$ni3RymXm)4>VSE((UlG*e7JjTZKMGQ7# zEI*{A%oWiLwKQ`awF_o;GE$$4?lF>Nj5K|T`HbvCBFfzIY1d!S2*ga;LEC)MPJ-vM zj2mBOy%|$*>_%ZrCnM;z9SE#~9Hju}m+f?z@FFZwdRvpZc+?BBZMUL?SQSYX z9gh2P(FVZ6p2>gQ!raVLkQ-rKbF<9)Bmu^nrJBJ~xgjTf$ox=J4>1WWbZQ<_Fx4Au zs&kp$CG1$ORFTSVv?QOwIb1XRr}Ppw-jsY)v@1@KvqT7nSt2I{Qh+>_=th@9`-bGis$DWQA)#J7LG(z_zPXKfb{lMvkn%0nANdvs}; zu^vFFrl*>HGDvL2?oSC^wxaW!_X+T}pPgt$p;Aer^7a~;meq#%9H)%WUzaLQ+6?bo zVh#U#R#URXqE2kDrrykxgfuaUiHI$1jG;V=t*%=05_F8aaW+T`@*FVjlC5@^vZG`r zs3V~3fLb=P(NgC)S$mF0)%;9gvD|=+&?)q^Zm>D@dY2HoUdyQE1DqSh=3DWC&g@+A-5JkpO&7Njv>uW3^Jt`x;x;N zZpFz78Lxnv;$6>x4DS5T-=`^sPgS3TQ%tjcX(LQ2gfC#6E>)kA1)eA*XgGyt`PbYe z@ZFHd0j7O`!p3T6j2I>@z=_0!&83#Pg&-KDX*K^*L1u)*U+@lfNZSmJizSJh*e2d0 z9E|(2B^tvt?;=67!_{_$t*qPi7kWiGFbPuP`t#;-W>Sg@7oy+FpE=WoiNGj*F z#ZwxHftAN;gx6?rK`z*Zcvlo?nhHEd+i?;LGAqr|#5Ov7#A1D(vRZ1g~ zadO~HmsdBFWgqD5^NVkO@sIq}cYgCf|7+;bo3A0CZBq}GB-3w%uu74r@X4*X1`7rAHdB%S#g2m}Kn5^{ z%bARA8%wgzwp(i&NBZJq*Mn(ujV?fDgMpVUC<>Up<>6zJ%HS@9Y)Xx0=ABJvi+D?L zHklQJq$<0_!J`HCsiM_YtcMN)Nv(qQWAsDY7`ovIbL>8XC!8QQGg~tQaT6CX+g^|; zDzL_G7vPlddm;*wB%z#eT3%sV5TCTqrTzj7nR$S%`~1QH03ZNKL_t&qDFpTK5>i50 zx=esJT7l>QgvfonmN~v1csS;XUDJzi_-z_b`o3DVzYlz@d`l`x@-Ol%> zrz?`+YN5`$tHp7Y&Y9B-j1FvbQdONf2!v^%vZ-Q$k5ac1XJkus9lTiO0{6@N z?|<)){ro@v8~@(F+wb3HreCE4o$p>=-hY(qI4TPhG;uCg;PT+*-FH>o@;kjMn`TQ{ zSCi=Wb~`0MG-PRmEg=TvRocyeSQ?;lt!FGd;0@qF>{~&)!o|@> zP?FGu1X;S2wM7iRp%S-KMuZEIZPj)+5w4apl2%SUNvzy!EPufWu}1erT%nH=o*@Ui zfz=Vho;37EV}o*WyfkE^pdZ3UBgGTDJ8M=0o-MKHeLmfZ-W+auqZzRVU^3HAPwJWy z1YGlq3#n2VqzmjK`GeBuVAA|XduJD42qan{6t!L1JbWar-!s6(oA;2LUXKy1) zIn5arD1?pix)JeFr9Vn!R;4IVz@FD{y~#wtibRn$8+?fk@zwaB#R$#rmjE@$)M?Wu zYCw{Z6{Ugl49JM19!Ns14ysZ)4Z=p$g}tYYD3Y;;rIKXfK@PoG@)7{7`G#FU1kzw! z>G>{74W;KKNcRCCv)rsp677=Kx1q8IFP1$3h(1)3vZ5+U(nR_Yj&QGe0;n3QvFQlN zeEl5*9R~nnTM3p=2pgi}g(hOgH@c#>k=&Pqi1dnWbc}Fb0!a!NzwyaMI*cv=gH%<9 zunf#;{juzF(%~>lyh)J{W>KmcRwhY)ZQJDrSQm@QT?NCT0<~J~q*b(9Y~RqvG1n#c z9d|Bbg8mKpD>^G(FrNpwN`+g?x27Umip4kJr%7^Kl2nq8Z+!6wHBoAekO{3hB^w); zrZVW`-l}7sU@f|IJnOLXkT1;Sjn9r2Bt;=wA?(I%lo%h;_!F92g+;kq1oyEzpm4Aw zh&JL6N@f>^mNN8>AMWCT2eh;aLLz~AwYbUwW7W~fXGYHSK57I8kg)U2*{wWHfWt_( z@){ra#5)Z|R6`(-B+I=R{mMWWZRymKb%kGxbdCEGmj%rnlDph~{@T1@S`hWT# z{e^G;=#Q$;hnsx2_+x*3cM3{JWi=;Q_S&6Nj8XYq8rA*av@3)Abw%`b$8cF;6(_g; zB;r8Y{rwe&l%#UX$6=;wx4Hn*;-Zdra0133U#BUF^>4sK8>Fcql_u&Hkt3z~ut+yd zH<)$UZaxT~FBekCC=Ar@M>mMYb^F+kp`x?mxf-unXpMnQ2&>D#m8NYtsCmT(X-TEi4=LOcO>M`%i4YST}<* zoGs~cUFdhxf=Jl7$pqX56I$N8%H#YZnWbfkoLIS$1SFYa30jg0*+R;<3(iAjd>(eY zt&fkdJ&q&auIt89XO4~gqy|G@*x&*K2kiHF6A)|VG=Oll3idUis-qr zX%KGaO;CU_LRoB9lMVCzFW4Hpz3WWtWk`^V$zDSDOXFOli+TAaWMtzJ8w<(}*PzHCEdm36q`sNrWQ=L-QXm-mE#&Abpc2?V?P4tOD(OIVjzr*QQa z3)tRIdhKkXh-9ND#U?PwAs)1KC|jcxYw@74I4u?=XsWTJI%$@&(_*!$MD!($05|rG zp2}`O$>iZ^POhEUt?pAwpjCocO?Od4Mlp1pGOig9>?{f?jsx7W9p%$~o_%(oFCWkI zx)p6k8_4MBqm%nb1Yjo(io?(#}Q!(hw3bjNAY(zICe$EP!|43Rysv=dFn8s+AXb z2&XrL<9I;4C4$7|k!b|6X29mU+3_d*F(uOKrb7T25rM>%A+&*%KZWbu?c`zobxLnu zti;K+cudL0bkmXM|0*0d46NzeqJHOsjbz;!-NVwZ98NWt(fUq-+bu@2;8|e9)5gU; zB;r0i5ztcUI1b7>>Uh+fH|XQL^WB#p(IfGsGsWoaUtm?{OI=k}4^?%4>)_x2bARsJ z-}<^gzJqRecXyFDet;x(ccy?3CDN%f8fXKk_AdZf!lfRKpFBQixfG4gyWD-WEU{M zfrj&r7QmJ*x2LyDM3B`34ImlYJ`>_0;L(sB4H*Y4CO1$jtj1wO61aU(cy`H4vLb4v zUQ{(%Qk4>_qq0c@Bs$urve%v`Eik4+8Jqy2Lu6tJ(M%?1oipzWLYQ&iqv4q zszxyxGwuQDs1j+S7ESdIm@9%jrF4&H(E%i;e{Dr9h^ZIRDxj4a)hoB{HeG9FiBUzB zVB97X@3t{FIRZS43Gn6%Ab}LDvkR!zw)bTxcLorU^Hj|s$?M4)QP*U!MYl_#I9{9m6$P1Mvq2eg7 z*E3C2T$q6It&9lPe&mZZw6H)@Hl$5gZyc=gG7m8e3R^X&Up5fnrHu6l4vSAJ3`~lW z*Sh3x;RzPYDwv}?2Fn)Ndk2|vW28(31c*w`^CxhaIkPBNSv{vj+it_5mfi@OquN#} zkD6`DX@x0ap~hY1HjSEeN25}8e|Y!z|CxXKPyX`1`lsG~`TavLWj*S!`@_2ztfP

      R@w5Al&`{zht%8=rp*X8K2Q=F3^fz(XeY>nE}d=!9TYdlf-RS1M*7*+ z0Wy)=Y$hXuegl1iHHf^nhX`rNGy?~PpELkafTyhZEGqpNh=c-uhJp% z#N$G9SI8XtgC<>i}2dqv@AEOFAm!_6brQCx?Afw(R0SD3b)A{qe`X z{q5iT5B|ho`!9cs<8jm*qMpOhay)zvjZWxq&!d83qNJ?Cc9n9PpJqG_Bv*O5y^s>0 zrEYXM-zO+JJJ;m{w01r_zYahnNjgBr!cD&91*mOX2DK1vWjqWOsmdAKvzu*%DID$F zSO=MH8HyQADRLk0k8@8~AEu(nNdh?bBM5}EbF{*Rfjs;LSgWYmS(=HDfs&%TBQu$@ zF;?KFm*t-cDV$H)9GSC?pQJ9bV1b2fJ48Y_EWsQXDIX=wOM9Xn-3H}6yJqCXSS>jr zWR?)hbh8d{8P3Y=a~KQ*X&)Q8Z%WW9puCX3 z$ntRWA2g!cO=$+{2w;SS%h?r1P*R~nv`ZyXo)Ssb&YNiSs{BK;UF)DBO_+{2nni;o zl|sm(MT6y2&W>`PG!Mf|td%?(NR4tq`B=V`+p3u^fir|jdx9KX9^5w)omhnoM#iy? z=Wj}Gx2sCD25GE)W!AcF17nz~!2qBO+vRafg->bjpNhq5)|%WXMLNFm#dn3P>Od+X z9pgrhEhH^I3PHZKrM8sO9RxXW8HF=;RHag28!7G9+~VCDRu$~ZH3jn?9Gh~;Ba1P^JO zGO8S+ZOWMrs#;lGP$yy961MVHXvZ9Ki7E$WJ*1M|b1Fk6pGL^%yRUxVPyWPz@)v&N zN9xVN`NsO%+Xwc$@4x5dkfb6yGRU@eOpYu|_JT3kmLvW#I+}_Rb6c5D^`7#jiUkHB z%L0wI7WClIjAgr&FnwzuF{WVC-PNmrjUrBTnfbZlM7NcZJx^t)JV<@l>&;BNhm3D9ogv)Z+FWVK@0+v&(%NKb7#B?hRD!3% z$Yu!)C3_|glQu?CW~`Q0;R{%NPvK3ZmG=GAMj>$nic@#*!WkT1o9{JfO*Xi@8|IzRKFMs>*{LbI}TlIJ=>Flm7Ejkk+ zQ7#?hH6k94!c|G*DE27oDD{b|D1+D7)Tu5i-(GHk3QMZ&h*(gn&V*h~momVWZEMD# z)}Rz19ZJMSvMnmVLDq!?uCuUpK#R^2*CPOcF(s*0VRE#bCnhtziX@HtVWghS23Zbd zmdjl5BIcB^p_JnjF?HNk;n@x%Y0bJLo6>%bu8XVO8xSR9SvJ;Mz=$AbP0Vuo6mKaY z=t@`#3z8h|R24_X>Un|?QG_jKpG%+-qhd3)mEaCUH3bat5DZ^e8WA^u!#1Pk6z0Fg zHc)sXaO0F0ovDAKw3-vn8JUuq+Tn0z0OV1nN(qZ1nUOD193XmMtBI@`YZUGXWwP$_ z@Q5Q3;Z1grX45Q;IZ`j7OJ#*2*6@>7+Cq0Rq_r4sY(V)%h>YVyB3d7HtkQLf-ZHL> zb?)wzN)@rx?XVM=rI9ByO}Zq@4N=9wr-0a4#4|UaHwGsAWIDb$NZ=Wk5o;EyT?!L6q`=7XgdhQ2jzkN`BOqV?wk=T!{7g^- zV&9`V#6G5QrHHZbo<8nM1m|=hH^W*6yOvwweMqjuzbIK5FqD&EovC)ZrtEC@n>nyk z2IYOq88cj>vJ16l#N-5>bRGw$mRH+teAXFy29@!_As#O;kMeK)`oHrtKlRh+hxdok zLr3f5yBEK_6jdA%VuEid-I8_yy2aj0RoiY?#d6yzyWKn^PA|L{-&?Octc!$T*)G>O z^sT{4S3X?pshhn@Q>g`fPxp4Ad#g--ILqIv&|E+$!8n}D#-#;qq>>QWX4Y?3=42v` z?=nkzW-CNI$cC&*9%nr_YDlwBT8~l*J!MP)hSkljK*I)Pimx?tQI$ z2*DvFS7j<)ASA0wN42CJ{3j90AX~H!#r>*Wl#pGn5~uRb-8!s-Dw(2Y9%WH>=lhD{ z%&T-Lo$ez}9UIX?4bYOhU43-v@jzW5wU0xP{nV}dY`|`NjK$YIk#263HmfFPxnOq( zO8+ZkgpvVCQf7ueIvtW(;7FOKg@TuF`rc_WiVe3Dj09tXVU(~?2o5plthBjHS5XO- z4UG^wq(d`RJfztZ#^5-AkV`^ck=t2DppvZ;Yu2IK9&L6680IboCmC3?gCybfxF~F7 zn#=~90WJt*fmSYM_r|;6R&L^lQ>7yLTsG+DEMZh2mJnC#0OoU3oTfqyQ@g>i zxRJ9szmig@>1Vf!r@JZGWm?X^^tcE;r}&ems{$NIGpF|{h`Md)^}0)Oy7L~ijnTS| zcD6X&PR&$FG+;82j9E2P2?Tf+51<_BS~XLNz*6$-dP{N3TX8@g*2Ek+h(`)#zP%vt z(nOZI#Ari*6Ti2+r6D%ORRxd(UyGhhex(w~(pK7bLRb`Vlqk$;pBag)ylM$rFz08h zx-iGNXmTbXR9DCzvgE^nEnAJ$(s_RQ<@bK+U;Gz-@t^z?@4x)sL-n=C+qZh0A7A|O z=dW9=f5OdGaUo5yU{HT&gTLK zdUp=*hU5P%Km(ean^Lw4JTwK&*4HjiT;nB0~bm^r=(@ZM#S;>yRk)#(LBnfAqWWe|5fC z6e2r3#Km}IgDOKXCxm1u@C#C^Mi^i3O@v$xiJNONONkx9;IT0 z%9acbIf0$oKNN)y)!~QxJf*{?DeM!X)Y!MU;edFXMf$gv8!U@mB>A_GquQ1wnS5NmQ|=YRWTyivm-WbgbUZ=^SkcDJOHEEQw^Ye+O&9FR}$5FjB zW6(TOB8Su3SzM|#?Qe4pU%fAgv;s)#ry_jBk`7eKKuJ>Rk%RCy%8U(s%Ia47;&_#p z;*AR>gpqGuJBnG7Ukbq>>FhJK=}18=aKUMU%9dz~tz#B_4tyj{`+=FL9PW*V*Y{%r@=vgR(x2(XG8K~E|F`z)*ud51#obI!Wc$P4n zO^Pz|2g2hO8vyPMk3MJDXhA-+cuP)N7>=)h;}1whcS#fZg@k8CYn5Z;GbnA$1QbFj zpI2_W;Y7Cs*`V4%X;_n@>Yc*0AOc+#r08lMLKzWA>_Vz?++LYk$+4rs0}Ec{*X^L$ z<=U64@XloMY;0z|0A__2_cWLV}@?RbZ(=?Uxt2WYqK{%c3ly^N59( zbz0^uM21B9v~;+A+NN~6fpWs6`%oS7`!B!ubKm{$fAH&n{`~mv&5Ty!^!qR0iz*() z7<0*fDVj@m1J*rs+;kb`UR53~J?U*VcrF*hO1b7fFacWAS%o}o-w4sETA5cAuXKrZ zqyHq%l`h!lbfJ5C}xP(W$k`lt;`D$$1vZ5;m0PtBR!Q;&pLsX-55l z5iGTN6guTjO`30pSM7p(P76ZBnNpTn{)jNP0Hi`~A5@OshT@a4E=I%UzFAoT(i2BF zDPVCiQY0m_#d$5uvhJtUuI#f=`R=dl6zL?V6H*bKFa|*s^ypuKW zlyF&y(o4Vm^ml&$-}$?L?{EB#-#OmCbj6Rge}8q4oL=;QS>bx z=;SP1V&-E02v|+$Ric{5s&1=f+w;mvnY@iz)*R_SZP}J6>LYfyGDR&o$y!BCRmII` zc#GMU({NS`UXXE~93V9h2_#fSo@N(q8k7KSyh4OXNAWVtl_ro1F5Abp5KE>PLdKx0 z5@JyIv@lZE5WP_Db_}v9%+u(!i}C_`ZwE3hO0<`!Wuoe}jHE4RF*!sQd(IZe!R^4l zq0l_6j}I4=jGQlk+9h*3-$gMDLpF0_K*25H__jQPOfq8tDi|W^aKyqP7_pbU5{d6x zMiWM5$EuW4Hlg0<(n)|RqimYBEM>me|LIa>=+H-eQ-5hCHYK8VXM=LC%bS+Evg~uB}?QTWGuJ4uG$UJ`vJ{h8JxZ^q(koXMCFw!iV8{$TElJs0W*+H60#lIFv zvNt6@3l_BiQJFw$6xMG*V!^3`#OCpFrOX4qV9pHM_1K^m+W z62gN_s?wRS+lz733|CTio)D6xdBErFChUkbas_S*Cf#*-*(SHOi>(oAseb+z8h2Kq(|Iw?tjV(_^w?Iq58!Yutcdxm)r_?<`%*|athUuaqwEB>*F#)6~ z>rxsM=(zE0fb3?{U}1C|xX*04Csg6{;|Cx0_VFlpXXYknZ)a$z$voXi^65YP^FQ~O z|BK)H-uJ$LRw?g-(b~Iob39}j`_TN*04ikG(K7GBOD%a01y%Mu%2@3g0X&wn$Qd2m zotex`^X*0wB`QaFT0)07nf>+7;1)=bq8}9Ud+f9*3GkYC%QOYu)A3$SvxC zb9z8c{0_3q(~@>3H!~^$W9G?gxoA>kT&R?K#am^&qA@9YBOND9GFd6ihlFmN?0hP2 zJRl~F5zkY0ujgC9Mp2c~Of6QXP#0F;P604;-^n1(G;P{lwcNs9!c~?UF{vaKqQ+tw zt|H~u)RI}V?1;c30Y*_}3^I_n)MO&GaOBvNJc@spGdpQ}(r4%; zbH66V!Gl$V&NI+!Znpk`Wr;iR>ajOGD+YyqkLqlMG#l|kh~w+u`a>>DR=xn$RCVL5 z>?Z~Sq9~cQSrEn-AA+s5@TdU^l%n?WN}$x#Y^P*&RE65UmzK`z|J=SdxTM(Y0X)vx?(|H_~I z#b11R|D~umhiHBH>OJkMW4vZ-;8M!ETbY5|Zbn!jd|axS%2lGP0bF_|2D~@YP`Q!|OARMQ;fM1otGtR}vCDfD)T@7!H0b~{ev(d9~zzxrn zuz2*i#v}tvd9XFK(*16#9H`xp)h|=Gq4(!-wF}nTI8`gs% zHwtRQaHo}7X_PlbS50zv$TS=QURBKJU5}p>Y4`Lnm9ncr@=MDgO*D~Hu+xV8WHi)h z9G;jlECYst8@9!XZu#3KMWM)rG?#IayZZ?vvcH2@Jh>;arvBL-7Oe-!#k_V?oOYuE zVZEF$Z{AeBIYRxQ4*M)sHq}d+UYoMJ&zEm}{Tn~_lmF0P`OUxj=FQt2Ht9wxmsCf7 zR7m7I$3^z8K$0aH;bU!^%N9P7X=#3Awe7=?I5zv>dYm?IRZ&ZWutb(17Z**w0Bq*%DupOJtIS@a{t+|86gECQAx1upZZ5xX4NRtJ$kG<{B_~K_E zYLIN=>T(=7ZU@*)JB8SOzmkU=$hHl(m4}F9*%o8wSFN_rSE;7azDt$zFZuL=DNbZf z#|azCgc!&0gJas}emUE#w3&HjwhFlFrSj#S-w+%8mUe={KK)aQFw(sY4S&J`5~#tCY~N3`2Z2{xEVZD``u15r1X5Tas6} zy^6vh8k}47EziF0k|&5wD9qpBo-PY&-z>-rdL+I;6Kz4;8aFg(AS7lOhC7o_z!xL; zG%(AuuLaclgG>p*h~eteWSVXY2C3})EQxJHNkvsIu0ML zN6uweCz3()sQPfe{Pa(K=YRfN|LgDk&A)wo?eGg!wm2ljnTZ0oYS%fC=^j zbd-=qo{F5=x(tPSko6G?YAyKGXR%e=V7%L;Sgy+DH zVlZr1m0chinW9XL?-K56DQ~A`I%9+DbSC|>2__^w3|*HJ*8DzQsujr8N030L@w8PK zCg1Ha9BS6XiNFg)&}L9hi;Xt<+(rGtI*J{->9y9B(UQmkx;zFL1AAzCvAKyXs=3{A zu8)$&y|;QU_C|E&pD>IE4QM{3j1D#4bwjcKZ+=!(oYGgGwQ+A6h;Vi@-r_51-7FP- zCXJH=%gPJX3_rRnu7*Qx$mll!;d!H62%{P4&e8ne{2HRgRlU=bNKZ9MHd=nd1@Sq^ zn|>Fo0M8GjMxOx37vKD@7%izvGB%W*&^Mr(bXVbxezLJ}lv5>KGUQLI@|N^U^s6}6 zC5lM|*ha}L=msjnVi2sx!-_7Lot_y<-Ga@HcmD#G6!`(@(p(Pwq+=>7;ya_#4Jg(_KqXNLJ;#Y5@wS@5L2K7ZSu- z71@_)`vZYBz3oZN$p{>o_i8eNPChk(9ap+3Nb`T)Uiql;rX<0{#H{^=OrhCQ36N2t z-5ZP|?h=5N--K_}pKqQjR>{ThQpGE)(4R`n3;YmBQM$R!20`J^isME!RkX{mynvg#hvvfjyQfzKv=|U${8i5k(DKe(ndLEl7sf=)CM?TF2gs#tWDA#N z=1dI-$MNRzkc@C6)J>I<)MzahjqL9FLqGeof9Ws(m5=W~*4ww9HE?C9$y;;s)U>-* z7-4YODYEqubrm#L74lgas-a4>p=7I?HLuz8?QXo^AXz#ZiOi&3ZHmn1B_SU=O0scb zWF%ESAIuK^DdnVdhB?WS^-$57{+n!v7wtOG96C8>PJ)(eZexu*DBiF=YIUduwSeYg zqUV?|7^RFdSECHbD%r-i<^yAjEM=hFI5xMf!y?AIIQ85W6twXh3v7{%x1pHjfBy1r z3n3j{Djm-8t>Jt2qpF4E!vbVCmL;sSi z4Tr;qaKI`iA1QQ+^PGlICk_e-x#wnLmfuq>q6AMHfsnIQ8{0XR@mvZdWVN)FiA@*E ztGk#KrVXo1=`bVAVq)h@(cCygpGeV zeRTy{mY0)q8By`yg>mt9V}IxNeFO^eW&JQ35XZp*r~7T3iDhXqjc#c%3k~=rUm;W!5 z1@bQQE6f|!oso@vo5w8FyXcN*2fC-Uxmxw&iJhy-?e4;IdDwIL+$~83O2CT4Kfz8> zcC=Du#!rAt-#cBH_x*EH$`rZ?<-zV&_--O1!6sKyTrT%QIFo}Lor^c^i@hPn3R{m7 zdhHHKi2!rEE`fAFuF`wvvnf18$9LK{5HpPba7-5;g3Dc>%UMzywg>nJV%Msio1NX* zUZ*?BDOuk0`39fyla;QD3+o*?Ubl3~jAa-*7Uj|eN!8lPrms`bb09k0?3k}bElVU?wa8`)UxDf%2k))$e)8mGHC@11LvP3xTJmsK3DEvnEVVbAxJu}Ox=X5n5Lxzj0TC!z5Gr&dJ zmY6z=(M8F&GUZQ~K#V#-gD34)^TnTz)+Qs(7T6)>ouEbKEkBA0@;K3%VsxqM+M?}4 zGl`q06DG^T0_VQ~`$P{LE6|LuA+?Br2E};8YlKQl8`ipHopin{_?Eq7eY$T(BN-(& z#1rV!%P#OVx`V&QFQ7&|k_2OSOOWS3D@Y~(^&RSzR~a+?3^cHBO6recZm#OReDtP%TGl1G?}TVO^8A6V2{O#(Bj3R8t} zNe5-q?#fn0_0b`kJ|)r8{MJCp%~^?oJ~LECM;CEks@1OdGKmKQb^_O4XzY{)W zoqTxzl3ycu=&^UkKJ6t@iN}q8K>8syf~gn2g#KY^y9Nwbg8UJUE@oGDtd`ppe?mnv z@zdM_pE}<&f6Jn~;-Wa|?gxWWarGI``3wQ~tfw>1GM!^V+;7tO3Bwc5L*Z&Nd`be} z0`=;NGRSjk6~eTp^E>x9ziifxQawluxYL30Iz0Aol1LQ3cT#C9RJ z3U3$;nogOzohC-zi+*6*e6P>N>a^Bnat+*OLC<7!_rs~Cc{0BnhokWgG?_bjK)0cI zs^;#-Y9PuKOczv)Uefarm^{q%5N9$~fGRMuQHeYQGF^4huCq_o@pxObIb1De|Fl63 zh1|AO5@(-3^N;<^fAin{w}0<{|G(ZGUvtTN$qz=jAF3SXC|ji}SE=L@CC)=Sja0GM z0VN7|@A#rq@ycbg6z@+hZO9VGtP=`E-r*B@K{8D#uy#g;e&O1$78JV^xxsepC|h(F z6fI~}NHTJUO-WHBa=5(PTaZ~5+RSgiWX&KZ>8b;icTNbQTht9iP)cTg!^ho~uW2u& zv9ly9gU#?T?T}`Q4T=#Aq8qz}i+z+IAw`VC94_OYX+xgOxYy`%fGD@J9c3hI3GeD- z$$<_T+u5G3{K|4#9F&3whejk1Q6nQu#g(Eso2F=8x~!?Pv`e%+*Mg*p>gpK1@h zq|)rBN2CZ6&6Pom>J9&uO?(VANYWvjqH8TI1GQIQ>9t^yNs=-bnwV*cCtCNEHJl?{ ztgfh6wh@c9|wC)8Q54clYXG=k`B#9l45{WX`RK|pc8sCtSPE-gbWx}o(N z3|9G2+W;wgHpG}OAD0lj&H6M*yy}zpq>W$zJ^@^`pVHX;Rg8b#l-G>iV4CTNsh`z3 zAVmqn=ay~vie5Vt^S>MfO4v(RVhRbqsDJz76wm)jVLLCZ0O5H(rK-U|87IJB@@vV7NJ|;AVqhh|jK~D?0^4CI~m|1s66Y?4Ak(v)$ps zr%#`5*`f+{&h>zx-6v;vb|H>~)1|6IZI15q<=sF06F>gv|E*v1c^3WlI39Yuynpfg zmqT^vadrh zbLF3shk_RY-)LPQ^1cnK&Bjj7<&!coy=!VNRyTv*n`^7&77GmMN!ZsifY4QH<#xul zMIL90p{$D$7nS65Eug9dd0Y@&E4E+U!__{~eOGO7R;kIGi2ysuz&oA}ERP+*{{^FE%$r|uW> zcKNkz_MEWU`LkV{HoSRP>W}gCw6(-I}Q&@I8nJQ9+@Gb7||~QmrJx zh4nU!x^2WQ@*7Y}wC=AI?05DIqKS%qT6TMSK>^ic8D(JDyk+)cV7bZCTGFMErA$o# zAI9u-PkU`7yX}Tq>KNRv62ivDZ5$OhkRQP?l+q#bkliD2c(Y+^&EB)F+%uuUl+RSP0TplC)S%?x~qZIm6w?{+uoZ zxAYm%7($VL%4F}wuwWelYBZ0PZWdO>t;k?TEgzZcjfG1g_o^FNrlah9J_q6zxNcNJ z+=K^o>tL0gAO!JNPaH6cCpq_FcNJvEgoQkV0Z84Q&24thnm|(CrEEt2E`rnBJfNNHpD1v7kn-~)ojdL3`WzJ`&`#o>Bhzq)o}`f zW_p4FX}Z_w^^Vd`oxyGcB^gO37!fi9CZsVt`P*sm-imHSvpGCq$*}5HK|~6b5pOAi z<$Z%;vApnKuBWYnC+2n`ESQUn6wh_HGQ??D!K*Y{cq)1VevMGvJn+_UK1)Gt0YS^K zH*C3GnVT-|1qvb9DiSO#yB;z)?|nY&nraH51`b&!bG&+R25-g;t8^Ug6GK}TAJ6mg zc)WQWWnM(7jJi!01nSb!tDADDke4 z*e8peHEl_c$|kci*U7{o8&@_Z$xXR%D?eJ}GfNy=YD%utIg~$2w6SKyvjmNe?R--e zHwIgYoCi7HEM(l&QF3c``oVxLfu$;(11YF0Z+;_w9d*Mp;`|qbW71W*hYxapeO~^s zN_7h_o;ewX4L4YW>5|Sqvk1uZ0a`&K8r8T9I3pBFTjdDCS^QAo}%g5i+7d`7dkz_pXixR0+$4#?8l z2$+(geu01-{~K|yB;taC5qXzSK8>ER&F9M9^>D{+qTqYkl|IKzZnQ<+)t^Ta6t_P0 z$|k!P-_t-0_&wwU5FgM+X5xag!j_BdwW!1)9o1{S+iP#)Wn=3+L>ncO2YplbnloQn zB_A%pVB>E4?)4K;ZfV!p7rOYHkntx9n*Xan(*f{2a}3z?XQnAn5jB;I-?_bgOqyE3 z?i@?jB+FB@QK$(FJ+GR~6K8)FIg=s26|y z{`Y?2kN?qs{-672U%vY4@&6I_cCWT=U3%B=X=9!DqbjeJ4`Ylb1yWk6il9gcNby+|KnQ!*kK@E8P7n@!+eMf3l0 z39RPH_lCzvezVAhRBsF8qPKJTJP;`!PQm%nwn+SW2 ztY-jkK#;$2_yez_THGTsGThbc9Z0gBcV_w)+Rhs_BVzu)2x7Q+*-`SfOxtz6h6>~s z#lV{6wdjSX6U(=Ly2G58)DV>0mIEGbAFwFCf?e--c^GW3#zr%+ylK^nY`)D3$^wrI zA?`f8>72%5aOryBU~p~b2^UHReo%VT+c!QwefzE3{iZh~I={K;D)KO?Pf2b2-K9Ri z`}W`c)nEA?zxg-oOUo)Byj!Lq$Mm2Y;~R>C{A2-hTVR7zSvMX=P%618$dl(QcdflT zvsqV`Cw5MJDY~j7KqUj)D(w70mFlDVC>3%Jwi}RLRdt!{aUY`MGUX86&N2FW3dW>6 zz#i^w_Xv1% zG&j3!%eZH?lg*KMrmh!ylT~4az2VS?Yl1V0#Mjwj*Xnte731#CsikU^s&w}pH34pS z_dN8)M-6>}68hqiHwV?z_v$&7k!s#nIXw~9`nS*QwBJ^L=>Q|Z4x3xR*P^`L?!A^L z8ScYo%d|bBK}cAu?zK=@DsaU~S}p^bN!=OVHx5h;jG7l*7p6#xS}Y$wo9_VVm|=rN z8CfmS5<1nw)C}Z(*8Mjww@n~VY+@yP0=nh?K*_m&v|gC=nP~80&PQbuoE8hsSl4Bm zS57hfQh0IX<&oRuSKp^#h{*;5DJNA3TFilqXdxt*vrcvNvB~%P%XLbso?i{N$bpy- zXctnp*7(pNmGK)ot4fubQ%F8k=uLY6^jm(*PyUPl{IC4%kAFnJzZsqW?%U5gB`RMy z094A6%>?S`Fq*Ay0Ia2-?}LVUfoz$UX-KQUL0v8iS8p&rsYHMQX7H}N)}4|xXjf0C z;en}mD83jkYrxxO1zuv{XozMH^SA_?Z*AiWwj^qDi=B@tyjeU)Hl{eUBxr~z#9wVybLIVY7+?VSPO3XSo~cqwYJlJO3=N3%}tV!Eyx2xQCNpC8hQ?+l`;%%tSa(*1S4^76iGOz z#xk}kkY{UPw-7Gd-SfMgF^|V)wvD4;I!R`p>WTmYI=7%=L$pddA5#?gKt%~x*}Ow} zh^1ErYdhaGQasMN;Bn<%Dkj>-7pZPy_jm-P$-#rZ*|ZY!ARCkAc8FUbOW4w5l_v2N z+$7(Iw9<4lP9PwM)ouRkLlVdUEy*^PvZ9dZR|^HZ{*7p6BI@Rsm|4h+80)dcfAj)h zwZ-4dkR9MvjTmTpE|vBE*z?(Ad2E-D=Plq5O$M(>|Lef$6X zsek)V{QS@VJpH*)>G=HVv)|7W%FR?ml8h#!uC!6dAvGp3#QBQ+i#&vgA#S5?rz>4C z)WUInHeh29P{gC2t!?K2=@zmVqeyL&NnvCfcETAc&xXlBBd6i=#5ymCW@x;Ygowba z*G=p$p*ejY5|1Vi#V^R9-!vVx4`oLFCDx-p9l28MP|K3r1bp?L%Wb`?q4j7P`nLg|Me}wC zDcpS@pR1uZO+4Bzd2SIVf(CHOGWtTa)O5RK`yk}f)=-jy{0Or2THN=2LA2LviZ!JHOgo9=3^#TWJVR+6czL+|zFyHDSJ_ZjMq!#*p&q3_9Q@*Weq zRo16p`y>DAAN+m)^e^-6x9E#_6Gcu8jZMp0#^>3Mikg=whO7r=MBQtiJZ@rO9$Svk z4Zt4nFCC!V21_N?AsQ%IRVr1R>V9`ywW1j`I~ipVi2=oJgyF2y#tHBvfv{a1nVGdsu!|WNeIMmpg3;vf>Ao zW}g^%x@5UKd{@aVZb&HRwd69O9Yo*SdZyWVUG0K7S$ZiRS}z*CC^r(^ytkI|0ffb) zb0AmKhZ4xDh<(#}lwo*VFjr0+C|=XUD34+W>jV3zpikh^M9{8DG6PSK>Gy$~f#b(N z`Q_NR6JcPSZ}dlqj;x2AjGJMWWHQSHU6ap~_L|*mHcM(dm7vL>r+&T9_xk zrO8r(Pd>9_R-xoOKv0`ortgED_#ij;#kP!m4;-yVS1H(As#)MrB;R=Scb=M@(Io|Ld#Zm5>!e{$8h5`^D?NWCO}#xfK_!n-2wlxvUa@R9>5oy#d!e^%dN+GkwN)byc_)z z?&_a6udmX0HzammTrDIVeQwyZe7u_L(C}v37c8Mjcg4<#F-m3WoW{5V1_L7rd(UDY zC28gwHLIpl4{=Fz4l_(|-$tM*%ltOR)FGSnh*e-)lAyq2bcH7n%aVK|CNp(Q&N4? z0?7r3g?(D(B=md@x0UY{mXWNg%9BBvo-im2Zj=92h^){(A)I2ng(Wm+sV#H5yDOVu zc&m)nw-O1~#r5|Xv6;<;zB1RNmA5DtP_DMW0gVeFlj#LzUw&6VYgq>_x> zMpP36BaGx(YJ^}M>3EOyMxOgrHkzda1{y4il~IZjBUy{=acx@4<5p47a@HzB)$u_1!FmQOagf(#_fiJ z$7R61+>%Qz)P%-3Y>`4YW*wpLXckTrY#`FZ|UV`m2kTiSqodv7RB^rZx7rVRr?3S_JtB?ib(hr?AW_WFx z=0NWE#hCb=i;w2LFh{gu`;ojy`V37rU;2jQzS&9~bCR9D^hER?XbA1o!Nv$x+Q zVDO1cZ*m#pWJP79xIyQsH%bD!=n|Im-#{^Ec57v8M$`(d!ZeEI^4lQ6QQ z*!PrMYl0Ot6V+3{JUvZ!EqLHT&xpHwPFY$neLq<8gLnak*iCYg)_uO3r$tw$2;?CR zh*(*$+AjQovG3%42@g31>uL});vO5{B5>YizHS-kks;tcdrBg^blp#-Wh9U+ZDHMDj|r zi1i}iMF-L&cE4y{#Viv?rpT5yUl@@0saRJ%C7d4|r{4#hXCDE#$5PgACRrXWWv@(; z(T`r93lF*mFXbu0Q<ndF|4nnqB7f*- zmdnK<9q;e@^`CzG`E%FXQO57x>eif!oJ|!vcsu${b$ zeaVuU#y*|pqpUZ9jLkWJmLPGcjp3MIR8>NIw38mAnoN*eGA;yAm*=PQp7k)K+??io*!#lU$S2L`D#6-Wl>$F!nbMQcb9+*ey> zxP6^u^5N9;AxWsJvVdAp1<&ax`nQ z^WHC5MeR?pqDJo{gJKt>iM52eObx`=>HCsQZZ3Vu~@J zlN3v1P+!jVv}5LZ!UfBtLfP3ruQsoWfz}tg1u_i3oix3RvCWN761O*{sJF|zyghIY zn^V$eA}lL_f_3-bfgEpPHp&9-Mb6Wguuk2$vEAKcrF&fHt_!f#lsh-=AEyFVLf>nJw_9o(y-WJC}VdVGk{@*|OZ~l>g{a^b-{ppjs%k|~E&uZn=&f@?<#_gqP zDV4~r+Xg{@n0r3r=v>So6eQSQ{WbO2W{5tRrR+Gqu0PIp3q|2Qa}RLIa#I~|co1SO zw#Ox&5L(rq+&>7zE4e3d%U9wvY%jn_ZUA3-@3o%_Kb8Qm|Jm^dtT~Y+V<|TbB$71} zZHc;|+6Ox337?6eSW*(wV6J3CA7iAN)&F$P+h~-}yrw)=TJzorQA6U2M_{ub!Wke@ zS91ke=t5g+2EDlEw))4I+Op*Cix;dbCr$TP61q&!YOzqUJfj9|_O8i2k> z-iO0qi-?()2(Xr32}ZOa3Gmz_eOTxsFC*rsA!?{VLCz!=0EB@#jZ8zEJnU~k())SV z@%DqaH_MGxmE`~xf+|~8#~WBD{mZ}ni+}B}|Mhp5Rpzj*@^K=ILd}_#fQjUi<(cJB zME4=75z6!XX%fuWuP!}dwH7h`BASqru;o&fY&)Bcn6^ZYqy<%|B&$@o?4~%+E**AD zK$i<}xng=tg$q<}VS^-2T;`yh`wA1}G3c%Mv+VT;)oy^uaNUa=N^~8XN~k>OVT5PA zD;iI~M3ZuR!iFk&cLuX8F~XgJFp8*XwOSII=Z;)t{{b!Ab~s32)5a>fus{~^q$!cV zT@XYlABcu|ut0JfPAS>8a{{80GDQ5)R-%wqW98U9T$|)i#Qh&wkBk{%%(6`bEf8Ez zNK|T;=HSkai_0_fc`+I2Rq+s4k(B{N3})m{e*r?bPhlh&525MxY~QAtMkc2_?HXECEKi8ND0G$C zTJD?IRC$HZWs8@A@Jdg)ffzc5@djuVyfj_%(|o;+sILN>_;403JoVcf_r=8#Z400g_iG-4nPH}E*gTQ9{c9FmzxHYuUtt+a zHqhWw844e&Ml;9~?rt<&YfE$sxm>D4)hlE+Wp?+cZ~yV%@mv4uU;a;jpvIJs^YiB~ zpU%=-{4N+f$EPdxwHnFAi`7@VJICFcZ>j5YjI`x83Q8X(1|q-%`O(dSWVno|7n-4X zjX4Awqg})ufW#yYMdK+Sq<_eGfj$D@E4=Tyy5Ht=IYp|B9gTuj$CZ(-h@Q5B4A7`E zfjneab>)?6{sEAajHyeJA&i7T4kwk{$dV*^s2a_Q2!0A|;DvSAmc>V)I87UXr zrIpqUthsownv&KR(3I~1#-y>&Ex}i)+D5^+Q*fDSqt394WkWW6v&E3ZB@qMa+~?Q6 zhDIXiXmh@w$2V{NhK1@d!Kv&fu%&8aT!(+#@Az$>znp*bfB0Ma=Er^J+=twRD9fgh zN^T1%z&JO_OFO@nCGA6mQ1)$j{62z>g=Uo-7^@0kqR&g~l#tyw5+s_A2!>@8 zO_%I(1WY#mKqPx}g1kU%jSzRyvNEVwq3x=tvP;aY2e+gK%r<5~WHyy906!M=_K&Uf8Z|Lzu5$FViUq zG1qIEp%Suz0J=iw?xCT$;I;iSTUz?iOCGZt*67VwKmRb?z4aPyZJ7ZW@u#~^I=z(YFCm{xYH7xy&h`evFMY^~x zcsAUi1duUUIv#zKq!(=)9-6!s-)#1CV*C*suk9C1kn9CW^Fl5+p8H=jN6Z14Z3I%2 zU$q9wQ2BY!>0+J2qO8nWqTsDKu)qBDKmLpV(eM4`-}B|u=QkH$&QHJo>7WjBQ%)%J z&O^l5A_7csJoQ?P888>%H?rBQ{#$!N6-atee72hFya`?=t*e+To^!bgL-XcDz(^fl zp*W-N&QsH$SVilliyE)B?*R$ex&N!Ozz~#1fa^WVUJO^vJJSR%1@?eGDJvs0-A8$7 zXBr&d2&8;%ES)9hUzU3JHUJ^pm8X>=p$aSwpWyIXdh*#thQ;#e@KSCRTR})xdFloO zm`I3YBSGx+lw*W{ynV&>1)AzyeGlqg9wL0WnD#aGt9w7v)VkKJ$EL-h@j~;e+!}BB ziuMQl-dDOWHF54Nlr5N{H2w*ZBFB>kq-sl!Zv@?l9D8>`i^Gi@0m@omlHS!n{NXnO z%WhLX4jUa*o*Am5sj|-YFaDFi^k4tCf8(b={g!WjfbtvrUHSGesH&L=H!k6$Y#$I) z6E3omWktC};n?L`JWA=HNNi!06a4C;R2`%U>tl{S%a%%SX~gI#3b#u-!@!8$ffs7$ zDRCcT<-_vz)1-hVA6Ff+ou>}~O2lRBoLm9p!TLHP?jmcPMbN#W_6QPblg~?$@KD}1 z3BfEvz^V$c?c64jqP@AoI{h1z$xP5yyTySGyN*+0@v?Osb(Cm~C#vjBjn~mYwC{MY zp%f>j&8JDsfU{w1Uw*G629iOxkq)G>>s4gwh}a=}+vBAUJg$UP*s$YjS80;b0=JUV zA)wq@G70neDoT%ywiukf6ubO?X{;~^7sJqc@_h!>_Ey063B90%vR#eoHNwoJCaM|j zT=J$JEHqXSB*mxNaw&sAt@ZMfX*XOpEF&ipNPortF;WDdj~O%q;%gyA4vZ8LMe<%c zT_5v)5scU8GNExd-)QshAZ6lB8IOhWqpW9Vwgf!fWFU_|29F_Ou*)zzFz!$S^4#I@ zE=8Koea1K~MrA}al2bt!(Xc2msh{gwVsI2;tS=5N#k=HjI7SEj}=O1x7#+h zsrN&Q0}thHCT+_nP0OZH)7>CjmP+TB^Vfgv|Na+#|L^}-{^ft^%Xi~0OJAZ4ct+3EMhVMa=7L1xLkwo9+_IUAzwZAJeWrQ?eTg?jUg_hT%KFSE zlX_Iquw_ElkWuNj2DAxvx-cQQGKPY72{7*~wsBJQwn|V6?F;-tZpxG)sqDo=N#Y}E zAlqqTFX8z*P^NbV27s`Ir!@%UN-uKoE~Ue1Wzlq4tjEV)9^Sl)3>(Y?8Ez)bj4{MU z;kh1zRbbT!!Xqpl4j1*;@*!T}MY^Y;Yrw@+gd2b_gj4$&rPyMm; zyPtl;yBeSE*6Du7@A9eBV7Urq^N?GszX`8GI#1gT&e&>_SR3+1%Y=U0r_U2amn|CktZpbFOSX$Hnrcz*6JX?u zM8tX4s1O8*JJ;vYaBGW_41af15(siu0v-Z>bY5JTAkhQ}*d4Y4N%n3iN}a{P?g0e0 zRV9@YD^SC3e*Hga5yK#5y~-g|*r)QF7_^XDoQF(R9Xi0p{2<;T9XXcZM55KsskH?8 zV-dvTo*dH29n3WbCRkp}1Pw77m-c3aM`&s(Yb}#3roh6FG4HaufsRHwmjQ=-tPl6V z7j*hJdCUE+${FLU{G(*X!9d_1(^~3^hUAdd;=BeWJ!?MrT!mVSky> zhIk_R>;T4KL>C6-Q3P0uH}u-9_0-*Sh!Vd~gFraD z%#6q8#nu^9kaC8{K~^XI#2)&oK6>eB#1 zmxiz9_?e&mrAGkVSORJ8C+Gkym3>9#jaF-eTsj{(!EB5tZ{EFXH^_j)b-KQmhcsIo z-#s9AyTUx|3MNzy-5yDGQWEr&mHN7tM{y}* zP^!u?L!oMsA!F*Oa?5h5&$E=HgbEpsDjjOKzx?E9e)L!W%3uDizxgNbvzqr$U;6Wx zH+>`WUB1z7#=L+-EPIz9ZNc9@SKqB(V=vbi@rMN-?Vxq(3-ks*;pTC=C4@l}8rXrw zizCz+euW;l$M>&}TX&~uUoYBh8IZ#K274$!aQ=ZsTv0XvfpnXA5rii!9~>vdg`%)L z%}PYnMKDsS5KtA}sxo~t4dF8H7Z#|*%UDj<5>i5TL<%MHA)&gW zYZY8Kkxzl_V1pJqvfOqY^1?!sUxwbYyvqVs+V3l`O^>Uu`ou*>Q8(>_6+tfH#fZ*g zFD&kU5Z`_=|5r@)eFNqHuXuRgoh|N>rEPd+r*a=v6uH};1xCi*V-^yXSe(#}8`PZO zM1gI1f79D>APLz%rK6y$tE(~P0$HEmzx#z>{Kdcd-~X-u^Y8y(#}9t!E~`(IhrqFH zSC^FlwzC{B+Ppzy+mS^xM)xosfLXR&p(06@uP`HKj zj6y!GocRlxP6~~CuS6uF${?ZZz?9l83Spmz3Lh28W8fUSA}bOG%2+#|yZ+CB8vF-* z(2c5My&Y&%e03h!c6R!Ca7scs@{d_3yo8W^g;Imaw=oE?GXI7;z!7Fjh#0`7BtLhp zEpxOZnqqQt9Y@bULaKOSG$oxwtH3md2tz07LZ$(Wyr>AGfN^tL7jZJYy=Sz1-3(=qJbr~v_E(t z#!>)e8-u4SnA1^5va;iMf+Lx$V)*O`9KrI!bfl4((}$7m`Z+9ss=Dq2$dU@p`QXNR zUI1C)Uu6d7$CA!r?a}7$CrD_Bzq>Hf%@ig6-&W*mC&BEn481LB015k zwIE=|Z(D(JlnC|(pVqkUZo0G(phOhR08Y!=sVaZiYyJzhk@uJuy7PA;>9#R#w-S^B z8h<(4bPKvl1v6*_R|w@gB23%BYn+=)MY(xx zEChF%K8Fkt--)EJNj0~87Hk4qbw=L1%;g15U=QgDayv{&h>A|~(};aZo&Z~&;wiQ^ zY$RlyKcoY;RawSt*glku=-U-Zl9<~>lq4xC>Io0SKl?TVueM!PPVT`-Qj|(~CUkW@eg)Xh#lLr!!hPKsJb-YpP zIa;qSsG>^M%ELMH8xDo>tXGvU>>lEqI{aSWef!0z4y^pald?+hRtdYT948&tkHx?L zXMTnA6Q92nElbY9d`T+G?QZtjhmu{EebO9JT(C}QoUbj$=8kQ>X2^PTj0!ZMHz=s4 zK=yflqx=<|c)&{Vw=1(}LvsvH7dmI%)Uu=_3%X)PP=nj;vw`kncN;!%GD27Z%HiPC zx7#+5Xah#z3=qK;8F%f{RG@fFSG-R2B6!SSz?HJ3>{3jYL}#ojz#YvjiMU5dQf!?b zvyi)0u}yB_iiz^cGr)oAHY#MRz=^xed1x3l3m5Hbt7u|J0RayND zD%uylYFy46cOZ#L+JG7Isos^jy6~Kb5$5_n{vRZVY}k!nPUf)k5Yx&G2utz=MJb|3 zBV${8ECf(;pRv3oJGXP#lky-b|3!=fkgpTPazV)S_ADx8`&M#Am@SO<;!e#3co}uT z#x(cXfMf(335+al5MA9Bqxq8BMV2Z?&PR<%!%dAk@Z1f!1k*!v!#qr7y3g+JZZ|$9 zah@?;!pwHomX&e5Wz2f~?DZw!)j4bgsu^?$V|h$-AgEHd?}+@GuhIzwP8MYN7`f1^5RIV==02Yk)xn7E| zWodd%wdDc~vzYe5`(uYo))%1iCMGfhc|kLG!|<5~uc6W*Kt|o`dfHPS9qx)*I|o$4 zNe&c9wclH-g5*#Mn8OlDTEZfaT3*-rH0gNc$=b@S_7DS}5=Vp3NDKp2Hg6PHs}!?x577? z0tX(+=uvLhPf;YuTP%>-Y)I*AI-37?iQ&>E^n){yThw@DCxmodQu%cHJs=F^vX(X@ z1wX`|eQq>7s}3)u5{4EvM}5M1a;2)MSh<8_MkgKTf56W)W zoAVn6%3=;d7J1E=K7amq|E~Y>fBK*QxBuhs*V~U;?o5FZy|EmHs@~3a}M;`QH&MIsyqmt$BItuK_fKA4Npw}-l zb&zmTb2Igc!qH4;Y@8L1z)JDY5e zT!Jc!!=Cc1s3@pKyk65G6n=xUEz8H`J2f*df@5Ifbary3IXo_~ zZHtV+s)RRoz9lkP%KCE#l`N#gxJM(BC}f9SRx5-sq8X#LmFL|vVXgL4vXfYY-3~Od z{$Pz30C}7`>MVxc`FTs^0EamO5>|IGji=_biSTV(7c5Rlbx+qS|h;sYv{`~&=yFdS{zw(ox`I%~cQ|>S4=WoxFaIB&1<6+c@ z%XXlx&PHY;+Kdx?X?|P54B8Tn2SAQJmfH@VtBggrb`MO5nXc+EONEK)aJ&RG5bt_$ zUy1?WijQ2u75EnJ+4FWhagp?@5iZCcsPdd>J7=k0yt&<=5m}?^VzH-O^&=Xb_ zRdjAls@q+@Rv5#i0XHTy2Xx;Rx{2Y6zFA9#=xEQXkb=63>O zOfXJvX);Ac`MQV{9dS+688OrGKvgonMY0fWYQGp@km{)8I7GgLko`!z)mh-ps>&m1 zCtgEx?znG$m<%D1FVoqfIaik*2XPe^%4MukI^7d?hpHs0;-yhI6ejjSBKs5w&(y8; zgCg`4!7LAH=vi@yPDjD`AfIc+A@LxzYL4)u!eS&`;=KpX);uvZk#Je?x-3K+o2ZYj zulzy=;2F!MmrmvK314;l^M_UI4=s$Ls_)# zj#4?bZAVj`<{W)Jms2o_H2=`}C-0uCT+M8EiOe5|!-;VYjeo>-5vT z*_jV=TT<2Ry}Dbv4JTE$1fWdT0ng8Xj!+UY5f~auNVvgpn39`)A|-KaW-Hiv?u!BD zXISHJ*?EXCt*JzWwZT;uX__vQIO36kvfTr^I>^o*sdtxBgT=$(h%2my!jN9v;dTQn z*9Ndn9dA&kgJ4;zIAfU@lrtzx%n!lI#^kiaGTa%WBq003rl8?)9bl_u)JAnyalq$a z`+xqxKmYsw{Ga{R^SkffT<4eWPhSozKMMrdlxDk_sZ)`4%`u8ffVk~Ww0;4+6h-QZ zloz-)V{AY{`G}aSF;u)MG~J}4x~G^vdoAX|xcMGy;N2^hs+mk%xmfCN+6kB}0*QjX5`7~cC!qxD-JI!U1DpYzPinLT5aR%5Wi$gK7Q5hHqc12-JV!i zRq5~qeo*Nu-wzfb&kG|N)P<~)O-}k{P}%RA$Le`V0*d)u7Wo4QZBbiC=P zL#IsbTw7@JWJ&FJ&M%*S=kNNRfBSF$-M{m{{@vq;KccKgNGjJEE#B0ebP1Kzhuv~Z zIny#ol0^G0N0QaHV$GujYMd;H$S@g;kdFC-wACOq#y*TR%lNEyT(5w7=<>@ZR zyCbTWVvya?o@M8zK!|Pgf(gXvAe3(v zjv&h&F`0PDTrbVulmfZV*?<$f3FP@r-yPwOJAfHNVKASXLwg3yBXy5|)96HJ6?W?7 zBkObtTS!H=uSagFR92iDLXa^l3E7g#0R{5p2$bf+KIf!AWh5G{q+|lmU|nS{q`{A# ztNiGJsj7U@Ud(hJbO3LM!43$*H8du6Z@jxaD6N?{E(6)h>haQHivT+(GwU5+l$=1E z5V;gj^Q;`eWN=2ID3XFZ9sBGAMS8Bdw_egDrEM;x>&p+(P%dK53RO&DQ{C`ywK-(DV|wT0lA?F&xshp^>S`6NfvXPm5}&qoX3(oF6${T zNJ`+TRFgKQ=_-@6=E{L2zOSH_1V*V~$3WL$(sXqOQi?D_4#6;9`wSd`?;*MUMvQna z;1?&ZcQjEHfh`pIQZiC+y5FPwRfeY=hFeWT*z9PM!AwSpl`vq$yfADG4J5t5SZlp- zx}y=Z=JN&MF>NLp(_7r2n%fX)rdsvQ2v?>PgfNP^1w7Ys;n z9IB$E!#ec##_{F5FQ2~HeK7f$qqMmvnQYHczWe;A|D8Yn!|HzjmUeO4r}ZV#Ah{_Y zJ|x+)EHyM8OJns1>H^VpFho^v4d)oZMoc*%bEyn8&PF7kQ*MIfeL*p zKj;H+NPBcFx^qJWI6UNOC>3+aX{s`f8^yxo*Q^UQ@j!xODW~M>0hpfqy76U@of{KY zLd{0pU&Fi1@}NI`&1Y;Unb>noiNH24gfN`|>Pc7CKoPJ`tlCf)nXoXzSZe2? z7w2c65`aLjlHD9=H_p4p<_D9*OEIIW{J_N(lisq*_wY>^N-=GUJf0a|UGr_6*s*J3B&g&Cff>@gC;@$~VK~R^ z4=Cc6WkgU&qKcqlk}^@{I0Q*jHv03UOta#WmA`7bmX*|myA;Yux(~Ic-w5KK*WIJHCDjre0f>?#EsUNwaiwW4mX?T-K&6_w-N2zLi{3j; zZ5cI0B%3zg`wzAt2CM6fog_FU>5_^OSazpr5Xi2{-0?CcZk)dZ5CdEo1`3(75)B~7 zGV|cUc!=mKAP+I@v~W=~2z&6i*TbT`I_ps!A;s+bmLp94t6tHABw7HqF-X~kDg!R! z-)tudD9QIh_I527@#&R>VL4B7Mj`SnR#`@s2PHA89APADA~kaMPa&rmL52pA(~I%i zG0Zao9d-Eq%TIs$Z~f6f^l$v(Km7T-@7{d$cc0%sy&pcxhl;jrhi~YPwIrhVmHy@~ z_|q#UK7R@GtAtQ<DF$Z{wrvxD5EwxK(ML{MuCnE+%rc_Le}F;>mzD@D*+yJL}UY zP zQque?>H=bF32qZOnq4$Ts_!qh716m|#GDA-f?lQ)6Dlk1gK*g0T3^6Rm+z6ntb1i6 zO=8*Q$Zyzr*t`OBl^&@ZuHX>jn)KH;(hljnn=&iq4Wx_Jg*N23G8%0E7~mxzT^;hN zuMG87S<6gtU~-7cUc!iMlIAWRW%V2~mrO%dGJ_~&#fKW7bn@oMyrOMK~a^mXq1!%+Wbz&SYRz4)2kD z5-azXtKK3(LXzvKv5^pgqg8MaYknZs1sKGavRu}J1H5}jOA#Y|AYn$fS76|7foZ7{ zEO??OqgeQrt#7`{C5m2#EZvd4=L&+#JYdOtO9YFOEJHDKgsK$oU5HsNkw60Eq}_D1 zlQXi>Z`qSSNou3seSZG*kNozZ`_(`BC%%08^|$VRKR`RGz&BvGZL6%)BFi#352 zk3q)3Wrzs0mU!Cdy4-$1!axhX>D)LmV*t;dbCGVgHlhG%8z)UeR1fmHLpI~SiH^}4 zRxyJt`vOEh!K{y0gol!s2zCaRcrdsdUo^SYpJGSK1sS?e_pM;}NbknRiKFn_!In{` zl|CyJr!8uvX?%UFIL*3c^1fLhY3Dzjh_I|wE}>)jnEyptkYwC@e7KgmnCa=uCHK)r zQ?#nApP%G1o2L%M`8*YqfJ!$_M>O^v+^Z;;uafuy2=LKG19doU%LEl`p0orvIJc)K zPzNAGM(84fS1*{c3%m@-XoDIUvveUJp5FLsB%HKO*0R5UIyjS{5{BS05?bl z)u!@rOE)w*19yRaCc0xHwIubI{`T8XmVLTa99h~s>%HPF;Vao+zWuX*`j7wU2X*u( zsnrej)=E4_JaPKabrY;{yH)NhodS7^2Yi}FI-}bhfvQYk1+(|O&;8>T0Gfu%KngX3 znm1WH!(5QUdvyds8epV+1`5)VNXAGkY$TPaNI zrY`8VKum-bCbI#dyN06~`lD2`14>)glRNfWFRDvg*G`KKmRv!qAr%b>mal*|-RU)I zVA{tdC|PlfJ1ggzv#uJ3Dqz+~3`ra8Zqsv96la6xS0OOD!{8M-@gQ3$viN-Kj|)#Cg5qfrIX<{X;MZxPFlc!LVPtn15%RhV0GXY*!T>0@ z*(1!bHa(&gg{a742ni*&;_SnSI%FHkkZT>uZc`R{q*75Pwp3M$*9&A7$}fC5&33AL|H(yxuT2nIGlHjin>NRfGvisv6E@R4cDj-> zRFd0L2r_;HvtM@ORBih6zS8LAV>Jw?9~)jcVjzYWyH!UlDk%YeV+oKHLmO0EH8}>u z`4Tmtj$(TxA zkd-(27BW?0+#!i3-**W)=R1SKb(HDYrC@w}gJGY}`M!%XHtOzvp1<@f zzw&?lpa0iC`xpQ6_|Bj0K4j2=;++wwur;4_lc>?O#H_Hhg~^grAQ}bJ$iLi23Un0+ z&(dF5bqM!H4YC_Kp&F_c()dHCo6NsQ2t1TXprlS&R zZkbXWlFIks$>dP9a6yq>Utv=m_~aNT0xVnk1VXLbn^2gngq>hPuU9-l+_pkx z>1I&bcJw=G<;GaM{_Fe_7hR1zy9cW!Z#e>WqqgC17N8JSL zO0g|dCE@ctg$*4U+h=bs$T`-iKWx$**rzdtaSo@T>rp=R^z+L%Kl$Mg|MDOG!5==~ zz8pII-ah;BcpUl!Xn5YC0?P)AHG_&J-TJbiajLD)mt-`3R$;^SpxKbX=n%tZVUl6S z;8x^rQ?UD5-5KKQad(ew3qEhQGaMAuH`<8!+bV?q>IXf=Nd6%?{46@D3#tWtYq|h+ z)vubxL#{aK1cUKGfrSN{Npw4>-2#;Gfih}CY>lEhwOUEOsp#9&G#kv)Euai;w;ODm z_#-c4bI68>u;_J4x*%c>B7T%Lh*lM&uayJHdLVgN`DkSdt7ft*XoNtXPG4F<7x*IX z)mbZ({LXB4R;4a9RvbS=_s%o7(6TlSpt?6&MI)U#L%gVNcw zu@VVzqXzd0d1}HgK`mpf@Pe>skI-};U#-~$x80_jb?!!0L6v5av%WIDtLn7nPX$@H zG=vkHXc8K=N(tu$0@0PPuXKVuAkpFpBdNr_&y# zBxcxX9m)pva#}C}SR*c#0^9aN3T+DEB8S%Gn|Zn#(KKU+(F;{6Z9^&0NqrVk3-dYh^7z}l2myZMI~k13Qp*qP1P0@1g&zcw@ePq-cYDf z-^bIX2#~T#*_A44o(dQ(+EJ)Qtx-r&LqdcU6G^Z`Sr}A&Xx|Bq3W3pn-iABi$`7ec zksI|76}JYFkpmu~hY;g?RYys9w3#|e$eN+JrQhhsS5#6hEx1Pl;Y{Td)|4*kyJ*J` zFW1aa3qW*(GTJmgc3wG!t=H%}U8VAP6HUyBgRowM}5KX6?0D z1dE&n7+TjON+;}mL(AG-Rwe*lK%&1*H)kBOyIsmg+qRiOn(p#!G*Tg0p8-g)xGQ!5 zwX6|#g|I3IGM;XT;XB^Dg=dCbylGTB%_he2D10?eo3fM$mzT+hYYgax;U+wnVDB1n zfTG5D?3Qy841E-=!-}*C8y1s^9TF-DlMgZ__+(=8;ET8=i=t{CjLSdh6P$gfrBW3w z$`tP7IG|3qMdZ>$D&{)ieEYBeQS31JRFKhP;#u8brUB!g}^(YjL1xy5MfTLj^4JXgZenL8QXl`>N zRynfDrYh&#S7#-v(4=6E@LnQE$>TZM9YE#8w z9d@<0c4?Gv)Bw#7A7IT`kYSuqsL1^ciMg_LY>gTr$F&GL5z=MGM(cmZxH3yG3?S8- zA`Asi&<(-Wdlh!N=Lz2*s&)^F#f&wiB%&ceT0BV8?I?uRl`HOoEIXnmL6p6_U!*-k z?kYR)O62zR+p@uadr7k)Fd@g{FO~ts9tP3>(qY<#jp>;TKPEwV92|^(6t1LAY}Y7C zTkwGjwPA@`xP{jIxH4fftwy%+WWv0Sk%hsQ;Wm>k3Q>o$44&FJXi3USH3q4)X7|Ol zKVY-#WH+8&tCeb&rYM^YoZatA=ZqpC=xiH9&QUh7#_q;#3(uLv^qR3TpvbW$ARMKJ z%Gg)|ySm-Av0t@hPAu1Ox6Cq-!${mpXltdzGmvFRY3aqC76IpZGTFhREFs~@Zbe8I zwq)C!hjdt*_baXlVC-GIy? zt1~dfh6#gX&V_usOIV#BI^C5k-Pla_gE*5+n7?XX z0GeivqE;HYhnGvW&fko*1$T$o*f&++J`T5TULbHREN-BN&1v!(AO>NRr>r4|xPa(9 zaZy5roX7sj8rYyC@dbmrAYb!uP*ai$4(XwX-u2B--);PqPfpL$*0d1zfuy74^PA5< z{yTsBSAOBUe*fG$IS!$$Co(%o91_r!?C$l=AW%7ip`hRGOpKn70zk!gq9`Fx%(ILo zgr%Ay^a=2`EUT_;dXRDIHh3BTsc+)ifOHBCc0Q zSKG2|Hz#15#%`Svh3>s7DhirU8C+zfxAXfrb#w!Lp66VtbW;(eq3v8Z5c`t36vFS7 z&$SS12vbd0cWf}YplF<4S-}`%vsMAO`vaH z9KC^WL(R~4BH>Izx)up(4W|asZJ;ZayFAcN-?piN&eAkU*Ww?;@$*0b!LR0N0HOaDJo<{^e6#UGKni9dV9eUt1K#UaY;d5N4F(|jyl?-@Q_}~j2>(b@;Idw z83Gvp6@rqAL7!N+_<`|OV3=L?D-)d(gg4a(hnIJxs;Y8|19>iRC`o%o)R?K-7XSbt z07*naRM;9*yNWVaLt7SO3#L|L67TyE*2@t{l3nx*Msbqq`4|K4iB`RY%T&s<94Ldwy!D z$oI4nLqj*`NG<~b-1nUX3y8p$}j7>7$hmkSU~d(0pvCf?HY zxmD=ZF<}J1!wKX5vMVxqKo+q+U1mJva#7!IVmnBtYBsXDaf?POk5+t zAS4h8nxm3EnWiZa!54ViGVmZQdkeJ&62S1K?$VN4j>viKk)5^ZAb%I>vuN;3~`9f zJnFeFIBYvl!7Rvm&Hot~fag(7!wNeZZbUR|gbs92ms&?=7{FA57nXY~Gdy=)YO}A} zdT@mCVhp^7Yno&aL&0Qn#%5bSyPK2dDcgMl)>){|;(Yt^)Bo>p{@#D`JHP!`{Qmyn zQ@@>hc4=)HhoN{JOR=6InpJx@`-#(D;Wl}Z*`~wR)b5mZMMBxC3x+AzykD0;hVgh{ zYed`CV?m^-JHvNH#WV(T8wa!82fzy~>J*r41qT4*WwL^5=FBADMLi%l=;Xtr3%EQveJ#IQRU!{9pCX|oC!}h8iuB>6& zH!Y&r@g4$laVO2em2Hd@7E(b_wT0Y9SPO-lwBLxwQiAwwhVYVMQBG95$u{N_L}O|B z*QsMBhwT($97y-6TI7~%C>`WP3{^s`{<`#)FUACvUxLbl1TWY$@`F>PGyRrLN zkG$Fz8uMmoh&(<;9)J)kVz`+(BxK0v&>PAWnfb(gG@+0Vz&ce2RaQb7ud11}sz%*H zye?is{MH5-?fK(lTD9Tt&b&8= z_k`_NIDiZXMthlCa&~?|r`aa}$jDCT+__4i>p-9l%k@ zzxe0>!H@sg zU!FSs%{MKwDd|GI8Vw49z>iy-#ASn}>h_E;34~_PpT5^<9#I!_OO#X(k!1?6=z+mD zwyl}$_fUW3chIw>yEQBUcaMkxAP4Kx#bS2(ZHyc7Zs~6*?Z;RTv62$8(_D)v971s;Z~wnHHP{&DvF) zPN3;T8=%X5h%VfxAAP8Gz5$g5o-ChO)z$FCJaow2Z=e6tZ~W@-{l|ZU=X+^Ydj@$98%>D26jg^J}kmbx;pw`C5iF+N+lh=o$;pjS4W z>nuCkyxd73T0Rrz-Pt6xecH~}N=Rz39$An=SGh#B0P;JjuI}dSMqTt=6@V)9#BkO* z@E4{dP85<42}JJ2ofXN`;_U%8J8&lo1k8>^x zkPgv(#GMunjV)F9IK;7ns)*T{)rjaz3>kAqUA9GG*#W#qJWa>>U~M*Fyq#1EsG&RQ4mWJBwqYC@3E>f(#c?>9(&`v0ca|2h zyxmp@!SW)`o&G(qCFUlT;De{jI^@{BW_V6x4#Z0;4lxht%XM(XZ=+{G?g}3l=Wyc+ zWN`2&NKLDdBxxSEosU@MstmBrIvw1qBWl(7sz#syNq{Nak-TPuftPEeAlq&fiIV~| zR4xg&cL)h(j!XMZO2+CU18D*c3@L{_^GRn?L+p|JlF&Z~Yt9KEUT$^!4d+G-?~~aPq@yLsOZRb8IjnZ=%O~ zPZ&&LX8hMw^9S6nQUJ*5L>k5l%cVSaTDrX}X6r8_FIr1&{>L&P!yNw2@O(vsWNcj{ zIGDBr%vCGT9r_=_|8_cQ}R-$g$H(n=wGhx<_=(eUZLNc!g;g zQ@p&GkeOcKn+D95|4w_-jo4Mv{B0CBq0p{h>KmwWx(p?oE=o|)or7oSuF8UbmLT0o z90jgAB3oBgfvKwME?2%*v1#hiA^E62^R+)in8)$eNRtOMwD`jqPzc8FsYVPPvKmiY;aa=*gy(*Og@3r`4*0 znpvAAuFQm-vA8RKGC$f1P}P*L*}#<5?S_|c@OXLws^;qyn7a-Dk!9*;>tmW;QC9?KkulRVf4*Dol3}gTXA*ef&)j64bhFc)A#syV z+>|XKlg~{GgK+L;JicCgh?)-fH--$y&wuZSsRXscdSu$KD=2_P?=MJZ2N)E0kybET zGD_Q)mB+)!2{gC?TslhCOLmjh|EWm~?n^o8xl#DY%Fk|X#n{pwKEfH?LH99T%q+%t z#cTbQcKO!#sy7l=B544&2I8jv!y~zoNHQ{LiL!fdq%Q%c`=EYN<(;&m22K83lbNv*ue?`p7(hsJXQ)Pm zK^v7|rN;|(7(t}uXP_DBiYb8YTnhy0miGf2H{D6Ioe&J#uU?h+V6d$r&kgIi3nLH2 zho)8x#Vf5g6L(xdNmn2xdKX<>*yRDi)VzG7avY1|-G(2&ncM8EOA7!)$x zCt1=g-l66bzzMri^fp+&%Lh^9;Sqsl#WslqCFRI${4?M5F%f0{hjxP5m^cJpyzQmo zD#7v?*kSfcgHbmDZ)uphql`|Z&QZ7|@pN(x`5>ING^rAr-+8mk*pQz`kn&KD6L<{c z%f`fHqHGtB#J$R&b*Lol-m;WOy5ZVTWMw{l)C8`>0yNMvd3MbttoK<8HIHGSIEW%> zTL&e&&#Ey}jqxzm9-}+DVrkZ3vV~niGqr~bRY}t5Q_^@0(91REC23H9-SDTRSP8I& z@Wn)URcTB>Ot5f^k!IRR{ieG3`;e}>P|eU;W_oeMk4f{Fx3E2&$6qV_4^Nw=Z{;LD z9+)KxXh)JARPX5ndntt?3RB+#@S)I&IR>N|(~CvTLveZJ1%X^bLUnvx=nAP~C@MqH6R1TWS1^{k&lTy1OF9Jl5ljY9P=G}RT7Y&SOEe@!k0&LlC znWBy5f}?!8pU?j3PyXM(^M^nBjbH!u>hpY_es*=i5n*2RZ-f#L{i>jCStmE_z(9uO zt7Y7J{W1FSF6VU~44gExWnK+oY1b**2&@D0fz$ArcxET&MTR? zy9kF>bok!xPlO&!LTL&wxAdW4?$ndB06XVBo5MsJ)A7puKX%rzxlt?{Qa8W%y#O9z zS9&R|wCXm-#D!V+0Er0quHF$5P?$^arNmc82C*SdJnq=1W1}_h%yL1S%<0HlJWkWX zxdyow0WFiRE`lYFy-x|TEzJ%)5Yqs#sR7Qg41d5)3nS8c8Iz@UB)R>E+0-p!Q&?#i zBFl&BJJA<_EzrDPa_n3((P|jMlhufJ{TDG%x-=2UfGx5YN>rx}ZZ&ily`E4J%*iX{ zA;Ox6gN&(iOSzoXgazk8{jzw}L2;bt=bwI7TP?Ssj^m)ML(NkMh*BSNb^rbU;zx(h ze!eM(#3z(ayACzdyQ|4g#`Jk2RHAC0`Do_NLQ)f<+Z{DV|^>)HaQ6!yW>jC)gW< zvl5n&z){Ia;V>ki(MYzE69qyJlI0>J7~h}dc+>`5@Gn?lWR_vvR^SN+5Z6nU?Mj6} zqLowh@MPQW)5?dXfr5%Ix>E#7WjlvrGF63CbufEV1d8joC7QN7Pv`{2EIuZUzpr@Q zcKbBcsPa`IE30l-SGVyK(c0JOPt!EjMU!g;MGq}{9=p!48)WOHHy3ne%)KVr=CpAJ zZ_=R3ZsJ=~S~UXP$(ph7CFE}Oa-VKbH8MEU5AWPS2xsri6*fp;u8zV@|h&gxOA;73x*-*&ym4)E??~vy42EUcu#x#ZLxK!13^n&So8Q(-V6txtLixo((0RS z?4%rz{OM_Vm>eV%WUYBMFM&{qGPJ{-E@sKILm4hrIs89Al|OaSMlO6BTaVWB`Su&X z{%b$_{r|Mzp8e$g%a^cP*%0rY60pX50w~e+PBMtSUtMjw0s&;|Sm4V71FD;VAGOta z*#XK4yDWi|&7L`W5OIRb3M{mV*tco|EY_xb0}Dp5@s~ld5G~TJl)NdgBf071%3b&%>?_;_N93r=hJLXBZ)+v7A zU}}@5HiKbRY)+ytv1L`9fx)0!?g$-kD-~T?b2#--eVRN)w;rMc5C@+SbXQeYcnz|u zySj@y&r_C`cYgaf^?dtOD!2XaGe0rb z*4cUvZ7hyMwj9Du$t_L_+x7IpJfkdk8LR?hW0z=jsAv~rb zrmfCWKF@M&t+;R$Te4~nh4UX{IO`asG~ni_Ih>HnR%e4^X2gmUIWb5jIJ>eNu(9mJ zAj~;6BuL$2@8)miC-?&}01A?o-MJ>c;b>d=2K`0 zX9(sKDDJW>ozSzEN2tt0I^a{jfUlo`&sLjmFzR*BJA7a&zw>}3uY<>H>Z0>o&Hxq` zI+R7L@Y8+LwtMz&Ui-6_`c(GV6N}C>>-VbpgQAOOozd3=Q0zo$#W`sviwv=XjJB#> zw~|YKVBx~!3K)@9Z$5CiBizlo*T+lwIi!N_oYPew4y(McKa;dCcyNeBz+ZA2K2FPey*q7=Ue zE$2>m=j~ODso%*ZXTd6GXWvq6(KBvtXRhW30Zz(~P~6j(ldrR%<;w z-aWBqCC%SQ_JM@N*ZPLhWsVY{=Ha&;JdzBGmjK>l&7-Zo^R>+a8)jlPrs7g!e9#w1 zN+rY~wS)AG7s1#skjwo?W|b#*Pw6D!#cSeQPBny0Fpf8wXg-Z$bxdO0-RIsyw_6~} z4V;orZ2Az@Z|C{t`Q@7*|J^_OrSJc;pC@Nm^FT#KoVgyx2o;FK8ogZ1M-TzyYD-Lo zy;b3H{6H=U7Vm=;qSQpkRZehQ1ja9S_mrc_K`kxrvd2MG2DEB3Wt$nd@IZ`y{^$LbcOLqcVPf9%S(eHp>v=X z0et7dj^9u4T9R#v3>K|bmj=b)%8VC%ZzU%s)&a~VuW)#|xE--WKfGUi2HR#KiDDFf zVF^0P`%kdPJupKlnD9E7h0#g_+*ltGm)m;%AI*gWAnHwaio}nm=Pa*BOmRa>BACB{1Wc0?|5=BN!(SE=K#kmTg zAG*S5WvnY-XPD&X%ujYvA+ojI+?QtxBk}R7fSQb(|e$ zx-qizAuOnI`4JUN&k3stprzSd?0Pa|?xlt03DpQ{zGRu1IiM+bQEJ)P)EQ$5h=n!Y zMgoGcZIc=R#oC~*s&xyKrty^I&GsU&vx-ZFL(>6>AQ$W{-E=Y>PxY>ytTg@w!!yl1 z0EF!ZOLd5&j;g9sdVy+KQI!=1KyHp2+3oo(liT6^s6rIjrk$kFPIA^QwA-g(&eaR( zrW=|koC8kHqyJq|4nW6Cq5|XjeFNF;WU^7F-EQatjJk?kNw~-{U~bWtW!QQv2VkY- z`(6bVo*_hE+OkcF6}N$8H{sh#CgwHRAa$n6xQnvl7c!|>T10u0Gpdy&l~fo}sHj7j z+7EIF4V+RX1?56JaQVNf|?a z4Z}fy;d&+hAuUGkI(2qMtF$sN$=`~1>+;*`fw^}ABrzu~r=aF0_Jv%yU+6}WhD5s( z)jC=QF{YX0iIp6MY=%SzKG>GV3!^Y!*Y^c8$N~&l+}85C-#<#L!uup zRjD}3eZKwHZ~nzU`2D~2{^e)q^U2xYsmDVP=}4G#K9^I{M2PCLDmR~t^YO8p8Vv|! zBFb>4F{ClfI33t6@hur3E}OvA^S9b&1YHBkTp?gPi59H_(0xP%$@+g4l8ZnWD&NVj zG|)y>guPT16V{gW+%)l$TR1K?B_|jy?}pWM?_#ztVa=N($C5?Llr~d4#tW^6Pmf+M zE`1}T{4EA%-eeHuJ-keaF=%l+K|a#FPBDW@AZGHA$jXv6c1FCfEXQ;N$nznzl4=lPLu%$Sah8x~#Sv*(P�gZv%WI7 zjPvtXmG9i_xjvo&Pw&-V~L89kTW?%@e#2>BsLr2 zfxoBT3n!b8uAaT6Ign49-*l5UmgMGX^8OA#O6+dA(z3#qCqpI0OXjXFxgQhxivb8#Y*Bi0cRY}^gbuUY3VioJirtU@LlbD^&ci&V_&Cn=l_f7b{ARVIR2yV$;>OgNL;?HO`L zr%cbqF*Yw51owe33fVOS z1x64_?P(ZxS7z&0%$1W^rzq9aI0~BFCMAZEjXO#m4yQ}(;8mHHOisgv0V|dcs^A}2 zG^_}>UWOs1Wu??kTO^tuc;{AWwJ$$O1758w=c_FkhP%4~U#0;E-7QzQ$!Wrj z7KmSE+AO@p)WHcc?kX?2O0wP9ohj^U-2|s>n6P?P+T`B`n9L+0jWwM^F`NgNg`|lI z+M2RTsYfs)cW|c5NF1Xq~()FYVJOBV707*naRCdBC<}Yf$ z+v{e@M7%Nk>OOkeZn0c9|HJRVDRK0MaQ?2hB&~`?;o^pkK;RWPckc~2uc6Jj*#;Km za<6vy-{7TuBjO%6mK$uq|^rMv|5T1V&(PGXk4$A}}L4L1NG*aLsHuxNf0mtKJw? z*ho?^dWB{ix1V{OJ)+p_;s$i~86-F>gl(YVx^2sD<}Tm!`w3;bJ8BbDDaY`0WY$^P z-{-G_+1{?Jo-DKhj_FtGvOv|$%#1!nOK!kY)nmEUC{ZOzRoS}4r^zt5QZU*iw3+PiaH@TW zw=dGWQiEfcfN*e0K*Spt{L_-OfYW1pW4hXeH7&V;?H^YKy4k>95`u+{L*rsQa5wJ0 zV)G&~y~cGHnoaIQ&y4Uj0~uyCZh()J%ne4#MGVqsn`BCBqc06E|o zS{ZbZV8k}Mz#=0TfVKKJwxt1|Al7X+WI-S(#+n96W7T-N?s99ag0KYkni)z=)G6UL zNUo0ibcUO-7tA72_OLkemxY#)BWm6~nn}p;QSuqn$)jU|C@2+%d~(?t&d<{znST5H z+rRbi{K-H5$H)7-p6$1Gch}MF%o4(GAC$t7VeUBgFnJMV7q+ghBHT2ycmyz+YqvRX zK+CJHWHh*6yoky84!C%F;q&UH=;}fQ_{H<@BE2SYDSC5yqyEzO!m%A|)p~la(uh0b z7T9$1Rs*}5mqRG^9>~404fMT-hl!~eps&)hZwfF(oZP+Jl!o5SLocsc>kP2H+<0-J z2xlh#D6}^TZ!loSx#kaJt)>5hd!^3A0n$(Grr~PX_~?OEwd+S|GJ7wPPJ`{rF4^ww z_q4QlZ_u}Z&ItODmJq}FO1t;8yr8PGUE9kg6m^U)Zt3RIT!N&l)_{8Hw}Mu}vA+`I zrsC11yGu%#11AK#Mde||)aKkMPLLrlIShJ^JWrie=^gZ_?h@wVgy5=fKm4dqpFW*f zXt@O9Y@eqa9VHun`3Jx8-~A8&eRELXsSt6^IqPZ32SSz46&I)vtK9RkpVEP$#$r-| zY0E=ZP5fnRex^xWJOCkX7`Yg8(YKEq6ZMnWLQwDtG5|XclNpqS!khe6>w9 zDXnV&1ucNQMquaCAg9!|qfXDFtE)lfOna9Q_Z%Q))?7}I#;<@=fA5(Hl`{l^S#f!% zRmkwC@P!_i>2qpxnPZO9iGP_(y|N^w>*6`p|-NL7`P#sXt^kYu}5)Hq@4F%e`!%nH;}w0cb(H5zhA2MnyL z_{I#sq>I&^ro$BOZ(@b7S0K< z>ew}Fs>&rRIS7G7UVG8aU~syor;U|`s|h=EA&9&p!AjHMD97VT>BoNElGvfBaZ@VY z+JOm6E0bg77a6DIt6Flci#FlqR3DSQqy%!WSM(Xa0q2|*tcmVzHDKi|;52@e?05RT z&;H}T_g{YR)6eN?`{0R8nL^q14~j)D^H9ZQKBy2vaq@<-%@ij)Y_0~AfTZ1)nD42d-iL;H^NMkZOk;#!UC?S-uccvDfz3!TFV z%UKZP<<}sCOLGDxW=)M3!2H`JasHGxK%u1-&2Bth0@)$F(QpOhZ%GPA8`+NXfZHfF zi8ADx7<dwT5o`#HyRkg>&9p#l4o|o!KUMz>qZ1@&)-+bYDp0u$J zBJlCxfx3O3&n)*`hrrHRFyhQlaYd<;-0su4>k^8iRZ2@Mm*#WBs;M+awvZ0NX>0Cx z%br2xZ6+JLooA{WytWyjSZ-MwG*87}D!y52D3b*v(@l#{=eX64WZQNv-@QkOOQ==o z$Ta*4mi(ca!1fXn1saqqqNIQLzPP_SE$@ndWXXJ!G6*uXhu1I#kt#%;%}yeT?YRO7 zeFGlXAHrGnIv*XjWe)8}T>!@F#Pi3+sT$V@?47OU{7si#dA2L0#_L0Pu`4mxOq}61 zqU=ayV#HZfT=6`*&;-rU*pbNKJtVJGX%lb@0J@Re)wYdSKuGY+nyso#_j#i9OgAZx z3ascN33Hz3tkYuQo~0nsoaf5LJwE=?k9JzJ-P&cgK&c4f11H6DXU-BmKO6G;}&DMTR8XSXtGj*W`1XI5S!c%Vyd+h<6>JA{U>9uzI zYDzuZ(4@M<#elo4iay`|*5CfEKmNl%eEa;ZD8IeiT}W>GG^H_PX4RpZ4BZsh zBTP37xf@Hw-x+4b^H zF=1f2p3aCK0;mT1*N=D{JVweJny^T1~?% zdrBr-5Z!oiuA+sw;bQN$Z=T`kLRyyzr5FIcIi zH9=VZfU}B{wY6iAS08R{Z8q*%Kpb&cm~LB04plLovWN&)3zHpe#c!L5V}6}s>{-yt zXxb0XuZh=p7f5KmQl+31{7uPa$z8&>eEK{s!OTVZQe#Q6evi$7N zpMU=6e^!2eB_Cv&>?|51>DhfqIwT_(XBOPshXDJ4&YFly-7yk$BJA^7QB~}2jf|sA zc?2X2zN7A0tO1oK5Y63#AOmVrWhvuOs0v20L#pNde#^`la|WZ-WuVAuCpb}0H_A?z zfqW2kAzZcvwsAdBha71z(l|Z;+W!(-FX9;PE#&+ki%>(sB+Xl!YR>e}TX8ZeNbPxb_;BJEmv`aHwov?CkS0cRqneh`$&nt}9uT58 z{+I1nOIDlCV}_BZn+rh4yR zszxm}th+=e&UvQhNN52`GexU{jw)=e#}+1gm5vF6-Bl%(q*7JMZbuDiD}AcEn)?82 z&;|4|Bjsw)u#L+*dllSZEMg#~87OsS+=_*E;gsikFg0lpCZKBquprgeRAKVGNKLQ+ z-$YH}E3=D$1i0vm^_rp&tI^EnmMR9}`aku@qSo1$7i#`it7zHI!XaB$__2c@l{ zwf-rUXVVvM+K{FiR`hUF!x#xlu_Sou#Pm(uFe`-%|IVmc-IG?v2LZ7Lqm81($MA9M zk;zJ?lDxIE^5Wqh6K&z z--2J-j957Da~zx$rYO7(`WS$RGn^Jc%zIbvVr5BhGdzR2sG4PganThM0^JiEQnn}y zY|lWrbh}$L%t?SDd?{`6f;y$&0TU3evakqOE6%0DSeUyJn+#ZWt#FNp`VDhviVxg< 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

        (i-T+3M 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 {-Lma< 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[Sd}WtLf#X5$uZ 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 literal 0 HcmV?d00001 diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/AppIconMonochromePreview.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/AppIconMonochromePreview.imageset/Contents.json new file mode 100644 index 00000000..cbd838a3 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/AppIconMonochromePreview.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "filename" : "aiden-app-icon-monochrome.png", + "idiom" : "universal", + "scale" : "1x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/AppIconMonochromePreview.imageset/aiden-app-icon-monochrome.png b/ios/AidenOnTheGo/Resources/Assets.xcassets/AppIconMonochromePreview.imageset/aiden-app-icon-monochrome.png new file mode 100644 index 0000000000000000000000000000000000000000..d07186add514126e741d59e56d5ea477bf1b6b60 GIT binary patch literal 53942 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 SV}KTLQ(9_zs+9=au>S#m7Or0a literal 0 HcmV?d00001 diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/AidenOnTheGo/Resources/Info.plist b/ios/AidenOnTheGo/Resources/Info.plist new file mode 100644 index 00000000..78155b6b --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Info.plist @@ -0,0 +1,77 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleDisplayName + $(APP_DISPLAY_NAME) + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleURLTypes + + + CFBundleURLName + $(PRODUCT_BUNDLE_IDENTIFIER).share + CFBundleURLSchemes + + $(APP_URL_SCHEME) + + + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + ITSAppUsesNonExemptEncryption + + AidenAppGroupIdentifier + $(APP_GROUP_IDENTIFIER) + AidenKeychainService + $(KEYCHAIN_SERVICE) + AidenURLScheme + $(APP_URL_SCHEME) + NSCameraUsageDescription + Aiden On The Go uses the camera to scan Aiden Agent pairing QR codes. + NSBonjourServices + + _aiden-agent._tcp + + NSLocalNetworkUsageDescription + Aiden On The Go connects to your Aiden Agent on your local network. + NSMicrophoneUsageDescription + Aiden On The Go uses the microphone only when you start on-device voice input. + NSSpeechRecognitionUsageDescription + Aiden On The Go transcribes your voice on this device to fill the chat composer. + NSSupportsLiveActivities + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UILaunchScreen + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ios/AidenOnTheGo/Resources/Localizable.xcstrings b/ios/AidenOnTheGo/Resources/Localizable.xcstrings new file mode 100644 index 00000000..00ebfd34 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Localizable.xcstrings @@ -0,0 +1,6 @@ +{ + "sourceLanguage" : "en", + "strings" : { + }, + "version" : "1.0" +} diff --git a/ios/AidenOnTheGo/Resources/PrivacyInfo.xcprivacy b/ios/AidenOnTheGo/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..0a95d093 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,23 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + + diff --git a/ios/AidenOnTheGo/Resources/ThirdPartyNotices/Hermex-LICENSE.txt b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/Hermex-LICENSE.txt new file mode 100644 index 00000000..7c6ff3a2 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/Hermex-LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Uzair Ansar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ios/AidenOnTheGo/Resources/ThirdPartyNotices/KeychainAccess-LICENSE.txt b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/KeychainAccess-LICENSE.txt new file mode 100644 index 00000000..1c519dba --- /dev/null +++ b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/KeychainAccess-LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 kishikawa katsumi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ios/AidenOnTheGo/Resources/ThirdPartyNotices/MarkdownUI-LICENSE.txt b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/MarkdownUI-LICENSE.txt new file mode 100644 index 00000000..8f993169 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/MarkdownUI-LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Guillermo Gonzalez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ios/AidenOnTheGo/Resources/ThirdPartyNotices/NOTICE.txt b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/NOTICE.txt new file mode 100644 index 00000000..264a11ef --- /dev/null +++ b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/NOTICE.txt @@ -0,0 +1,17 @@ +Aiden On The Go third-party notices + +This folder contains license notices for third-party code and assets bundled +with the iOS app. + +- Hermex (adapted SwiftUI interaction and implementation foundation) + See Hermex-LICENSE.txt. +- KeychainAccess 4.2.2 + See KeychainAccess-LICENSE.txt. +- Thinking Orbs 0.3.1 (vendored SwiftUI iOS port) + See ThinkingOrbs-LICENSE.txt. +- MarkdownUI 2.4.1 + See MarkdownUI-LICENSE.txt. +- NetworkImage 6.0.1 (transitive dependency of MarkdownUI) + See NetworkImage-LICENSE.txt. +- swift-cmark 0.8.0 (transitive dependency of MarkdownUI) + See swift-cmark-COPYING.txt. diff --git a/ios/AidenOnTheGo/Resources/ThirdPartyNotices/NetworkImage-LICENSE.txt b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/NetworkImage-LICENSE.txt new file mode 100644 index 00000000..925effba --- /dev/null +++ b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/NetworkImage-LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Guille Gonzalez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ios/AidenOnTheGo/Resources/ThirdPartyNotices/ThinkingOrbs-LICENSE.txt b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/ThinkingOrbs-LICENSE.txt new file mode 100644 index 00000000..0e9405a2 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/ThinkingOrbs-LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Jakub Antalik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ios/AidenOnTheGo/Resources/ThirdPartyNotices/swift-cmark-COPYING.txt b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/swift-cmark-COPYING.txt new file mode 100644 index 00000000..db88a81b --- /dev/null +++ b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/swift-cmark-COPYING.txt @@ -0,0 +1,170 @@ +Copyright (c) 2014, John MacFarlane + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----- + +houdini.h, houdini_href_e.c, houdini_html_e.c, houdini_html_u.c + +derive from https://github.com/vmg/houdini (with some modifications) + +Copyright (C) 2012 Vicent Martí + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----- + +buffer.h, buffer.c, chunk.h + +are derived from code (C) 2012 Github, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----- + +utf8.c and utf8.c + +are derived from utf8proc +(), +(C) 2009 Public Software Group e. V., Berlin, Germany. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +----- + +The normalization code in normalize.py was derived from the +markdowntest project, Copyright 2013 Karl Dubost: + +The MIT License (MIT) + +Copyright (c) 2013 Karl Dubost + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----- + +The CommonMark spec (test/spec.txt) is + +Copyright (C) 2014-15 John MacFarlane + +Released under the Creative Commons CC-BY-SA 4.0 license: +. + +----- + +The test software in test/ is + +Copyright (c) 2014, John MacFarlane + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ios/AidenOnTheGoTests/AidenChatTests.swift b/ios/AidenOnTheGoTests/AidenChatTests.swift new file mode 100644 index 00000000..525ad59e --- /dev/null +++ b/ios/AidenOnTheGoTests/AidenChatTests.swift @@ -0,0 +1,529 @@ +import Foundation +import SwiftUI +import UIKit +import XCTest +@testable import AidenOnTheGo + +final class AidenChatTests: XCTestCase { + func testRelativeTimestampUsesProductSpecificBoundaries() { + let now = Date(timeIntervalSince1970: 2_000_000_000) + XCTAssertEqual(AidenRelativeTimestamp.text(for: now, now: now), "just now") + XCTAssertEqual(AidenRelativeTimestamp.text(for: now.addingTimeInterval(-59), now: now), "just now") + XCTAssertEqual(AidenRelativeTimestamp.text(for: now.addingTimeInterval(-60), now: now), "1 min ago") + XCTAssertEqual(AidenRelativeTimestamp.text(for: now.addingTimeInterval(-120), now: now), "2 mins ago") + XCTAssertEqual(AidenRelativeTimestamp.text(for: now.addingTimeInterval(-3_600), now: now), "1 hr ago") + XCTAssertEqual(AidenRelativeTimestamp.text(for: now.addingTimeInterval(-7_200), now: now), "2 hrs ago") + XCTAssertEqual(AidenRelativeTimestamp.text(for: now.addingTimeInterval(-86_400), now: now), "1 day ago") + XCTAssertEqual(AidenRelativeTimestamp.text(for: now.addingTimeInterval(-172_800), now: now), "2 days ago") + XCTAssertEqual(AidenRelativeTimestamp.text(for: now.addingTimeInterval(30), now: now), "just now") + } + + func testMarkdownDocumentParsesHeadingsListsAndInlineEmphasis() { + let markdown = """ + I'll explore the repository to understand what it is. + + ## Long Live Kodak 📷 + + - **Film Frame Editor** — adjustable parameters + - `Batch processing` and export + """ + + let plainText = AidenMarkdownDocument.plainText(from: markdown) + XCTAssertTrue(plainText.hasPrefix("I'll explore")) + XCTAssertTrue(plainText.contains("Long Live Kodak 📷")) + XCTAssertTrue(plainText.contains("Film Frame Editor — adjustable parameters")) + XCTAssertTrue(plainText.contains("Batch processing and export")) + XCTAssertFalse(plainText.contains("##")) + XCTAssertFalse(plainText.contains("**")) + XCTAssertFalse(plainText.contains("`")) + } + + func testMarkdownRenderingPolicyBoundsCharactersAndCrossPlatformLineBreaks() { + XCTAssertNil(AidenMarkdownRenderingPolicy.fallbackReason( + for: String(repeating: "a", count: AidenMarkdownRenderingPolicy.maximumCharacterCount) + )) + XCTAssertEqual( + AidenMarkdownRenderingPolicy.fallbackReason( + for: String(repeating: "a", count: AidenMarkdownRenderingPolicy.maximumCharacterCount + 1) + ), + .tooManyCharacters + ) + + let allowedLines = Array( + repeating: "line", + count: AidenMarkdownRenderingPolicy.maximumLineCount + ).joined(separator: "\r\n") + XCTAssertNil(AidenMarkdownRenderingPolicy.fallbackReason(for: allowedLines)) + XCTAssertEqual( + AidenMarkdownRenderingPolicy.fallbackReason(for: allowedLines + "\u{2028}overflow"), + .tooManyLines + ) + } + + @MainActor + func testMarkdownViewRendersBlockContentAtTheFullProposedWidth() throws { + let renderer = ImageRenderer(content: AidenMarkdownView(content: """ + ## Heading + + - First item + - Second item + """).frame(width: 320)) + renderer.scale = 1 + + let image = try XCTUnwrap(renderer.uiImage) + XCTAssertEqual(image.size.width, 320, accuracy: 0.5) + XCTAssertGreaterThan(try XCTUnwrap(image.pngData()).count, 1_000) + } + + @MainActor + func testUserTextStaysContentSizedWhileAssistantMarkdownUsesTranscriptWidth() { + let userHost = UIHostingController(rootView: AidenMessageTextView( + role: .user, + content: "Short prompt" + )) + let assistantHost = UIHostingController(rootView: AidenMessageTextView( + role: .assistant, + content: "A short reply" + )) + let proposal = CGSize(width: 320, height: 1_000) + + let userSize = userHost.sizeThatFits(in: proposal) + let assistantSize = assistantHost.sizeThatFits(in: proposal) + + XCTAssertLessThan(userSize.width, 200) + XCTAssertEqual(assistantSize.width, proposal.width, accuracy: 0.5) + } + + func testSSEParserAcceptsCanonicalFrameAndRejectsIdentityMismatches() throws { + let json = eventJSON(sequence: 1, type: "text_delta", payload: "{\"text\":\"Hello\"}") + var parser = AidenSSEParser() + XCTAssertNil(try parser.consume(line: "id: 1")) + XCTAssertNil(try parser.consume(line: "event: text_delta")) + XCTAssertNil(try parser.consume(line: "data: \(json)")) + let event = try XCTUnwrap(parser.consume(line: "")) + XCTAssertEqual(event.sequence, 1) + XCTAssertEqual(event.payload?.text, "Hello") + + var wrongID = AidenSSEParser() + _ = try wrongID.consume(line: "id: 2") + _ = try wrongID.consume(line: "data: \(json)") + XCTAssertThrowsError(try wrongID.consume(line: "")) { + XCTAssertEqual($0 as? AidenSSEParserError, .eventIDMismatch) + } + + var wrongName = AidenSSEParser() + _ = try wrongName.consume(line: "id: 1") + _ = try wrongName.consume(line: "event: reasoning_delta") + _ = try wrongName.consume(line: "data: \(json)") + XCTAssertThrowsError(try wrongName.consume(line: "")) { + XCTAssertEqual($0 as? AidenSSEParserError, .eventNameMismatch) + } + } + + func testSSEParserRejectsDuplicateJSONKeysAndOversizedFrames() throws { + let duplicate = """ + {"protocolVersion":1,"streamId":"stream-1","sequence":1,"sequence":1, + "timestamp":"2026-08-19T07:00:00.000Z","type":"heartbeat","terminal":false,"payload":{}} + """ + var parser = AidenSSEParser() + _ = try parser.consume(line: "id: 1") + _ = try parser.consume(line: "data: \(duplicate)") + XCTAssertThrowsError(try parser.consume(line: "")) { + guard case .duplicateJSONKey("sequence") = $0 as? AidenRemoteContractError else { + return XCTFail("Expected duplicate key rejection, received \($0)") + } + } + + var oversized = AidenSSEParser() + XCTAssertThrowsError( + try oversized.consume(line: String(repeating: "x", count: AidenRemoteProtocol.maxSSEFrameBytes + 1)) + ) { + XCTAssertEqual($0 as? AidenSSEParserError, .frameTooLarge) + } + } + + func testChatCacheIsScopedByInstallationAndRestoresStreamCursor() async throws { + let root = FileManager.default.temporaryDirectory + .appending(path: "aiden-chat-cache-tests-\(UUID().uuidString)", directoryHint: .isDirectory) + defer { try? FileManager.default.removeItem(at: root) } + let cache = AidenChatCache(root: root) + let chat = sampleChat() + + try await cache.saveChats([chat], instanceId: "instance-a", workspaceId: "workspace-1") + try await cache.saveChat(chat, instanceId: "instance-a") + try await cache.saveActiveStream( + .init(streamId: "stream-1", turnId: "turn-1", lastSequence: 14), + instanceId: "instance-a", + chatId: chat.id + ) + + let chatsA = await cache.loadChats(instanceId: "instance-a", workspaceId: "workspace-1") + let chatsB = await cache.loadChats(instanceId: "instance-b", workspaceId: "workspace-1") + let cachedChatA = await cache.loadChat(instanceId: "instance-a", chatId: chat.id) + let cachedChatB = await cache.loadChat(instanceId: "instance-b", chatId: chat.id) + let stream = await cache.loadActiveStream(instanceId: "instance-a", chatId: chat.id) + XCTAssertEqual(chatsA, [chat]) + XCTAssertNil(chatsB) + XCTAssertEqual(cachedChatA, chat) + XCTAssertNil(cachedChatB) + XCTAssertEqual(stream, .init(streamId: "stream-1", turnId: "turn-1", lastSequence: 14)) + + await cache.removeChat(instanceId: "instance-a", chatId: chat.id) + let removedChat = await cache.loadChat(instanceId: "instance-a", chatId: chat.id) + let removedStream = await cache.loadActiveStream(instanceId: "instance-a", chatId: chat.id) + XCTAssertNil(removedChat) + XCTAssertNil(removedStream) + } + + func testAttachmentModelsRoundTripMetadataWithoutInlineContents() throws { + let json = """ + {"id":"message-1","role":"user","text":"", + "attachments":[{"id":"att_\(String(repeating: "A", count: 43))","name":"notes.md", + "mimeType":"text/markdown","kind":"text","size":7}], + "createdAt":"2026-08-19T07:00:00.000Z"} + """ + let message = try AidenRemoteJSONDecoder.decode(AidenChatMessage.self, from: Data(json.utf8)) + XCTAssertEqual(message.attachments?.first?.name, "notes.md") + let encoded = try JSONEncoder().encode(message) + let object = try XCTUnwrap(JSONSerialization.jsonObject(with: encoded) as? [String: Any]) + let attachment = try XCTUnwrap((object["attachments"] as? [[String: Any]])?.first) + XCTAssertNil(attachment["text"]) + XCTAssertNil(attachment["data"]) + XCTAssertNil(attachment["path"]) + } + + func testAttachmentReferenceValidationFailsClosed() { + let valid = AidenAttachmentReference( + id: "att_\(String(repeating: "A", count: 43))", + name: "notes.md", + mimeType: "text/markdown", + kind: .text, + size: 7, + expiresAt: Date().addingTimeInterval(60) + ) + XCTAssertTrue(valid.isValid()) + XCTAssertFalse(AidenAttachmentReference( + id: valid.id, + name: "../notes.md", + mimeType: valid.mimeType, + kind: valid.kind, + size: valid.size, + expiresAt: valid.expiresAt + ).isValid()) + XCTAssertFalse(AidenAttachmentReference( + id: "attachment-1", + name: valid.name, + mimeType: "application/octet-stream", + kind: valid.kind, + size: valid.size, + expiresAt: valid.expiresAt + ).isValid()) + XCTAssertFalse(AidenAttachmentReference( + id: valid.id, + name: valid.name, + mimeType: valid.mimeType, + kind: valid.kind, + size: valid.size, + expiresAt: Date().addingTimeInterval(-1) + ).isValid()) + } + + func testTextAttachmentPreparationIsBoundedUTF8AndAllowlisted() throws { + let upload = try AidenAttachmentPreparation.textUpload( + data: Data("let value = 1".utf8), + name: "Example.swift", + mimeType: "application/octet-stream" + ) + XCTAssertEqual( + upload, + .text(name: "Example.swift", mimeType: "text/plain", text: "let value = 1") + ) + XCTAssertThrowsError( + try AidenAttachmentPreparation.textUpload( + data: Data([0xC3, 0x28]), + name: "bad.txt", + mimeType: "text/plain" + ) + ) { XCTAssertEqual($0 as? AidenAttachmentPreparationError, .invalidText) } + XCTAssertThrowsError( + try AidenAttachmentPreparation.textUpload( + data: Data(repeating: 0x61, count: AidenAttachmentPreparation.maximumTextBytes + 1), + name: "large.txt", + mimeType: "text/plain" + ) + ) { XCTAssertEqual($0 as? AidenAttachmentPreparationError, .fileTooLarge) } + } + + func testImageAttachmentPreparationTranscodesAndBoundsDimensions() throws { + let format = UIGraphicsImageRendererFormat.default() + format.scale = 1 + let renderer = UIGraphicsImageRenderer( + size: CGSize(width: 4_096, height: 2_048), + format: format + ) + let source = renderer.pngData { context in + UIColor.systemBlue.setFill() + context.fill(CGRect(x: 0, y: 0, width: 4_096, height: 2_048)) + } + let upload = try AidenAttachmentPreparation.imageUpload(data: source, name: "camera.heic") + guard case .image(let name, let mimeType, let data) = upload else { + return XCTFail("Expected an image upload") + } + XCTAssertEqual(name, "camera.jpg") + XCTAssertEqual(mimeType, "image/jpeg") + XCTAssertLessThanOrEqual(data.count, AidenAttachmentPreparation.maximumImageBytes) + let image = try XCTUnwrap(UIImage(data: data)) + XCTAssertLessThanOrEqual(max(image.size.width, image.size.height), 3_072) + } + + func testTextFilePreparationReadsABoundedPrefixAndMarksTruncation() throws { + let url = FileManager.default.temporaryDirectory.appending(path: "aiden-attachment-\(UUID().uuidString).txt") + defer { try? FileManager.default.removeItem(at: url) } + try Data(repeating: 0x61, count: AidenAttachmentPreparation.maximumTextBytes + 100).write(to: url) + let upload = try AidenAttachmentPreparation.fileUpload(url: url) + guard case .text(_, let mimeType, let text) = upload else { + return XCTFail("Expected a text upload") + } + XCTAssertEqual(mimeType, "text/plain") + XCTAssertTrue(text.hasSuffix("… [truncated]")) + XCTAssertLessThanOrEqual(text.unicodeScalars.count, AidenAttachmentPreparation.maximumTextScalars) + XCTAssertLessThanOrEqual(Data(text.utf8).count, AidenAttachmentPreparation.maximumTextBytes) + } + + func testTurnAttemptTrackerReusesOnlyTheExactAmbiguousRequestKey() { + var tracker = AidenTurnAttemptTracker() + let request = AidenTurnStart(text: "Hello", attachmentIds: ["att_\(String(repeating: "A", count: 43))"]) + let first = tracker.key(for: request) + XCTAssertEqual(tracker.key(for: request), first) + XCTAssertNotEqual(tracker.key(for: AidenTurnStart(text: "Edited")), first) + tracker.reset() + XCTAssertNotEqual(tracker.key(for: request), first) + } + + private func eventJSON(sequence: Int, type: String, payload: String) -> String { + """ + {"protocolVersion":1,"streamId":"stream-1","sequence":\(sequence), + "timestamp":"2026-08-19T07:00:00.000Z","type":"\(type)","terminal":false,"payload":\(payload)} + """ + } + + private func sampleChat() -> AidenChat { + AidenChat( + id: "chat-1", + workspaceId: "workspace-1", + title: "Aiden chat", + providerId: "openai", + modelId: "gpt-5.6", + messages: [ + AidenChatMessage( + id: "message-1", + role: .user, + text: "Hello", + createdAt: Date(timeIntervalSince1970: 1_787_100_000) + ), + ], + createdAt: Date(timeIntervalSince1970: 1_787_100_000), + updatedAt: Date(timeIntervalSince1970: 1_787_100_001), + revision: "revision-1" + ) + } +} + +final class AidenAppearanceTests: XCTestCase { + private struct Fixture: Decodable { + let version: Int + let presets: [Preset] + } + + private struct Preset: Decodable { + let id: String + let label: String + let light: Palette + let dark: Palette + } + + private struct Palette: Decodable, Equatable { + let canvas: String + let sidebar: String + let raised: String + let foreground: String + let secondary: String + let accent: String + let success: String + let warning: String + let danger: String + + init(_ value: AidenPalette) { + canvas = value.canvasHex + sidebar = value.sidebarHex + raised = value.raisedHex + foreground = value.foregroundHex + secondary = value.secondaryHex + accent = value.accentHex + success = value.successHex + warning = value.warningHex + danger = value.dangerHex + } + } + + func testSwiftPalettesExactlyMatchSharedElectronFixture() throws { + let bundle = Bundle(for: AidenAppearanceTests.self) + let url = try XCTUnwrap(bundle.url(forResource: "aiden-appearance-v1", withExtension: "json")) + let fixture = try JSONDecoder().decode(Fixture.self, from: Data(contentsOf: url)) + XCTAssertEqual(fixture.version, 1) + XCTAssertEqual(fixture.presets.map(\.id), AidenThemePresetID.allCases.map(\.rawValue)) + + for entry in fixture.presets { + let preset = try XCTUnwrap(AidenThemePresetID(rawValue: entry.id)) + XCTAssertEqual(entry.label, preset.title) + XCTAssertEqual(entry.light, Palette(AidenThemeCatalog.palette(preset: preset, scheme: .light))) + XCTAssertEqual(entry.dark, Palette(AidenThemeCatalog.palette(preset: preset, scheme: .dark))) + } + } + + @MainActor + func testAppearanceSelectionIsDeviceLocalAndPersistsAllChoices() throws { + let suiteName = "AidenAppearanceTests.\(UUID().uuidString)" + let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) + defer { defaults.removePersistentDomain(forName: suiteName) } + + let initial = AidenAppearanceStore(defaults: defaults) + XCTAssertEqual(initial.mode, .system) + XCTAssertEqual(initial.lightPreset, .aiden) + XCTAssertEqual(initial.darkPreset, .aiden) + initial.mode = .dark + initial.lightPreset = .berry + initial.darkPreset = .moss + initial.lightUIFont = .rounded + initial.darkUIFont = .humanist + initial.lightCodeFont = .menlo + initial.darkCodeFont = .monaco + initial.lightContrast = 72 + initial.darkContrast = 84 + initial.lightTranslucentSidebar = false + initial.darkTranslucentSidebar = false + initial.reduceMotion = .on + initial.uiFontSize = 18 + initial.codeFontSize = 17 + initial.diffMarkers = .color + + let restored = AidenAppearanceStore(defaults: defaults) + XCTAssertEqual(restored.mode, .dark) + XCTAssertEqual(restored.lightPreset, .berry) + XCTAssertEqual(restored.darkPreset, .moss) + XCTAssertEqual(restored.lightUIFont, .rounded) + XCTAssertEqual(restored.darkUIFont, .humanist) + XCTAssertEqual(restored.lightCodeFont, .menlo) + XCTAssertEqual(restored.darkCodeFont, .monaco) + XCTAssertEqual(restored.lightContrast, 72) + XCTAssertEqual(restored.darkContrast, 84) + XCTAssertFalse(restored.lightTranslucentSidebar) + XCTAssertFalse(restored.darkTranslucentSidebar) + XCTAssertEqual(restored.reduceMotion, .on) + XCTAssertEqual(restored.uiFontSize, 18) + XCTAssertEqual(restored.codeFontSize, 17) + XCTAssertEqual(restored.diffMarkers, .color) + XCTAssertEqual(restored.palette(for: .light).accentHex, "#B42C70") + XCTAssertEqual(restored.palette(for: .dark).accentHex, "#42B596") + XCTAssertNotEqual(restored.palette(for: .light).secondaryHex, "#6E6470") + XCTAssertTrue(restored.resolvedReduceMotion(system: false)) + + restored.lightContrast = -10 + restored.darkContrast = 110 + restored.uiFontSize = 99 + restored.codeFontSize = 1 + let normalized = AidenAppearanceStore(defaults: defaults) + XCTAssertEqual(normalized.lightContrast, 0) + XCTAssertEqual(normalized.darkContrast, 100) + XCTAssertEqual(normalized.uiFontSize, 18) + XCTAssertEqual(normalized.codeFontSize, 10) + } + + func testWorkspaceSelectionSurvivesAdaptiveLayoutChangesAndReconcilesCRUD() { + let ids = ["workspace-a", "workspace-b", "workspace-c"] + var selected = AidenWorkspaceNavigation.reconciledSelection(current: nil, workspaceIDs: ids) + XCTAssertEqual(selected, "workspace-a") + + selected = "workspace-b" + XCTAssertEqual( + AidenWorkspaceNavigation.reconciledSelection(current: selected, workspaceIDs: ids), + "workspace-b", + "Compact/regular layout changes must not replace a valid selection" + ) + XCTAssertEqual( + AidenWorkspaceNavigation.reconciledSelection(current: selected, workspaceIDs: ["workspace-a", "workspace-c"]), + "workspace-a", + "Removing the selected workspace should converge on an available detail" + ) + XCTAssertNil(AidenWorkspaceNavigation.reconciledSelection(current: selected, workspaceIDs: [])) + } + + func testCompactWorkspacePathPreservesOnlyAnAvailableDestination() { + let ids = ["workspace-a", "workspace-b", "workspace-c"] + + XCTAssertEqual( + AidenWorkspaceNavigation.reconciledCompactPath( + current: ["workspace-a", "workspace-b"], + workspaceIDs: ids + ), + ["workspace-b"], + "Compact navigation should preserve the visible workspace when SwiftUI reports a deeper path" + ) + XCTAssertEqual( + AidenWorkspaceNavigation.reconciledCompactPath( + current: ["workspace-b"], + workspaceIDs: ["workspace-a", "workspace-c"] + ), + [], + "Deleting the visible workspace should pop back to the workspace list" + ) + XCTAssertEqual( + AidenWorkspaceNavigation.reconciledCompactPath(current: [], workspaceIDs: ids), + [] + ) + } + + func testCompactWorkspacePathOnlyPushesWhenTransitioningFromSplitView() { + let ids = ["workspace-a", "workspace-b"] + + XCTAssertEqual( + AidenWorkspaceNavigation.compactPath( + enteringFromSplit: true, + current: [], + selectedWorkspaceID: "workspace-b", + workspaceIDs: ids + ), + ["workspace-b"], + "An iPad size-class transition should preserve the workspace that was visible in split view" + ) + XCTAssertEqual( + AidenWorkspaceNavigation.compactPath( + enteringFromSplit: false, + current: [], + selectedWorkspaceID: "workspace-a", + workspaceIDs: ids + ), + [], + "Launching on iPhone should start at the workspace list instead of auto-pushing the first row" + ) + XCTAssertEqual( + AidenWorkspaceNavigation.compactPath( + enteringFromSplit: true, + current: ["workspace-a"], + selectedWorkspaceID: "workspace-b", + workspaceIDs: ids + ), + ["workspace-a"], + "An existing compact destination should win over stale split-view selection" + ) + XCTAssertEqual( + AidenWorkspaceNavigation.compactPath( + enteringFromSplit: true, + current: [], + selectedWorkspaceID: "workspace-missing", + workspaceIDs: ids + ), + [] + ) + } +} diff --git a/ios/AidenOnTheGoTests/AidenNativeIntegrationTests.swift b/ios/AidenOnTheGoTests/AidenNativeIntegrationTests.swift new file mode 100644 index 00000000..fe6ad556 --- /dev/null +++ b/ios/AidenOnTheGoTests/AidenNativeIntegrationTests.swift @@ -0,0 +1,464 @@ +import ActivityKit +import Foundation +import XCTest +@testable import AidenOnTheGo + +final class AidenNativeIntegrationTests: XCTestCase { + func testIntentCatalogContainsOnlyBoundedDisplayNamesAndStableIDs() throws { + let suiteName = "AidenNativeIntegrationTests.\(UUID().uuidString)" + let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) + defer { defaults.removePersistentDomain(forName: suiteName) } + let store = AidenIntentCatalogStore(defaults: defaults) + + try store.update( + installations: [.init(id: "instance-1", name: "Studio Mac")], + activeInstallationId: "instance-1", + workspaces: [.init(id: "workspace-1", instanceId: "instance-1", name: "Aiden")], + for: "instance-1" + ) + + XCTAssertEqual(store.load(), AidenIntentCatalogSnapshot( + installations: [.init(id: "instance-1", name: "Studio Mac")], + workspaces: [.init(id: "workspace-1", instanceId: "instance-1", name: "Aiden")], + activeInstallationId: "instance-1" + )) + let data = try XCTUnwrap(defaults.data(forKey: "aiden.intent-catalog.v1")) + let serialized = try XCTUnwrap(String(data: data, encoding: .utf8)).lowercased() + for forbidden in ["https://", "credential", "token", "pin", "/users/"] { + XCTAssertFalse(serialized.contains(forbidden)) + } + } + + func testIntentCatalogDropsUnsafeAndOrphanedRecords() throws { + let suiteName = "AidenNativeIntegrationTests.\(UUID().uuidString)" + let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) + defer { defaults.removePersistentDomain(forName: suiteName) } + let store = AidenIntentCatalogStore(defaults: defaults) + try store.update( + installations: [ + .init(id: "instance-1", name: "Studio"), + .init(id: "instance-2", name: "Laptop"), + ], + activeInstallationId: "instance-1", + workspaces: [ + .init(id: "../../secret", instanceId: "instance-1", name: "Unsafe"), + .init(id: "workspace-2", instanceId: "missing", name: "Orphan"), + .init(id: "shared-id", instanceId: "instance-1", name: "First"), + .init(id: "shared-id", instanceId: "instance-2", name: "Second"), + ], + for: "instance-1" + ) + XCTAssertEqual(store.load().workspaces.map(\.name).sorted(), ["First", "Second"]) + XCTAssertNotEqual( + AidenWorkspaceIntentEntity(workspaceId: "shared-id", instanceId: "instance-1", name: "First").id, + AidenWorkspaceIntentEntity(workspaceId: "shared-id", instanceId: "instance-2", name: "Second").id + ) + let serialized = try XCTUnwrap(String( + data: try XCTUnwrap(defaults.data(forKey: "aiden.intent-catalog.v1")), + encoding: .utf8 + )) + XCTAssertFalse(serialized.contains("../../secret")) + XCTAssertFalse(serialized.contains("Orphan")) + } + + func testDeepLinksCarryOnlyStableIdentifiersAndRejectAmbiguousInput() throws { + let newChat = try XCTUnwrap(AidenDeepLink.newChatURL( + instanceId: "instance-1", + workspaceId: "workspace-1", + startsVoice: true + )) + XCTAssertEqual( + AidenDeepLink.request(from: newChat), + AidenNavigationRequest( + destination: .newChat, + instanceId: "instance-1", + workspaceId: "workspace-1", + startsVoice: true + ) + ) + XCTAssertFalse(newChat.absoluteString.lowercased().contains("prompt")) + XCTAssertFalse(newChat.absoluteString.lowercased().contains("token")) + XCTAssertFalse(newChat.absoluteString.contains("/Users/")) + + let chat = try XCTUnwrap(AidenDeepLink.chatURL(instanceId: "instance-1", chatId: "chat-1")) + XCTAssertEqual(AidenDeepLink.request(from: chat)?.destination, .chat("chat-1")) + XCTAssertNil(AidenDeepLink.request(from: URL(string: "aiden-otg://chat?instance=a&instance=b&chat=c")!)) + XCTAssertNil(AidenDeepLink.request(from: URL(string: "aiden-otg://chat?instance=a&chat=../../secret")!)) + XCTAssertNil(AidenDeepLink.request(from: URL(string: "aiden-otg://chat/path?instance=a&chat=c")!)) + XCTAssertNil(AidenDeepLink.request(from: URL(string: "aiden-otg://chat?instance=a&chat=c&prompt=hello")!)) + } + + @MainActor + func testLiveActivityStateIsBoundedAndResponseExcerptDefaultsOff() throws { + let longTitle = String(repeating: "Title ", count: 30) + let longText = String(repeating: "private response ", count: 30) + let initial = AgentRunActivityStateReducer.initialState( + sessionID: "chat-1", + sessionTitle: longTitle + ) + let updated = AgentRunActivityStateReducer.appendingToken(longText, to: initial) + XCTAssertLessThanOrEqual(updated.sessionTitle.count, AgentRunActivitySanitizer.maximumSessionTitleCharacters) + XCTAssertLessThanOrEqual(updated.responseExcerpt.count, AgentRunActivitySanitizer.maximumExcerptCharacters) + + let suiteName = "AidenNativeIntegrationTests.\(UUID().uuidString)" + let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) + defer { defaults.removePersistentDomain(forName: suiteName) } + let manager = AidenRemoteLiveActivityManager(defaults: defaults) + XCTAssertFalse(manager.includesResponseExcerpts) + XCTAssertEqual(initial.responseExcerpt, "") + } + + @MainActor + func testPhysicalActivityKitLifecycleUsesPrivateBoundedStateAndImmediateCleanup() async throws { + guard ActivityAuthorizationInfo().areActivitiesEnabled else { + throw XCTSkip("Live Activities are disabled on this physical device.") + } + + let proofID = "physical-proof-\(UUID().uuidString)" + let suiteName = "AidenNativeIntegrationTests.\(UUID().uuidString)" + let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) + defer { defaults.removePersistentDomain(forName: suiteName) } + let manager = AidenRemoteLiveActivityManager(defaults: defaults) + + await manager.start( + instanceID: proofID, + chatID: proofID, + title: "Aiden verification", + streamID: proofID + ) + + guard let activity = Activity.activities.first(where: { + $0.attributes.instanceID == proofID && $0.attributes.streamID == proofID + }) else { + await manager.endAll(forInstanceID: proofID) + XCTFail("ActivityKit did not create the requested Aiden Live Activity.") + return + } + + XCTAssertEqual(activity.content.state.status, .starting) + XCTAssertEqual(activity.content.state.responseExcerpt, "") + XCTAssertFalse(activity.content.state.isFinal) + + await manager.toolStarted(name: "read_file", streamID: proofID) + await manager.appendResponse("private response text", streamID: proofID) + await manager.markStale(streamID: proofID) + + let staleDeadline = Date().addingTimeInterval(2) + while !activity.content.state.isStale && Date() < staleDeadline { + try await Task.sleep(nanoseconds: 20_000_000) + } + XCTAssertTrue(activity.content.state.isStale) + XCTAssertEqual(activity.content.state.status, .responding) + XCTAssertEqual(activity.content.state.responseExcerpt, "") + + await manager.endAll(forInstanceID: proofID) + + let endDeadline = Date().addingTimeInterval(2) + while (activity.activityState == .active || activity.activityState == .stale), + Date() < endDeadline { + try await Task.sleep(nanoseconds: 20_000_000) + } + XCTAssertTrue(activity.activityState == .ended || activity.activityState == .dismissed) + XCTAssertFalse(Activity.activities.contains(where: { $0.id == activity.id })) + } + + @MainActor + func testFreshManagerReconcilesPersistedActivityThroughAuthenticatedClient() async throws { + guard ActivityAuthorizationInfo().areActivitiesEnabled else { + throw XCTSkip("Live Activities are disabled on this physical device.") + } + + let proofID = "relaunch-proof-\(UUID().uuidString)" + let suiteName = "AidenNativeIntegrationTests.\(UUID().uuidString)" + let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) + defer { + defaults.removePersistentDomain(forName: suiteName) + AidenNativeActivityURLProtocol.handler = nil + } + + var originalManager: AidenRemoteLiveActivityManager? = AidenRemoteLiveActivityManager(defaults: defaults) + await originalManager?.start( + instanceID: proofID, + chatID: proofID, + title: "Relaunch verification", + streamID: proofID + ) + + guard let activity = Activity.activities.first(where: { + $0.attributes.instanceID == proofID && $0.attributes.streamID == proofID + }) else { + await originalManager?.endAll(forInstanceID: proofID) + XCTFail("ActivityKit did not persist the Aiden activity for adoption.") + return + } + originalManager = nil + + AidenNativeActivityURLProtocol.handler = { request in + XCTAssertEqual(request.httpMethod, "GET") + XCTAssertEqual(request.url?.path, "/api/aiden/v1/streams/\(proofID)") + XCTAssertEqual(request.value(forHTTPHeaderField: "Authorization"), "Bearer proof-credential") + XCTAssertEqual(request.value(forHTTPHeaderField: "Aiden-Protocol-Version"), "1") + let response = try XCTUnwrap(HTTPURLResponse( + url: try XCTUnwrap(request.url), + statusCode: 200, + httpVersion: nil, + headerFields: ["Content-Type": "application/json"] + )) + return (response, Data(""" + {"streamId":"\(proofID)","chatId":"\(proofID)","turnId":"turn-1", + "state":"running","lastSequence":3,"updatedAt":"2026-08-19T19:00:00.000Z"} + """.utf8)) + } + let configuration = URLSessionConfiguration.ephemeral + configuration.protocolClasses = [AidenNativeActivityURLProtocol.self] + let client = AidenRemoteClient( + endpoint: URL(string: "https://aiden.test/api/aiden/v1")!, + credential: "proof-credential", + session: URLSession(configuration: configuration) + ) + let adoptingManager = AidenRemoteLiveActivityManager(defaults: defaults) + + await adoptingManager.reconcile(client: client) + let reconcileDeadline = Date().addingTimeInterval(2) + while activity.content.state.status != .responding && Date() < reconcileDeadline { + try await Task.sleep(nanoseconds: 20_000_000) + } + XCTAssertEqual(activity.content.state.status, .responding) + XCTAssertFalse(activity.content.state.isStale) + XCTAssertEqual(activity.content.state.responseExcerpt, "") + + await adoptingManager.endAll(forInstanceID: proofID) + let endDeadline = Date().addingTimeInterval(2) + while (activity.activityState == .active || activity.activityState == .stale), + Date() < endDeadline { + try await Task.sleep(nanoseconds: 20_000_000) + } + XCTAssertTrue(activity.activityState == .ended || activity.activityState == .dismissed) + XCTAssertFalse(Activity.activities.contains(where: { $0.id == activity.id })) + } + + @MainActor + func testOptInPhysicalActivityKitProcessBoundaryPhase() async throws { + let environment = ProcessInfo.processInfo.environment + let proofIDValue = environment["AIDEN_ACTIVITYKIT_PROCESS_PROOF_ID"] + let phaseValue = environment["AIDEN_ACTIVITYKIT_PROCESS_PHASE"] + + guard proofIDValue != nil || phaseValue != nil else { + XCTAssertNil(proofIDValue) + XCTAssertNil(phaseValue) + return + } + + let proofID = try XCTUnwrap(proofIDValue) + let phase = try XCTUnwrap(phaseValue) + let permittedProofCharacters = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "-_.")) + XCTAssertFalse(proofID.isEmpty) + XCTAssertLessThanOrEqual(proofID.utf8.count, 128) + XCTAssertNil(proofID.unicodeScalars.first(where: { !permittedProofCharacters.contains($0) })) + guard !proofID.isEmpty, + proofID.utf8.count <= 128, + proofID.unicodeScalars.allSatisfy(permittedProofCharacters.contains) + else { + return + } + guard ActivityAuthorizationInfo().areActivitiesEnabled else { + throw XCTSkip("Live Activities are disabled on this physical device.") + } + + let suiteName = "AidenNativeIntegrationTests.ProcessBoundary.\(proofID)" + let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) + defer { defaults.removePersistentDomain(forName: suiteName) } + + switch phase { + case "start": + let manager = AidenRemoteLiveActivityManager(defaults: defaults) + await manager.endAll(forInstanceID: proofID) + await manager.start( + instanceID: proofID, + chatID: proofID, + title: "Process relaunch verification", + streamID: proofID + ) + let activity = try XCTUnwrap(Activity.activities.first(where: { + $0.attributes.instanceID == proofID && $0.attributes.streamID == proofID + })) + XCTAssertTrue(activity.activityState == .active || activity.activityState == .stale) + XCTAssertEqual(activity.content.state.status, .starting) + XCTAssertEqual(activity.content.state.responseExcerpt, "") + print("AIDEN_ACTIVITYKIT_PROCESS checkpoint=started proof=\(proofID)") + + case "reconcile": + guard let activity = Activity.activities.first(where: { + $0.attributes.instanceID == proofID && $0.attributes.streamID == proofID + }) else { + XCTFail("The system-persisted Aiden Live Activity was not available after process relaunch.") + return + } + + AidenNativeActivityURLProtocol.handler = { request in + XCTAssertEqual(request.httpMethod, "GET") + XCTAssertEqual(request.url?.path, "/api/aiden/v1/streams/\(proofID)") + XCTAssertEqual(request.value(forHTTPHeaderField: "Authorization"), "Bearer process-proof-credential") + XCTAssertEqual(request.value(forHTTPHeaderField: "Aiden-Protocol-Version"), "1") + let response = try XCTUnwrap(HTTPURLResponse( + url: try XCTUnwrap(request.url), + statusCode: 200, + httpVersion: nil, + headerFields: ["Content-Type": "application/json"] + )) + return (response, Data(""" + {"streamId":"\(proofID)","chatId":"\(proofID)","turnId":"turn-process-proof", + "state":"running","lastSequence":4,"updatedAt":"2026-08-19T19:00:00.000Z"} + """.utf8)) + } + defer { AidenNativeActivityURLProtocol.handler = nil } + + let configuration = URLSessionConfiguration.ephemeral + configuration.protocolClasses = [AidenNativeActivityURLProtocol.self] + let client = AidenRemoteClient( + endpoint: URL(string: "https://aiden.test/api/aiden/v1")!, + credential: "process-proof-credential", + session: URLSession(configuration: configuration) + ) + let manager = AidenRemoteLiveActivityManager(defaults: defaults) + await manager.reconcile(client: client) + + let reconcileDeadline = Date().addingTimeInterval(2) + while activity.content.state.status != .responding && Date() < reconcileDeadline { + try await Task.sleep(nanoseconds: 20_000_000) + } + XCTAssertEqual(activity.content.state.status, .responding) + XCTAssertFalse(activity.content.state.isStale) + XCTAssertEqual(activity.content.state.responseExcerpt, "") + + await manager.endAll(forInstanceID: proofID) + let endDeadline = Date().addingTimeInterval(2) + while (activity.activityState == .active || activity.activityState == .stale), + Date() < endDeadline { + try await Task.sleep(nanoseconds: 20_000_000) + } + XCTAssertTrue(activity.activityState == .ended || activity.activityState == .dismissed) + XCTAssertFalse(Activity.activities.contains(where: { $0.id == activity.id })) + print("AIDEN_ACTIVITYKIT_PROCESS checkpoint=reconciled-and-ended proof=\(proofID)") + + case "cleanup": + let manager = AidenRemoteLiveActivityManager(defaults: defaults) + await manager.endAll(forInstanceID: proofID) + XCTAssertFalse(Activity.activities.contains(where: { + $0.attributes.instanceID == proofID + })) + print("AIDEN_ACTIVITYKIT_PROCESS checkpoint=cleanup proof=\(proofID)") + + default: + XCTFail("AIDEN_ACTIVITYKIT_PROCESS_PHASE must be start, reconcile, or cleanup.") + } + } + + func testVoiceDraftIsLocalExplicitAndRejectsInvalidAudioInput() throws { + XCTAssertEqual( + ComposerVoiceDraftComposer.composedDraft(baseDraft: "Please", transcript: "summarize locally"), + "Please summarize locally" + ) + XCTAssertFalse(ComposerVoiceInputStartPolicy.canStart(appIsActive: false)) + XCTAssertThrowsError(try ComposerVoiceInputStartPolicy.validateAudioSessionInput( + isInputAvailable: false, + sampleRate: 44_100, + inputNumberOfChannels: 1 + )) + } + + func testHostedAppDeclaresVoiceAndLiveActivityPrivacyKeys() throws { + XCTAssertNotNil(Bundle.main.object(forInfoDictionaryKey: "NSMicrophoneUsageDescription")) + XCTAssertNotNil(Bundle.main.object(forInfoDictionaryKey: "NSSpeechRecognitionUsageDescription")) + XCTAssertNotNil(Bundle.main.object(forInfoDictionaryKey: "NSCameraUsageDescription")) + XCTAssertNotNil(Bundle.main.object(forInfoDictionaryKey: "NSLocalNetworkUsageDescription")) + XCTAssertEqual(Bundle.main.object(forInfoDictionaryKey: "NSSupportsLiveActivities") as? Bool, true) + XCTAssertNil(Bundle.main.object(forInfoDictionaryKey: "NSAppTransportSecurity")) + + let privacyManifestURL = try XCTUnwrap(Bundle.main.url( + forResource: "PrivacyInfo", + withExtension: "xcprivacy" + )) + let privacyManifest = try XCTUnwrap( + PropertyListSerialization.propertyList( + from: try Data(contentsOf: privacyManifestURL), + format: nil + ) as? [String: Any] + ) + XCTAssertEqual(privacyManifest["NSPrivacyTracking"] as? Bool, false) + XCTAssertTrue((privacyManifest["NSPrivacyCollectedDataTypes"] as? [Any])?.isEmpty == true) + + let noticeURL = try XCTUnwrap(Bundle.main.url( + forResource: "NOTICE", + withExtension: "txt", + subdirectory: "ThirdPartyNotices" + )) + let notice = try String(contentsOf: noticeURL, encoding: .utf8) + XCTAssertTrue(notice.contains("Hermex (adapted SwiftUI interaction and implementation foundation)")) + XCTAssertTrue(notice.contains("KeychainAccess 4.2.2")) + XCTAssertTrue(notice.contains("MarkdownUI 2.4.1")) + XCTAssertTrue(notice.contains("NetworkImage 6.0.1")) + XCTAssertTrue(notice.contains("swift-cmark 0.8.0")) + XCTAssertFalse(notice.contains("swift-eventsource")) + + for licenseName in ["MarkdownUI-LICENSE", "NetworkImage-LICENSE", "swift-cmark-COPYING"] { + let licenseURL = try XCTUnwrap(Bundle.main.url( + forResource: licenseName, + withExtension: "txt", + subdirectory: "ThirdPartyNotices" + )) + XCTAssertFalse(try String(contentsOf: licenseURL, encoding: .utf8).isEmpty) + } + + let hermexLicenseURL = try XCTUnwrap(Bundle.main.url( + forResource: "Hermex-LICENSE", + withExtension: "txt", + subdirectory: "ThirdPartyNotices" + )) + let hermexLicense = try String(contentsOf: hermexLicenseURL, encoding: .utf8) + XCTAssertTrue(hermexLicense.contains("MIT License")) + XCTAssertTrue(hermexLicense.contains("Copyright (c) 2026 Uzair Ansar")) + } + + func testPublicPolicyAndSupportLinksUseCanonicalHTTPSDestinations() { + XCTAssertEqual(AppConfig.privacyPolicyURL.absoluteString, "https://chatwithaiden.com/privacy") + XCTAssertEqual( + AppConfig.supportURL.absoluteString, + "https://chatwithaiden.com/" + ) + XCTAssertEqual(AppConfig.privacyPolicyURL.scheme, "https") + XCTAssertEqual(AppConfig.supportURL.scheme, "https") + } + + func testUnpairedDeepLinkUsesGenericAidenErrorPresentation() { + XCTAssertEqual(AidenPairingAlertCopy.title, "Aiden On The Go") + XCTAssertEqual( + AidenPairingAlertCopy.fallbackMessage, + "Try again from Aiden Agent Remote Access settings." + ) + } +} + +private final class AidenNativeActivityURLProtocol: URLProtocol, @unchecked Sendable { + static var handler: ((URLRequest) throws -> (HTTPURLResponse, Data))? + + override class func canInit(with request: URLRequest) -> Bool { true } + override class func canonicalRequest(for request: URLRequest) -> URLRequest { request } + + override func startLoading() { + guard let handler = Self.handler else { + client?.urlProtocol(self, didFailWithError: URLError(.badServerResponse)) + return + } + do { + let (response, data) = try handler(request) + client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) + client?.urlProtocol(self, didLoad: data) + client?.urlProtocolDidFinishLoading(self) + } catch { + client?.urlProtocol(self, didFailWithError: error) + } + } + + override func stopLoading() {} +} diff --git a/ios/AidenOnTheGoTests/AidenRemoteClientTests.swift b/ios/AidenOnTheGoTests/AidenRemoteClientTests.swift new file mode 100644 index 00000000..459586c0 --- /dev/null +++ b/ios/AidenOnTheGoTests/AidenRemoteClientTests.swift @@ -0,0 +1,1165 @@ +import Foundation +import XCTest +@testable import AidenOnTheGo + +final class AidenRemoteClientTests: XCTestCase { + override func setUp() { + super.setUp() + AidenRemoteMockURLProtocol.handler = nil + } + + override func tearDown() { + AidenRemoteMockURLProtocol.handler = nil + super.tearDown() + } + + func testPairingUsesBootstrapSecretWithoutBearerAndValidatesExchange() async throws { + let now = Date(timeIntervalSince1970: 1_787_100_000) + let bootstrap = makeBootstrap(now: now) + let payload = makePairingPayload(bootstrap: bootstrap) + let session = makeSession() + AidenRemoteMockURLProtocol.handler = { request in + XCTAssertEqual(request.url?.absoluteString, "https://aiden.test/api/aiden/v1/pairing/exchange") + XCTAssertEqual(request.httpMethod, "POST") + XCTAssertEqual(request.value(forHTTPHeaderField: "Aiden-Protocol-Version"), "1") + XCTAssertNil(request.value(forHTTPHeaderField: "Authorization")) + let body = try Self.bodyData(request) + let object = try XCTUnwrap(JSONSerialization.jsonObject(with: body) as? [String: Any]) + XCTAssertEqual(object["secret"] as? String, String(repeating: "B", count: 43)) + XCTAssertEqual(object["deviceName"] as? String, "Sambit's iPhone") + XCTAssertEqual(object["deviceType"] as? String, "iphone") + XCTAssertEqual(object["clientVersion"] as? String, "1.0") + return Self.response( + for: request, + status: 200, + json: """ + { + "protocolVersion": 1, + "instanceId": "instance-1", + "deviceId": "device-1", + "credential": "\(String(repeating: "C", count: 43))", + "capabilities": ["server:read", "workspace:read", "workspace:manage"], + "endpoint": "https://aiden.test/api/aiden/v1", + "serverSpkiSha256": "\(bootstrap.serverSpkiSha256)" + } + """ + ) + } + + let exchange = try await AidenRemoteClient.pair( + payload: payload, + deviceName: "Sambit's iPhone", + deviceType: .iphone, + clientVersion: "1.0", + session: session, + now: now + ) + + XCTAssertEqual(exchange.instanceId, "instance-1") + XCTAssertEqual(exchange.deviceId, "device-1") + XCTAssertEqual(exchange.capabilities, [.serverRead, .workspaceRead, .workspaceManage]) + } + + func testWorkspaceListUsesBearerAndStrictAidenProtocolHeader() async throws { + let client = makeClient() + AidenRemoteMockURLProtocol.handler = { request in + XCTAssertEqual(request.url?.absoluteString, "https://aiden.test/api/aiden/v1/workspaces") + XCTAssertEqual(request.value(forHTTPHeaderField: "Authorization"), "Bearer device-credential") + XCTAssertEqual(request.value(forHTTPHeaderField: "Aiden-Protocol-Version"), "1") + return Self.response( + for: request, + status: 200, + json: """ + {"workspaces":[{ + "id":"workspace-1","name":"Aiden Agent","permission":"ask", + "hasFolder":true,"isManagedWorktree":false, + "repositoryName":"aiden-agent", + "git":{"isRepo":true,"branch":"main","uncommitted":2}, + "createdAt":"2026-08-19T07:00:00.000Z", + "updatedAt":"2026-08-19T07:01:00.000Z","revision":"rev-1" + }]} + """ + ) + } + + let workspaces = try await client.workspaces() + XCTAssertEqual(workspaces.count, 1) + XCTAssertEqual(workspaces[0].permission, .ask) + XCTAssertEqual(workspaces[0].git?.uncommitted, 2) + } + + func testUsageReadsPrivacySafeMacAggregate() async throws { + let client = makeClient() + AidenRemoteMockURLProtocol.handler = { request in + XCTAssertEqual(request.url?.absoluteString, "https://aiden.test/api/aiden/v1/usage?range=30d") + XCTAssertEqual(request.value(forHTTPHeaderField: "Authorization"), "Bearer device-credential") + return Self.response( + for: request, + status: 200, + json: """ + { + "range":"30d","startDate":"2026-07-21","endDate":"2026-08-19", + "totals":{ + "requests":12,"completedRequests":11,"failedRequests":1,"cancelledRequests":0, + "reportedTokenRequests":10,"unmeteredRequests":2,"localRequests":3, + "costedRequests":8,"unpricedHostedRequests":1,"hostedCostUsd":1.25, + "activeDays":4,"currentStreak":2,"longestStreak":3, + "tokens":{"input":100,"output":50,"cacheRead":10,"cacheWrite":2,"cacheWrite1h":1,"reasoning":8,"total":170} + }, + "days":[],"models":[] + } + """ + ) + } + + let usage = try await client.usage() + XCTAssertEqual(usage.range, "30d") + XCTAssertEqual(usage.totals.requests, 12) + XCTAssertEqual(usage.totals.tokens.total, 170) + XCTAssertEqual(usage.totals.hostedCostUsd, 1.25) + } + + func testWorkspaceCreateUpdateAndDeleteCarryMutationPreconditions() async throws { + let client = makeClient() + var step = 0 + AidenRemoteMockURLProtocol.handler = { request in + step += 1 + switch step { + case 1: + XCTAssertEqual(request.httpMethod, "POST") + XCTAssertNotNil(request.value(forHTTPHeaderField: "Idempotency-Key")) + let object = try Self.jsonBody(request) + XCTAssertEqual(object["mode"] as? String, "folderless") + XCTAssertEqual(object["name"] as? String, "New Workspace") + return Self.workspaceResponse(for: request, status: 201, revision: "rev-1") + case 2: + XCTAssertEqual(request.httpMethod, "PATCH") + XCTAssertEqual(request.value(forHTTPHeaderField: "If-Match"), "rev-1") + let object = try Self.jsonBody(request) + XCTAssertEqual(object["permission"] as? String, "full") + XCTAssertEqual(object["confirmedForeground"] as? Bool, true) + return Self.workspaceResponse(for: request, status: 200, revision: "rev-2") + case 3: + XCTAssertEqual(request.httpMethod, "DELETE") + XCTAssertEqual(request.value(forHTTPHeaderField: "If-Match"), "rev-2") + return (HTTPURLResponse(url: request.url!, statusCode: 204, httpVersion: nil, headerFields: nil)!, Data()) + default: + XCTFail("Unexpected request") + return Self.response(for: request, status: 500, json: "{}") + } + } + + let created = try await client.createWorkspace(.folderless(name: "New Workspace")) + let updated = try await client.updateWorkspace( + id: created.id, + revision: created.revision, + patch: AidenWorkspacePatch(permission: .full) + ) + try await client.removeWorkspace(id: updated.id, revision: updated.revision) + XCTAssertEqual(step, 3) + } + + func testApprovedFolderBrowserUsesOpaqueLocationsAndSelection() async throws { + let client = makeClient() + var step = 0 + let location = "loc_\(String(repeating: "L", count: 43))" + AidenRemoteMockURLProtocol.handler = { request in + step += 1 + switch step { + case 1: + XCTAssertEqual(request.url?.path, "/api/aiden/v1/workspace-browser/children") + let components = try XCTUnwrap(URLComponents(url: request.url!, resolvingAgainstBaseURL: false)) + XCTAssertEqual(components.queryItems, [URLQueryItem(name: "location", value: location)]) + return Self.response( + for: request, + status: 200, + json: """ + {"rootId":"root-1","label":"Projects","breadcrumbs":[], + "entries":[{"id":"entry-1","name":"aiden-agent","location":"\(location)"}]} + """ + ) + case 2: + XCTAssertEqual(request.url?.path, "/api/aiden/v1/workspace-browser/selections") + XCTAssertEqual(try Self.jsonBody(request)["location"] as? String, location) + return Self.response( + for: request, + status: 201, + json: """ + {"selection":"sel_\(String(repeating: "S", count: 43))", + "displayName":"aiden-agent","expiresAt":"2026-08-19T07:05:00.000Z"} + """ + ) + default: + XCTFail("Unexpected request") + return Self.response(for: request, status: 500, json: "{}") + } + } + + let page = try await client.browserChildren(location: location) + XCTAssertEqual(page.entries.first?.name, "aiden-agent") + let selection = try await client.createWorkspaceSelection(location: location) + XCTAssertTrue(selection.selection.hasPrefix("sel_")) + } + + func testCredentialRevocationIsTypedAndNeverEchoesCredential() async throws { + let client = makeClient() + AidenRemoteMockURLProtocol.handler = { request in + Self.response( + for: request, + status: 401, + json: """ + {"error":{"code":"credential_revoked","message":"Pair this device again.", + "requestId":"request-1","retryable":false}} + """ + ) + } + + do { + _ = try await client.workspaces() + XCTFail("Expected credential revocation") + } catch let error as AidenRemoteClientError { + XCTAssertTrue(error.isCredentialRevoked) + XCTAssertFalse(error.localizedDescription.contains("device-credential")) + } + } + + func testChatCRUDModelsTurnCancelAndApprovalUseCanonicalRoutes() async throws { + let client = makeClient() + var step = 0 + AidenRemoteMockURLProtocol.handler = { request in + step += 1 + switch step { + case 1: + XCTAssertEqual(request.httpMethod, "GET") + XCTAssertEqual(request.url?.path, "/api/aiden/v1/chats") + XCTAssertEqual(URLComponents(url: request.url!, resolvingAgainstBaseURL: false)?.queryItems, + [URLQueryItem(name: "workspaceId", value: "workspace-1")]) + return Self.response(for: request, status: 200, json: "{\"chats\":[]}") + case 2: + XCTAssertEqual(request.httpMethod, "POST") + XCTAssertNotNil(request.value(forHTTPHeaderField: "Idempotency-Key")) + XCTAssertEqual(try Self.jsonBody(request)["workspaceId"] as? String, "workspace-1") + return Self.chatResponse(for: request, status: 201, revision: "revision-1") + case 3: + XCTAssertEqual(request.httpMethod, "PATCH") + XCTAssertEqual(request.value(forHTTPHeaderField: "If-Match"), "revision-1") + XCTAssertEqual(try Self.jsonBody(request)["title"] as? String, "Renamed") + return Self.chatResponse(for: request, status: 200, revision: "revision-2", title: "Renamed") + case 4: + XCTAssertEqual(request.url?.path, "/api/aiden/v1/models") + return Self.response( + for: request, + status: 200, + json: """ + {"providers":[{"id":"openai","label":"OpenAI","models":[ + {"id":"gpt-5.6","label":"GPT-5.6","thinkingLevels":["high","max"]}]}], + "defaults":{"providerId":"openai","modelId":"gpt-5.6"}} + """ + ) + case 5: + XCTAssertEqual(request.url?.path, "/api/aiden/v1/chats/chat-1/turns") + XCTAssertNotNil(request.value(forHTTPHeaderField: "Idempotency-Key")) + let body = try Self.jsonBody(request) + XCTAssertEqual(body["text"] as? String, "Work on this") + XCTAssertEqual(body["thinkingLevel"] as? String, "max") + return Self.response( + for: request, + status: 202, + json: """ + {"turnId":"turn-1","streamId":"stream-1","status":"accepted", + "message":{"id":"message-1","role":"user","text":"Work on this", + "createdAt":"2026-08-19T07:00:00.000Z"}} + """ + ) + case 6: + XCTAssertEqual(request.url?.path, "/api/aiden/v1/streams/stream-1") + return Self.streamStatusResponse(for: request, state: "running") + case 7: + XCTAssertEqual(request.url?.path, "/api/aiden/v1/streams/stream-1/cancel") + XCTAssertNotNil(request.value(forHTTPHeaderField: "Idempotency-Key")) + return Self.streamStatusResponse(for: request, state: "cancelled") + case 8: + XCTAssertEqual(request.url?.path, "/api/aiden/v1/approvals/approval-1/respond") + XCTAssertEqual(try Self.jsonBody(request)["decision"] as? String, "allow") + return Self.response( + for: request, + status: 200, + json: "{\"approvalId\":\"approval-1\",\"decision\":\"allow\",\"resolvedAt\":\"2026-08-19T07:00:00.000Z\"}" + ) + case 9: + XCTAssertEqual(request.httpMethod, "DELETE") + XCTAssertEqual(request.value(forHTTPHeaderField: "If-Match"), "revision-2") + return (HTTPURLResponse(url: request.url!, statusCode: 204, httpVersion: nil, headerFields: nil)!, Data()) + default: + XCTFail("Unexpected chat request") + return Self.response(for: request, status: 500, json: "{}") + } + } + + let initialChats = try await client.chats(workspaceId: "workspace-1") + XCTAssertEqual(initialChats, []) + let created = try await client.createChat(workspaceId: "workspace-1") + let updated = try await client.updateChat(id: created.id, revision: created.revision, title: "Renamed") + let catalog = try await client.modelCatalog() + XCTAssertEqual(catalog.providers.first?.models.first?.thinkingLevels, ["high", "max"]) + let turn = try await client.startTurn( + chatId: updated.id, + request: .init(text: "Work on this", providerId: "openai", modelId: "gpt-5.6", thinkingLevel: "max") + ) + let runningStatus = try await client.streamStatus(id: turn.streamId) + let cancelledStatus = try await client.cancelStream(id: turn.streamId) + let approval = try await client.respondToApproval(id: "approval-1", decision: .allow) + XCTAssertEqual(runningStatus.state, .running) + XCTAssertEqual(cancelledStatus.state, .cancelled) + XCTAssertEqual(approval.decision, .allow) + try await client.removeChat(id: updated.id, revision: updated.revision) + XCTAssertEqual(step, 9) + } + + func testAttachmentUploadTurnProjectionAndRemovalUseBoundedCanonicalRoutes() async throws { + let client = makeClient() + let attachmentID = "att_\(String(repeating: "A", count: 43))" + var step = 0 + AidenRemoteMockURLProtocol.handler = { request in + step += 1 + switch step { + case 1: + XCTAssertEqual(request.httpMethod, "POST") + XCTAssertEqual(request.url?.path, "/api/aiden/v1/chats/chat-1/attachments") + let body = try Self.jsonBody(request) + XCTAssertEqual(body["name"] as? String, "notes.md") + XCTAssertEqual(body["mimeType"] as? String, "text/markdown") + XCTAssertEqual(body["kind"] as? String, "text") + XCTAssertEqual(body["text"] as? String, "# Notes") + XCTAssertNil(body["path"]) + return Self.response( + for: request, + status: 201, + json: """ + {"id":"\(attachmentID)","name":"notes.md","mimeType":"text/markdown", + "kind":"text","size":7,"expiresAt":"2026-08-19T07:10:00.000Z"} + """ + ) + case 2: + XCTAssertEqual(request.url?.path, "/api/aiden/v1/chats/chat-1/turns") + let body = try Self.jsonBody(request) + XCTAssertEqual(body["text"] as? String, "") + XCTAssertEqual(body["attachmentIds"] as? [String], [attachmentID]) + return Self.response( + for: request, + status: 202, + json: """ + {"turnId":"turn-1","streamId":"stream-1","status":"accepted", + "message":{"id":"message-1","role":"user","text":"", + "attachments":[{"id":"\(attachmentID)","name":"notes.md", + "mimeType":"text/markdown","kind":"text","size":7}], + "createdAt":"2026-08-19T07:00:00.000Z"}} + """ + ) + case 3: + XCTAssertEqual(request.httpMethod, "DELETE") + XCTAssertEqual(request.url?.path, "/api/aiden/v1/chats/chat-1/attachments/\(attachmentID)") + return (HTTPURLResponse(url: request.url!, statusCode: 204, httpVersion: nil, headerFields: nil)!, Data()) + default: + XCTFail("Unexpected attachment request") + return Self.response(for: request, status: 500, json: "{}") + } + } + + let reference = try await client.uploadAttachment( + chatId: "chat-1", + upload: .text(name: "notes.md", mimeType: "text/markdown", text: "# Notes") + ) + let turn = try await client.startTurn( + chatId: "chat-1", + request: .init(text: "", attachmentIds: [reference.id]) + ) + XCTAssertEqual(turn.message.attachments?.first?.id, attachmentID) + XCTAssertEqual(turn.message.attachments?.first?.size, 7) + try await client.removeAttachment(chatId: "chat-1", attachmentId: attachmentID) + XCTAssertEqual(step, 3) + } + + func testPhysicalDevicePairingAndWorkspaceCRUDWhenConfigured() async throws { + let environment = ProcessInfo.processInfo.environment + guard let payloadValue = environment["AIDEN_PHASE6_PAIRING_PAYLOAD"] else { + throw XCTSkip("Set the Phase 6 pairing payload in the physical-device xctestrun file.") + } + let payload = try AidenRemoteJSONDecoder.decodePairingPayload( + from: Data(payloadValue.utf8) + ).validated() + let exchange = try await AidenRemoteClient.pair( + payload: payload, + deviceName: "Physical iPhone 13 Pro", + deviceType: .iphone, + clientVersion: "1.0" + ) + let installation = AidenInstallation( + exchange: exchange, + pairingTrust: payload.trust, + name: "Physical Aiden Agent" + ) + let client = try AidenRemoteClient( + installation: installation, + credential: exchange.credential + ) + + let server = try await client.server() + XCTAssertEqual(server.instanceId, exchange.instanceId) + let expectedConnectionMode = environment["AIDEN_PHASE6_EXPECTED_CONNECTION_MODE"] + .flatMap(AidenConnectionMode.init(rawValue:)) ?? .lan + XCTAssertEqual(server.connectionMode, expectedConnectionMode) + let initialWorkspaces = try await client.workspaces() + XCTAssertEqual(initialWorkspaces, []) + + let folderless = try await client.createWorkspace(.folderless(name: "Phone Context")) + XCTAssertFalse(folderless.hasFolder) + XCTAssertEqual(folderless.permission, .ask) + let updated = try await client.updateWorkspace( + id: folderless.id, + revision: folderless.revision, + patch: AidenWorkspacePatch(name: "Renamed Context", permission: .full) + ) + XCTAssertEqual(updated.name, "Renamed Context") + XCTAssertEqual(updated.permission, .full) + try await client.removeWorkspace(id: updated.id, revision: updated.revision) + + let scratch = try await client.createWorkspace(.scratch) + XCTAssertTrue(scratch.hasFolder) + try await client.removeWorkspace(id: scratch.id, revision: scratch.revision) + + let roots = try await client.browserRoots() + let root = try XCTUnwrap(roots.first) + let page = try await client.browserChildren(location: root.location) + let approvedFolder = try XCTUnwrap(page.entries.first) + XCTAssertEqual(approvedFolder.name, "aiden-agent") + let selection = try await client.createWorkspaceSelection(location: approvedFolder.location) + let selected = try await client.createWorkspace( + .selectedFolder(selection: selection.selection, name: "Selected Project") + ) + XCTAssertTrue(selected.hasFolder) + XCTAssertEqual(selected.name, "Selected Project") + do { + _ = try await client.createWorkspace( + .selectedFolder(selection: selection.selection, name: "Replay") + ) + XCTFail("A selected-folder nonce must be single use.") + } catch let error as AidenRemoteClientError { + guard case .server(let statusCode, let body) = error else { + return XCTFail("Expected a typed server error for selection replay.") + } + XCTAssertEqual(statusCode, 409) + XCTAssertEqual(body.code.rawValue, "handle_invalid") + } + try await client.removeWorkspace(id: selected.id, revision: selected.revision) + let finalWorkspaces = try await client.workspaces() + XCTAssertEqual(finalWorkspaces, []) + } + + func testPhysicalDeviceChatStreamingWhenConfigured() async throws { + let environment = ProcessInfo.processInfo.environment + guard let payloadValue = environment["AIDEN_PHASE7_PAIRING_PAYLOAD"] else { + throw XCTSkip("Set the Phase 7 pairing payload in the physical-device xctestrun file.") + } + let payload = try AidenRemoteJSONDecoder.decodePairingPayload(from: Data(payloadValue.utf8)).validated() + let exchange = try await AidenRemoteClient.pair( + payload: payload, + deviceName: "Physical iPhone 13 Pro", + deviceType: .iphone, + clientVersion: "1.0" + ) + let client = try AidenRemoteClient( + installation: AidenInstallation( + exchange: exchange, + pairingTrust: payload.trust, + name: "Physical Aiden Agent" + ), + credential: exchange.credential + ) + + print("AIDEN_PHASE7_PHYSICAL checkpoint=workspace") + let workspace = try await client.createWorkspace(.folderless(name: "Phase 7 Chat")) + let created = try await client.createChat(workspaceId: workspace.id) + let renamed = try await client.updateChat( + id: created.id, + revision: created.revision, + title: "Physical Stream Proof" + ) + print("AIDEN_PHASE7_PHYSICAL checkpoint=models") + let catalog = try await client.modelCatalog() + XCTAssertEqual(catalog.defaults["modelId"], "gpt-5.6") + + print("AIDEN_PHASE7_PHYSICAL checkpoint=attachment-upload") + let attachment = try await client.uploadAttachment( + chatId: renamed.id, + upload: .text( + name: "physical-proof.md", + mimeType: "text/markdown", + text: "# Physical attachment" + ) + ) + XCTAssertTrue(attachment.isValid()) + let discardedAttachment = try await client.uploadAttachment( + chatId: renamed.id, + upload: .text(name: "discard.txt", mimeType: "text/plain", text: "discard") + ) + try await client.removeAttachment(chatId: renamed.id, attachmentId: discardedAttachment.id) + + let turn = try await client.startTurn( + chatId: renamed.id, + request: .init( + text: "Prove reconnect and approval", + providerId: "openai", + modelId: "gpt-5.6", + thinkingLevel: "max", + attachmentIds: [attachment.id] + ) + ) + XCTAssertEqual(turn.message.attachments?.first?.id, attachment.id) + XCTAssertEqual(turn.message.attachments?.first?.name, "physical-proof.md") + XCTAssertEqual(turn.message.attachments?.first?.mimeType, "text/markdown") + do { + _ = try await client.startTurn( + chatId: renamed.id, + request: .init(text: "Replay consumed attachment", attachmentIds: [attachment.id]) + ) + XCTFail("An attachment reference must be single use.") + } catch let error as AidenRemoteClientError { + guard case .server(let statusCode, let body) = error else { + return XCTFail("Expected a typed server error for attachment replay.") + } + XCTAssertEqual(statusCode, 409) + XCTAssertEqual(body.code.rawValue, "handle_invalid") + } + print("AIDEN_PHASE7_PHYSICAL checkpoint=first-stream") + var firstConnection: [AidenRemoteStreamEvent] = [] + for try await event in client.streamEvents(id: turn.streamId, after: 0) { + firstConnection.append(event) + } + XCTAssertEqual(firstConnection.map(\.sequence), [1, 2]) + XCTAssertEqual(firstConnection.last?.payload?.text, "Hello ") + + print("AIDEN_PHASE7_PHYSICAL checkpoint=replay-stream") + var replayConnection: [AidenRemoteStreamEvent] = [] + for try await event in client.streamEvents(id: turn.streamId, after: 2) { + replayConnection.append(event) + } + XCTAssertEqual(replayConnection.map(\.sequence), [3, 4, 5, 6, 7]) + let approval = try XCTUnwrap(replayConnection.last?.payload?.approvalId) + let waitingStatus = try await client.streamStatus(id: turn.streamId) + XCTAssertEqual(waitingStatus.state, .waitingForApproval) + _ = try await client.respondToApproval(id: approval, decision: .allow) + do { + _ = try await client.respondToApproval(id: approval, decision: .allow) + XCTFail("A resolved approval must reject duplicate decisions.") + } catch let error as AidenRemoteClientError { + guard case .server(let statusCode, let body) = error else { + return XCTFail("Expected a typed server error for duplicate approval.") + } + XCTAssertEqual(statusCode, 409) + XCTAssertEqual(body.code.rawValue, "approval_expired") + } + + print("AIDEN_PHASE7_PHYSICAL checkpoint=terminal-stream") + var terminalConnection: [AidenRemoteStreamEvent] = [] + for try await event in client.streamEvents(id: turn.streamId, after: 7) { + terminalConnection.append(event) + } + XCTAssertEqual(terminalConnection.map(\.sequence), [8, 9, 10]) + XCTAssertEqual(terminalConnection.last?.type, .done) + let authoritative = try await client.chat(id: renamed.id) + XCTAssertEqual(authoritative.messages.last?.role, .assistant) + XCTAssertEqual(authoritative.messages.last?.text, "Hello from Aiden.") + let authoritativeUserMessage = try XCTUnwrap( + authoritative.messages.first(where: { $0.role == .user }) + ) + XCTAssertEqual(authoritativeUserMessage.attachments?.first?.id, attachment.id) + XCTAssertEqual(authoritativeUserMessage.attachments?.first?.kind, .text) + + print("AIDEN_PHASE7_PHYSICAL checkpoint=cancel") + let cancelledTurn = try await client.startTurn( + chatId: authoritative.id, + request: .init(text: "Please cancel this turn") + ) + let cancelled = try await client.cancelStream(id: cancelledTurn.streamId) + XCTAssertEqual(cancelled.state, .cancelled) + var cancellationEvents: [AidenRemoteStreamEvent] = [] + for try await event in client.streamEvents(id: cancelledTurn.streamId, after: 0) { + cancellationEvents.append(event) + } + XCTAssertEqual(cancellationEvents.last?.type, .cancelled) + + let finalChat = try await client.chat(id: authoritative.id) + try await client.removeChat(id: finalChat.id, revision: finalChat.revision) + try await client.removeWorkspace(id: workspace.id, revision: workspace.revision) + let finalChats = try await client.chats(workspaceId: workspace.id) + XCTAssertEqual(finalChats, []) + } + + func testPhysicalDeviceServerRestartWhenConfigured() async throws { + let environment = ProcessInfo.processInfo.environment + guard let payloadValue = environment["AIDEN_PHASE12_RESTART_PAIRING_PAYLOAD"], + let repairPayloadValue = environment["AIDEN_PHASE12_REPAIR_PAIRING_PAYLOAD"] else { + throw XCTSkip("Set both Phase 12 pairing payloads in the physical-device xctestrun file.") + } + let payload = try AidenRemoteJSONDecoder.decodePairingPayload( + from: Data(payloadValue.utf8) + ).validated() + let repairPayload = try AidenRemoteJSONDecoder.decodePairingPayload( + from: Data(repairPayloadValue.utf8) + ).validated() + XCTAssertEqual(repairPayload.bootstrap.instanceId, payload.bootstrap.instanceId) + XCTAssertEqual(repairPayload.bootstrap.endpoint, payload.bootstrap.endpoint) + XCTAssertEqual(repairPayload.bootstrap.serverSpkiSha256, payload.bootstrap.serverSpkiSha256) + XCTAssertEqual(repairPayload.trust, payload.trust) + XCTAssertNotEqual(repairPayload.bootstrap.secret, payload.bootstrap.secret) + let exchange = try await AidenRemoteClient.pair( + payload: payload, + deviceName: "Physical iPhone 13 Pro Restart Proof", + deviceType: .iphone, + clientVersion: "1.0" + ) + let installation = AidenInstallation( + exchange: exchange, + pairingTrust: payload.trust, + name: "Physical Aiden Agent Restart Proof" + ) + let client = try AidenRemoteClient(installation: installation, credential: exchange.credential) + + let beforeRestart = try await client.server() + XCTAssertEqual(beforeRestart.instanceId, "phase7-physical-device-spike") + print("AIDEN_PHASE12_PHYSICAL checkpoint=restart-ready") + try await Task.sleep(for: .seconds(10)) + + var lastError: Error? + for _ in 0..<20 { + do { + let reconnectedClient = try AidenRemoteClient( + installation: installation, + credential: exchange.credential, + waitsForConnectivity: false, + requestTimeout: 2 + ) + let afterRestart = try await reconnectedClient.server() + XCTAssertEqual(afterRestart.instanceId, beforeRestart.instanceId) + lastError = nil + print("AIDEN_PHASE12_PHYSICAL checkpoint=reconnected") + break + } catch { + lastError = error + try await Task.sleep(for: .milliseconds(500)) + } + } + if lastError != nil { + throw try XCTUnwrap(lastError) + } + + print("AIDEN_PHASE12_PHYSICAL checkpoint=revocation-ready") + try await Task.sleep(for: .seconds(30)) + do { + let revokedClient = try AidenRemoteClient( + installation: installation, + credential: exchange.credential, + waitsForConnectivity: false, + requestTimeout: 2 + ) + _ = try await revokedClient.server() + XCTFail("The revoked credential must stop authenticating immediately.") + } catch let error as AidenRemoteClientError { + guard case .server(let statusCode, let body) = error else { + return XCTFail("Expected a typed server error after credential revocation.") + } + XCTAssertEqual(statusCode, 403) + XCTAssertEqual(body.code.rawValue, "credential_revoked") + print("AIDEN_PHASE12_PHYSICAL checkpoint=revoked") + } + + print("AIDEN_PHASE12_PHYSICAL checkpoint=repair-ready") + try await Task.sleep(for: .seconds(30)) + let repairExchange = try await AidenRemoteClient.pair( + payload: repairPayload, + deviceName: "Physical iPhone 13 Pro Re-pair Proof", + deviceType: .iphone, + clientVersion: "1.0" + ) + XCTAssertEqual(repairExchange.instanceId, exchange.instanceId) + XCTAssertNotEqual(repairExchange.deviceId, exchange.deviceId) + XCTAssertNotEqual(repairExchange.credential, exchange.credential) + let repairedInstallation = AidenInstallation( + exchange: repairExchange, + pairingTrust: repairPayload.trust, + name: "Physical Aiden Agent Re-pair Proof" + ) + let repairedClient = try AidenRemoteClient( + installation: repairedInstallation, + credential: repairExchange.credential, + waitsForConnectivity: false, + requestTimeout: 2 + ) + let repairedServer = try await repairedClient.server() + XCTAssertEqual(repairedServer.instanceId, beforeRestart.instanceId) + do { + _ = try await AidenRemoteClient.pair( + payload: repairPayload, + deviceName: "Physical iPhone 13 Pro Re-pair Replay", + deviceType: .iphone, + clientVersion: "1.0" + ) + XCTFail("The repair pairing secret must be one-use.") + } catch let error as AidenRemoteClientError { + guard case .server(let statusCode, let body) = error else { + return XCTFail("Expected a typed server error after repair pairing replay.") + } + XCTAssertEqual(statusCode, 401) + XCTAssertEqual(body.code.rawValue, "pairing_closed") + } + print("AIDEN_PHASE12_PHYSICAL checkpoint=repaired") + + print("AIDEN_PHASE12_PHYSICAL checkpoint=repair-restart-ready") + try await Task.sleep(for: .seconds(30)) + let repairedAfterRestart = try AidenRemoteClient( + installation: repairedInstallation, + credential: repairExchange.credential, + waitsForConnectivity: false, + requestTimeout: 2 + ) + let serverAfterRepairRestart = try await repairedAfterRestart.server() + XCTAssertEqual(serverAfterRepairRestart.instanceId, beforeRestart.instanceId) + do { + let oldAfterRepair = try AidenRemoteClient( + installation: installation, + credential: exchange.credential, + waitsForConnectivity: false, + requestTimeout: 2 + ) + _ = try await oldAfterRepair.server() + XCTFail("The replaced credential must remain invalid after re-pair and restart.") + } catch let error as AidenRemoteClientError { + guard case .server(let statusCode, let body) = error else { + return XCTFail("Expected a typed server error for the replaced credential.") + } + XCTAssertEqual(statusCode, 403) + XCTAssertEqual(body.code.rawValue, "credential_revoked") + } + print("AIDEN_PHASE12_PHYSICAL checkpoint=repair-restarted") + } + + @MainActor + func testRePairingAtomicallyReplacesInstallationScopedCredential() throws { + let keychain = AidenRemoteMemoryKeychain() + let store = AidenInstallationStore(keychain: keychain) + let initial = makeExchange( + instanceId: "instance-repair", + deviceId: "device-before", + credential: String(repeating: "A", count: 43) + ) + let repaired = makeExchange( + instanceId: "instance-repair", + deviceId: "device-after", + credential: String(repeating: "B", count: 43) + ) + let createdAt = Date(timeIntervalSince1970: 1_787_100_000) + let first = try store.savePairing(initial, trust: makeSystemTrust(), name: "Aiden Mac", now: createdAt) + let second = try store.savePairing( + repaired, + trust: makeSystemTrust(), + name: "Aiden Mac", + now: createdAt.addingTimeInterval(60) + ) + + XCTAssertEqual(store.installations.count, 1) + XCTAssertEqual(store.activeInstallationId, "instance-repair") + XCTAssertEqual(second.createdAt, first.createdAt) + XCTAssertEqual(second.deviceId, "device-after") + XCTAssertEqual(try store.credential(for: second), repaired.credential) + XCTAssertEqual( + keychain.scoped[KeychainStore.scopedKey(.remoteCredential, scope: "instance-repair")], + repaired.credential + ) + XCTAssertFalse(keychain.scoped.values.contains(initial.credential)) + } + + @MainActor + func testInstallationStoreKeepsCredentialsScopedAndSwitchesWithoutLeakage() throws { + let keychain = AidenRemoteMemoryKeychain() + let store = AidenInstallationStore(keychain: keychain) + let now = Date(timeIntervalSince1970: 1_787_100_000) + let first = makeExchange(instanceId: "instance-1", deviceId: "device-1", credential: String(repeating: "A", count: 43)) + let second = makeExchange(instanceId: "instance-2", deviceId: "device-2", credential: String(repeating: "B", count: 43)) + + let firstInstallation = try store.savePairing(first, trust: makeSystemTrust(), name: "Home Mac", now: now) + let secondInstallation = try store.savePairing(second, trust: makeSystemTrust(), name: "Studio Mac", now: now) + XCTAssertEqual(store.activeInstallationId, "instance-2") + XCTAssertEqual(try store.credential(for: firstInstallation), first.credential) + XCTAssertEqual(try store.credential(for: secondInstallation), second.credential) + + try store.setActive("instance-1") + XCTAssertEqual(store.activeInstallationId, "instance-1") + XCTAssertEqual(try store.credential(for: store.activeInstallation!), first.credential) + + try store.remove("instance-1") + XCTAssertNil(keychain.scoped[KeychainStore.scopedKey(.remoteCredential, scope: "instance-1")]) + XCTAssertEqual(keychain.scoped[KeychainStore.scopedKey(.remoteCredential, scope: "instance-2")], second.credential) + } + + @MainActor + func testInstallationTrustPersistsAndLegacyMetadataFailsClosed() throws { + let keychain = AidenRemoteMemoryKeychain() + let firstStore = AidenInstallationStore(keychain: keychain) + let exchange = makeExchange( + instanceId: "instance-trusted", + deviceId: "device-trusted", + credential: "credential-trusted" + ) + _ = try firstStore.savePairing( + exchange, + trust: makeSystemTrust(), + name: "Trusted Mac" + ) + let restoredStore = AidenInstallationStore(keychain: keychain) + XCTAssertEqual(restoredStore.activeInstallation?.pairingTrust, makeSystemTrust()) + + let legacySnapshot = """ + {"installations":[{"instanceId":"legacy-instance","deviceId":"legacy-device", + "name":"Legacy Mac","endpoint":"https://aiden.test/api/aiden/v1", + "serverSpkiSha256":"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "capabilities":["server:read"],"createdAt":0}], + "activeInstallationId":"legacy-instance"} + """ + keychain.values[.remoteInstallations] = legacySnapshot + let legacyStore = AidenInstallationStore(keychain: keychain) + let legacyInstallation = try XCTUnwrap(legacyStore.activeInstallation) + XCTAssertNil(legacyInstallation.pairingTrust) + XCTAssertThrowsError(try AidenRemoteClient( + installation: legacyInstallation, + credential: "legacy-credential" + )) { error in + guard let clientError = error as? AidenRemoteClientError, + case .missingTrustConfiguration = clientError else { + return XCTFail("Legacy metadata must require secure re-pairing.") + } + } + } + + @MainActor + func testCoordinatorConnectsAndAppliesWorkspaceCRUDWithoutLosingAuthoritativeState() async throws { + let keychain = AidenRemoteMemoryKeychain() + let store = AidenInstallationStore(keychain: keychain) + let exchange = makeExchange( + instanceId: "instance-1", + deviceId: "device-1", + credential: "credential-one" + ) + _ = try store.savePairing(exchange, trust: makeSystemTrust(), name: "Unverified Mac") + + let session = makeSession() + AidenRemoteMockURLProtocol.handler = { request in + switch (request.httpMethod, request.url?.path) { + case ("GET", "/api/aiden/v1/server"): + return Self.response( + for: request, + status: 200, + json: """ + {"protocolVersion":1,"instanceId":"instance-1","name":"Home Mac", + "appVersion":"1.0.0","capabilities":["server:read","workspace:read","workspace:manage"], + "connectionMode":"lan","serverTime":"2026-08-19T07:00:00.000Z"} + """ + ) + case ("GET", "/api/aiden/v1/workspaces"): + return Self.response( + for: request, + status: 200, + json: """ + {"workspaces":[ + {"id":"workspace-z","name":"Zulu","permission":"ask","hasFolder":false, + "isManagedWorktree":false,"createdAt":"2026-08-19T07:00:00.000Z", + "updatedAt":"2026-08-19T07:00:00.000Z","revision":"rev-z"}, + {"id":"workspace-a","name":"Alpha","permission":"ask","hasFolder":false, + "isManagedWorktree":false,"createdAt":"2026-08-19T07:00:00.000Z", + "updatedAt":"2026-08-19T07:00:00.000Z","revision":"rev-a"}]} + """ + ) + case ("POST", "/api/aiden/v1/workspaces"): + return Self.workspaceResponse(for: request, status: 201, revision: "rev-created") + case ("PATCH", "/api/aiden/v1/workspaces/workspace-1"): + return Self.workspaceResponse(for: request, status: 200, revision: "rev-updated") + case ("DELETE", "/api/aiden/v1/workspaces/workspace-1"): + return ( + HTTPURLResponse(url: request.url!, statusCode: 204, httpVersion: nil, headerFields: nil)!, + Data() + ) + default: + XCTFail("Unexpected coordinator request: \(request.httpMethod ?? "nil") \(request.url?.path ?? "nil")") + return Self.response(for: request, status: 500, json: "{}") + } + } + + let coordinator = AidenRemoteCoordinator( + installationStore: store, + clientFactory: { installation, credential in + AidenRemoteClient(endpoint: installation.endpoint, credential: credential, session: session) + } + ) + await coordinator.start() + + XCTAssertEqual(coordinator.connectionState, .connected) + XCTAssertEqual(coordinator.server?.name, "Home Mac") + XCTAssertEqual(store.activeInstallation?.name, "Home Mac") + XCTAssertEqual(coordinator.workspaces.map(\.name), ["Zulu", "Alpha"]) + + let createdResult = await coordinator.createWorkspace(.folderless(name: "New Workspace")) + let created = try XCTUnwrap(createdResult) + XCTAssertTrue(coordinator.workspaces.contains(where: { $0.id == created.id })) + let updatedResult = await coordinator.updateWorkspace(created, permission: .full) + let updated = try XCTUnwrap(updatedResult) + XCTAssertEqual(updated.revision, "rev-updated") + let removed = await coordinator.removeWorkspace(updated) + XCTAssertTrue(removed) + XCTAssertFalse(coordinator.workspaces.contains(where: { $0.id == updated.id })) + } + + @MainActor + func testCoordinatorRevocationRemovesOnlyAffectedInstallationAndConnectsNextMac() async throws { + let keychain = AidenRemoteMemoryKeychain() + let store = AidenInstallationStore(keychain: keychain) + let first = makeExchange( + instanceId: "instance-1", + deviceId: "device-1", + credential: "credential-one" + ) + let second = makeExchange( + instanceId: "instance-2", + deviceId: "device-2", + credential: "credential-two" + ) + _ = try store.savePairing(first, trust: makeSystemTrust(), name: "Revoked Mac") + _ = try store.savePairing(second, trust: makeSystemTrust(), name: "Backup Mac") + try store.setActive("instance-1") + + let session = makeSession() + AidenRemoteMockURLProtocol.handler = { request in + if request.value(forHTTPHeaderField: "Authorization") == "Bearer credential-one" { + return Self.response( + for: request, + status: 401, + json: """ + {"error":{"code":"credential_revoked","message":"Pair this device again.", + "requestId":"request-revoked","retryable":false}} + """ + ) + } + XCTAssertEqual(request.value(forHTTPHeaderField: "Authorization"), "Bearer credential-two") + if request.url?.path == "/api/aiden/v1/server" { + return Self.response( + for: request, + status: 200, + json: """ + {"protocolVersion":1,"instanceId":"instance-2","name":"Backup Mac", + "appVersion":"1.0.0","capabilities":["server:read","workspace:read"], + "connectionMode":"lan","serverTime":"2026-08-19T07:00:00.000Z"} + """ + ) + } + return Self.response(for: request, status: 200, json: "{\"workspaces\":[]}") + } + + let coordinator = AidenRemoteCoordinator( + installationStore: store, + clientFactory: { installation, credential in + AidenRemoteClient(endpoint: installation.endpoint, credential: credential, session: session) + } + ) + await coordinator.start() + + XCTAssertEqual(coordinator.connectionState, .connected) + XCTAssertEqual(store.activeInstallationId, "instance-2") + XCTAssertNil(keychain.scoped[KeychainStore.scopedKey(.remoteCredential, scope: "instance-1")]) + XCTAssertEqual( + keychain.scoped[KeychainStore.scopedKey(.remoteCredential, scope: "instance-2")], + "credential-two" + ) + XCTAssertTrue(coordinator.presentedError?.contains("revoked") == true) + } + + private func makeClient() -> AidenRemoteClient { + AidenRemoteClient( + endpoint: URL(string: "https://aiden.test/api/aiden/v1")!, + credential: "device-credential", + session: makeSession() + ) + } + + private func makeSession() -> URLSession { + let configuration = URLSessionConfiguration.ephemeral + configuration.protocolClasses = [AidenRemoteMockURLProtocol.self] + return URLSession(configuration: configuration) + } + + private func makeBootstrap(now: Date) -> AidenRemoteContractFixture.PairingBootstrap { + AidenRemoteContractFixture.PairingBootstrap( + protocolVersion: 1, + instanceId: "instance-1", + endpoint: URL(string: "https://aiden.test/api/aiden/v1")!, + serverSpkiSha256: "sha256/\(Data(repeating: 7, count: 32).base64EncodedString())", + secret: String(repeating: "B", count: 43), + expiresAt: now.addingTimeInterval(120) + ) + } + + private func makeSystemTrust() -> AidenRemoteContractFixture.PairingTrust { + AidenRemoteContractFixture.PairingTrust(mode: .system) + } + + private func makePairingPayload( + bootstrap: AidenRemoteContractFixture.PairingBootstrap + ) -> AidenRemoteContractFixture.PairingPayload { + AidenRemoteContractFixture.PairingPayload( + bootstrap: bootstrap, + trust: makeSystemTrust() + ) + } + + private func makeExchange( + instanceId: String, + deviceId: String, + credential: String + ) -> AidenRemoteContractFixture.PairingExchange { + AidenRemoteContractFixture.PairingExchange( + protocolVersion: 1, + instanceId: instanceId, + deviceId: deviceId, + credential: credential, + capabilities: [.serverRead, .workspaceRead], + endpoint: URL(string: "https://aiden.test/api/aiden/v1")!, + serverSpkiSha256: "sha256/\(Data(repeating: 7, count: 32).base64EncodedString())" + ) + } + + private static func jsonBody(_ request: URLRequest) throws -> [String: Any] { + let data = try bodyData(request) + return try XCTUnwrap(JSONSerialization.jsonObject(with: data) as? [String: Any]) + } + + private static func bodyData(_ request: URLRequest) throws -> Data { + if let body = request.httpBody { return body } + let stream = try XCTUnwrap(request.httpBodyStream) + stream.open() + defer { stream.close() } + var data = Data() + var buffer = [UInt8](repeating: 0, count: 4_096) + while stream.hasBytesAvailable { + let count = stream.read(&buffer, maxLength: buffer.count) + guard count >= 0 else { throw stream.streamError ?? URLError(.cannotDecodeRawData) } + if count == 0 { break } + data.append(buffer, count: count) + } + return data + } + + private static func workspaceResponse( + for request: URLRequest, + status: Int, + revision: String + ) -> (HTTPURLResponse, Data) { + response( + for: request, + status: status, + json: """ + {"id":"workspace-1","name":"New Workspace","permission":"full", + "hasFolder":false,"isManagedWorktree":false, + "createdAt":"2026-08-19T07:00:00.000Z", + "updatedAt":"2026-08-19T07:01:00.000Z","revision":"\(revision)"} + """ + ) + } + + private static func chatResponse( + for request: URLRequest, + status: Int, + revision: String, + title: String = "New Chat" + ) -> (HTTPURLResponse, Data) { + response( + for: request, + status: status, + json: """ + {"id":"chat-1","workspaceId":"workspace-1","title":"\(title)","messages":[], + "createdAt":"2026-08-19T07:00:00.000Z","updatedAt":"2026-08-19T07:00:01.000Z", + "revision":"\(revision)"} + """ + ) + } + + private static func streamStatusResponse( + for request: URLRequest, + state: String + ) -> (HTTPURLResponse, Data) { + response( + for: request, + status: state == "cancelled" ? 202 : 200, + json: """ + {"streamId":"stream-1","chatId":"chat-1","turnId":"turn-1","state":"\(state)", + "lastSequence":3,"updatedAt":"2026-08-19T07:00:01.000Z"} + """ + ) + } + + private static func response( + for request: URLRequest, + status: Int, + json: String + ) -> (HTTPURLResponse, Data) { + let response = HTTPURLResponse( + url: request.url!, + statusCode: status, + httpVersion: nil, + headerFields: ["Content-Type": "application/json"] + )! + return (response, Data(json.utf8)) + } +} + +private final class AidenRemoteMockURLProtocol: URLProtocol, @unchecked Sendable { + static var handler: ((URLRequest) throws -> (HTTPURLResponse, Data))? + + override class func canInit(with request: URLRequest) -> Bool { true } + override class func canonicalRequest(for request: URLRequest) -> URLRequest { request } + + override func startLoading() { + guard let handler = Self.handler else { + client?.urlProtocol(self, didFailWithError: URLError(.badServerResponse)) + return + } + do { + let (response, data) = try handler(request) + client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) + client?.urlProtocol(self, didLoad: data) + client?.urlProtocolDidFinishLoading(self) + } catch { + client?.urlProtocol(self, didFailWithError: error) + } + } + + override func stopLoading() {} +} + +private final class AidenRemoteMemoryKeychain: KeychainStoring { + var values: [KeychainStore.Key: String] = [:] + var scoped: [String: String] = [:] + + func save(_ value: String, forKey key: KeychainStore.Key) throws { values[key] = value } + func load(_ key: KeychainStore.Key) throws -> String? { values[key] } + func delete(_ key: KeychainStore.Key) throws { values[key] = nil } + + func save(_ value: String, forKey key: KeychainStore.Key, scope: String) throws { + scoped[KeychainStore.scopedKey(key, scope: scope)] = value + } + + func load(_ key: KeychainStore.Key, scope: String) throws -> String? { + scoped[KeychainStore.scopedKey(key, scope: scope)] + } + + func delete(_ key: KeychainStore.Key, scope: String) throws { + scoped[KeychainStore.scopedKey(key, scope: scope)] = nil + } +} diff --git a/ios/AidenOnTheGoTests/AidenRemotePhase0Tests.swift b/ios/AidenOnTheGoTests/AidenRemotePhase0Tests.swift new file mode 100644 index 00000000..81479a8f --- /dev/null +++ b/ios/AidenOnTheGoTests/AidenRemotePhase0Tests.swift @@ -0,0 +1,904 @@ +import CryptoKit +import Foundation +import Security +import XCTest +@testable import AidenOnTheGo + +final class AidenRemotePhase0Tests: XCTestCase { + private let approvedKeychainService = "sbtbiswas.AidenOnTheGo.pairing" + private let caCertificateDER = "MIIBpzCCAU6gAwIBAgIUIHmU6u43BGrkVPj4FQ5phcJ7K8EwCgYIKoZIzj0EAwIwIDEeMBwGA1UEAwwVQWlkZW4tUGhhc2UwLUxvY2FsLUNBMB4XDTI2MDgxODIwNTgwM1oXDTM2MDgxNTIwNTgwM1owIDEeMBwGA1UEAwwVQWlkZW4tUGhhc2UwLUxvY2FsLUNBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZwqoZbvPSf8paC937p5+TnciNpAxHE/4fwll/5YlUGW6xkSUmvFj7CpD3IPvY0PRgN+sZl/CzBFzn+wv9atnkaNmMGQwHQYDVR0OBBYEFK2vesnPv0ymHuSE6yQ9EoM+B7EYMB8GA1UdIwQYMBaAFK2vesnPv0ymHuSE6yQ9EoM+B7EYMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMCA0cAMEQCIHawuTBf/AOiSWTY+XpLIUzSxxFdKmTZl1Vol4HRJQ5VAiBpYlpHpxEzMd2j/VK8fUfZ8DU6y7XKme2iJFS8M7d1lw==" + private let originalCertificateDER = "MIIB4jCCAYmgAwIBAgIULA5eC0u0KgewVf5FJD89CeRl5icwCgYIKoZIzj0EAwIwIDEeMBwGA1UEAwwVQWlkZW4tUGhhc2UwLUxvY2FsLUNBMB4XDTI2MDgxODIwNTgwM1oXDTI2MDkxNzIwNTgwM1owJDEiMCAGA1UEAwwZYWlkZW4tcGhhc2UwLmV4YW1wbGUudGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABI5u4+Ne8MXXQeyVvmFDduB1soFoJQvIv296OVjGuty9Z0VyUpKn2+oBKTSuD0GNooaSlIHqptxLFT/cpEYxrRqjgZwwgZkwJAYDVR0RBB0wG4IZYWlkZW4tcGhhc2UwLmV4YW1wbGUudGVzdDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQUnnUkXuqHkoGw2LKXeyU7bjyCVYcwHwYDVR0jBBgwFoAUra96yc+/TKYe5ITrJD0Sgz4HsRgwCgYIKoZIzj0EAwIDRwAwRAIgd2WNDX68uxSxGQYJsDiUXohxKlBeEjXESlgHx6WRrJgCIFJN5ineCyCIYL17DW2sJ/9h2qA3GdOo/aiUWc+e6FCV" + private let renewedCertificateDER = "MIIB9TCCAZugAwIBAgIULA5eC0u0KgewVf5FJD89CeRl5igwCgYIKoZIzj0EAwIwIDEeMBwGA1UEAwwVQWlkZW4tUGhhc2UwLUxvY2FsLUNBMB4XDTI2MDgxODIwNTgwM1oXDTI2MDkxNzIwNTgwM1owNjEiMCAGA1UEAwwZYWlkZW4tcGhhc2UwLmV4YW1wbGUudGVzdDEQMA4GA1UECwwHcmVuZXdlZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABI5u4+Ne8MXXQeyVvmFDduB1soFoJQvIv296OVjGuty9Z0VyUpKn2+oBKTSuD0GNooaSlIHqptxLFT/cpEYxrRqjgZwwgZkwJAYDVR0RBB0wG4IZYWlkZW4tcGhhc2UwLmV4YW1wbGUudGVzdDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQUnnUkXuqHkoGw2LKXeyU7bjyCVYcwHwYDVR0jBBgwFoAUra96yc+/TKYe5ITrJD0Sgz4HsRgwCgYIKoZIzj0EAwIDSAAwRQIhANjQ3dAkNt/zT66IhAfodEWh75Ig5XmAju3MYn2sLSicAiBUomVIhfwnYjxs54zSiHzuyGpPmRKVCBHjzadb+U9MTw==" + private let rotatedCertificateDER = "MIIB9TCCAZugAwIBAgIULA5eC0u0KgewVf5FJD89CeRl5ikwCgYIKoZIzj0EAwIwIDEeMBwGA1UEAwwVQWlkZW4tUGhhc2UwLUxvY2FsLUNBMB4XDTI2MDgxODIwNTgwM1oXDTI2MDkxNzIwNTgwM1owNjEiMCAGA1UEAwwZYWlkZW4tcGhhc2UwLmV4YW1wbGUudGVzdDEQMA4GA1UECwwHcm90YXRlZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABMFeD3fbzVvwD6XsOV0zTS9/afPUm0BzWjsDlPoPKR+s5dlo3aAIe1B0tsMvEOgdHb0BV8D9RQcOg3H+/qqKYLKjgZwwgZkwJAYDVR0RBB0wG4IZYWlkZW4tcGhhc2UwLmV4YW1wbGUudGVzdDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQU6Vpq2GlIaP7m9AHtEE+fve8bWDYwHwYDVR0jBBgwFoAUra96yc+/TKYe5ITrJD0Sgz4HsRgwCgYIKoZIzj0EAwIDSAAwRQIhAKdQOzcQ+qJZP/gpVvT8uDz+DvRx9JAEWTLmuUqoJ9ujAiABuU2rRJ/ivMhm5lGDDFrunkN9Kk04oUPjJ1w8CplkQg==" + + private func trust(for certificate: SecCertificate, ca: SecCertificate, host: String) throws -> SecTrust { + var value: SecTrust? + let status = SecTrustCreateWithCertificates( + [certificate, ca] as CFArray, + SecPolicyCreateSSL(true, host as CFString), + &value + ) + XCTAssertEqual(status, errSecSuccess) + return try XCTUnwrap(value) + } + + private func assertTransportLifecycleFixtures() throws { + let original = try XCTUnwrap(SecCertificateCreateWithData(nil, try XCTUnwrap(Data(base64Encoded: originalCertificateDER)) as CFData)) + let renewed = try XCTUnwrap(SecCertificateCreateWithData(nil, try XCTUnwrap(Data(base64Encoded: renewedCertificateDER)) as CFData)) + let rotated = try XCTUnwrap(SecCertificateCreateWithData(nil, try XCTUnwrap(Data(base64Encoded: rotatedCertificateDER)) as CFData)) + let ca = try XCTUnwrap(SecCertificateCreateWithData(nil, try XCTUnwrap(Data(base64Encoded: caCertificateDER)) as CFData)) + let privateCAPolicy = AidenServerTrustPolicy.privateCA(SecCertificateCopyData(ca) as Data) + let originalPin = try AidenServerTrust.spkiSHA256(certificate: original) + XCTAssertEqual(try AidenServerTrust.spkiSHA256(certificate: renewed), originalPin) + XCTAssertNotEqual(try AidenServerTrust.spkiSHA256(certificate: rotated), originalPin) + let validDate = try XCTUnwrap(ISO8601DateFormatter().date(from: "2026-08-19T00:00:00Z")) + XCTAssertNoThrow(try AidenServerTrust.evaluate(serverTrust: trust(for: original, ca: ca, host: "aiden-phase0.example.test"), expectedHost: "aiden-phase0.example.test", expectedFingerprint: originalPin, policy: privateCAPolicy, verificationDate: validDate)) + XCTAssertNoThrow(try AidenServerTrust.evaluate(serverTrust: trust(for: renewed, ca: ca, host: "aiden-phase0.example.test"), expectedHost: "aiden-phase0.example.test", expectedFingerprint: originalPin, policy: privateCAPolicy, verificationDate: validDate)) + XCTAssertThrowsError(try AidenServerTrust.evaluate(serverTrust: trust(for: original, ca: ca, host: "aiden-phase0.example.test"), expectedHost: "aiden-phase0.example.test", expectedFingerprint: originalPin, policy: .system, verificationDate: validDate)) + XCTAssertThrowsError(try AidenServerTrust.evaluate(serverTrust: trust(for: original, ca: ca, host: "aiden-phase0.example.test"), expectedHost: "aiden-phase0.example.test", expectedFingerprint: originalPin, policy: .privateCA(SecCertificateCopyData(rotated) as Data), verificationDate: validDate)) + XCTAssertThrowsError(try AidenServerTrust.evaluate(serverTrust: trust(for: rotated, ca: ca, host: "aiden-phase0.example.test"), expectedHost: "aiden-phase0.example.test", expectedFingerprint: originalPin, policy: privateCAPolicy, verificationDate: validDate)) + XCTAssertThrowsError(try AidenServerTrust.evaluate(serverTrust: trust(for: original, ca: ca, host: "wrong.example.test"), expectedHost: "wrong.example.test", expectedFingerprint: originalPin, policy: privateCAPolicy, verificationDate: validDate)) + let expiredDate = try XCTUnwrap(ISO8601DateFormatter().date(from: "2040-01-01T00:00:00Z")) + XCTAssertThrowsError(try AidenServerTrust.evaluate(serverTrust: trust(for: original, ca: ca, host: "aiden-phase0.example.test"), expectedHost: "aiden-phase0.example.test", expectedFingerprint: originalPin, policy: privateCAPolicy, verificationDate: expiredDate)) + } + private var sharedContractFixtureURL: URL? { + Bundle(for: Self.self).url(forResource: "contract", withExtension: "json") + } + + func testLocalNetworkPrivacyAndBonjourContractIsDeclared() throws { + let bundle = Bundle.main + let usageDescription = try XCTUnwrap( + bundle.object(forInfoDictionaryKey: "NSLocalNetworkUsageDescription") as? String + ) + XCTAssertFalse(usageDescription.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) + let services = try XCTUnwrap(bundle.object(forInfoDictionaryKey: "NSBonjourServices") as? [String]) + XCTAssertEqual(services, ["_aiden-agent._tcp"]) + } + + func testBuiltAppHasNoInsecureHTTPOrArbitraryLoadATSException() throws { + let bundle = Bundle.main + guard let transportSecurity = bundle.object( + forInfoDictionaryKey: "NSAppTransportSecurity" + ) as? [String: Any] else { + return + } + + XCTAssertNotEqual(transportSecurity["NSAllowsArbitraryLoads"] as? Bool, true) + XCTAssertNotEqual(transportSecurity["NSAllowsArbitraryLoadsForMedia"] as? Bool, true) + XCTAssertNotEqual(transportSecurity["NSAllowsArbitraryLoadsInWebContent"] as? Bool, true) + XCTAssertNotEqual(transportSecurity["NSAllowsLocalNetworking"] as? Bool, true) + XCTAssertNil(transportSecurity["NSExceptionDomains"]) + } + + func testAidenKeychainServiceResolvesToApprovedIdentity() throws { + XCTAssertEqual( + Bundle.main.object(forInfoDictionaryKey: "AidenKeychainService") as? String, + approvedKeychainService + ) + } + + func testBuiltAppUsesOnlyApprovedAidenProductIdentity() throws { + let bundle = Bundle.main + XCTAssertEqual(bundle.bundleIdentifier, "sbtbiswas.AidenOnTheGo") + XCTAssertEqual( + bundle.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String, + "Aiden On The Go" + ) + XCTAssertEqual( + bundle.object(forInfoDictionaryKey: "AidenAppGroupIdentifier") as? String, + "group.sbtbiswas.AidenOnTheGo" + ) + XCTAssertEqual( + bundle.object(forInfoDictionaryKey: "AidenURLScheme") as? String, + "aiden-otg" + ) + + let urlTypes = try XCTUnwrap( + bundle.object(forInfoDictionaryKey: "CFBundleURLTypes") as? [[String: Any]] + ) + let schemes = urlTypes.flatMap { $0["CFBundleURLSchemes"] as? [String] ?? [] } + XCTAssertEqual(schemes, ["aiden-otg"]) + XCTAssertFalse( + schemes.contains { scheme in + let value = scheme.lowercased() + return value.contains("hermes") || value.contains("hermex") + } + ) + } + + func testSignedDeviceAidenKeychainIsolationWhenConfigured() throws { + guard ProcessInfo.processInfo.environment["AIDEN_PHASE0_KEYCHAIN_PROOF"] == "1" else { + throw XCTSkip("Set AIDEN_PHASE0_KEYCHAIN_PROOF=1 only for a normally signed device run.") + } + + let scope = "aiden-phase0-\(UUID().uuidString)" + let probe = UUID().uuidString + let store = KeychainStore() + let otherStore = KeychainStore(service: "\(approvedKeychainService).phase0-isolation") + defer { + try? store.delete(.remoteCredential, scope: scope) + try? otherStore.delete(.remoteCredential, scope: scope) + } + + XCTAssertNil(try store.load(.remoteCredential, scope: scope)) + try store.save(probe, forKey: .remoteCredential, scope: scope) + XCTAssertEqual(try store.load(.remoteCredential, scope: scope), probe) + XCTAssertNil(try otherStore.load(.remoteCredential, scope: scope)) + try store.delete(.remoteCredential, scope: scope) + XCTAssertNil(try store.load(.remoteCredential, scope: scope)) + } + + func testSharedContractFixtureDecodesAndSequencesAreTerminalSafe() throws { + let fixtureURL = try XCTUnwrap(sharedContractFixtureURL) + let data = try Data(contentsOf: fixtureURL) + let fixture = try AidenRemoteJSONDecoder.decode( + AidenRemoteContractFixture.self, + from: data + ) + + XCTAssertEqual(fixture.contractRevision, 2) + XCTAssertEqual(fixture.protocolVersion, AidenRemoteProtocol.version) + XCTAssertTrue(fixture.health.ok) + XCTAssertEqual(fixture.health.protocolVersion, AidenRemoteProtocol.version) + XCTAssertEqual(Set(fixture.capabilities), Set(AidenRemoteCapability.v1Known)) + XCTAssertEqual(Set(fixture.events.map(\.type)), Set(AidenRemoteEventType.v1Known)) + XCTAssertEqual(fixture.pairingBootstrap.protocolVersion, AidenRemoteProtocol.version) + XCTAssertEqual(fixture.pairingBootstrap.endpoint.scheme, "https") + XCTAssertGreaterThanOrEqual(fixture.pairingBootstrap.secret.count, 32) + XCTAssertTrue(fixture.pairingBootstrap.serverSpkiSha256.hasPrefix("sha256/")) + let fixtureReferenceDate = try XCTUnwrap( + ISO8601DateFormatter().date(from: "2026-08-18T19:00:00Z") + ) + XCTAssertNoThrow(try fixture.pairingBootstrap.validated(at: fixtureReferenceDate)) + XCTAssertNoThrow(try fixture.pairingExchange.validated(against: fixture.pairingBootstrap)) + + var lastSequence: [String: Int] = [:] + var terminalStreams = Set() + for event in fixture.events { + XCTAssertFalse(terminalStreams.contains(event.streamId)) + XCTAssertEqual(event.sequence, (lastSequence[event.streamId] ?? 0) + 1) + lastSequence[event.streamId] = event.sequence + if event.terminal { terminalStreams.insert(event.streamId) } + } + } + + func testHealthResponseRequiresExactSuccessfulV1Contract() throws { + let valid = Data(#"{"ok":true,"protocolVersion":1}"#.utf8) + XCTAssertNoThrow(try AidenRemoteJSONDecoder.decode(AidenRemoteContractFixture.Health.self, from: valid)) + + let invalidResponses = [ + Data(#"{"ok":true,"protocolVersion":1,"unexpected":true}"#.utf8), + Data(#"{"protocolVersion":1}"#.utf8), + Data(#"{"ok":true}"#.utf8), + Data(#"{"ok":false,"protocolVersion":1}"#.utf8), + Data(#"{"ok":true,"protocolVersion":2}"#.utf8), + ] + for response in invalidResponses { + XCTAssertThrowsError( + try AidenRemoteJSONDecoder.decode(AidenRemoteContractFixture.Health.self, from: response) + ) + } + } + + func testSharedFixtureContainsNoForbiddenWireFieldsOrMachinePaths() throws { + let fixtureURL = try XCTUnwrap(sharedContractFixtureURL) + let data = try Data(contentsOf: fixtureURL) + let object = try JSONSerialization.jsonObject(with: data) + let forbidden = Set([ + "authorization", "credentialDigest", "providerFingerprint", + "mcpServerBindings", "folderPath", "repositoryPath", "worktreePath", + "worktreeGitDir", "ownershipToken", "worktreeDevice", "worktreeInode", + "createdFromHead", "canonicalPath", "absolutePath", "scriptPath", + "environment", "stdout", "stderr", + ]) + + func inspect(_ value: Any) throws { + if let dictionary = value as? [String: Any] { + XCTAssertTrue(forbidden.isDisjoint(with: dictionary.keys)) + for child in dictionary.values { try inspect(child) } + } else if let array = value as? [Any] { + for child in array { try inspect(child) } + } else if let string = value as? String { + XCTAssertFalse(string.contains("/Users/")) + XCTAssertFalse(string.contains("BEGIN PRIVATE KEY")) + } + } + try inspect(object) + } + + func testP256SPKIFingerprintMatchesIndependentFixture() throws { + let rawKey = Data([0x04] + Array(repeating: UInt8(0), count: 64)) + XCTAssertEqual( + try AidenServerTrust.spkiSHA256(p256ExternalRepresentation: rawKey), + "sha256/FhPubfxu6YoU7IG0Hq45pUOLUPvLv4oAgUflVyabRMs=" + ) + } + + func testCertificateRenewalRotationWrongHostAndExpiryFailClosed() throws { + try assertTransportLifecycleFixtures() + } + + func testP256SPKIFingerprintRejectsMalformedOrWrongCurveRepresentations() { + XCTAssertThrowsError( + try AidenServerTrust.spkiSHA256(p256ExternalRepresentation: Data(repeating: 0, count: 65)) + ) + XCTAssertThrowsError( + try AidenServerTrust.spkiSHA256(p256ExternalRepresentation: Data([0x04])) + ) + } + + func testPairingBootstrapValidationFailsClosed() throws { + let now = Date(timeIntervalSince1970: 2_000_000_000) + let valid = AidenRemoteContractFixture.PairingBootstrap( + protocolVersion: 1, + instanceId: "instance_fixture", + endpoint: try XCTUnwrap(URL(string: "https://aiden.example.test/api/aiden/v1")), + serverSpkiSha256: "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + secret: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + expiresAt: now.addingTimeInterval(60) + ) + XCTAssertNoThrow(try valid.validated(at: now)) + let validWithExplicitPort = AidenRemoteContractFixture.PairingBootstrap( + protocolVersion: valid.protocolVersion, + instanceId: valid.instanceId, + endpoint: try XCTUnwrap(URL(string: "https://aiden.example.test:7443/api/aiden/v1")), + serverSpkiSha256: valid.serverSpkiSha256, + secret: valid.secret, + expiresAt: valid.expiresAt + ) + XCTAssertNoThrow(try validWithExplicitPort.validated(at: now)) + for encodedPath in [ + "/api/aiden%2Fv1", + "/api/aiden%2fv1", + "/%61pi/aiden/v1", + ] { + let encodedEndpoint = AidenRemoteContractFixture.PairingBootstrap( + protocolVersion: valid.protocolVersion, + instanceId: valid.instanceId, + endpoint: try XCTUnwrap(URL(string: "https://aiden.example.test\(encodedPath)")), + serverSpkiSha256: valid.serverSpkiSha256, + secret: valid.secret, + expiresAt: valid.expiresAt + ) + XCTAssertThrowsError(try encodedEndpoint.validated(at: now)) + } + for invalidEndpoint in [ + "https://:443/api/aiden/v1", + "https://:65536/api/aiden/v1", + "https://aiden.example.test:0/api/aiden/v1", + "https://aiden.example.test:65536/api/aiden/v1", + "https://aiden.example.test/api/aiden/v1?", + "https://aiden.example.test/api/aiden/v1#", + ] { + let endpoint = AidenRemoteContractFixture.PairingBootstrap( + protocolVersion: valid.protocolVersion, + instanceId: valid.instanceId, + endpoint: try XCTUnwrap(URL(string: invalidEndpoint)), + serverSpkiSha256: valid.serverSpkiSha256, + secret: valid.secret, + expiresAt: valid.expiresAt + ) + XCTAssertThrowsError(try endpoint.validated(at: now), invalidEndpoint) + } + XCTAssertThrowsError(try AidenRemoteContractFixture.PairingBootstrap( + protocolVersion: valid.protocolVersion, + instanceId: String(repeating: "i", count: 129), + endpoint: valid.endpoint, + serverSpkiSha256: valid.serverSpkiSha256, + secret: valid.secret, + expiresAt: valid.expiresAt + ).validated(at: now)) + XCTAssertThrowsError(try AidenRemoteContractFixture.PairingBootstrap( + protocolVersion: 1, + instanceId: valid.instanceId, + endpoint: try XCTUnwrap(URL(string: "http://aiden.example.test/api/aiden/v1")), + serverSpkiSha256: valid.serverSpkiSha256, + secret: valid.secret, + expiresAt: valid.expiresAt + ).validated(at: now)) + XCTAssertThrowsError(try AidenRemoteContractFixture.PairingBootstrap( + protocolVersion: 1, + instanceId: valid.instanceId, + endpoint: valid.endpoint, + serverSpkiSha256: "sha256/not-a-digest", + secret: valid.secret, + expiresAt: valid.expiresAt + ).validated(at: now)) + XCTAssertThrowsError(try AidenRemoteContractFixture.PairingBootstrap( + protocolVersion: 1, + instanceId: valid.instanceId, + endpoint: valid.endpoint, + serverSpkiSha256: valid.serverSpkiSha256, + secret: "too-short", + expiresAt: valid.expiresAt + ).validated(at: now)) + XCTAssertThrowsError(try AidenRemoteContractFixture.PairingBootstrap( + protocolVersion: 1, + instanceId: valid.instanceId, + endpoint: valid.endpoint, + serverSpkiSha256: valid.serverSpkiSha256, + secret: valid.secret, + expiresAt: now + ).validated(at: now)) + XCTAssertThrowsError(try AidenRemoteContractFixture.PairingBootstrap( + protocolVersion: 1, + instanceId: valid.instanceId, + endpoint: valid.endpoint, + serverSpkiSha256: valid.serverSpkiSha256, + secret: valid.secret, + expiresAt: now.addingTimeInterval(301) + ).validated(at: now)) + } + + func testEndpointAuthorityGrammarMatchesDesktopVectors() throws { + // Keep this authority vector byte-for-byte aligned with + // main/services/aiden-remote-protocol.test.ts. + let endpointAuthorityVectors: [(String, Bool)] = [ + ("aiden.example.test", true), + ("localhost", true), + ("aiden-lan.local", true), + ("192.168.1.42", true), + ("192.0.2.1:443", true), + ("aiden.0", false), + ("aiden.123", false), + ("aiden.example.test:1", true), + ("aiden.example.test:65535", true), + ("[::]", true), + ("[::1]", true), + ("[2001:db8::1]:443", true), + ("[::ffff:192.0.2.1]", true), + ("aiden.example.test:0443", false), + ("aiden.example.test:00001", false), + ("aiden.example.test:0", false), + ("aiden.example.test:65536", false), + ("aiden.example.test:abc", false), + ("aiden.example.test:", false), + (":443", false), + ("aiden.example.test:1:2", false), + ("aiden.example.test%2eexample.test", false), + ("aiden.example.test%25", false), + ("aiden.example.test", false), + ("aiden\u{0301}.example.test", false), + ("aiden.example.test\u{0009}", false), + ("aiden.example.test\u{001f}", false), + ("aiden.example.test\u{007f}", false), + ("aiden..example.test", false), + ("-aiden.example.test", false), + ("aiden-.example.test", false), + ("aiden_example.test", false), + ("123", false), + ("192.168.001.1", false), + ("256.1.1.1", false), + ("[fe80::1%25en0]", false), + ("[v1.fe]", false), + ("[::1", false), + ("[::1]x", false), + ("::1", false), + ("[::1]:00001", false), + ("[::1]:65536", false), + ("[2001:db8::1::2]", false), + ("[192.0.2.1::]", false), + ("[::ffff:192.000.2.1]", false), + ("[2001:db8:0:0:0:0:0]", false), + ] + let now = Date(timeIntervalSince1970: 2_000_000_000) + let formatter = ISO8601DateFormatter() + formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] + let expiry = formatter.string(from: now.addingTimeInterval(60)) + + for (authority, valid) in endpointAuthorityVectors { + let fields: [String: Any] = [ + "protocolVersion": 1, + "instanceId": "instance_fixture", + "endpoint": "https://\(authority)/api/aiden/v1", + "serverSpkiSha256": "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "secret": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "expiresAt": expiry, + ] + let data = try JSONSerialization.data(withJSONObject: fields) + if valid { + let bootstrap = try AidenRemoteJSONDecoder.decodePairingBootstrap(from: data) + XCTAssertNoThrow(try bootstrap.validated(at: now), authority) + } else { + XCTAssertThrowsError( + try AidenRemoteJSONDecoder.decodePairingBootstrap(from: data), + authority + ) + } + } + } + + func testStrictRFC3339DatesRejectPermissivePairingAndEventForms() throws { + let pairingFields: [String: Any] = [ + "protocolVersion": 1, + "instanceId": "instance_fixture", + "endpoint": "https://aiden.example.test/api/aiden/v1", + "serverSpkiSha256": "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "secret": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "expiresAt": "2026-08-18T19:05:00.000Z", + ] + let eventFields: [String: Any] = [ + "protocolVersion": 1, + "streamId": "stream-1", + "sequence": 1, + "timestamp": "2026-08-18T19:01:01.000Z", + "type": "heartbeat", + "terminal": false, + "payload": [:], + ] + let malformedDates = [ + "2026-08-18", + "2026-08-18T19:05:00.000+0000", + "2026-02-30T19:05:00.000Z", + ] + + for date in malformedDates { + var pairing = pairingFields + pairing["expiresAt"] = date + XCTAssertThrowsError( + try AidenRemoteJSONDecoder.decodePairingBootstrap( + from: JSONSerialization.data(withJSONObject: pairing) + ) + ) + + var event = eventFields + event["timestamp"] = date + XCTAssertThrowsError( + try AidenRemoteJSONDecoder.decodeSSEEvent( + from: JSONSerialization.data(withJSONObject: event) + ) + ) + } + } + + func testCanonicalPairingPayloadRequiresExactKindAndTrustShape() throws { + let now = Date(timeIntervalSince1970: 2_000_000_000) + let formatter = ISO8601DateFormatter() + formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] + let bootstrap: [String: Any] = [ + "protocolVersion": 1, + "instanceId": "instance_fixture", + "endpoint": "https://aiden.example.test/api/aiden/v1", + "serverSpkiSha256": "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "secret": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "expiresAt": formatter.string(from: now.addingTimeInterval(60)), + ] + let valid: [String: Any] = [ + "kind": "aiden-pairing-v1", + "bootstrap": bootstrap, + "trust": [ + "mode": "private-ca", + "caCertificateDerBase64": caCertificateDER, + ], + ] + let payload = try AidenRemoteJSONDecoder.decodePairingPayload( + from: JSONSerialization.data(withJSONObject: valid) + ) + XCTAssertNoThrow(try payload.validated(at: now)) + XCTAssertEqual(payload.trust.caCertificateDER?.base64EncodedString(), caCertificateDER) + + var wrongKind = valid + wrongKind["kind"] = "future-pairing" + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodePairingPayload( + from: JSONSerialization.data(withJSONObject: wrongKind) + )) + + var unknownOuterKey = valid + unknownOuterKey["endpoint"] = "https://attacker.invalid" + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodePairingPayload( + from: JSONSerialization.data(withJSONObject: unknownOuterKey) + )) + + for invalidTrust in [ + ["mode": "system", "caCertificateDerBase64": caCertificateDER], + ["mode": "private-ca"], + ["mode": "private-ca", "caCertificateDerBase64": "not-base64"], + ["mode": "system", "future": "field"], + ] { + var invalid = valid + invalid["trust"] = invalidTrust + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodePairingPayload( + from: JSONSerialization.data(withJSONObject: invalid) + )) + } + + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodePairingPayload( + from: Data(repeating: 0x20, count: AidenRemoteProtocol.maxPairingPayloadBytes + 1) + )) { error in + XCTAssertEqual(error as? AidenRemoteContractError, .payloadTooLarge) + } + } + + func testStrictRFC3339DatePreservesFractionAndUTCOffsetInstant() throws { + let utcEvent = try AidenRemoteJSONDecoder.decodeSSEEvent( + from: JSONSerialization.data(withJSONObject: [ + "protocolVersion": 1, + "streamId": "stream-utc", + "sequence": 1, + "timestamp": "2026-08-18T13:31:01.250Z", + "type": "heartbeat", + "terminal": false, + "payload": [:], + ]) + ) + let offsetEvent = try AidenRemoteJSONDecoder.decodeSSEEvent( + from: JSONSerialization.data(withJSONObject: [ + "protocolVersion": 1, + "streamId": "stream-offset", + "sequence": 1, + "timestamp": "2026-08-18T19:01:01.25+05:30", + "type": "heartbeat", + "terminal": false, + "payload": [:], + ]) + ) + XCTAssertEqual(offsetEvent.timestamp, utcEvent.timestamp) + } + + func testErrorEnvelopeRejectsUnknownFieldsCodesAndBounds() throws { + let unknownField = Data(#"{"error":{"code":"internal_error","message":"safe","requestId":"request-1","retryable":false,"unexpected":true}}"#.utf8) + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decode(AidenRemoteErrorEnvelope.self, from: unknownField)) + + let unknownCode = Data(#"{"error":{"code":"future_error","message":"safe","requestId":"request-1","retryable":false}}"#.utf8) + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decode(AidenRemoteErrorEnvelope.self, from: unknownCode)) + + let oversizedRequestID = try JSONSerialization.data(withJSONObject: [ + "error": [ + "code": "internal_error", + "message": "safe", + "requestId": String(repeating: "r", count: 129), + "retryable": false, + ], + ]) + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decode(AidenRemoteErrorEnvelope.self, from: oversizedRequestID)) + + let invalidDetails = try JSONSerialization.data(withJSONObject: [ + "error": [ + "code": "internal_error", + "message": "safe", + "requestId": "request-1", + "retryable": false, + "details": ["retryAfterSeconds": 86_401], + ], + ]) + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decode(AidenRemoteErrorEnvelope.self, from: invalidDetails)) + } + + func testNullMembersAreNotTreatedAsAbsent() throws { + let nullEventPayloads = [ + Data(#"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"heartbeat","terminal":false,"payload":{"text":null}}"#.utf8), + Data(#"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"status","terminal":false,"payload":{"state":null}}"#.utf8), + Data(#"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"snapshot","terminal":false,"payload":{"chatId":"chat-1","turnId":"turn-1","nextSequence":null}}"#.utf8), + Data(#"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"heartbeat","terminal":false,"payload":{"future":null}}"#.utf8), + ] + for event in nullEventPayloads { + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodeSSEEvent(from: event)) + } + + let nullErrorMembers = [ + Data(#"{"error":{"code":"internal_error","message":"safe","requestId":"request-1","retryable":false,"details":null}}"#.utf8), + Data(#"{"error":{"code":"internal_error","message":"safe","requestId":"request-1","retryable":false,"details":{"retryAfterSeconds":null}}}"#.utf8), + Data(#"{"error":{"code":"internal_error","message":"safe","requestId":"request-1","retryable":false,"details":{"currentRevision":null}}}"#.utf8), + Data(#"{"error":{"code":"internal_error","message":null,"requestId":"request-1","retryable":false}}"#.utf8), + Data(#"{"error":{"code":"internal_error","message":"safe","requestId":null,"retryable":false}}"#.utf8), + Data(#"{"error":{"code":"internal_error","message":"safe","requestId":"request-1","retryable":null}}"#.utf8), + ] + for envelope in nullErrorMembers { + XCTAssertThrowsError( + try AidenRemoteJSONDecoder.decode(AidenRemoteErrorEnvelope.self, from: envelope) + ) + } + } + + func testUnknownCapabilitiesAndEventsRemainForwardCompatibleButErrorsAreStrict() throws { + let capability = try AidenRemoteJSONDecoder.decode( + AidenRemoteCapability.self, + from: Data("\"future:read\"".utf8) + ) + let event = try AidenRemoteJSONDecoder.decode( + AidenRemoteEventType.self, + from: Data("\"future_event\"".utf8) + ) + XCTAssertEqual(capability.rawValue, "future:read") + XCTAssertEqual(event.rawValue, "future_event") + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decode( + AidenRemoteErrorCode.self, + from: Data("\"future_error\"".utf8) + )) + + let futureNonterminal = Data(#"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"future_progress","terminal":false,"payload":{"future":"ignored"}}"#.utf8) + let ignored = try AidenRemoteJSONDecoder.decodeSSEEvent(from: futureNonterminal) + XCTAssertFalse(ignored.shouldApply) + XCTAssertNil(ignored.payload) + + let futureNonterminalWithoutPayload = Data(#"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"future_progress","terminal":false}"#.utf8) + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodeSSEEvent(from: futureNonterminalWithoutPayload)) + + let unsafeUnknown = Data(#"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"future_progress","terminal":false,"payload":{"absolutePath":"/private/secret"}}"#.utf8) + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodeSSEEvent(from: unsafeUnknown)) + + var oversizedUnknownPayload: [String: Any] = [:] + for index in 0...32 { + oversizedUnknownPayload["future_\(index)"] = true + } + let oversizedUnknown = try JSONSerialization.data(withJSONObject: [ + "protocolVersion": 1, + "streamId": "stream-1", + "sequence": 1, + "timestamp": "2026-08-18T19:00:00Z", + "type": "future_progress", + "terminal": false, + "payload": oversizedUnknownPayload, + ]) + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodeSSEEvent(from: oversizedUnknown)) + + let futureTerminal = Data(#"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"future_terminal","terminal":true,"payload":{}}"#.utf8) + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodeSSEEvent(from: futureTerminal)) + + let unknownErrorCode = Data(#"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"error","terminal":true,"payload":{"code":"future_error","message":"safe"}}"#.utf8) + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodeSSEEvent(from: unknownErrorCode)) + + let oversizedErrorMessage = try JSONSerialization.data(withJSONObject: [ + "protocolVersion": 1, + "streamId": "stream-1", + "sequence": 1, + "timestamp": "2026-08-18T19:00:00Z", + "type": "error", + "terminal": true, + "payload": [ + "code": "internal_error", + "message": String(repeating: "x", count: 2_001), + ], + ]) + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodeSSEEvent(from: oversizedErrorMessage)) + + let unsafeKnown = Data(#"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"heartbeat","terminal":false,"payload":{"absolutePath":"/private/secret"}}"#.utf8) + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodeSSEEvent(from: unsafeKnown)) + } + + func testPinnedSessionRedirectPolicyRejectsDowngradeAndAuthorityChanges() throws { + let delegate = AidenPinnedServerSessionDelegate( + expectedHost: "aiden.example.test", + expectedPort: 7443, + expectedFingerprint: "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + trustPolicy: .system + ) + XCTAssertTrue(delegate.allowsRedirect(to: try XCTUnwrap(URL(string: "https://aiden.example.test:7443/api/aiden/v1/health")))) + XCTAssertFalse(delegate.allowsRedirect(to: try XCTUnwrap(URL(string: "http://aiden.example.test:7443/api/aiden/v1/health")))) + XCTAssertFalse(delegate.allowsRedirect(to: try XCTUnwrap(URL(string: "https://other.example.test:7443/api/aiden/v1/health")))) + XCTAssertFalse(delegate.allowsRedirect(to: try XCTUnwrap(URL(string: "https://aiden.example.test:8443/api/aiden/v1/health")))) + + let defaultPortDelegate = AidenPinnedServerSessionDelegate( + expectedHost: "aiden.example.test", + expectedFingerprint: "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + trustPolicy: .system + ) + XCTAssertTrue(defaultPortDelegate.allowsRedirect(to: try XCTUnwrap(URL(string: "https://aiden.example.test/api/aiden/v1/health")))) + XCTAssertTrue(defaultPortDelegate.allowsRedirect(to: try XCTUnwrap(URL(string: "https://aiden.example.test:443/api/aiden/v1/health")))) + XCTAssertFalse(defaultPortDelegate.allowsRedirect(to: try XCTUnwrap(URL(string: "https://aiden.example.test:7443/api/aiden/v1/health")))) + XCTAssertTrue(delegate.responds(to: #selector(URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)))) + } + + func testStreamEnvelopeRejectsInvalidIdentityAndSequence() throws { + let invalidEvents = [ + #"{"protocolVersion":2,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"heartbeat","terminal":false,"payload":{}}"#, + #"{"protocolVersion":1,"streamId":"","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"heartbeat","terminal":false,"payload":{}}"#, + #"{"protocolVersion":1,"streamId":"stream-1","sequence":0,"timestamp":"2026-08-18T19:00:00Z","type":"heartbeat","terminal":false,"payload":{}}"#, + #"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"status","terminal":false,"payload":{"state":"done"}}"#, + ] + for event in invalidEvents { + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decodeSSEEvent(from: Data(event.utf8))) + } + + let reconciling = #"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"status","terminal":false,"payload":{"state":"reconciling"}}"# + XCTAssertNoThrow(try AidenRemoteJSONDecoder.decodeSSEEvent(from: Data(reconciling.utf8))) + + let additiveEnvelope = #"{"protocolVersion":1,"streamId":"stream-1","sequence":1,"timestamp":"2026-08-18T19:00:00Z","type":"heartbeat","terminal":false,"payload":{},"futureEnvelopeMetadata":{"ignored":true}}"# + XCTAssertNoThrow(try AidenRemoteJSONDecoder.decodeSSEEvent(from: Data(additiveEnvelope.utf8))) + + var additiveEnvelopeFields: [String: Any] = [ + "protocolVersion": 1, + "streamId": "stream-additive", + "sequence": 1, + "timestamp": "2026-08-18T19:00:00Z", + "type": "heartbeat", + "terminal": false, + "payload": [:], + ] + for index in 0..<33 { + additiveEnvelopeFields["futureEnvelope_\(index)"] = true + } + XCTAssertNoThrow( + try AidenRemoteJSONDecoder.decodeSSEEvent( + from: JSONSerialization.data(withJSONObject: additiveEnvelopeFields) + ) + ) + + var wideNestedMetadata: [String: Any] = [:] + for index in 0..<33 { + wideNestedMetadata["futureNested_\(index)"] = true + } + var nestedMetadataEvent = additiveEnvelopeFields + nestedMetadataEvent["futureMetadata"] = wideNestedMetadata + XCTAssertNoThrow( + try AidenRemoteJSONDecoder.decodeSSEEvent( + from: JSONSerialization.data(withJSONObject: nestedMetadataEvent) + ) + ) + + func nestedMetadata(depth: Int) -> [String: Any] { + var value: [String: Any] = ["leaf": true] + for index in 0.. (HTTPURLResponse, Data) { + (HTTPURLResponse(url: request.url!, statusCode: status, httpVersion: nil, headerFields: nil)!, Data(json.utf8)) + } + + private static func body(_ request: URLRequest) throws -> [String: Any] { + let data: Data + if let body = request.httpBody { + data = body + } else { + let stream = try XCTUnwrap(request.httpBodyStream) + stream.open() + defer { stream.close() } + var received = Data() + var buffer = [UInt8](repeating: 0, count: 4_096) + while stream.hasBytesAvailable { + let count = stream.read(&buffer, maxLength: buffer.count) + guard count >= 0 else { throw stream.streamError ?? URLError(.cannotDecodeRawData) } + if count == 0 { break } + received.append(buffer, count: count) + } + data = received + } + return try XCTUnwrap(JSONSerialization.jsonObject(with: data) as? [String: Any]) + } +} + +private final class ScheduledRequestRecorder: @unchecked Sendable { + private let lock = NSLock() + private var stored: [URLRequest] = [] + func record(_ request: URLRequest) { lock.lock(); stored.append(request); lock.unlock() } + var requests: [URLRequest] { lock.lock(); defer { lock.unlock() }; return stored } +} + +private final class ScheduledMockURLProtocol: URLProtocol, @unchecked Sendable { + nonisolated(unsafe) static var handler: ((URLRequest) throws -> (HTTPURLResponse, Data))? + override class func canInit(with request: URLRequest) -> Bool { true } + override class func canonicalRequest(for request: URLRequest) -> URLRequest { request } + override func startLoading() { + do { + let (response, data) = try Self.handler?(request) ?? { throw URLError(.badServerResponse) }() + client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) + client?.urlProtocol(self, didLoad: data) + client?.urlProtocolDidFinishLoading(self) + } catch { client?.urlProtocol(self, didFailWithError: error) } + } + override func stopLoading() {} +} diff --git a/ios/AidenOnTheGoTests/AidenWorkspaceEnvironmentTests.swift b/ios/AidenOnTheGoTests/AidenWorkspaceEnvironmentTests.swift new file mode 100644 index 00000000..f545f3f5 --- /dev/null +++ b/ios/AidenOnTheGoTests/AidenWorkspaceEnvironmentTests.swift @@ -0,0 +1,268 @@ +import Foundation +import XCTest +@testable import AidenOnTheGo + +final class AidenWorkspaceEnvironmentTests: XCTestCase { + override func tearDown() { + EnvironmentMockURLProtocol.handler = nil + super.tearDown() + } + + func testFileAndGitDTOsRejectUnsafeOrUnboundServerData() throws { + let fileID = "file_\(String(repeating: "f", count: 43))" + let index = try AidenRemoteJSONDecoder.decode( + AidenWorkspaceFileIndex.self, + from: Data(""" + {"snapshotId":"files-1","entries":[{"id":"\(fileID)","displayPath":"Sources/App.swift","name":"App.swift","kind":"file","size":12,"language":"Swift"}],"truncated":false,"maxEntries":4000,"maxDepth":20} + """.utf8) + ) + XCTAssertNoThrow(try AidenWorkspaceEnvironmentValidation.validated(index)) + + let escaped = AidenWorkspaceFileIndex( + snapshotId: "files-2", + entries: [.init(id: fileID, displayPath: "../Secret", name: "Secret", kind: .file, size: nil, language: nil)], + truncated: false, + maxEntries: 4_000, + maxDepth: 20 + ) + XCTAssertThrowsError(try AidenWorkspaceEnvironmentValidation.validated(escaped)) + + XCTAssertThrowsError(try AidenRemoteJSONDecoder.decode( + AidenWorkspaceFileIndex.self, + from: Data(""" + {"snapshotId":"files-1","entries":[],"truncated":false,"maxEntries":4000,"maxDepth":20,"repositoryPath":"/private/project"} + """.utf8) + )) + } + + func testEnvironmentCacheIsInstallationAndWorkspaceScoped() async throws { + let directory = FileManager.default.temporaryDirectory + .appending(path: UUID().uuidString, directoryHint: .isDirectory) + defer { try? FileManager.default.removeItem(at: directory) } + let cache = AidenWorkspaceEnvironmentCache(directory: directory) + let fileID = "file_\(String(repeating: "f", count: 43))" + let index = AidenWorkspaceFileIndex( + snapshotId: "files-1", + entries: [.init(id: fileID, displayPath: "App.swift", name: "App.swift", kind: .file, size: 14, language: "Swift")], + truncated: false, + maxEntries: 4_000, + maxDepth: 20 + ) + let document = AidenWorkspaceFileDocument( + id: fileID, + displayPath: "App.swift", + content: "let value = 1\n", + version: "version-1", + truncated: false, + warning: nil + ) + try await cache.store(index: index, instanceId: "instance-1", workspaceId: "workspace-1") + try await cache.store(document: document, instanceId: "instance-1", workspaceId: "workspace-1") + let loaded = await cache.load(instanceId: "instance-1", workspaceId: "workspace-1") + XCTAssertEqual(loaded?.index, index) + XCTAssertEqual(loaded?.documents[fileID], document) + let otherWorkspace = await cache.load(instanceId: "instance-1", workspaceId: "workspace-2") + let otherInstallation = await cache.load(instanceId: "instance-2", workspaceId: "workspace-1") + XCTAssertNil(otherWorkspace) + XCTAssertNil(otherInstallation) + } + + func testClientUsesOpaqueFileRoutesAndConfirmedGitMutationHeaders() async throws { + let fileID = "file_\(String(repeating: "f", count: 43))" + let snapshotID = "snap_\(String(repeating: "s", count: 43))" + let recorder = EnvironmentRequestRecorder() + EnvironmentMockURLProtocol.handler = { request in + recorder.record(request) + let path = request.url?.path ?? "" + switch (request.httpMethod, path) { + case ("GET", "/api/aiden/v1/workspaces/workspace-1/files"): + return Self.response(request, 200, """ + {"snapshotId":"files-1","entries":[{"id":"\(fileID)","displayPath":"App.swift","name":"App.swift","kind":"file","size":14}],"truncated":false,"maxEntries":4000,"maxDepth":20} + """) + case ("GET", "/api/aiden/v1/workspaces/workspace-1/files/\(fileID)"): + return Self.response(request, 200, """ + {"id":"\(fileID)","displayPath":"App.swift","content":"let value = 1\\n","version":"version-1","truncated":false} + """) + case ("PUT", "/api/aiden/v1/workspaces/workspace-1/files/\(fileID)"): + return Self.response(request, 200, """ + {"id":"\(fileID)","displayPath":"App.swift","content":"let value = 2\\n","version":"version-2","truncated":false} + """) + case ("GET", "/api/aiden/v1/workspaces/workspace-1/git/review"): + return Self.response(request, 200, """ + {"operationId":"op_review","status":"snapshot","snapshotId":"\(snapshotID)","result":{"kind":"review","branch":"main","uncommitted":1,"files":[{"id":"\(fileID)","displayPath":"App.swift","status":"modified","staged":false,"additions":1,"deletions":1}]}} + """) + case ("POST", "/api/aiden/v1/workspaces/workspace-1/git/worktrees"): + return Self.response(request, 202, """ + {"operationId":"op_create","status":"succeeded","result":{"kind":"mutation","message":"Created managed worktree.","branch":"feature/mobile","workspaceId":"workspace-2"}} + """) + case ("DELETE", "/api/aiden/v1/workspaces/workspace-2/git/managed-worktree"): + return Self.response(request, 202, """ + {"operationId":"op_delete","status":"succeeded","result":{"kind":"mutation","message":"Removed managed worktree.","workspaceId":"workspace-2"}} + """) + default: + throw URLError(.unsupportedURL) + } + } + let configuration = URLSessionConfiguration.ephemeral + configuration.protocolClasses = [EnvironmentMockURLProtocol.self] + let client = AidenRemoteClient( + endpoint: URL(string: "https://aiden.test/api/aiden/v1")!, + credential: "credential", + session: URLSession(configuration: configuration) + ) + + _ = try await client.workspaceFiles(workspaceId: "workspace-1") + let document = try await client.workspaceFile(workspaceId: "workspace-1", fileId: fileID) + _ = try await client.writeWorkspaceFile( + workspaceId: "workspace-1", + fileId: fileID, + content: "let value = 2\n", + expectedVersion: document.version + ) + _ = try await client.gitReview(workspaceId: "workspace-1") + _ = try await client.createGitWorktree( + workspaceId: "workspace-1", + branch: "feature/mobile", + name: "Mobile" + ) + _ = try await client.deleteManagedGitWorktree(workspaceId: "workspace-2", revision: "revision-2") + + let requests = recorder.requests + let write = try XCTUnwrap(requests.first { $0.httpMethod == "PUT" }) + XCTAssertEqual(try Self.jsonBody(write)["expectedVersion"] as? String, "version-1") + let create = try XCTUnwrap(requests.first { $0.httpMethod == "POST" && $0.url?.path.hasSuffix("/git/worktrees") == true }) + XCTAssertNotNil(create.value(forHTTPHeaderField: "Idempotency-Key")) + XCTAssertEqual(try Self.jsonBody(create)["confirmedForeground"] as? Bool, true) + let remove = try XCTUnwrap(requests.first { $0.httpMethod == "DELETE" }) + XCTAssertEqual(remove.value(forHTTPHeaderField: "If-Match"), "revision-2") + XCTAssertNotNil(remove.value(forHTTPHeaderField: "Idempotency-Key")) + XCTAssertEqual(try Self.jsonBody(remove)["confirmedForeground"] as? Bool, true) + XCTAssertFalse(requests.contains { ($0.url?.absoluteString ?? "").contains("private") }) + } + + @MainActor + func testGitMutationReconnectReusesOriginalIdempotencyKey() async throws { + let snapshotID = "snap_\(String(repeating: "s", count: 43))" + let recorder = EnvironmentRequestRecorder() + let attempts = EnvironmentAttemptCounter() + EnvironmentMockURLProtocol.handler = { request in + recorder.record(request) + let path = request.url?.path ?? "" + if request.httpMethod == "POST", path.hasSuffix("/git/commit") { + if attempts.increment() == 1 { throw URLError(.networkConnectionLost) } + return Self.response(request, 202, """ + {"operationId":"op_commit","status":"succeeded","result":{"kind":"mutation","message":"Committed reviewed changes.","branch":"main","commitId":"abc"}} + """) + } + if request.httpMethod == "GET", path.hasSuffix("/git/review") { + return Self.response(request, 200, """ + {"operationId":"op_review","status":"snapshot","snapshotId":"\(snapshotID)","result":{"kind":"review","branch":"main","uncommitted":0,"files":[]}} + """) + } + if request.httpMethod == "GET", path.hasSuffix("/git/branches") { + return Self.response(request, 200, """ + {"operationId":"op_branches","status":"snapshot","snapshotId":"\(snapshotID)","result":{"kind":"branches","current":"main","branches":["main"]}} + """) + } + if request.httpMethod == "GET", path.hasSuffix("/git/worktrees") { + return Self.response(request, 200, """ + {"operationId":"op_worktrees","status":"snapshot","result":{"kind":"worktrees","worktrees":[]}} + """) + } + throw URLError(.unsupportedURL) + } + let configuration = URLSessionConfiguration.ephemeral + configuration.protocolClasses = [EnvironmentMockURLProtocol.self] + let client = AidenRemoteClient( + endpoint: URL(string: "https://aiden.test/api/aiden/v1")!, + credential: "credential", + session: URLSession(configuration: configuration) + ) + let model = AidenWorkspaceGitModel() + model.reviewSnapshotId = snapshotID + await model.commit(client: client, workspaceId: "workspace-1", message: "Update", stagedOnly: false) + XCTAssertTrue(model.canRetryPendingMutation) + await model.retryPendingMutation(client: client, workspaceId: "workspace-1") + XCTAssertFalse(model.canRetryPendingMutation) + XCTAssertEqual(model.lastMessage, "Committed reviewed changes.") + + let keys = recorder.requests + .filter { $0.httpMethod == "POST" && $0.url?.path.hasSuffix("/git/commit") == true } + .compactMap { $0.value(forHTTPHeaderField: "Idempotency-Key") } + XCTAssertEqual(keys.count, 2) + XCTAssertEqual(Set(keys).count, 1) + } + + private static func response(_ request: URLRequest, _ status: Int, _ json: String) -> (HTTPURLResponse, Data) { + (HTTPURLResponse(url: request.url!, statusCode: status, httpVersion: nil, headerFields: nil)!, Data(json.utf8)) + } + + private static func jsonBody(_ request: URLRequest) throws -> [String: Any] { + let data: Data + if let body = request.httpBody { + data = body + } else { + let stream = try XCTUnwrap(request.httpBodyStream) + stream.open() + defer { stream.close() } + var value = Data() + var buffer = [UInt8](repeating: 0, count: 4_096) + while stream.hasBytesAvailable { + let count = stream.read(&buffer, maxLength: buffer.count) + if count <= 0 { break } + value.append(buffer, count: count) + } + data = value + } + return try XCTUnwrap(JSONSerialization.jsonObject(with: data) as? [String: Any]) + } +} + +private final class EnvironmentRequestRecorder: @unchecked Sendable { + private let lock = NSLock() + private var stored: [URLRequest] = [] + + func record(_ request: URLRequest) { + lock.lock() + stored.append(request) + lock.unlock() + } + + var requests: [URLRequest] { + lock.lock() + defer { lock.unlock() } + return stored + } +} + +private final class EnvironmentAttemptCounter: @unchecked Sendable { + private let lock = NSLock() + private var value = 0 + + func increment() -> Int { + lock.lock() + defer { lock.unlock() } + value += 1 + return value + } +} + +private final class EnvironmentMockURLProtocol: URLProtocol, @unchecked Sendable { + nonisolated(unsafe) static var handler: ((URLRequest) throws -> (HTTPURLResponse, Data))? + + override class func canInit(with request: URLRequest) -> Bool { true } + override class func canonicalRequest(for request: URLRequest) -> URLRequest { request } + + override func startLoading() { + do { + let (response, data) = try Self.handler?(request) ?? { throw URLError(.badServerResponse) }() + client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) + client?.urlProtocol(self, didLoad: data) + client?.urlProtocolDidFinishLoading(self) + } catch { + client?.urlProtocol(self, didFailWithError: error) + } + } + + override func stopLoading() {} +} diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md new file mode 100644 index 00000000..d40c8022 --- /dev/null +++ b/ios/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +## Unreleased + +- Converted the imported SwiftUI foundation into the Aiden On The Go product identity and signed application shell. +- Added canonical Aiden Remote pairing, pinned transport, multiple installations, Keychain credential isolation, workspace CRUD, and approved-root browsing foundations. +- Removed the imported Hermes WebUI product surface and compatibility configuration from the build. +- Completed the Hermex-informed Aiden shell with managed-scratch New Agent creation, reliable connected-state navigation handoff, explicit handoff-chat dismissal, and Aiden-logo starting/thinking states across chat and Live Activity UI. +- Rendered completed and streaming assistant replies with bounded block Markdown, full transcript width, and content-sized literal user prompts. diff --git a/ios/CODE_OF_CONDUCT.md b/ios/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..1ca7786b --- /dev/null +++ b/ios/CODE_OF_CONDUCT.md @@ -0,0 +1,131 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances + of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported privately to the owner of the Aiden Agent repository. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/ios/CONTEXT.md b/ios/CONTEXT.md new file mode 100644 index 00000000..1fd0bf78 --- /dev/null +++ b/ios/CONTEXT.md @@ -0,0 +1,9 @@ +# Aiden On The Go terms + +- **Aiden installation:** one paired Aiden Agent Mac, identified by its server-issued instance ID. +- **Device credential:** the per-phone or per-iPad bearer secret stored only in Keychain and revocable on the Mac. +- **Workspace:** an Aiden registry entry with `full`, `ask`, or `none` permission. +- **Approved root:** a Mac folder explicitly exposed for remote browsing by a local desktop action. +- **Location handle:** a short-lived opaque browser capability; never a filesystem path. +- **Selection:** a short-lived, single-use capability consumed atomically when registering a selected folder. +- **Remote turn:** one idempotently admitted user message whose generation remains owned by Aiden Agent across network loss. diff --git a/ios/CONTRACT_TESTS.md b/ios/CONTRACT_TESTS.md new file mode 100644 index 00000000..d1128d45 --- /dev/null +++ b/ios/CONTRACT_TESTS.md @@ -0,0 +1,5 @@ +# Aiden Remote contract tests + +The normative API is [`../protocol/aiden-remote/v1/openapi.json`](../protocol/aiden-remote/v1/openapi.json). Cross-platform fixtures live under `../protocol/aiden-remote/v1/fixtures/`. + +Desktop and Swift tests must agree on protocol versioning, pairing, error envelopes, bounds, opaque handles, revision/idempotency preconditions, SSE sequence and terminal behavior, and forbidden private fields. A passing decoder test is not permission to add an undocumented endpoint or payload. diff --git a/ios/CONTRIBUTING.md b/ios/CONTRIBUTING.md new file mode 100644 index 00000000..b20e90cb --- /dev/null +++ b/ios/CONTRIBUTING.md @@ -0,0 +1,7 @@ +# Contributing to Aiden On The Go + +Follow [`AGENTS.md`](AGENTS.md), the phase plan, the normative remote API, and the repository-wide test requirements. Preserve the imported MIT license and bundled third-party notices. + +Do not commit personal signing overrides. Put local identity overrides in `Config/Local.xcconfig`, which is ignored. Do not add a third-party dependency or a new remote endpoint without explicit approval and corresponding contract coverage. + +Every behavior change needs focused XCTest coverage and physical-device verification proportional to risk. Never use a simulator as a substitute for signed Keychain, Local Network, Bonjour, or production trust evidence. diff --git a/ios/Config/Shared.xcconfig b/ios/Config/Shared.xcconfig new file mode 100644 index 00000000..fe352b8a --- /dev/null +++ b/ios/Config/Shared.xcconfig @@ -0,0 +1,17 @@ +// Shared app-identity and signing defaults, attached to the project as the +// base configuration for both build configurations (Debug and Release). +// +// Contributors: never edit this file or project.pbxproj to sign with your own +// Apple team. Create Config/Local.xcconfig (gitignored) instead — see +// CONTRIBUTING.md ("Code signing for contributors"). + +DEVELOPMENT_TEAM = 5WP229CBB8 +APP_IDENTIFIER_SUFFIX = +APP_BUNDLE_IDENTIFIER = sbtbiswas.AidenOnTheGo$(APP_IDENTIFIER_SUFFIX) +APP_GROUP_IDENTIFIER = group.sbtbiswas.AidenOnTheGo$(APP_IDENTIFIER_SUFFIX) +KEYCHAIN_SERVICE = sbtbiswas.AidenOnTheGo.pairing$(APP_IDENTIFIER_SUFFIX) +APP_URL_SCHEME = aiden-otg$(APP_URL_SCHEME_SUFFIX) + +// Optional per-machine overrides. This include must stay last: later +// assignments win, so Local.xcconfig values override the defaults above. +#include? "Local.xcconfig" diff --git a/ios/DEVELOPMENT.md b/ios/DEVELOPMENT.md new file mode 100644 index 00000000..c9a6c7c1 --- /dev/null +++ b/ios/DEVELOPMENT.md @@ -0,0 +1,9 @@ +# iOS development + +Requirements: current Xcode, iOS 18 or newer, and access to this repository's configured Apple development team for signed hardware runs. + +Build and test the `AidenOnTheGo` scheme from `AidenOnTheGo.xcodeproj`. Use an explicit physical-device destination for acceptance evidence. Do not place credentials, pairing payloads, LAN addresses, certificates, or generated test-run files in source control. + +The mobile client implements only `/api/aiden/v1`. When a wire shape changes, update the OpenAPI contract, shared fixture, desktop parser/tests, Swift parser/tests, and protocol documentation together. + +Per-phase evidence belongs under `../docs/testing/aiden-on-the-go/`. See [`AGENTS.md`](AGENTS.md) for the working agreement and safety rules. diff --git a/ios/LICENSE b/ios/LICENSE new file mode 100644 index 00000000..48b4aa2f --- /dev/null +++ b/ios/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Sambit Biswas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ios/PROJECT_INTENT.md b/ios/PROJECT_INTENT.md new file mode 100644 index 00000000..11a7f5f6 --- /dev/null +++ b/ios/PROJECT_INTENT.md @@ -0,0 +1,13 @@ +# Aiden On The Go — Project Intent + +Aiden On The Go lets a user securely control their own Aiden Agent Mac from iPhone or iPad. It is a client, not an agent runtime or hosted service. + +Core boundaries: + +- Pair explicitly with a Mac using a short-lived QR bootstrap and per-device revocable credential. +- Connect over pinned local HTTPS or the Mac's explicitly configured Tailscale route. +- Match Aiden's chat and workspace behavior without widening permissions or exposing private Mac paths. +- Keep credentials in Keychain and bounded offline presentation state on device. +- Use native SwiftUI controls and adaptive Apple navigation. + +The normative scope is in [`PROJECT_SPEC.md`](PROJECT_SPEC.md). diff --git a/ios/PROJECT_SPEC.md b/ios/PROJECT_SPEC.md new file mode 100644 index 00000000..baecbe23 --- /dev/null +++ b/ios/PROJECT_SPEC.md @@ -0,0 +1,122 @@ +# Aiden On The Go — iOS/iPadOS Project Specification + +Status: Approved implementation specification +Protocol: Aiden Remote API v1 (`/api/aiden/v1`) +Product: Aiden On The Go +Platforms: iPhone and iPad, iOS/iPadOS 18+ +Authority: `docs/plans/aiden-on-the-go-plan.md` and `docs/aiden-remote-api-v1.md` + +## 1. Product contract + +Aiden On The Go is the native mobile control plane for a user-owned Aiden Agent desktop installation. Aiden Agent remains the execution and persistence authority. The phone connects over an explicitly enabled local-network or Tailscale transport, pairs to one installation, and authenticates every command and event stream with a revocable per-device credential. + +The app is not a Hermes WebUI client, a hosted service, a web view, or an agent runtime. Imported Hermex code is an implementation foundation only. New code must use Aiden Remote API v1 DTOs and events; do not add or preserve Hermes endpoints as a compatibility layer. + +## 2. Confirmed scope + +The complete planned product includes: + +- Multiple paired Aiden installations with QR pairing, Keychain credentials, discovery, manual URL entry, switching, and revocation handling. +- Chat list/open/create/rename/delete, bounded attachments, provider/model/thinking selection, atomic turn start, resumable streaming, cancel, reasoning/tool/timeline status, and allow/deny approvals. +- Workspace registry list/create/update/unregister, including folderless, managed scratch, and folders selected through a server-approved Mac directory browser. +- Workspace Settings from the conversation toolbar ellipsis. Workspace permission is never a composer control. +- Device-local Aiden, Slate, Berry, and Moss appearance presets plus supported mobile appearance options. +- Aiden workspace file index/read/version-checked write and the existing Aiden Git review/diff/compare/branch/commit/push/managed-worktree operations. +- Aiden scheduled-task list/create/edit/remove/pause/resume/run-now/preview/history/settings. +- Cache-only App Intents, app-driven Live Activities, on-device dictation, and local read-aloud. +- Offline read-only display of previously fetched data. Mutations are disabled while disconnected. + +Remove Kanban, Hermes projects/profiles/personalities, Skills/Memory/Insights panels, Cloudflare-specific onboarding, server TTS/transcription, voice-note upload, terminal, Computer Use, and every control without an Aiden service contract. Share Extension and cloud push remain deferred. + +## 3. Security invariants + +- Remote Access is off by default and has no listener until enabled on the Mac. +- Tailscale supplies reachability, never app authorization. Aiden manages only the exact non-Funnel Serve route it owns and never invokes `tailscale serve reset`. +- Local-network production transport is HTTPS. QR pairing pins the Aiden installation's stable P-256 SPKI SHA-256 fingerprint. Plain HTTP is development-build-only. +- Pairing secrets are high entropy, short lived, single use, rate limited, and never logged. Human-sized codes require a reviewed PAKE/SAS or explicit fingerprint confirmation. +- Device credentials are random, stored as digests on Mac and in Keychain on iOS, capability scoped, revocable, and never placed in URLs, App Group data, App Intents, logs, or Live Activities. +- DTOs are allowlists. Absolute paths, provider/MCP credentials, raw diagnostics, Git admin paths/tokens, schedule runtime internals, and private agent history never cross the API. +- Directory and file handles are opaque server-side capabilities bound to instance, device, workspace/root identity, policy revision, expiry, and snapshot. The client never submits a free-form Mac path. +- Workspace selection consumption and workspace creation are atomic and idempotent. Filesystem identity and canonical root membership are revalidated immediately before mutation. +- Remote turns honor the workspace's saved `full`, `ask`, or `none` permission. The transport cannot mint Assistant/unattended modes or enable Computer Use. +- A dropped TCP/SSE connection does not resend a prompt or cancel server-owned work. The client reconciles by stable turn, stream, operation, and run IDs. + +## 4. Navigation and interaction + +Root destinations: + +- Chats +- Workspaces +- Scheduled Tasks +- Settings / paired installations + +Files and Git are workspace-scoped destinations, not global tabs. iPhone uses native navigation stacks. iPad uses `NavigationSplitView` and must remain useful in split view, rotation, and Stage Manager sizes. + +The conversation toolbar has a top-right ellipsis. For a workspace-backed chat it opens Workspace Settings and workspace-scoped destinations. Workspace Settings contains name, permission, folder/worktree display state, Files, Git, and management links. The composer contains only message/on-device voice input, attachments, model/thinking controls, send, and stop. + +Use retained Hermex native SwiftUI components where their behavior maps 1:1. Do not invent custom interaction systems for branding. Apply Aiden semantic tokens and native menus, lists, forms, sheets, alerts, toolbars, and split navigation. + +## 5. Protocol source of truth + +Normative contract documents and cross-platform fixtures live at repository root: + +- `docs/aiden-remote-api-v1.md` +- `protocol/aiden-remote/v1/openapi.json` +- `protocol/aiden-remote/v1/fixtures/contract.json` + +TypeScript and Swift tests must decode the same checked-in fixtures. Codable models are tolerant of unknown response fields but strict about required identity, ownership, sequence, and mutation-precondition fields. Unknown SSE event types are ignored and logged without including payload secrets; unknown terminal semantics fail closed and trigger snapshot reconciliation. + +## 6. Streaming rules + +- A turn starts through one atomic command and returns `turnId`, `streamId`, accepted state, and the canonical user message. +- Every SSE event has protocol version, stream ID, monotonically increasing sequence, timestamp, type, and typed payload. +- Initial events: `snapshot`, `status`, `text_delta`, `reasoning_delta`, `tool_started`, `tool_finished`, `timeline`, `approval_required`, `done`, `error`, `cancelled`, and `heartbeat`. +- Reconnect sends `Last-Event-ID` or `after`; duplicate sequences are ignored. +- The phone never retries turn creation solely because its stream disconnected. +- Approval IDs are device/stream bound and responses are idempotent. Only `allow` and `deny` are supported initially. +- Provider failure, cancellation, interruption, completion, and revocation are explicit terminal states. + +## 7. Apple identity + +Derived from the owner's Contact Sheet Generator project: + +- Signing style: Automatic +- Apple team: `5WP229CBB8` +- Main bundle: `sbtbiswas.AidenOnTheGo` +- Unit tests: `sbtbiswas.AidenOnTheGoTests` +- UI tests: `sbtbiswas.AidenOnTheGoUITests` +- Live Activity widget: `sbtbiswas.AidenOnTheGo.LiveActivityWidget` +- App Group: `group.sbtbiswas.AidenOnTheGo` +- Keychain service: `sbtbiswas.AidenOnTheGo.pairing` +- URL scheme: `aiden-otg` +- App Store SKU: `aiden-on-the-go-ios` +- Device families: iPhone and iPad + +The installed Apple Development identity currently belongs to team `7EK65FX44E`. Do not claim release/signing readiness until a physical-device build provisions the identifiers above under team `5WP229CBB8`. + +## 8. App Intents, Live Activities, and voice + +- App Intents use current open/navigation protocols and App Group-cached installation/workspace IDs only. The intent process performs no network or Keychain access and never sends a prompt or mutation. +- Live Activity state is under 4 KB and excludes response text by default, paths, tool arguments, approval details, provider errors, and credentials. With no push relay, it shows honest last-known/stale state while the app is terminated and reconciles only when the authenticated app next runs. +- Voice is on-device dictation into an editable draft plus optional on-device read-aloud. Remove server STT, audio upload, voice-note attachments, and hold-to-record. If on-device recognition is unavailable, fall back to text rather than uploading audio. + +## 9. Testing gates + +Every phase adds or updates tests before review. Before a phase can advance: + +1. Its focused TypeScript/XCTest suites pass. +2. Desktop type-check and applicable full tests pass. +3. Signed physical-device build/launch passes for iOS behavior changes; simulator use remains excluded by owner direction. +4. Physical-device verification runs when the phase acceptance explicitly requires hardware, LAN, microphone, signing, or ActivityKit behavior. +5. A direct source-and-test audit is completed without subagent/reviewer delegation while the owner's no-subagents direction remains active. +6. Every P0 and P1 finding is fixed and the affected tests are rerun. + +Phase acceptance evidence belongs in `docs/testing/aiden-on-the-go/`. Evidence must distinguish automated proof, physical-device proof, and anything not yet verified. + +## 10. Delivery order + +Follow `docs/plans/aiden-on-the-go-plan.md` phases 0 through 12 in order. Do not expose production chat/workspace endpoints during Phase 0. Do not advance merely because code compiles: satisfy the phase acceptance gate and record evidence first. + +## 11. Attribution + +Preserve the imported Hermex upstream MIT license, copyright, and third-party notices while removing Hermes/Hermex product identity. New Aiden protocol and product documentation is owned by this repository. diff --git a/ios/README.md b/ios/README.md new file mode 100644 index 00000000..7b578224 --- /dev/null +++ b/ios/README.md @@ -0,0 +1,24 @@ +# Aiden On The Go + +Aiden On The Go is the native SwiftUI companion for Aiden Agent on macOS. The Mac owns execution, persistence, providers, workspaces, and permissions; iPhone and iPad provide an authenticated remote control surface over a local network or Tailscale. + +The product and protocol sources of truth are: + +- [`PROJECT_SPEC.md`](PROJECT_SPEC.md) +- [`../docs/plans/aiden-on-the-go-plan.md`](../docs/plans/aiden-on-the-go-plan.md) +- [`../docs/aiden-remote-api-v1.md`](../docs/aiden-remote-api-v1.md) +- [`../protocol/aiden-remote/v1/openapi.json`](../protocol/aiden-remote/v1/openapi.json) + +Open `AidenOnTheGo.xcodeproj` and use the `AidenOnTheGo` scheme. Hardware verification is performed with terminal-based `xcodebuild` and `xcrun` commands against an explicitly selected physical device. + +To verify ActivityKit persistence and authenticated reconciliation across two real app-host processes without reinstalling the app between phases, run: + +```sh +npm run ios:activitykit-process-proof -- \ + --xcode-device-id \ + --core-device-id +``` + +The command rejects simulated or mismatched destinations, builds once, starts a uniquely identified Live Activity, confirms the first test host has exited, relaunches the installed destination artifacts, reconciles through `AidenRemoteClient`, ends the activity, and moves its temporary result bundles to Trash. Its cleanup phase is best-effort even when a proof phase fails. + +Aiden On The Go is MIT-licensed under [`LICENSE`](LICENSE). The original Hermex MIT notice is retained under `AidenOnTheGo/Resources/ThirdPartyNotices/` for adapted implementation pieces; no Hermes WebUI compatibility layer or product identity is shipped. diff --git a/ios/SECURITY.md b/ios/SECURITY.md new file mode 100644 index 00000000..593f6b4a --- /dev/null +++ b/ios/SECURITY.md @@ -0,0 +1,5 @@ +# Security + +Report security issues privately through the sole repository's GitHub security-advisory flow. Do not include device credentials, provider keys, pairing payloads, prompts, filesystem paths, or private logs in a public issue. + +The mobile threat model is [`../docs/security/aiden-remote-threat-model.md`](../docs/security/aiden-remote-threat-model.md). Remote Access is off by default, uses per-device revocable credentials, and cannot widen Aiden workspace authority or enable Computer Use. diff --git a/ios/TESTFLIGHT.md b/ios/TESTFLIGHT.md new file mode 100644 index 00000000..04a2132d --- /dev/null +++ b/ios/TESTFLIGHT.md @@ -0,0 +1,89 @@ +# TestFlight readiness + +The approved production identity is: + +- App: `sbtbiswas.AidenOnTheGo` +- Live Activity widget: `sbtbiswas.AidenOnTheGo.LiveActivityWidget` +- App Group: `group.sbtbiswas.AidenOnTheGo` +- URL scheme: `aiden-otg` +- SKU: `aiden-on-the-go-ios` + +## Verified archive command + +Run from the repository root. This targets generic iOS hardware and does not use a simulator: + +```sh +xcodebuild archive \ + -project ios/AidenOnTheGo.xcodeproj \ + -scheme AidenOnTheGo \ + -configuration Release \ + -destination 'generic/platform=iOS' \ + -archivePath /tmp/AidenOnTheGo.xcarchive \ + DEVELOPMENT_TEAM=5WP229CBB8 \ + CODE_SIGN_STYLE=Automatic \ + -allowProvisioningUpdates +``` + +The Phase 12 audit produced a valid Release archive with the approved bundle IDs, team, App Group, Live Activity widget, App Intents metadata, privacy manifest, and third-party notices. The locally available signing identity produced an Apple Development archive with `get-task-allow=true`. That archive is useful for readiness validation but must not be exported or uploaded to TestFlight. + +Distribution signing is now provisioned. The local Apple Distribution identity belongs to team `5WP229CBB8`, and App Store profiles exist for both `sbtbiswas.AidenOnTheGo` and `sbtbiswas.AidenOnTheGo.LiveActivityWidget`. The exported internal IPA must still be checked for `get-task-allow=false`, exact bundle/App Group identifiers, and Apple Distribution signing before every upload. + +## Export configurations + +The checked-in export options under `ci/` are adapted from the Hermex release foundation: + +- `ci/TestFlightExportOptions.plist` is internal-TestFlight-only. +- `ci/ExternalTestFlightExportOptions.plist` can produce an external-capable App Store Connect upload after owner approval. + +Both pin team `5WP229CBB8`, use automatic signing, preserve the selected project version/build number, and upload symbols. The external configuration deliberately omits `testFlightInternalTestingOnly`; exporting a build does not invite testers or submit it for review. + +## Manual GitHub upload workflows + +The owner-gated upload workflows are adapted from Hermex's proven release structure: + +- `.github/workflows/aiden-on-the-go-internal-testflight.yml` requires a manual run from `main`, the exact confirmation `INTERNAL`, and the protected GitHub environment `aiden-on-the-go-internal-testflight`. It uses the internal-only export policy. +- `.github/workflows/aiden-on-the-go-external-testflight.yml` requires a manual run from `main`, the exact confirmation `EXTERNAL_REVIEW`, and the separate protected environment `aiden-on-the-go-external-testflight`. It uses the external-capable policy and refuses a marketing version whose App Store version train is already closed. + +Configure these three secrets independently in each protected environment before use: + +- `APP_STORE_CONNECT_KEY_ID` +- `APP_STORE_CONNECT_ISSUER_ID` +- `APP_STORE_CONNECT_PRIVATE_KEY` containing the `.p8` key text + +Both workflows query App Store Connect for the latest build in the current marketing version and select the next valid `CFBundleVersion`; the optional input can override it only with a greater one-to-three-component numeric version. Both archive generic iOS hardware and upload only. The external workflow does not assign a tester group, invite testers, or submit Beta App Review; those remain explicit App Store Connect actions. + +The workflows intentionally fail closed until the protected environments, App Store Connect app record, API-key access, and valid iOS distribution signing are owned and configured. Do not weaken the gates to make an unprovisioned run pass. Their deterministic policy and selector tests run with `npm run test:ios-release` and are registered in the repository's normal test command. + +Use the read-only and owner-operations guidance in `ASC_CLI.md` to inspect App Store Connect after upload, validate the exact version, and drive Codex status automations. The upload workflow deliberately does not depend on installing `asc` on a hosted runner. The distinct **Aiden On The Go** record is App ID `6803233275`; the legacy **Aiden - Quick AI** record is not reused. + +On 2026-08-19, version `0.1.0` build `7` was distribution-signed, exported with the internal-only policy, uploaded through the explicitly selected owner-authorized ASC profile, processed as `VALID`, and assigned to the internal group. Exact App Store Connect build `717b6381-4dec-4cce-85d1-72b503c28590` reports `internalBuildState=IN_BETA_TESTING` and `externalBuildState=NOT_APPLICABLE`. Build `4` was superseded by build `5` after a cold-connect home-load edge; build `6` added scratch New Agent creation, navigation handoff, and Live Activity branding; build `7` adds Thinking Orbs, non-bloating New Agent choices, hopeful server-confirmed mutations, relative-time/landing cleanup, the theme-continuous Liquid Glass composer, keyboard dismissal, and nested per-model thinking levels. Build `1` was rejected before processing because its App Store icon contained alpha; builds `2`–`7` use the exact opaque RayChat Icon Composer artwork. + +The local `npm run ios:asc-monitor` command is the only approved Codex-automation entry point. It requires the exact numeric App ID, exact build ID where applicable, and an explicit Aiden Keychain profile; enforces telemetry-off strict authentication; uses only read operations; and redacts TestFlight tester/feedback/crash content into change-detection summaries. Its focused tests are registered in `npm run test:ios-release` and the normal repository test gate. + +For a deliberate local export after the owner installs valid iOS distribution signing and confirms the App Store Connect record, export the exact reviewed archive with one of these configurations: + +```sh +xcodebuild -exportArchive \ + -archivePath /tmp/AidenOnTheGo.xcarchive \ + -exportPath /tmp/AidenOnTheGoExport \ + -exportOptionsPlist ios/ci/TestFlightExportOptions.plist \ + -allowProvisioningUpdates +``` + +Use `ios/ci/ExternalTestFlightExportOptions.plist` only when the build is intentionally eligible for external TestFlight/Beta App Review. + +## Distribution gates + +Before export or upload: + +1. The owner selected version `0.1.0` for the first internal TestFlight train. Build `1` was rejected during upload because the App Store icon contained alpha; build `2` corrected the icon, build `3` corrected compact iPhone navigation, build `4` introduced the Aiden-native shell, build `5` fixed cold-connect home loading, build `6` added the final scratch/navigation/activity-branding repairs, and build `7` is the current internal candidate with the latest interaction/composer/model work. Increment the build number for later uploads; selecting the first public version remains a later product decision. +2. Configure iOS App Store distribution signing/profiles for the app, Live Activity widget, and App Group under team `5WP229CBB8`, then verify the exported app and extension have `get-task-allow=false` and the exact approved identifiers. +3. With the correct Aiden credential, inspect the existing **Aiden - Quick AI** TestFlight record and confirm whether Aiden On The Go reuses it or requires a distinct App Store Connect record and SKU. +4. Publish the mobile-specific privacy-policy additions, make a working support contact visible at the resolved support URL, then supply the age-rating answers and required iPhone/iPad screenshots. The public URLs, owner name, feedback email, and copyright are resolved in `APP_STORE_METADATA.md`. +5. Provide App Review with a reachable companion Aiden Agent and explicit pairing/review instructions. Do not submit placeholder credentials or a setup that only works on the developer's private LAN. +6. Complete the physical-iPad, real-Tailscale, background/reconnect, Siri, microphone/dictation, and Live Activity acceptance gates recorded in the implementation plan. +7. Re-run the full release checklist against the exact distribution-signed archive and exported IPA before upload. + +Draft product copy and the unresolved App Store fields live in `APP_STORE_METADATA.md`. + +Never upload an archive built with a personal/imported compatibility identifier, an Apple Development identity, or `get-task-allow=true`. diff --git a/ios/app-store/MOBILE_PRIVACY_SUPPORT_COPY.md b/ios/app-store/MOBILE_PRIVACY_SUPPORT_COPY.md new file mode 100644 index 00000000..0465e550 --- /dev/null +++ b/ios/app-store/MOBILE_PRIVACY_SUPPORT_COPY.md @@ -0,0 +1,43 @@ +# Mobile privacy and support copy + +Status: ready for owner/legal review and publication on `chatwithaiden.com`. The website source is not part of this repository, so this file does not claim that the live page has changed. + +The current public policy says Aiden is a local-first macOS app. Before submitting Aiden On The Go, replace that product-limited wording and add the following sections while preserving the existing provider and website-hosting disclosures. + +## Overview replacement + +Aiden is designed as a local-first Mac app with an optional native iPhone and iPad companion called Aiden On The Go. We do not collect, sell, rent, or share personal information through the Aiden website or apps. Aiden On The Go connects directly to an Aiden Agent installation that you choose and control; Aiden does not provide a hosted relay or central synchronization service for that connection. + +## Local app data replacement + +Aiden Agent stores chat history, workspace configuration, and app settings locally on your Mac. Provider API keys are stored on your Mac, such as in macOS Keychain, and are not copied into Aiden On The Go or sent to Aiden servers. + +Aiden On The Go stores its pairing credential in the iPhone or iPad Keychain. It may keep device-local settings and bounded caches for paired-installation names, workspaces, chats, navigation, and last-known run status. App Intents use a limited App Group cache containing stable identifiers and display names; they do not receive the pairing credential or contact the network. You can remove a paired installation from the mobile app, revoke a device from Aiden Agent, or remove the app and its local data using normal iOS or iPadOS controls. + +## Mobile remote access + +Remote Access is off by default in Aiden Agent. When you enable and pair Aiden On The Go, the mobile app connects directly to your Mac over your local network or a Tailscale connection you configure. Pairing uses a short-lived, one-use session and creates a revocable device credential. Aiden does not enable Tailscale Funnel or route this traffic through an Aiden-operated service. + +Chats, prompts, selected attachments, workspace operations, and approval decisions sent from Aiden On The Go go to the paired Mac. If a request uses an AI provider configured in Aiden Agent, the Mac may then send prompts, selected files, metadata, and responses to that provider or local model service under the provider's privacy policy, retention rules, and account settings. + +## iPhone and iPad permissions + +- **Local Network:** used only to discover or connect to an Aiden Agent installation on a network you choose. +- **Camera:** used when you choose to scan a pairing QR code. Camera frames are processed for pairing and are not uploaded to Aiden. +- **Photos and Files:** content is accessed only after you select it. Selected content is sent to the paired Mac and may be processed by the AI provider you chose for the request. +- **Microphone and Speech Recognition:** requested only when you start dictation. Aiden On The Go requires on-device recognition and does not upload or retain a voice recording. If on-device recognition is unavailable or permission is denied, dictation is unavailable and the text composer remains usable. +- **Notifications and Live Activities:** used for device-local status. Live Activities contain bounded last-known run state, use no Aiden cloud push relay, and hide assistant response excerpts by default. + +Opening a web link or displaying externally hosted transcript media can make a normal network request to that third-party host. Aiden credentials are not forwarded to the host; the host may receive ordinary request information such as the device's network address under its own policy. + +## Contact replacement + +Questions about privacy or support for Aiden Agent and Aiden On The Go can be sent to `hey@sambitbiswas.com`. Include the Aiden version, device model, and a description of the issue, but do not send provider API keys, pairing credentials, private prompts, or confidential files. + +## Publication checklist + +- Update the policy's “Last updated” date when this copy is published. +- Preserve the existing disclosure that third-party AI providers process requests under their own policies. +- Keep the direct statement that Aiden does not collect chats, prompts, selected files, provider keys, model responses, device identifiers, precise location, payment information, or analytics events unless the shipped product or operational services change. +- Make the support email visibly reachable from `https://chatwithaiden.com/`, which is the App Store support URL. +- Recheck this copy against the final distribution candidate and App Privacy answers before every submission. diff --git a/ios/app-store/metadata/app-info/en-US.json b/ios/app-store/metadata/app-info/en-US.json new file mode 100644 index 00000000..b79f839a --- /dev/null +++ b/ios/app-store/metadata/app-info/en-US.json @@ -0,0 +1,5 @@ +{ + "name": "Aiden On The Go", + "subtitle": "Your Aiden Agent, anywhere", + "privacyPolicyUrl": "https://chatwithaiden.com/privacy" +} diff --git a/ios/app-store/metadata/version/0.1.0/en-US.json b/ios/app-store/metadata/version/0.1.0/en-US.json new file mode 100644 index 00000000..5d381a34 --- /dev/null +++ b/ios/app-store/metadata/version/0.1.0/en-US.json @@ -0,0 +1,6 @@ +{ + "description": "Aiden On The Go is the native iPhone and iPad companion for Aiden Agent on your Mac. Pair directly with a Mac you control over your local network or Tailscale, then continue Aiden chats and manage workspaces from your mobile device.\n\nReview conversations, stream responses, handle approval requests, inspect workspace files, work with supported Git flows, and manage scheduled tasks. App Intents provide quick navigation, Live Activities show bounded run status, and optional voice dictation and read-aloud stay on device.\n\nYour Mac remains the execution authority. Remote Access is off by default, each mobile device uses a revocable credential, and provider credentials remain on the Mac.", + "keywords": "AI assistant,agent,developer,Git,workspace,chat,automation,remote,Tailscale,Swift", + "marketingUrl": "https://chatwithaiden.com/", + "supportUrl": "https://chatwithaiden.com/" +} diff --git a/ios/ci/ExternalTestFlightExportOptions.plist b/ios/ci/ExternalTestFlightExportOptions.plist new file mode 100644 index 00000000..7836adf0 --- /dev/null +++ b/ios/ci/ExternalTestFlightExportOptions.plist @@ -0,0 +1,18 @@ + + + + + destination + upload + manageAppVersionAndBuildNumber + + method + app-store-connect + signingStyle + automatic + teamID + 5WP229CBB8 + uploadSymbols + + + diff --git a/ios/ci/TestFlightExportOptions.plist b/ios/ci/TestFlightExportOptions.plist new file mode 100644 index 00000000..e938eddf --- /dev/null +++ b/ios/ci/TestFlightExportOptions.plist @@ -0,0 +1,20 @@ + + + + + destination + upload + manageAppVersionAndBuildNumber + + method + app-store-connect + signingStyle + automatic + teamID + 5WP229CBB8 + testFlightInternalTestingOnly + + uploadSymbols + + + diff --git a/ios/ci/select_testflight_build_number.rb b/ios/ci/select_testflight_build_number.rb new file mode 100644 index 00000000..819da611 --- /dev/null +++ b/ios/ci/select_testflight_build_number.rb @@ -0,0 +1,295 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "base64" +require "json" +require "net/http" +require "openssl" +require "time" +require "uri" + +class TestFlightBuildNumberSelector + API_BASE = "https://api.appstoreconnect.apple.com" + BUILD_NUMBER_PATTERN = /\A\d+(?:\.\d+){0,2}\z/ + + # App Store version states in which Apple has approved the version and closed + # its pre-release train: any later upload must carry a higher + # CFBundleShortVersionString (ASC upload errors 90186/90062, hit on + # 2026-06-02 with 1.0 and again on 2026-08-04 with 1.4). + APPROVED_APP_STORE_STATES = %w[ + READY_FOR_SALE + READY_FOR_DISTRIBUTION + PENDING_DEVELOPER_RELEASE + PENDING_APPLE_RELEASE + PROCESSING_FOR_APP_STORE + ].freeze + + class SelectionError < StandardError; end + + def self.valid_build_number?(value) + BUILD_NUMBER_PATTERN.match?(value.to_s) + end + + def self.compare_build_numbers(left, right) + validate_build_number!(left, "left build number") + validate_build_number!(right, "right build number") + + left_parts = left.split(".").map(&:to_i) + right_parts = right.split(".").map(&:to_i) + max_length = [left_parts.length, right_parts.length].max + + max_length.times do |index| + left_value = left_parts[index] || 0 + right_value = right_parts[index] || 0 + return -1 if left_value < right_value + return 1 if left_value > right_value + end + + 0 + end + + def self.increment_build_number(value) + validate_build_number!(value, "latest build number") + + parts = value.split(".").map(&:to_i) + parts[-1] += 1 + parts.join(".") + end + + def self.select_build_number(requested_build_number:, latest_build_number:) + requested = requested_build_number.to_s.strip + + unless requested.empty? + validate_build_number!(requested, "requested build number") + + if latest_build_number && compare_build_numbers(requested, latest_build_number) <= 0 + raise SelectionError, + "Requested build number #{requested} must be greater than latest App Store Connect build #{latest_build_number}." + end + + return requested + end + + latest_build_number ? increment_build_number(latest_build_number) : "1" + end + + # Returns the approved App Store version that closes the train for + # marketing_version, or nil when the train is open. Approved version strings + # that are not plain dotted numerics cannot be compared and are ignored. + def self.closed_train_version(marketing_version:, approved_versions:) + validate_build_number!(marketing_version, "marketing version") + + approved_versions + .select { |value| valid_build_number?(value) } + .find { |value| compare_build_numbers(marketing_version, value) <= 0 } + end + + def self.validate_build_number!(value, label) + return if valid_build_number?(value) + + raise SelectionError, "#{label.capitalize} must contain one to three period-separated integers using only digits and dots. Received: #{value}" + end + + def initialize(env: ENV, now: Time.now) + @env = env + @now = now + @jwt_token = nil + end + + def run + bundle_id = required_env("BUNDLE_ID") + marketing_version = required_env("MARKETING_VERSION") + requested_build_number = @env.fetch("REQUESTED_BUILD_NUMBER", "") + + enforce_open_train!(bundle_id: bundle_id, marketing_version: marketing_version) if @env["ENFORCE_OPEN_TRAIN"] == "1" + + latest = latest_uploaded_build_number(bundle_id: bundle_id, marketing_version: marketing_version) + selected = self.class.select_build_number( + requested_build_number: requested_build_number, + latest_build_number: latest + ) + + if requested_build_number.to_s.strip.empty? + warn "Latest App Store Connect build for #{bundle_id} #{marketing_version}: #{latest || "none"}" + warn "Selected next build number: #{selected}" + else + warn "Latest App Store Connect build for #{bundle_id} #{marketing_version}: #{latest || "none"}" + warn "Using requested build number: #{selected}" + end + + selected + end + + private + + # Fails fast — before the ~15-minute archive step — when App Store Connect + # would reject the upload anyway because marketing_version's pre-release + # train is closed by an approved App Store version. + def enforce_open_train!(bundle_id:, marketing_version:) + blocking = self.class.closed_train_version( + marketing_version: marketing_version, + approved_versions: approved_app_store_versions(bundle_id: bundle_id) + ) + + if blocking + raise SelectionError, + "The #{marketing_version} pre-release train is closed: App Store version #{blocking} is already approved. " \ + "Bump MARKETING_VERSION in ios/AidenOnTheGo.xcodeproj/project.pbxproj above #{blocking}, land it on main, " \ + "and re-run this workflow (see ios/TESTFLIGHT.md)." + end + + warn "Pre-release train #{marketing_version} is open: no approved App Store version at or above it." + end + + def approved_app_store_versions(bundle_id:) + app_id = app_id_for_bundle_id(bundle_id) + versions = fetch_paginated_json( + "/v1/apps/#{app_id}/appStoreVersions", + "fields[appStoreVersions]" => "versionString,appStoreState", + "limit" => "200" + ) + + versions + .select { |item| APPROVED_APP_STORE_STATES.include?(item.dig("attributes", "appStoreState")) } + .map { |item| item.dig("attributes", "versionString") } + .compact + end + + def latest_uploaded_build_number(bundle_id:, marketing_version:) + app_id = app_id_for_bundle_id(bundle_id) + builds = fetch_paginated_json( + "/v1/builds", + "filter[app]" => app_id, + "filter[preReleaseVersion.version]" => marketing_version, + "fields[builds]" => "version,uploadedDate", + "limit" => "200" + ) + + build_numbers = builds.map { |item| item.dig("attributes", "version") }.compact + invalid_build_numbers = build_numbers.reject { |value| self.class.valid_build_number?(value) } + unless invalid_build_numbers.empty? + raise SelectionError, + "Cannot auto-select after non-numeric App Store Connect build numbers: #{invalid_build_numbers.uniq.join(", ")}" + end + + build_numbers.max { |left, right| self.class.compare_build_numbers(left, right) } + end + + # Memoized: the train preflight and build-number selection both need it. + def app_id_for_bundle_id(bundle_id) + @app_ids ||= {} + cached = @app_ids[bundle_id] + return cached if cached + + @app_ids[bundle_id] = uncached_app_id_for_bundle_id(bundle_id) + end + + def uncached_app_id_for_bundle_id(bundle_id) + apps = fetch_paginated_json( + "/v1/apps", + "filter[bundleId]" => bundle_id, + "fields[apps]" => "bundleId,name,sku", + "limit" => "10" + ) + + app = apps.find { |item| item.dig("attributes", "bundleId") == bundle_id } + raise SelectionError, "No App Store Connect app found for bundle ID #{bundle_id}." unless app + + app.fetch("id") + end + + def fetch_paginated_json(path, params) + url = URI.join(API_BASE, path) + url.query = URI.encode_www_form(params) + items = [] + + loop do + response = fetch_json(url) + data = response.fetch("data") + raise SelectionError, "Expected App Store Connect data array from #{url}." unless data.is_a?(Array) + + items.concat(data) + next_url = response.dig("links", "next") + break if next_url.to_s.empty? + + url = URI(next_url) + end + + items + end + + def fetch_json(url) + request = Net::HTTP::Get.new(url) + request["Authorization"] = "Bearer #{jwt_token}" + request["Accept"] = "application/json" + + response = Net::HTTP.start( + url.hostname, + url.port, + use_ssl: url.scheme == "https", + open_timeout: 10, + read_timeout: 30 + ) do |http| + http.request(request) + end + + unless response.is_a?(Net::HTTPSuccess) + raise SelectionError, "App Store Connect request failed with HTTP #{response.code}: #{response.body}" + end + + JSON.parse(response.body) + rescue JSON::ParserError => error + raise SelectionError, "App Store Connect returned invalid JSON: #{error.message}" + end + + def jwt_token + @jwt_token ||= begin + issued_at = @now.to_i - 60 + header = { + alg: "ES256", + kid: required_env("APP_STORE_CONNECT_KEY_ID"), + typ: "JWT" + } + payload = { + iss: required_env("APP_STORE_CONNECT_ISSUER_ID"), + iat: issued_at, + exp: issued_at + (20 * 60), + aud: "appstoreconnect-v1" + } + + signing_input = [base64url(header.to_json), base64url(payload.to_json)].join(".") + signature = base64url(es256_signature(signing_input)) + "#{signing_input}.#{signature}" + end + end + + def es256_signature(signing_input) + key = OpenSSL::PKey.read(File.read(required_env("APP_STORE_CONNECT_KEY_PATH"))) + der_signature = key.sign(OpenSSL::Digest::SHA256.new, signing_input) + sequence = OpenSSL::ASN1.decode(der_signature) + + r = sequence.value[0].value.to_i + s = sequence.value[1].value.to_i + hex_signature = [r.to_s(16).rjust(64, "0"), s.to_s(16).rjust(64, "0")].join + [hex_signature].pack("H*") + end + + def base64url(value) + Base64.strict_encode64(value).tr("+/", "-_").delete("=") + end + + def required_env(name) + value = @env[name].to_s + raise SelectionError, "Missing required environment variable: #{name}" if value.empty? + + value + end +end +if $PROGRAM_NAME == __FILE__ + begin + puts TestFlightBuildNumberSelector.new.run + rescue TestFlightBuildNumberSelector::SelectionError => error + warn error.message + exit 1 + end +end diff --git a/ios/ci/select_testflight_build_number_test.rb b/ios/ci/select_testflight_build_number_test.rb new file mode 100644 index 00000000..4fcd2c2e --- /dev/null +++ b/ios/ci/select_testflight_build_number_test.rb @@ -0,0 +1,255 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "minitest/autorun" +require "tempfile" +require_relative "select_testflight_build_number" + +class TestFlightBuildNumberSelectorTest < Minitest::Test + def test_selects_one_when_app_store_connect_has_no_builds_for_version + assert_equal( + "1", + TestFlightBuildNumberSelector.select_build_number( + requested_build_number: "", + latest_build_number: nil + ) + ) + end + + def test_selects_next_integer_build_number + assert_equal( + "19", + TestFlightBuildNumberSelector.select_build_number( + requested_build_number: "", + latest_build_number: "18" + ) + ) + end + + def test_selects_next_dotted_build_number + assert_equal( + "1.4", + TestFlightBuildNumberSelector.select_build_number( + requested_build_number: "", + latest_build_number: "1.3" + ) + ) + end + + def test_accepts_requested_build_number_above_latest + assert_equal( + "20", + TestFlightBuildNumberSelector.select_build_number( + requested_build_number: "20", + latest_build_number: "19" + ) + ) + end + + def test_rejects_requested_build_number_equal_to_latest + error = assert_raises(TestFlightBuildNumberSelector::SelectionError) do + TestFlightBuildNumberSelector.select_build_number( + requested_build_number: "19", + latest_build_number: "19" + ) + end + + assert_includes(error.message, "must be greater") + end + + def test_rejects_requested_build_number_below_latest + error = assert_raises(TestFlightBuildNumberSelector::SelectionError) do + TestFlightBuildNumberSelector.select_build_number( + requested_build_number: "18", + latest_build_number: "19" + ) + end + + assert_includes(error.message, "must be greater") + end + + def test_rejects_malformed_requested_build_number + error = assert_raises(TestFlightBuildNumberSelector::SelectionError) do + TestFlightBuildNumberSelector.select_build_number( + requested_build_number: "build-20", + latest_build_number: "19" + ) + end + + assert_includes(error.message, "digits and dots") + end + + def test_rejects_more_than_three_build_number_components + error = assert_raises(TestFlightBuildNumberSelector::SelectionError) do + TestFlightBuildNumberSelector.select_build_number( + requested_build_number: "1.2.3.4", + latest_build_number: "1.2.3" + ) + end + + assert_includes(error.message, "one to three") + end + + def test_rejects_empty_build_number_components + [".1", "1.", "1..2"].each do |value| + assert_raises(TestFlightBuildNumberSelector::SelectionError) do + TestFlightBuildNumberSelector.select_build_number( + requested_build_number: value, + latest_build_number: nil + ) + end + end + end + + def test_comparison_treats_omitted_trailing_zero_components_as_equal + assert_equal(0, TestFlightBuildNumberSelector.compare_build_numbers("1", "1.0")) + assert_equal(0, TestFlightBuildNumberSelector.compare_build_numbers("1.0", "1.0.0")) + end + + def test_train_is_open_when_marketing_version_is_above_all_approved_versions + assert_nil( + TestFlightBuildNumberSelector.closed_train_version( + marketing_version: "1.5", + approved_versions: ["1.4", "1.0.1", "1.0"] + ) + ) + end + + def test_train_is_closed_when_marketing_version_equals_an_approved_version + assert_equal( + "1.4", + TestFlightBuildNumberSelector.closed_train_version( + marketing_version: "1.4", + approved_versions: ["1.4", "1.0.1"] + ) + ) + end + + def test_train_is_closed_when_marketing_version_is_below_an_approved_version + assert_equal( + "1.4", + TestFlightBuildNumberSelector.closed_train_version( + marketing_version: "1.3.9", + approved_versions: ["1.4"] + ) + ) + end + + def test_train_comparison_is_numeric_not_lexicographic + assert_nil( + TestFlightBuildNumberSelector.closed_train_version( + marketing_version: "1.10", + approved_versions: ["1.9"] + ) + ) + end + + def test_train_check_ignores_non_numeric_approved_version_strings + assert_nil( + TestFlightBuildNumberSelector.closed_train_version( + marketing_version: "1.5", + approved_versions: ["2.0-beta", "1.4"] + ) + ) + end + + def test_train_check_with_no_approved_versions_is_open + assert_nil( + TestFlightBuildNumberSelector.closed_train_version( + marketing_version: "1.0", + approved_versions: [] + ) + ) + end + + def test_approved_versions_fetch_filters_to_approved_states + selector = TestFlightBuildNumberSelector.new(env: {}) + captured = [] + + selector.define_singleton_method(:app_id_for_bundle_id) { |_bundle_id| "app-123" } + selector.define_singleton_method(:fetch_paginated_json) do |path, params| + captured << [path, params] + [ + { "attributes" => { "versionString" => "1.4", "appStoreState" => "READY_FOR_SALE" } }, + { "attributes" => { "versionString" => "1.5", "appStoreState" => "PREPARE_FOR_SUBMISSION" } }, + { "attributes" => { "versionString" => "1.0.1", "appStoreState" => "REPLACED_WITH_NEW_VERSION" } }, + { "attributes" => { "appStoreState" => "READY_FOR_SALE" } } + ] + end + + versions = selector.send(:approved_app_store_versions, bundle_id: "sbtbiswas.AidenOnTheGo") + + assert_equal(["1.4"], versions) + assert_equal("/v1/apps/app-123/appStoreVersions", captured.first.first) + end + + def test_enforce_open_train_raises_with_actionable_message_when_closed + selector = TestFlightBuildNumberSelector.new(env: {}) + selector.define_singleton_method(:approved_app_store_versions) { |bundle_id:| ["1.4"] } + + error = assert_raises(TestFlightBuildNumberSelector::SelectionError) do + selector.send(:enforce_open_train!, bundle_id: "sbtbiswas.AidenOnTheGo", marketing_version: "1.4") + end + + assert_includes(error.message, "train is closed") + assert_includes(error.message, "Bump MARKETING_VERSION") + end + + def test_app_lookup_uses_supported_bundle_id_filter_only + selector = TestFlightBuildNumberSelector.new(env: {}) + captured = [] + + selector.define_singleton_method(:fetch_paginated_json) do |path, params| + captured << [path, params] + [ + { + "id" => "app-123", + "attributes" => { + "bundleId" => "sbtbiswas.AidenOnTheGo" + } + } + ] + end + + assert_equal( + "app-123", + selector.send(:app_id_for_bundle_id, "sbtbiswas.AidenOnTheGo") + ) + + assert_equal("/v1/apps", captured.first.first) + assert_equal("sbtbiswas.AidenOnTheGo", captured.first.last.fetch("filter[bundleId]")) + refute_includes(captured.first.last.keys, "filter[platform]") + end + + def test_jwt_uses_raw_es256_signature_format + key = OpenSSL::PKey::EC.generate("prime256v1") + + Tempfile.create("app-store-connect-key") do |file| + file.write(key.to_pem) + file.flush + + selector = TestFlightBuildNumberSelector.new( + env: { + "APP_STORE_CONNECT_KEY_ID" => "KEY123", + "APP_STORE_CONNECT_ISSUER_ID" => "00000000-0000-0000-0000-000000000000", + "APP_STORE_CONNECT_KEY_PATH" => file.path + }, + now: Time.at(1_700_000_000) + ) + + token_parts = selector.send(:jwt_token).split(".") + payload = JSON.parse(base64url_decode(token_parts[1])) + + assert_equal(3, token_parts.length) + assert_equal(20 * 60, payload.fetch("exp") - payload.fetch("iat")) + assert_equal(64, base64url_decode(token_parts[2]).bytesize) + end + end + + private + + def base64url_decode(value) + padding = (4 - (value.length % 4)) % 4 + Base64.urlsafe_decode64(value + ("=" * padding)) + end +end diff --git a/ios/scripts/check-swift-file-sizes b/ios/scripts/check-swift-file-sizes new file mode 100755 index 00000000..8783dea3 --- /dev/null +++ b/ios/scripts/check-swift-file-sizes @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +raw_limit="${AIDEN_SWIFT_FILE_SIZE_LIMIT:-500}" +scan_root="$repo_root/AidenOnTheGo" + +if [[ ! "$raw_limit" =~ ^[0-9]+$ ]]; then + echo "AIDEN_SWIFT_FILE_SIZE_LIMIT must be a positive integer. Received: $raw_limit" >&2 + exit 2 +fi + +limit="$((10#$raw_limit))" + +if (( limit <= 0 )); then + echo "AIDEN_SWIFT_FILE_SIZE_LIMIT must be a positive integer. Received: $raw_limit" >&2 + exit 2 +fi + +is_exempt_file() { + local relative_path="$1" + local basename="${relative_path##*/}" + + case "$relative_path" in + */Previews/*|*/Preview\ Content/*) + return 0 + ;; + esac + + case "$basename" in + *_Previews.swift|*.generated.swift|*.gen.swift|*Generated*.swift) + return 0 + ;; + esac + + return 1 +} + +warning_count=0 + +while IFS= read -r -d "" file_path; do + relative_path="${file_path#"$repo_root/"}" + + if is_exempt_file "$relative_path"; then + continue + fi + + line_count="$(wc -l < "$file_path" | tr -d "[:space:]")" + + if (( line_count > limit )); then + warning_count=$((warning_count + 1)) + + if [[ "${GITHUB_ACTIONS:-}" == "true" ]]; then + printf "::warning file=%s,line=1,title=Swift file size::%s has %s LOC, above the warning limit of %s. Split intentionally when practical.\n" \ + "$relative_path" "$relative_path" "$line_count" "$limit" + fi + + printf "warning: %s has %s LOC (limit %s)\n" "$relative_path" "$line_count" "$limit" + fi +done < <(find "$scan_root" -type f -name "*.swift" -print0 | sort -z) + +if (( warning_count > 0 )); then + printf "Swift file-size policy: warning-only. %s production app Swift file(s) exceed %s LOC.\n" "$warning_count" "$limit" + echo "Existing oversized files do not fail this check; use the output to make future drift visible." +else + printf "No production app Swift files exceed %s LOC.\n" "$limit" +fi diff --git a/scripts/check-ios-app-store-metadata.test.mjs b/scripts/check-ios-app-store-metadata.test.mjs new file mode 100644 index 00000000..8eb6e183 --- /dev/null +++ b/scripts/check-ios-app-store-metadata.test.mjs @@ -0,0 +1,151 @@ +import assert from "node:assert/strict"; +import { Buffer } from "node:buffer"; +import { readFile } from "node:fs/promises"; +import test from "node:test"; +import { URL } from "node:url"; + +const fileUrls = { + metadata: new URL("../ios/APP_STORE_METADATA.md", import.meta.url), + info: new URL("../ios/AidenOnTheGo/Resources/Info.plist", import.meta.url), + privacy: new URL("../ios/AidenOnTheGo/Resources/PrivacyInfo.xcprivacy", import.meta.url), + appConfig: new URL("../ios/AidenOnTheGo/Config/AppConfig.swift", import.meta.url), + packages: new URL( + "../ios/AidenOnTheGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved", + import.meta.url, + ), + ascRunbook: new URL("../ios/ASC_CLI.md", import.meta.url), + appInfoMetadata: new URL( + "../ios/app-store/metadata/app-info/en-US.json", + import.meta.url, + ), + versionMetadata: new URL( + "../ios/app-store/metadata/version/0.1.0/en-US.json", + import.meta.url, + ), + mobilePrivacySupport: new URL( + "../ios/app-store/MOBILE_PRIVACY_SUPPORT_COPY.md", + import.meta.url, + ), + gitignore: new URL("../.gitignore", import.meta.url), +}; + +async function loadFiles() { + const entries = await Promise.all( + Object.entries(fileUrls).map(async ([name, url]) => [name, await readFile(url, "utf8")]), + ); + return Object.fromEntries(entries); +} + +function backtickValue(source, label) { + const marker = "\u0060"; + const match = source.match(new RegExp(`^- ${label}: ${marker}([^${marker}]+)${marker}$`, "mu")); + assert.ok(match, `missing metadata field: ${label}`); + return match[1]; +} + +test("public App Store identity stays aligned with shipping app links", async () => { + const { metadata, appConfig } = await loadFiles(); + + assert.equal(backtickValue(metadata, "Bundle ID"), "sbtbiswas.AidenOnTheGo"); + assert.equal(backtickValue(metadata, "App Store SKU"), "aiden-on-the-go-ios"); + assert.equal(backtickValue(metadata, "Marketing URL"), "https://chatwithaiden.com/"); + assert.equal(backtickValue(metadata, "Support URL"), "https://chatwithaiden.com/"); + assert.equal( + backtickValue(metadata, "Privacy-policy URL"), + "https://chatwithaiden.com/privacy", + ); + assert.match(appConfig, /privacyPolicyURL = URL\(staticString: "https:\/\/chatwithaiden\.com\/privacy"\)/u); + assert.match(appConfig, /supportURL = URL\(staticString: "https:\/\/chatwithaiden\.com\/"\)/u); +}); + +test("ASC owner operations are telemetry-off, strict, and credential-safe", async () => { + const { ascRunbook, gitignore } = await loadFiles(); + + assert.match(ascRunbook, /ASC_TELEMETRY_DISABLED=1/u); + assert.match(ascRunbook, /--strict-auth/u); + assert.match(ascRunbook, /read-only/u); + assert.match(ascRunbook, /Do not create a placeholder automation for build `2`/u); + assert.match(ascRunbook, /721aeb9d-2b33-4729-8d10-5bc1783abbef/u); + assert.match(ascRunbook, /IN_BETA_TESTING/u); + assert.match(ascRunbook, /https:\/\/testflight\.apple\.com\/join\/s3T4T8y3/u); + assert.match(ascRunbook, /Zero accessible app records must not be treated as permission to auto-create one/u); + assert.match(ascRunbook, /resolve the existing record read-only/u); + assert.match(ascRunbook, /asc web privacy pull/u); + assert.match(ascRunbook, /asc screenshots validate/u); + assert.match(ascRunbook, /npm run ios:asc-monitor/u); + assert.match(ascRunbook, /builds info --build-id/u); + assert.match(ascRunbook, /never tester identity, feedback text, screenshot URLs, or crash content/u); + assert.doesNotMatch(ascRunbook, /AuthKey_[A-Z0-9]+\.p8/u); + assert.match(gitignore, /^\.asc\/config\.json$/mu); + assert.match(gitignore, /^\.asc\/sessions\/$/mu); +}); + +test("store copy respects App Store field limits and resolved categories", async () => { + const { metadata, appInfoMetadata, versionMetadata } = await loadFiles(); + const name = backtickValue(metadata, "Name"); + const subtitle = backtickValue(metadata, "Subtitle"); + const keywords = backtickValue(metadata, "Keywords draft"); + const appInfo = JSON.parse(appInfoMetadata); + const version = JSON.parse(versionMetadata); + + assert.ok(name.length >= 2 && name.length <= 30); + assert.ok(subtitle.length <= 30); + assert.ok(Buffer.byteLength(keywords, "utf8") <= 100); + assert.match(metadata, /^- Primary category: Developer Tools$/mu); + assert.match(metadata, /^- Secondary category: Productivity$/mu); + assert.deepEqual(appInfo, { + name, + subtitle, + privacyPolicyUrl: backtickValue(metadata, "Privacy-policy URL"), + }); + assert.equal(version.keywords, keywords); + assert.equal(version.marketingUrl, backtickValue(metadata, "Marketing URL")); + assert.equal(version.supportUrl, backtickValue(metadata, "Support URL")); + assert.ok(version.description.length > 0 && version.description.length <= 4_000); + assert.doesNotMatch(version.description, /<[^>]+>|\[[^\]]+\]\([^)]+\)/u); + assert.deepEqual(Object.keys(version).sort(), [ + "description", + "keywords", + "marketingUrl", + "supportUrl", + ]); +}); + +test("age and privacy drafts stay conservative and match the bundle manifest", async () => { + const { metadata, info, privacy, packages, mobilePrivacySupport } = await loadFiles(); + const packagePins = JSON.parse(packages).pins.map((pin) => pin.identity.toLowerCase()); + const telemetryPackages = [ + "amplitude", + "datadog", + "firebase", + "mixpanel", + "posthog", + "segment", + "sentry", + ]; + + assert.match(metadata, /\| Override \| 13\+ \|/u); + assert.match(metadata, /\*\*No, we do not collect data from this app\.\*\*/u); + assert.match(mobilePrivacySupport, /ready for owner\/legal review/u); + assert.match(mobilePrivacySupport, /iPhone or iPad Keychain/u); + assert.match(mobilePrivacySupport, /local network or a Tailscale connection/u); + assert.match(mobilePrivacySupport, /does not enable Tailscale Funnel/u); + assert.match(mobilePrivacySupport, /requires on-device recognition/u); + assert.match(mobilePrivacySupport, /App Intents use a limited App Group cache/u); + assert.match(mobilePrivacySupport, /hide assistant response excerpts by default/u); + assert.match(mobilePrivacySupport, /hey@sambitbiswas\.com/u); + assert.match(mobilePrivacySupport, /do not send provider API keys, pairing credentials/u); + assert.match(info, /ITSAppUsesNonExemptEncryption<\/key>\s*/u); + assert.match(info, /NSLocalNetworkUsageDescription<\/key>/u); + assert.match(info, /NSCameraUsageDescription<\/key>/u); + assert.match(info, /NSMicrophoneUsageDescription<\/key>/u); + assert.match(info, /NSSpeechRecognitionUsageDescription<\/key>/u); + assert.match(privacy, /NSPrivacyTracking<\/key>\s*/u); + assert.match(privacy, /NSPrivacyCollectedDataTypes<\/key>\s*/u); + assert.match(privacy, /CA92\.1<\/string>/u); + assert.deepEqual( + packagePins.filter((identity) => telemetryPackages.includes(identity)), + [], + "update App Privacy metadata before adding telemetry or analytics packages", + ); +}); diff --git a/scripts/check-ios-shipping-target.test.mjs b/scripts/check-ios-shipping-target.test.mjs new file mode 100644 index 00000000..fb2ae254 --- /dev/null +++ b/scripts/check-ios-shipping-target.test.mjs @@ -0,0 +1,290 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import { readFile, readdir } from "node:fs/promises"; +import test from "node:test"; +import { fileURLToPath, URL } from "node:url"; + +const iosRoot = fileURLToPath(new URL("../ios/", import.meta.url)); +const projectPath = fileURLToPath( + new URL("../ios/AidenOnTheGo.xcodeproj/project.pbxproj", import.meta.url), +); +const packageResolvedPath = fileURLToPath( + new URL( + "../ios/AidenOnTheGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved", + import.meta.url, + ), +); +const noticeDirectory = fileURLToPath( + new URL("../ios/AidenOnTheGo/Resources/ThirdPartyNotices/", import.meta.url), +); +const iosLicensePath = fileURLToPath(new URL("../ios/LICENSE", import.meta.url)); +const iconComposerPath = fileURLToPath( + new URL("../ios/AidenOnTheGo/Resources/AppIcon.icon/", import.meta.url), +); +const assetCatalogIconPath = fileURLToPath( + new URL( + "../ios/AidenOnTheGo/Resources/Assets.xcassets/AppIcon.appiconset/", + import.meta.url, + ), +); +const sidebarLogoPath = fileURLToPath( + new URL( + "../ios/AidenOnTheGo/Resources/Assets.xcassets/AidenSidebarLogo.imageset/", + import.meta.url, + ), +); + +const appSourcePaths = [ + "AidenOnTheGo/AidenOnTheGoApp.swift", + "AidenOnTheGo/AppIntents/AidenAppIntents.swift", + "AidenOnTheGo/Auth/KeychainStore.swift", + "AidenOnTheGo/Config/AidenAppearance.swift", + "AidenOnTheGo/Config/AppConfig.swift", + "AidenOnTheGo/ContentView.swift", + "AidenOnTheGo/Features/Chat/ComposerVoiceInputController.swift", + "AidenOnTheGo/Features/Remote/AidenChatFeature.swift", + "AidenOnTheGo/Features/Remote/AidenPairingView.swift", + "AidenOnTheGo/Features/Remote/AidenRemoteCoordinator.swift", + "AidenOnTheGo/Features/Remote/AidenScheduledTasksView.swift", + "AidenOnTheGo/Features/Remote/AidenWorkspaceEnvironmentView.swift", + "AidenOnTheGo/Features/Remote/AidenWorkspaceShellView.swift", + "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Core.swift", + "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Lattice.swift", + "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Morph.swift", + "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/OrbSpec.swift", + "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Orbits.swift", + "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Presets.swift", + "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Snapshot.swift", + "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Strands.swift", + "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/ThinkingOrb.swift", + "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Web.swift", + "AidenOnTheGo/LiveActivities/AgentRunActivityAttributes.swift", + "AidenOnTheGo/LiveActivities/AidenDeepLink.swift", + "AidenOnTheGo/LiveActivities/AidenRemoteLiveActivityManager.swift", + "AidenOnTheGo/Models/AidenChat.swift", + "AidenOnTheGo/Models/AidenInstallation.swift", + "AidenOnTheGo/Models/AidenScheduledTask.swift", + "AidenOnTheGo/Models/AidenWorkspaceEnvironment.swift", + "AidenOnTheGo/Networking/AidenRemoteClient.swift", + "AidenOnTheGo/Networking/AidenRemoteContract.swift", + "AidenOnTheGo/Networking/AidenSSEParser.swift", + "AidenOnTheGo/Networking/AidenServerTrust.swift", + "AidenOnTheGo/Persistence/AidenChatCache.swift", +]; + +const testSources = [ + "AidenChatTests.swift", + "AidenNativeIntegrationTests.swift", + "AidenRemoteClientTests.swift", + "AidenRemotePhase0Tests.swift", + "AidenScheduledTaskTests.swift", + "AidenWorkspaceEnvironmentTests.swift", +]; + +const widgetSources = [ + "AgentRunActivityAttributes.swift", + "AidenDeepLink.swift", + "AgentRunLiveActivityWidget.swift", +]; + +function phaseSourceNames(project, phaseId) { + const escaped = phaseId.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"); + const match = project.match( + new RegExp(`${escaped} /\\* Sources \\*/ = \\{[\\s\\S]*?files = \\(([\\s\\S]*?)\\);`, "u"), + ); + assert.ok(match, `missing source build phase ${phaseId}`); + return [...match[1].matchAll(/\/\* ([^*]+?) in Sources \*\//gu)] + .map((entry) => entry[1]) + .sort(); +} + +test("shipping, test, and widget source phases stay on the reviewed Aiden allowlists", async () => { + const project = await readFile(projectPath, "utf8"); + + assert.deepEqual( + phaseSourceNames(project, "1A2B3C4D5E6F700000000050"), + appSourcePaths.map((path) => path.split("/").at(-1)).sort(), + ); + assert.deepEqual( + phaseSourceNames(project, "1A2B3C4D5E6F700000000052"), + testSources.slice().sort(), + ); + assert.deepEqual( + phaseSourceNames(project, "A04500000000000000000050"), + widgetSources.slice().sort(), + ); +}); + +test("the iOS tree contains no orphan imported Swift sources", async () => { + const [appEntries, testEntries] = await Promise.all([ + readdir(`${iosRoot}AidenOnTheGo`, { recursive: true }), + readdir(`${iosRoot}AidenOnTheGoTests`, { recursive: true }), + ]); + + assert.deepEqual( + appEntries.filter((path) => path.endsWith(".swift")).sort(), + appSourcePaths.map((path) => path.replace("AidenOnTheGo/", "")).sort(), + ); + assert.deepEqual( + testEntries.filter((path) => path.endsWith(".swift")).sort(), + testSources.slice().sort(), + ); +}); + +test("the Aiden home, onboarding, composer, and activity retain the reviewed product shell", async () => { + const [shell, pairing, chat, widget, project, logoDefinition, logoArtwork] = await Promise.all([ + readFile(`${iosRoot}AidenOnTheGo/Features/Remote/AidenWorkspaceShellView.swift`, "utf8"), + readFile(`${iosRoot}AidenOnTheGo/Features/Remote/AidenPairingView.swift`, "utf8"), + readFile(`${iosRoot}AidenOnTheGo/Features/Remote/AidenChatFeature.swift`, "utf8"), + readFile(`${iosRoot}AidenLiveActivityWidget/AgentRunLiveActivityWidget.swift`, "utf8"), + readFile(projectPath, "utf8"), + readFile(`${sidebarLogoPath}Contents.json`, "utf8"), + readFile(`${sidebarLogoPath}aiden-sidebar-logo.png`), + ]); + + const scheduledIndex = shell.indexOf('title: "Scheduled Tasks"'); + const usageIndex = shell.indexOf('title: "Usage"', scheduledIndex); + const workspacesIndex = shell.indexOf('title: "Workspaces"', usageIndex); + const homeNavigationIndex = shell.indexOf("homeNavigationRows"); + const chatsIndex = shell.indexOf('Text("Chats")', homeNavigationIndex); + assert.ok(scheduledIndex >= 0 && scheduledIndex < usageIndex); + assert.ok(usageIndex < workspacesIndex); + assert.ok(homeNavigationIndex >= 0 && homeNavigationIndex < chatsIndex); + assert.match(shell, /Image\("AidenAppIcon"\)[\s\S]*?searchChrome/u); + assert.match(shell, /Image\(systemName: "magnifyingglass"\)[\s\S]*?person\.crop\.circle\.fill/u); + assert.match(shell, /AidenWorkspacesDirectoryView[\s\S]*?Label\("New Workspace"[\s\S]*?Label\("Add Mac Folder"/u); + assert.match(shell, /Label\("New Agent", systemImage: "square\.and\.pencil"\)/u); + assert.match(shell, /glassEffect\(\.regular\.tint\(tint\)\.interactive\(\), in: Capsule\(\)\)/u); + assert.match( + shell, + /Button\("Existing Workspace"\)[\s\S]*?Button\("New Workspace"\)[\s\S]*?Button\("Managed Scratch Workspace"\)/u, + ); + assert.doesNotMatch(shell, /messageLabel|chat\.messages\.count/u); + assert.doesNotMatch(shell, /count: homeModel\.scheduledTasks\.count|count: coordinator\.workspaces\.count/u); + assert.match(shell, /task\(id: coordinator\.connectionState\)[\s\S]*?homeModel\.load/u); + assert.match( + shell, + /AidenNavigationResolutionID\([\s\S]*?connectionState: coordinator\.connectionState[\s\S]*?resolveNavigationRequest/u, + ); + assert.match( + shell, + /func resolveNavigationRequest\(\)[\s\S]*?coordinator\.connectionState == \.connected[\s\S]*?defer \{ navigationRequest = nil \}[\s\S]*?intentChat = chat/u, + ); + assert.match( + shell, + /func createNewAgentInScratchWorkspace\(\)[\s\S]*?workspaceCreate: \.scratch[\s\S]*?func createNewAgent\([\s\S]*?createChat\(workspaceId: workspace\.id\)/u, + ); + assert.match(shell, /Button\("Close"\) \{ intentChat = nil \}/u); + assert.match(pairing, /Aiden, wherever you are\./u); + assert.match(pairing, /task\(id: step\)[\s\S]*?if step == 2[\s\S]*?discovery\.start\(\)/u); + assert.match(chat, /PhotosPicker/u); + assert.match(chat, /\.fileImporter\(/u); + assert.match(chat, /AidenSidebarLogo\(size: 15/u); + assert.match(chat, /ThinkingOrb\(state: activity\.orb, size: \.px20\)/u); + assert.match(chat, /ZStack\(alignment: \.bottom\)[\s\S]*?frame\(height: max\(96, composerHeight \+ 12\)\)[\s\S]*?AidenComposerView/u); + assert.match(chat, /glassEffect\(\.regular\.interactive\(\), in: shape\)/u); + assert.match(chat, /sendButtonBackground[\s\S]*?palette\.accent[\s\S]*?sendButtonForeground[\s\S]*?palette\.canvas/u); + assert.match(chat, /@FocusState private var composerIsFocused: Bool[\s\S]*?scrollDismissesKeyboard\(\.interactively\)[\s\S]*?TapGesture\(\)\.onEnded[\s\S]*?composerIsFocused = false/u); + assert.match(chat, /composerFocus: \$composerIsFocused[\s\S]*?\.focused\(composerFocus\)/u); + assert.match(chat, /candidate\.thinkingLevels[\s\S]*?Menu \{[\s\S]*?ForEach\(levels[\s\S]*?thinkingLevel: level/u); + assert.doesNotMatch(chat, /if let levels = model\.selectedModel\?\.thinkingLevels/u); + assert.doesNotMatch(chat, /\.background\(\.bar\)/u); + assert.match(widget, /status == \.starting \|\| status == \.thinking[\s\S]*?Image\("aiden-sidebar-logo"\)/u); + assert.match(project, /aiden-sidebar-logo\.png in Resources/u); + assert.doesNotMatch(`${shell}\n${pairing}\n${chat}\n${widget}`, /brain|sparkle/iu); + + assert.equal(JSON.parse(logoDefinition).properties["template-rendering-intent"], "template"); + assert.equal( + createHash("sha256").update(logoArtwork).digest("hex"), + "5119ab28448527d6855b8e4555492686220032d841ecd95fe7037c5a20fb58b6", + ); +}); + +test("shipping Swift literals contain only the Aiden API and no imported product identity", async () => { + const sources = await Promise.all( + appSourcePaths.map(async (path) => [path, await readFile(`${iosRoot}${path}`, "utf8")]), + ); + const forbiddenIdentity = /"[^"\n]*(?:hermes|hermex|kanban|cloudflare|cloudflared)[^"\n]*"/giu; + const nonAidenAPI = /"\/api\/(?!aiden\/v1)[^"\n]*"/gu; + + for (const [path, source] of sources) { + assert.doesNotMatch(source, forbiddenIdentity, `${path} contains imported product copy`); + assert.doesNotMatch(source, nonAidenAPI, `${path} contains a non-Aiden API endpoint`); + } +}); + +test("the Aiden MIT license, package graph, and bundled notices retain required attribution", async () => { + const [project, packageResolved, noticeFiles, notice, license] = await Promise.all([ + readFile(projectPath, "utf8"), + readFile(packageResolvedPath, "utf8"), + readdir(noticeDirectory), + readFile(`${noticeDirectory}NOTICE.txt`, "utf8"), + readFile(iosLicensePath, "utf8"), + ]); + const packages = JSON.parse(packageResolved); + + assert.deepEqual(packages.pins.map((pin) => pin.identity), [ + "keychainaccess", + "networkimage", + "swift-cmark", + "swift-markdown-ui", + ]); + assert.deepEqual( + packages.pins.map((pin) => [pin.identity, pin.state.version]), + [ + ["keychainaccess", "4.2.2"], + ["networkimage", "6.0.1"], + ["swift-cmark", "0.8.0"], + ["swift-markdown-ui", "2.4.1"], + ], + ); + assert.match( + project, + /packageProductDependencies = \(\s*1A2B3C4D5E6F700000000098 \/\* KeychainAccess \*\/,\s*BADA00000000000000000003 \/\* MarkdownUI \*\/,[\s\S]*?\);/u, + ); + assert.match(project, /repositoryURL = "https:\/\/github\.com\/gonzalezreal\/swift-markdown-ui\.git";/u); + assert.doesNotMatch(project, /swift-eventsource|Splash|Highlightr|SwiftMath/u); + assert.deepEqual(noticeFiles.sort(), [ + "Hermex-LICENSE.txt", + "KeychainAccess-LICENSE.txt", + "MarkdownUI-LICENSE.txt", + "NOTICE.txt", + "NetworkImage-LICENSE.txt", + "ThinkingOrbs-LICENSE.txt", + "swift-cmark-COPYING.txt", + ]); + assert.match(license, /MIT License/u); + assert.match(license, /Copyright \(c\) 2026 Sambit Biswas/u); + assert.doesNotMatch(license, /Uzair Ansar|Hermex/u); + assert.match(notice, /Hermex \(adapted SwiftUI interaction and implementation foundation\)/u); + assert.match(notice, /KeychainAccess 4\.2\.2/u); + assert.match(notice, /MarkdownUI 2\.4\.1/u); + assert.match(notice, /NetworkImage 6\.0\.1/u); + assert.match(notice, /swift-cmark 0\.8\.0/u); + assert.match(notice, /Thinking Orbs 0\.3\.1/u); + assert.doesNotMatch(notice, /swift-eventsource|Splash|Highlightr|SwiftMath|Lucide/u); +}); + +test("the shipping app icon is the reviewed opaque RayChat artwork", async () => { + const [project, iconDefinition, composerArtwork, catalogDefinition, catalogArtwork] = + await Promise.all([ + readFile(projectPath, "utf8"), + readFile(`${iconComposerPath}icon.json`, "utf8"), + readFile(`${iconComposerPath}Assets/aiden-icon-june15.png`), + readFile(`${assetCatalogIconPath}Contents.json`, "utf8"), + readFile(`${assetCatalogIconPath}aiden-icon-june15.png`), + ]); + + assert.match(project, /lastKnownFileType = wrapper\.icon; path = AppIcon\.icon;/u); + assert.match(project, /AppIcon\.icon in Resources/u); + assert.equal(JSON.parse(iconDefinition).groups[0].layers[0]["image-name"], "aiden-icon-june15.png"); + assert.equal(JSON.parse(catalogDefinition).images[0].filename, "aiden-icon-june15.png"); + assert.deepEqual(composerArtwork, catalogArtwork); + assert.equal(composerArtwork.readUInt32BE(16), 1024, "app icon width must be 1024 pixels"); + assert.equal(composerArtwork.readUInt32BE(20), 1024, "app icon height must be 1024 pixels"); + assert.equal(composerArtwork[25], 2, "PNG must use opaque RGB color rather than RGBA"); + assert.equal( + createHash("sha256").update(composerArtwork).digest("hex"), + "bb4c7fdd6f5597e415348823902e606bba75098a12289c15a3e434df7619fb6c", + ); +}); diff --git a/scripts/check-ios-testflight-policy.test.mjs b/scripts/check-ios-testflight-policy.test.mjs new file mode 100644 index 00000000..20e3d480 --- /dev/null +++ b/scripts/check-ios-testflight-policy.test.mjs @@ -0,0 +1,94 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import test from "node:test"; +import { URL } from "node:url"; + +const internalWorkflowUrl = new URL( + "../.github/workflows/aiden-on-the-go-internal-testflight.yml", + import.meta.url, +); +const externalWorkflowUrl = new URL( + "../.github/workflows/aiden-on-the-go-external-testflight.yml", + import.meta.url, +); +const internalExportUrl = new URL("../ios/ci/TestFlightExportOptions.plist", import.meta.url); +const externalExportUrl = new URL( + "../ios/ci/ExternalTestFlightExportOptions.plist", + import.meta.url, +); + +async function loadReleaseFiles() { + const [internalWorkflow, externalWorkflow, internalExport, externalExport] = await Promise.all([ + readFile(internalWorkflowUrl, "utf8"), + readFile(externalWorkflowUrl, "utf8"), + readFile(internalExportUrl, "utf8"), + readFile(externalExportUrl, "utf8"), + ]); + + return { internalWorkflow, externalWorkflow, internalExport, externalExport }; +} + +function assertCommonUploadPolicy(workflow, environment) { + assert.match(workflow, /^on:\n {2}workflow_dispatch:/mu); + assert.doesNotMatch(workflow, /^ {2}(?:push|pull_request|schedule):/mu); + assert.match(workflow, /permissions:\n {2}contents: read/u); + assert.match(workflow, new RegExp(`environment: ${environment}`, "u")); + assert.match(workflow, new RegExp(`group: ${environment}`, "u")); + assert.match(workflow, /cancel-in-progress: false/u); + assert.match(workflow, /\$\{GITHUB_REF_NAME\}" != "main"/u); + assert.match(workflow, /actions\/checkout@d23441a48e516b6c34aea4fa41551a30e30af803/u); + assert.doesNotMatch(workflow, /actions\/checkout@v\d+/u); + assert.match(workflow, /PROJECT_PATH: ios\/AidenOnTheGo\.xcodeproj/u); + assert.match(workflow, /SCHEME: AidenOnTheGo/u); + assert.match(workflow, /TEAM_ID: 5WP229CBB8/u); + assert.match(workflow, /BUNDLE_ID: sbtbiswas\.AidenOnTheGo/u); + assert.match(workflow, /ruby ios\/ci\/select_testflight_build_number\.rb/u); + assert.match(workflow, /-destination "generic\/platform=iOS"/u); + assert.match(workflow, /-authenticationKeyPath/u); + assert.match(workflow, /-authenticationKeyID/u); + assert.match(workflow, /-authenticationKeyIssuerID/u); + assert.match(workflow, /CURRENT_PROJECT_VERSION="\$\{BUILD_NUMBER\}"/u); + assert.doesNotMatch(workflow, /simulator/iu); + assert.doesNotMatch(workflow, /Hermes/u); +} + +test("internal TestFlight workflow remains manually gated and internal-only", async () => { + const { internalWorkflow, internalExport } = await loadReleaseFiles(); + + assertCommonUploadPolicy(internalWorkflow, "aiden-on-the-go-internal-testflight"); + assert.match(internalWorkflow, /confirm_internal_only/u); + assert.match(internalWorkflow, /CONFIRM_INTERNAL_ONLY\}" != "INTERNAL"/u); + assert.match(internalWorkflow, /EXPORT_OPTIONS_PLIST: ios\/ci\/TestFlightExportOptions\.plist/u); + assert.doesNotMatch(internalWorkflow, /ENFORCE_OPEN_TRAIN/u); + assert.match(internalExport, /testFlightInternalTestingOnly<\/key>\s*/u); +}); + +test("external TestFlight workflow is explicit, train-safe, and upload-only", async () => { + const { externalWorkflow, externalExport } = await loadReleaseFiles(); + + assertCommonUploadPolicy(externalWorkflow, "aiden-on-the-go-external-testflight"); + assert.match(externalWorkflow, /confirm_external_review/u); + assert.match(externalWorkflow, /CONFIRM_EXTERNAL_REVIEW\}" != "EXTERNAL_REVIEW"/u); + assert.match( + externalWorkflow, + /EXPORT_OPTIONS_PLIST: ios\/ci\/ExternalTestFlightExportOptions\.plist/u, + ); + assert.match(externalWorkflow, /ENFORCE_OPEN_TRAIN: "1"/u); + assert.match( + externalWorkflow, + /uploads only; App Store Connect external group assignment and Beta App Review submission remain manual/u, + ); + assert.doesNotMatch(externalExport, /testFlightInternalTestingOnly/u); +}); + +test("both export policies use automatic App Store Connect upload signing", async () => { + const { internalExport, externalExport } = await loadReleaseFiles(); + + for (const exportPolicy of [internalExport, externalExport]) { + assert.match(exportPolicy, /destination<\/key>\s*upload<\/string>/u); + assert.match(exportPolicy, /method<\/key>\s*app-store-connect<\/string>/u); + assert.match(exportPolicy, /signingStyle<\/key>\s*automatic<\/string>/u); + assert.match(exportPolicy, /teamID<\/key>\s*5WP229CBB8<\/string>/u); + assert.match(exportPolicy, /manageAppVersionAndBuildNumber<\/key>\s*/u); + } +}); diff --git a/scripts/ios-asc-monitor.mjs b/scripts/ios-asc-monitor.mjs new file mode 100644 index 00000000..af6933b0 --- /dev/null +++ b/scripts/ios-asc-monitor.mjs @@ -0,0 +1,268 @@ +#!/usr/bin/env node + +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import { spawnSync } from "node:child_process"; +import { pathToFileURL } from "node:url"; + +const MODES = new Set(["processing", "review", "testflight"]); +const APP_ID_PATTERN = /^\d{6,20}$/u; +const RESOURCE_ID_PATTERN = /^[A-Za-z0-9-]{6,80}$/u; +const VERSION_PATTERN = /^\d+(?:\.\d+){0,2}$/u; + +function hasControlCharacter(value) { + return [...value].some((character) => { + const codePoint = character.codePointAt(0); + return codePoint <= 0x1f || codePoint === 0x7f; + }); +} + +export function parseMonitorOptions(argv, env = process.env) { + const known = new Set(["--mode", "--profile", "--app-id", "--build-id", "--version"]); + const values = new Map(); + for (let index = 0; index < argv.length; index += 1) { + const name = argv[index]; + if (!known.has(name)) { + throw new Error(name.startsWith("--") ? `unknown option: ${name}` : `unexpected argument: ${name}`); + } + const value = argv[index + 1]; + if (!value || value.startsWith("--")) { + throw new Error(`${name} requires a value`); + } + if (values.has(name)) { + throw new Error(`${name} may be supplied only once`); + } + values.set(name, value); + index += 1; + } + + const mode = values.get("--mode"); + const profile = values.get("--profile") ?? env.AIDEN_ASC_PROFILE; + const appId = values.get("--app-id") ?? env.AIDEN_ASC_APP_ID; + const buildId = values.get("--build-id") ?? env.AIDEN_ASC_BUILD_ID; + const version = values.get("--version") ?? env.AIDEN_IOS_VERSION; + + if (!MODES.has(mode)) { + throw new Error("--mode must be processing, review, or testflight"); + } + if (!profile || profile.trim() !== profile || hasControlCharacter(profile)) { + throw new Error("an explicit control-free --profile (or AIDEN_ASC_PROFILE) is required"); + } + if (!APP_ID_PATTERN.test(appId ?? "")) { + throw new Error("an exact numeric --app-id (or AIDEN_ASC_APP_ID) is required"); + } + if ((mode === "processing" || mode === "testflight") && !RESOURCE_ID_PATTERN.test(buildId ?? "")) { + throw new Error(`an exact --build-id (or AIDEN_ASC_BUILD_ID) is required for ${mode}`); + } + if (version !== undefined && !VERSION_PATTERN.test(version)) { + throw new Error("--version must be a numeric App Store version such as 1.0 or 1.5.2"); + } + + return Object.freeze({ mode, profile, appId, buildId, version }); +} + +export function buildAscCommands(options) { + const prefix = ["--strict-auth", "--profile", options.profile]; + if (options.mode === "processing") { + return [[...prefix, "builds", "info", "--build-id", options.buildId, "--output", "json"]]; + } + if (options.mode === "review") { + const command = [ + ...prefix, + "review", + "status", + "--app", + options.appId, + "--platform", + "IOS", + "--output", + "json", + ]; + if (options.version) { + command.push("--version", options.version); + } + return [command]; + } + return [ + [ + ...prefix, + "testflight", + "crashes", + "list", + "--app", + options.appId, + "--build", + options.buildId, + "--app-platform", + "IOS", + "--limit", + "200", + "--sort", + "-createdDate", + "--output", + "json", + ], + [ + ...prefix, + "testflight", + "feedback", + "list", + "--app", + options.appId, + "--build", + options.buildId, + "--app-platform", + "IOS", + "--limit", + "200", + "--sort", + "-createdDate", + "--output", + "json", + ], + ]; +} + +function sha256(value) { + return createHash("sha256").update(value).digest("hex"); +} + +function resourceData(payload) { + if (Array.isArray(payload?.data)) return payload.data; + if (Array.isArray(payload)) return payload; + return []; +} + +function resourceDate(resource) { + const value = resource?.attributes?.createdDate ?? resource?.createdDate; + return typeof value === "string" ? value : null; +} + +export function summarizeTestFlightSubmissions(payload) { + const records = resourceData(payload) + .map((resource) => ({ + id: typeof resource?.id === "string" ? resource.id : "", + createdDate: resourceDate(resource) ?? "", + })) + .sort((left, right) => `${left.createdDate}:${left.id}`.localeCompare(`${right.createdDate}:${right.id}`)); + const newestCreatedDate = records.reduce( + (newest, record) => (record.createdDate > newest ? record.createdDate : newest), + "", + ); + return { + count: records.length, + newestCreatedDate: newestCreatedDate || null, + fingerprint: sha256(JSON.stringify(records)), + }; +} + +function resourceObject(payload) { + if (payload?.data && !Array.isArray(payload.data) && typeof payload.data === "object") { + return payload.data; + } + return payload && typeof payload === "object" ? payload : {}; +} + +function firstField(resource, names) { + for (const name of names) { + const value = resource?.attributes?.[name] ?? resource?.[name]; + if (["string", "number", "boolean"].includes(typeof value)) return value; + } + return null; +} + +export function summarizeBuild(payload) { + const resource = resourceObject(payload); + return { + id: firstField(resource, ["id"]), + version: firstField(resource, ["version", "buildNumber"]), + processingState: firstField(resource, ["processingState"]), + uploadedDate: firstField(resource, ["uploadedDate"]), + expirationDate: firstField(resource, ["expirationDate"]), + expired: firstField(resource, ["expired"]), + fingerprint: sha256(JSON.stringify(payload)), + }; +} + +function collectReviewFields(value, path = [], result = []) { + if (result.length >= 64 || value === null || value === undefined) return result; + if (Array.isArray(value)) { + value.forEach((item, index) => collectReviewFields(item, [...path, String(index)], result)); + return result; + } + if (typeof value !== "object") return result; + for (const [key, child] of Object.entries(value)) { + if (result.length >= 64) break; + const nextPath = [...path, key]; + if ( + /(?:state|status|action|platform|version)$/iu.test(key) && + ["string", "number", "boolean"].includes(typeof child) + ) { + result.push({ path: nextPath.join("."), value: child }); + } else { + collectReviewFields(child, nextPath, result); + } + } + return result; +} + +export function summarizeReview(payload) { + return { + fields: collectReviewFields(payload).sort((left, right) => left.path.localeCompare(right.path)), + fingerprint: sha256(JSON.stringify(payload)), + }; +} + +function parseJson(stdout, label) { + try { + return JSON.parse(stdout); + } catch { + throw new Error(`${label} returned non-JSON output`); + } +} + +export function runMonitor(options, execute = spawnSync) { + const commands = buildAscCommands(options); + const payloads = commands.map((args) => { + const result = execute("asc", args, { + encoding: "utf8", + env: { ...process.env, ASC_TELEMETRY_DISABLED: "1" }, + stdio: ["ignore", "pipe", "pipe"], + }); + if (result.error) throw result.error; + if (result.status !== 0) { + const message = result.stderr?.trim() || `asc exited with status ${result.status}`; + throw new Error(message); + } + return parseJson(result.stdout, `asc ${args.slice(3, 6).join(" ")}`); + }); + + const base = { schemaVersion: 1, mode: options.mode, appId: options.appId }; + if (options.mode === "processing") { + return { ...base, buildId: options.buildId, build: summarizeBuild(payloads[0]) }; + } + if (options.mode === "review") { + return { ...base, version: options.version ?? null, review: summarizeReview(payloads[0]) }; + } + return { + ...base, + buildId: options.buildId, + crashes: summarizeTestFlightSubmissions(payloads[0]), + feedback: summarizeTestFlightSubmissions(payloads[1]), + }; +} + +export function main(argv = process.argv.slice(2)) { + try { + const options = parseMonitorOptions(argv); + process.stdout.write(`${JSON.stringify(runMonitor(options), null, 2)}\n`); + } catch (error) { + assert(error instanceof Error); + process.stderr.write(`Aiden ASC monitor: ${error.message}\n`); + process.exitCode = 1; + } +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + main(); +} diff --git a/scripts/ios-asc-monitor.test.mjs b/scripts/ios-asc-monitor.test.mjs new file mode 100644 index 00000000..e6ae8a62 --- /dev/null +++ b/scripts/ios-asc-monitor.test.mjs @@ -0,0 +1,140 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { + buildAscCommands, + parseMonitorOptions, + runMonitor, + summarizeReview, + summarizeTestFlightSubmissions, +} from "./ios-asc-monitor.mjs"; + +const baseEnvironment = { + AIDEN_ASC_PROFILE: "Aiden Release ASC", + AIDEN_ASC_APP_ID: "1234567890", + AIDEN_ASC_BUILD_ID: "9876543210", +}; + +test("monitor requires exact identifiers and an explicit profile", () => { + assert.throws( + () => parseMonitorOptions(["--mode", "processing"], {}), + /explicit control-free --profile/u, + ); + assert.throws( + () => parseMonitorOptions(["--mode", "review"], { ...baseEnvironment, AIDEN_ASC_APP_ID: "Aiden" }), + /exact numeric --app-id/u, + ); + assert.throws( + () => parseMonitorOptions(["--mode", "testflight"], { ...baseEnvironment, AIDEN_ASC_BUILD_ID: "" }), + /exact --build-id/u, + ); + assert.throws( + () => parseMonitorOptions(["--mode", "review", "--unknown", "value"], baseEnvironment), + /unknown option/u, + ); +}); + +test("processing is scoped to the exact build and every command uses strict named auth", () => { + const options = parseMonitorOptions(["--mode", "processing"], baseEnvironment); + const commands = buildAscCommands(options); + + assert.deepEqual(commands, [[ + "--strict-auth", + "--profile", + "Aiden Release ASC", + "builds", + "info", + "--build-id", + "9876543210", + "--output", + "json", + ]]); +}); + +test("review status is read-only and version-scoped when supplied", () => { + const options = parseMonitorOptions( + ["--mode", "review", "--version", "1.5"], + baseEnvironment, + ); + const command = buildAscCommands(options)[0]; + + assert.deepEqual(command.slice(0, 6), [ + "--strict-auth", + "--profile", + "Aiden Release ASC", + "review", + "status", + "--app", + ]); + assert.ok(command.includes("1234567890")); + assert.ok(command.includes("IOS")); + assert.ok(command.includes("1.5")); + assert.ok(!command.some((argument) => /submit|create|update|delete|publish|upload/iu.test(argument))); +}); + +test("TestFlight summaries omit tester identity, feedback text, screenshots, and crash content", () => { + const payload = { + data: [ + { + id: "feedback-2", + attributes: { + createdDate: "2026-08-19T12:00:00Z", + comment: "private tester feedback", + email: "tester@example.com", + screenshotUrl: "https://example.com/private.png", + crashLog: "private crash content", + }, + relationships: { tester: { data: { id: "tester-1" } } }, + }, + { + id: "feedback-1", + attributes: { createdDate: "2026-08-18T12:00:00Z" }, + }, + ], + }; + + const summary = summarizeTestFlightSubmissions(payload); + assert.equal(summary.count, 2); + assert.equal(summary.newestCreatedDate, "2026-08-19T12:00:00Z"); + assert.match(summary.fingerprint, /^[a-f0-9]{64}$/u); + assert.doesNotMatch(JSON.stringify(summary), /private|tester@example|screenshot/u); +}); + +test("review summary retains state signals without contact or review-note fields", () => { + const summary = summarizeReview({ + status: "WAITING_FOR_REVIEW", + nextAction: "WAIT", + version: "1.5", + contactEmail: "review@example.com", + notes: "private review notes", + }); + + assert.deepEqual(summary.fields, [ + { path: "nextAction", value: "WAIT" }, + { path: "status", value: "WAITING_FOR_REVIEW" }, + { path: "version", value: "1.5" }, + ]); + assert.doesNotMatch(JSON.stringify(summary), /review@example|private review/u); +}); + +test("runner disables telemetry and emits only summarized TestFlight state", () => { + const calls = []; + const payloads = [ + { data: [{ id: "crash-1", attributes: { createdDate: "2026-08-19T10:00:00Z" } }] }, + { data: [{ id: "feedback-1", attributes: { createdDate: "2026-08-19T11:00:00Z" } }] }, + ]; + const execute = (binary, args, options) => { + calls.push({ binary, args, options }); + return { status: 0, stdout: JSON.stringify(payloads[calls.length - 1]), stderr: "" }; + }; + const options = parseMonitorOptions(["--mode", "testflight"], baseEnvironment); + const result = runMonitor(options, execute); + + assert.equal(calls.length, 2); + assert.ok(calls.every((call) => call.binary === "asc")); + assert.ok(calls.every((call) => call.options.env.ASC_TELEMETRY_DISABLED === "1")); + assert.ok(calls.every((call) => call.args.includes("--strict-auth"))); + assert.ok(calls.every((call) => call.args.includes("Aiden Release ASC"))); + assert.deepEqual(result.crashes.count, 1); + assert.deepEqual(result.feedback.count, 1); +}); diff --git a/scripts/ios-live-activity-process-proof.mjs b/scripts/ios-live-activity-process-proof.mjs new file mode 100644 index 00000000..b96b2c07 --- /dev/null +++ b/scripts/ios-live-activity-process-proof.mjs @@ -0,0 +1,342 @@ +#!/usr/bin/env node + +import assert from "node:assert/strict"; +import { randomUUID } from "node:crypto"; +import { spawnSync } from "node:child_process"; +import { + existsSync, + mkdirSync, + mkdtempSync, + readFileSync, + readdirSync, + renameSync, +} from "node:fs"; +import { homedir, tmpdir } from "node:os"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; + +const XCODE_DEVICE_ID_PATTERN = /^[0-9A-F]{8}-[0-9A-F]{16}$/iu; +const CORE_DEVICE_ID_PATTERN = /^[0-9A-F]{8}(?:-[0-9A-F]{4}){3}-[0-9A-F]{12}$/iu; +const TEST_IDENTIFIER = + "AidenOnTheGoTests/AidenNativeIntegrationTests/testOptInPhysicalActivityKitProcessBoundaryPhase"; +const TEST_TARGET_PATH = ":TestConfigurations:0:TestTargets:0"; + +export function parseActivityProcessProofOptions(argv, env = process.env) { + const known = new Set(["--xcode-device-id", "--core-device-id"]); + const values = new Map(); + for (let index = 0; index < argv.length; index += 1) { + const name = argv[index]; + if (!known.has(name)) { + throw new Error(name.startsWith("--") ? `unknown option: ${name}` : `unexpected argument: ${name}`); + } + const value = argv[index + 1]; + if (!value || value.startsWith("--")) throw new Error(`${name} requires a value`); + if (values.has(name)) throw new Error(`${name} may be supplied only once`); + values.set(name, value); + index += 1; + } + + const xcodeDeviceId = values.get("--xcode-device-id") ?? env.AIDEN_IOS_XCODE_DEVICE_ID; + const coreDeviceId = values.get("--core-device-id") ?? env.AIDEN_IOS_CORE_DEVICE_ID; + if (!XCODE_DEVICE_ID_PATTERN.test(xcodeDeviceId ?? "")) { + throw new Error("an exact physical --xcode-device-id (or AIDEN_IOS_XCODE_DEVICE_ID) is required"); + } + if (!CORE_DEVICE_ID_PATTERN.test(coreDeviceId ?? "")) { + throw new Error("an exact physical --core-device-id (or AIDEN_IOS_CORE_DEVICE_ID) is required"); + } + return Object.freeze({ xcodeDeviceId, coreDeviceId }); +} + +export function physicalDestination(xcodeDeviceId) { + return `platform=iOS,id=${xcodeDeviceId}`; +} + +export function destinationArtifactPlistCommands() { + return [ + `Add ${TEST_TARGET_PATH}:UseDestinationArtifacts bool true`, + `Add ${TEST_TARGET_PATH}:TestBundleDestinationRelativePath string __TESTHOST__/PlugIns/AidenOnTheGoTests.xctest`, + `Add ${TEST_TARGET_PATH}:UITargetAppBundleIdentifier string sbtbiswas.AidenOnTheGo`, + `Delete ${TEST_TARGET_PATH}:TestBundlePath`, + `Delete ${TEST_TARGET_PATH}:TestHostPath`, + `Delete ${TEST_TARGET_PATH}:DependentProductPaths`, + ]; +} + +export function validatePhysicalDeviceDetails(payload, options) { + const hardware = payload?.result?.hardwareProperties; + if (payload?.info?.outcome !== "success" || !hardware) { + throw new Error("CoreDevice did not return physical-device details"); + } + if (hardware.udid !== options.xcodeDeviceId) { + throw new Error("the CoreDevice UUID and Xcode device ID do not identify the same device"); + } + if (hardware.platform !== "iOS" || hardware.reality !== "physical") { + throw new Error("the selected destination must be a physical iOS device"); + } + return Object.freeze({ + udid: hardware.udid, + platform: hardware.platform, + reality: hardware.reality, + marketingName: hardware.marketingName ?? null, + }); +} + +function execute(command, args, options = {}) { + const result = spawnSync(command, args, { + cwd: options.cwd, + encoding: "utf8", + env: process.env, + stdio: options.capture ? ["ignore", "pipe", "pipe"] : "inherit", + }); + if (result.error) throw result.error; + if (result.status !== 0) { + const detail = options.capture ? result.stderr.trim() : ""; + throw new Error(`${command} exited with status ${result.status}${detail ? `: ${detail}` : ""}`); + } + return result.stdout; +} + +function findFile(root, suffix) { + for (const entry of readdirSync(root, { withFileTypes: true })) { + const candidate = join(root, entry.name); + if (entry.isDirectory()) { + const nested = findFile(candidate, suffix); + if (nested) return nested; + } else if (entry.name.endsWith(suffix)) { + return candidate; + } + } + return null; +} + +function plistBuddy(xctestrun, command) { + execute("/usr/libexec/PlistBuddy", ["-c", command, xctestrun], { capture: true }); +} + +function deletePlistEntryIfPresent(xctestrun, entry) { + const result = spawnSync("/usr/libexec/PlistBuddy", ["-c", `Delete ${entry}`, xctestrun], { + encoding: "utf8", + env: process.env, + stdio: ["ignore", "pipe", "pipe"], + }); + if (result.error) throw result.error; +} + +function setPhase(xctestrun, phase) { + plistBuddy( + xctestrun, + `Set ${TEST_TARGET_PATH}:EnvironmentVariables:AIDEN_ACTIVITYKIT_PROCESS_PHASE ${phase}`, + ); +} + +function configureDestinationArtifacts(xctestrun) { + const entriesToReplace = [ + `${TEST_TARGET_PATH}:UseDestinationArtifacts`, + `${TEST_TARGET_PATH}:TestBundleDestinationRelativePath`, + `${TEST_TARGET_PATH}:UITargetAppBundleIdentifier`, + ]; + for (const entry of entriesToReplace) deletePlistEntryIfPresent(xctestrun, entry); + for (const command of destinationArtifactPlistCommands().slice(0, 3)) plistBuddy(xctestrun, command); + for (const entry of [ + `${TEST_TARGET_PATH}:TestBundlePath`, + `${TEST_TARGET_PATH}:TestHostPath`, + `${TEST_TARGET_PATH}:DependentProductPaths`, + ]) { + deletePlistEntryIfPresent(xctestrun, entry); + } +} + +function runSelectedPhase({ xctestrun, destination, resultBundlePath, repositoryRoot }) { + execute( + "xcodebuild", + [ + "test-without-building", + "-xctestrun", + xctestrun, + "-destination", + destination, + `-only-testing:${TEST_IDENTIFIER}`, + "-resultBundlePath", + resultBundlePath, + "-quiet", + ], + { cwd: repositoryRoot }, + ); +} + +function readAidenProcesses(coreDeviceId, outputPath, repositoryRoot) { + execute( + "xcrun", + [ + "devicectl", + "device", + "info", + "processes", + "--device", + coreDeviceId, + "--search", + "Aiden", + "--json-output", + outputPath, + "--quiet", + ], + { cwd: repositoryRoot, capture: true }, + ); + const payload = JSON.parse(readFileSync(outputPath, "utf8")); + return Array.isArray(payload?.result?.runningProcesses) ? payload.result.runningProcesses : []; +} + +function validateSelectedDevice(options, outputPath, repositoryRoot) { + execute( + "xcrun", + [ + "devicectl", + "device", + "info", + "details", + "--device", + options.coreDeviceId, + "--json-output", + outputPath, + "--quiet", + ], + { cwd: repositoryRoot, capture: true }, + ); + return validatePhysicalDeviceDetails(JSON.parse(readFileSync(outputPath, "utf8")), options); +} + +function mainAppProcesses(processes) { + return processes.filter( + (process) => + typeof process?.executable === "string" && + process.executable.endsWith("/AidenOnTheGo.app/AidenOnTheGo"), + ); +} + +function ensureProcessBoundary(coreDeviceId, temporaryRoot, repositoryRoot) { + const beforePath = join(temporaryRoot, "processes-before-relaunch.json"); + const running = mainAppProcesses(readAidenProcesses(coreDeviceId, beforePath, repositoryRoot)); + if (running.length > 1) throw new Error("more than one Aiden test-host process is running"); + if (running.length === 1) { + const pid = running[0].processIdentifier; + if (!Number.isSafeInteger(pid) || pid <= 0) throw new Error("Aiden test-host PID is invalid"); + execute( + "xcrun", + ["devicectl", "device", "process", "terminate", "--device", coreDeviceId, "--pid", String(pid), "--quiet"], + { cwd: repositoryRoot, capture: true }, + ); + } + + const afterPath = join(temporaryRoot, "processes-after-termination.json"); + const remaining = mainAppProcesses(readAidenProcesses(coreDeviceId, afterPath, repositoryRoot)); + if (remaining.length !== 0) throw new Error("Aiden test host did not terminate before reconciliation"); +} + +function moveProofToTrash(temporaryRoot) { + const trashRoot = join(homedir(), ".Trash"); + mkdirSync(trashRoot, { recursive: true }); + const baseName = temporaryRoot.split("/").at(-1); + let destination = join(trashRoot, baseName); + if (existsSync(destination)) destination = `${destination}-${Date.now()}`; + renameSync(temporaryRoot, destination); + return destination; +} + +export function runActivityProcessProof(options) { + const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); + const temporaryRoot = mkdtempSync(join(tmpdir(), "aiden-activity-process-proof.")); + const destination = physicalDestination(options.xcodeDeviceId); + const proofId = `process-proof-${randomUUID().toUpperCase()}`; + let xctestrun; + let startAttempted = false; + let destinationArtifactsConfigured = false; + let completed = false; + + try { + const device = validateSelectedDevice( + options, + join(temporaryRoot, "physical-device-details.json"), + repositoryRoot, + ); + process.stdout.write(`Physical destination: ${device.marketingName ?? device.udid}.\n`); + execute( + "xcodebuild", + [ + "-project", + join(repositoryRoot, "ios", "AidenOnTheGo.xcodeproj"), + "-scheme", + "AidenOnTheGo", + "-configuration", + "Debug", + "-destination", + destination, + "-derivedDataPath", + temporaryRoot, + "build-for-testing", + "-quiet", + ], + { cwd: repositoryRoot }, + ); + xctestrun = findFile(join(temporaryRoot, "Build", "Products"), ".xctestrun"); + if (!xctestrun) throw new Error("build-for-testing did not produce an .xctestrun file"); + + plistBuddy( + xctestrun, + `Add ${TEST_TARGET_PATH}:EnvironmentVariables:AIDEN_ACTIVITYKIT_PROCESS_PROOF_ID string ${proofId}`, + ); + plistBuddy( + xctestrun, + `Add ${TEST_TARGET_PATH}:EnvironmentVariables:AIDEN_ACTIVITYKIT_PROCESS_PHASE string start`, + ); + startAttempted = true; + runSelectedPhase({ + xctestrun, + destination, + resultBundlePath: join(temporaryRoot, "start.xcresult"), + repositoryRoot, + }); + ensureProcessBoundary(options.coreDeviceId, temporaryRoot, repositoryRoot); + + setPhase(xctestrun, "reconcile"); + configureDestinationArtifacts(xctestrun); + destinationArtifactsConfigured = true; + runSelectedPhase({ + xctestrun, + destination, + resultBundlePath: join(temporaryRoot, "reconcile.xcresult"), + repositoryRoot, + }); + completed = true; + process.stdout.write(`Aiden ActivityKit process proof passed for ${proofId}.\n`); + } finally { + if (startAttempted && !completed && xctestrun) { + try { + setPhase(xctestrun, "cleanup"); + if (!destinationArtifactsConfigured) configureDestinationArtifacts(xctestrun); + runSelectedPhase({ + xctestrun, + destination, + resultBundlePath: join(temporaryRoot, "cleanup.xcresult"), + repositoryRoot, + }); + } catch (cleanupError) { + assert(cleanupError instanceof Error); + process.stderr.write(`Aiden ActivityKit cleanup warning: ${cleanupError.message}\n`); + } + } + const trashPath = moveProofToTrash(temporaryRoot); + process.stdout.write(`Proof artifacts moved to ${trashPath}.\n`); + } +} + +export function main(argv = process.argv.slice(2)) { + try { + runActivityProcessProof(parseActivityProcessProofOptions(argv)); + } catch (error) { + assert(error instanceof Error); + process.stderr.write(`Aiden ActivityKit process proof: ${error.message}\n`); + process.exitCode = 1; + } +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) main(); diff --git a/scripts/ios-live-activity-process-proof.test.mjs b/scripts/ios-live-activity-process-proof.test.mjs new file mode 100644 index 00000000..2ab47063 --- /dev/null +++ b/scripts/ios-live-activity-process-proof.test.mjs @@ -0,0 +1,102 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { + destinationArtifactPlistCommands, + parseActivityProcessProofOptions, + physicalDestination, + validatePhysicalDeviceDetails, +} from "./ios-live-activity-process-proof.mjs"; + +const XCODE_DEVICE_ID = "00008110-00063CD91E98801E"; +const CORE_DEVICE_ID = "826D57EC-4BCD-5BC0-9A31-AFB4147611F1"; + +test("physical ActivityKit proof requires exact physical-device identifiers", () => { + assert.deepEqual( + parseActivityProcessProofOptions([], { + AIDEN_IOS_XCODE_DEVICE_ID: XCODE_DEVICE_ID, + AIDEN_IOS_CORE_DEVICE_ID: CORE_DEVICE_ID, + }), + { xcodeDeviceId: XCODE_DEVICE_ID, coreDeviceId: CORE_DEVICE_ID }, + ); + assert.throws(() => parseActivityProcessProofOptions([], {}), /exact physical --xcode-device-id/u); + assert.throws( + () => + parseActivityProcessProofOptions([ + "--xcode-device-id", + "SIMULATOR-UUID", + "--core-device-id", + CORE_DEVICE_ID, + ]), + /exact physical --xcode-device-id/u, + ); + assert.throws( + () => + parseActivityProcessProofOptions([ + "--xcode-device-id", + XCODE_DEVICE_ID, + "--core-device-id", + CORE_DEVICE_ID, + "--unknown", + "value", + ]), + /unknown option/u, + ); +}); + +test("physical destination never selects an iOS Simulator", () => { + assert.equal(physicalDestination(XCODE_DEVICE_ID), `platform=iOS,id=${XCODE_DEVICE_ID}`); + assert.doesNotMatch(physicalDestination(XCODE_DEVICE_ID), /Simulator/u); +}); + +test("CoreDevice details must match the Xcode UDID and report physical iOS hardware", () => { + const options = { xcodeDeviceId: XCODE_DEVICE_ID, coreDeviceId: CORE_DEVICE_ID }; + const payload = { + info: { outcome: "success" }, + result: { + hardwareProperties: { + udid: XCODE_DEVICE_ID, + platform: "iOS", + reality: "physical", + marketingName: "iPhone 13 Pro", + }, + }, + }; + assert.deepEqual(validatePhysicalDeviceDetails(payload, options), { + udid: XCODE_DEVICE_ID, + platform: "iOS", + reality: "physical", + marketingName: "iPhone 13 Pro", + }); + assert.throws( + () => + validatePhysicalDeviceDetails( + { + ...payload, + result: { hardwareProperties: { ...payload.result.hardwareProperties, reality: "simulated" } }, + }, + options, + ), + /physical iOS device/u, + ); + assert.throws( + () => + validatePhysicalDeviceDetails( + { + ...payload, + result: { hardwareProperties: { ...payload.result.hardwareProperties, udid: "00008110-OTHER" } }, + }, + options, + ), + /do not identify the same device/u, + ); +}); + +test("relaunch phase reuses destination artifacts instead of reinstalling the app", () => { + const commands = destinationArtifactPlistCommands(); + assert(commands.includes("Add :TestConfigurations:0:TestTargets:0:UseDestinationArtifacts bool true")); + assert(commands.some((command) => command.includes("TestBundleDestinationRelativePath"))); + assert(commands.includes("Delete :TestConfigurations:0:TestTargets:0:TestBundlePath")); + assert(commands.includes("Delete :TestConfigurations:0:TestTargets:0:TestHostPath")); + assert(commands.includes("Delete :TestConfigurations:0:TestTargets:0:DependentProductPaths")); +}); From 0241dbdfd8cf130b5445114b311d930f919e35eb Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Wed, 19 Aug 2026 22:32:51 -0400 Subject: [PATCH 004/266] docs(ios): document remote architecture and rollout Record the Aiden Remote API, threat model, phased Aiden On The Go implementation and verification history, TestFlight status, and remaining release work. Remove the obsolete CLAUDE.md instructions while retaining the active migration plan and required attribution history. --- .papercuts/troubleshooting.md | 34 + CLAUDE.md | 23 - README.md | 3 + docs/aiden-on-the-go-remote-access.md | 52 ++ docs/aiden-remote-api-v1.md | 245 +++++++ docs/plans/README.md | 2 +- docs/plans/aiden-assistant-plan.md | 2 +- docs/plans/aiden-on-the-go-plan.md | 712 +++++++++++++++++++++ docs/security/aiden-remote-threat-model.md | 124 ++++ docs/testing/aiden-on-the-go/phase-0.md | 130 ++++ docs/testing/aiden-on-the-go/phase-1.md | 42 ++ docs/testing/aiden-on-the-go/phase-10.md | 25 + docs/testing/aiden-on-the-go/phase-11.md | 27 + docs/testing/aiden-on-the-go/phase-12.md | 96 +++ docs/testing/aiden-on-the-go/phase-2.md | 52 ++ docs/testing/aiden-on-the-go/phase-3.md | 47 ++ docs/testing/aiden-on-the-go/phase-4.md | 48 ++ docs/testing/aiden-on-the-go/phase-5.md | 10 + docs/testing/aiden-on-the-go/phase-6.md | 12 + docs/testing/aiden-on-the-go/phase-7.md | 23 + docs/testing/aiden-on-the-go/phase-8.md | 18 + docs/testing/aiden-on-the-go/phase-9.md | 20 + 22 files changed, 1722 insertions(+), 25 deletions(-) delete mode 100644 CLAUDE.md create mode 100644 docs/aiden-on-the-go-remote-access.md create mode 100644 docs/aiden-remote-api-v1.md create mode 100644 docs/plans/aiden-on-the-go-plan.md create mode 100644 docs/security/aiden-remote-threat-model.md create mode 100644 docs/testing/aiden-on-the-go/phase-0.md create mode 100644 docs/testing/aiden-on-the-go/phase-1.md create mode 100644 docs/testing/aiden-on-the-go/phase-10.md create mode 100644 docs/testing/aiden-on-the-go/phase-11.md create mode 100644 docs/testing/aiden-on-the-go/phase-12.md create mode 100644 docs/testing/aiden-on-the-go/phase-2.md create mode 100644 docs/testing/aiden-on-the-go/phase-3.md create mode 100644 docs/testing/aiden-on-the-go/phase-4.md create mode 100644 docs/testing/aiden-on-the-go/phase-5.md create mode 100644 docs/testing/aiden-on-the-go/phase-6.md create mode 100644 docs/testing/aiden-on-the-go/phase-7.md create mode 100644 docs/testing/aiden-on-the-go/phase-8.md create mode 100644 docs/testing/aiden-on-the-go/phase-9.md diff --git a/.papercuts/troubleshooting.md b/.papercuts/troubleshooting.md index 3efec01a..bc08ab14 100644 --- a/.papercuts/troubleshooting.md +++ b/.papercuts/troubleshooting.md @@ -24,3 +24,37 @@ - "Safer" compaction checks can be compatibility bugs. Before changing Aiden's Pi adapter, pin the exact upstream commit, hash the relevant sources, and mirror its tests and acceptance behavior—including empty/length summaries and usage-anchor skips—instead of preserving stricter local validators that upstream does not have. - An Electron E2E teardown deadline must exceed the app's sequential bounded shutdown phases. A 10-second fixture timeout can kill and report a healthy process while foreground, subagent, and packaged-soak drains are still inside their documented 6s + 5s + 5s ceilings. - GitHub release create/edit requests can return HTTP 503 after committing server-side state. Publication must re-read the exact tag, target SHA, draft state, and asset set before retrying or reconciling; never treat an unavailable lookup as a missing release. +- A physical XCTest transport spike can keep secrets out of the project and scheme: use a private temporary Derived Data directory, create an injected `.xctestrun` copy beside its `Build/Products` payload, inject an ephemeral canonical pairing-bootstrap JSON into that copy, then use `test-without-building`. Xcode still requires the physical device to remain unlocked through preflight and launch. +- A copied `.xctestrun` resolves `__TESTROOT__` relative to its own location. Keep the injected copy beside `Build/Products` (or deliberately rewrite every relative product path), and derive the advertised LAN address from the default-route interface instead of assuming Wi-Fi is `en0`; otherwise Xcode reports a missing test product or the phone silently times out against a link-local adapter. +- Simulator networking does not prove iOS Local Network privacy readiness. A direct physical LAN request fails as `Local network prohibited` when the host app omits `NSLocalNetworkUsageDescription`; lock both that key and the canonical `NSBonjourServices` value with an XCTest that inspects the built application bundle. +- A newly configured Tailscale Serve HTTPS handler can accept TCP before its tailnet certificate is locally available. Verify/request the node certificate through Tailscale's own CLI, retry the exact path-scoped Serve URL, then remove only that path with matching `--https` and `--set-path ... off`; never use `serve reset` as cleanup. +- Before the first handler exists, `tailscale serve status --json` can be `{}` even when tailnet HTTPS is enabled. Validate the exact normalized node DNS name against `tailscale status --json`'s certificate domains; do not require a pre-existing `TCP.443.HTTPS` listener, and still reject an explicitly incompatible 443 listener. +- Tailscale `--set-path` strips the mounted public prefix before reverse proxying. An Aiden `/api/aiden/v1` mount must target the loopback origin plus the exact same canonical API base, not the origin root; verify `/api/aiden/v1/health` through the real tailnet before claiming transport acceptance. +- `PlistBuddy Add ... string ` can strip JSON quoting when injecting a physical-test pairing payload. Use `plutil -replace ... -string`, compare the injected value's byte count/digest without printing it, and make every secret-bearing xctestrun and payload file owner-only. +- A self-signed `CA:FALSE` TLS leaf cannot be used as an Apple Security trust anchor, and an overlong server-leaf lifetime can fail Apple SSL policy even under a private anchor. Generate an installation-local CA, sign a short-lived `CA:FALSE`/server-auth leaf, present the full chain, anchor only the CA, and pin the leaf SPKI. +- A newly connected physical iPhone can remain an ineligible Xcode destination after pairing until Developer Mode is enabled, the reboot confirmation is accepted, and the phone is unlocked again. Re-read the CoreDevice/Xcode destination list before rebuilding; stale destination errors do not imply a signing failure. +- Foundation and JavaScript do not resolve duplicate JSON object keys the same way. For cross-platform security envelopes, scan raw UTF-8 JSON and reject duplicate keys—including escaped-equivalent names—before either `JSONDecoder` or `JSON.parse`; validating only the decoded object is too late. +- Cross-platform `maxLength` and date parsing need executable shared vectors: Swift `String.count` measures grapheme clusters while OpenAPI/JavaScript limits are Unicode-code-point based, and `ISO8601DateFormatter` accepts forms a strict RFC 3339 parser rejects. Use Unicode scalars for wire bounds and validate the complete timestamp grammar/calendar before constructing `Date`. +- A TTL alone is unsafe when idempotency state is pruned using wall time: after a forward jump and persisted prune, a rollback can make the same key look reusable. Persist a last-observed clock high-water mark with the ledger snapshot and fail closed for new keys until wall time advances beyond it; unresolved in-flight entries still never expire locally. +- Do not retain an unversioned idempotency-array migration path after adding a persisted clock high-water mark: even an empty legacy array is ambiguous and can reopen a pruned operation after rollback. Use an omitted snapshot only for a genuinely fresh ledger and reject every persisted shape that cannot carry the high-water value. +- WHATWG `URL` parsing removes raw and percent-encoded dot segments before exposing `pathname`. Security-sensitive canonical endpoint checks must compare the exact raw path before constructing `URL`, then apply the normal scheme, authority, query, fragment, and normalized-path checks. +- URL libraries also normalize authority bytes: controls may be stripped, Unicode hosts may become punycode, numeric hosts may change form, and padded ports lose their spelling. Cross-platform pairing identity needs a shared conservative raw authority grammar before either Foundation or WHATWG parsing, with the original endpoint string retained for equality checks. +- A `structuredClone`-safe value is not necessarily durable JSON: nested `undefined`, non-finite numbers, `-0`, sparse arrays, accessors, cycles, and nonplain objects can disappear or change. Idempotency results must be recursively validated/cloned into an exact JSON value and tested through `snapshot -> JSON.stringify/parse -> restore -> replay`. +- Entry-count limits alone do not bound durable state. Apply recursive shape limits plus per-result and aggregate serialized-byte budgets before accepting terminal replay data, and give live operation-owner registries an owner-checked terminal release path so the capacity ceiling does not become permanent exhaustion. +- Rork `asc` enables pseudonymous command telemetry by default; use `ASC_TELEMETRY_DISABLED=1` for Aiden operations and `--strict-auth` so stored profiles cannot silently mix with environment credentials. A zero-result app query is only evidence for the active key's scope, not proof of account-wide absence; check product-owned public TestFlight links and reconcile them through the correct account before creating a record. Initial app creation is now `asc web apps create`, requires an authenticated Apple web session, and must remain an explicit owner action. +- `NavigationSplitView` selection can highlight a row without pushing detail on a compact iPhone. Branch explicitly by horizontal size class: use a value-driven `NavigationStack` on compact layouts, retain the split view on regular layouts, and reconcile both selection and path across CRUD and size-class changes. +- Never convert a post-action serialization or snapshot-budget failure into a TTL-bound rejection. The external mutation may already have happened; retain its stable operation reference as an unexpiring unknown/in-flight record until authoritative reconciliation proves a terminal outcome. +- Dependency-injected application services must keep Electron-backed singleton imports type-only. Bind real stores, logging, and platform services in a separate `*-main.ts` module; otherwise focused Node tests load Electron before the pure service can be exercised. +- Local Swift caches should use their own symmetric encoder/decoder rather than the stricter network RFC 3339 decoder. A plain `JSONEncoder` persists `Date` numerically by default, so decoding that file with the wire decoder fails even though the cache is valid. +- Canonical workspace paths can differ textually on macOS (`/var` versus `/private/var`). Managed-worktree authorization must compare the shared realpath-resolved environment identity, not a raw temporary-directory spelling. +- macOS LibreSSL does not support OpenSSL's `-copy_extensions` certificate flag. For an installation-local server leaf, write a bounded temporary extension file with the reviewed SAN/key-usage values and pass it through `-extfile` instead of assuming GNU/OpenSSL CLI parity. +- A private LAN CA cannot satisfy normal iOS server trust from an SPKI fingerprint alone. Keep the leaf pin and carry the installation CA certificate in the locally displayed, versioned pairing envelope so the client can anchor that exact CA before applying hostname, validity, usage, and pin checks. +- A folder-browser selection is not safe merely because its opaque nonce is one-use. Revalidate the approved-root policy, canonical directory identity, and duplicate-workspace state inside the same serialized application-service commit that persists registration; otherwise a root removal or filesystem replacement can win between token consumption and save. +- Durable idempotency must persist the in-flight admission before invoking a workspace mutation and persist the terminal result afterward. A crash between those writes should fail closed as an unknown/in-flight operation rather than allow the same key to execute twice. +- A committed chat append and a started generation are separate durability boundaries. If provider setup fails after the append, return the accepted message with a terminal error stream; if append persistence has an indeterminate outcome, keep the idempotency entry in flight until authoritative reconciliation rather than allowing a duplicate prompt. +- Resumable token streams can produce many events faster than durable storage should be written. Coalesce journal snapshots while preserving monotonic in-memory sequence order, settle the latest snapshot during quit, and close only the revoked device's live responses; an SSE disconnect alone must not cancel server-owned generation. +- An Xcode App Intents localization file reference is not a resource by itself. Adding a missing `AppShortcuts.xcstrings` reference to Copy Bundle Resources makes metadata extraction fail at build-input validation; either provide the real catalog or leave the absent reference out of the shipping resources so extraction and shortcut training use the declared intent phrases. +- After an iOS target rewrite, imported source files can remain in the project navigator without belonging to the shipping target. Verify the active `PBXSourcesBuildPhase` before trusting or testing a configuration/UI file, and make CI compile the renamed scheme for generic hardware when simulator use is prohibited. +- Xcode can also retain unlinked Swift package references and stale `Package.resolved` pins after an imported target is narrowed. Audit the actual target dependency graph, remove unused project package/product references, then keep the resolved pins and bundled third-party notices under the same regression gate. +- `await Activity.update` does not guarantee `activity.content` has already advanced. A Live Activity manager that reduces the next rapid event from that public rendered snapshot can lose semantic transitions. Keep canonical state actor-isolated in the app process, hydrate only when adopting a persisted activity, and test rapid updates on physical hardware before immediate cleanup. +- Separate `xcodebuild test-without-building` invocations normally reinstall an app-hosted XCTest bundle, which removes its Live Activities and invalidates a relaunch-persistence proof. Build and install once, then set `UseDestinationArtifacts` with the destination-relative test bundle for the second phase; verify the first host is gone, keep a cleanup phase, and validate that the CoreDevice UUID and Xcode UDID describe the same physical device. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index a057eafe..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,23 +0,0 @@ -# Aiden Agent - -Aiden Agent is a privately owned Electron application. Its only source repository is `https://github.com/sambitcreate/aiden-agent`. - -## Project memory - -The `.memory/` folder contains context and history from previous work on this project. Read the relevant files there before making changes, and keep them updated when work changes the implementation, architecture, decisions, or status. - -## UI design references - -Before adding or materially restyling any UI element or component, always review both `docs/chatgpt-desktop-ui-inspiration.md` and `docs/chatgpt-ui-element-specimen.html` for interaction, styling, state, motion, and accessibility inspiration. Adapt the references to Aiden's existing visual language rather than copying them blindly, and use the semantic design tokens in `renderer/styles.css` and `renderer/shared/appearance.ts` instead of introducing one-off colors. - -## Release model metadata - -`npm run models:refresh` is the explicit development refresh, and `npm run dist` invokes the same release step before packaging. Those are the only paths that may contact models.dev. Never add a models.dev call to normal development, unpacked builds, or ordinary live-app reads. Artificial Analysis data and credentials must never be bundled: the live Electron app may contact its fixed Free endpoint only after the user explicitly chooses Connect & fetch or Fetch latest with their own key, then reads the normalized device-local cache offline. - -## Papercuts - -For complex workflows, record concise implementation friction in `.papercuts/troubleshooting.md` as it occurs. - -## Tests - -When adding a feature or changing behavior, layout, configuration, or contracts, always check whether existing tests need updating and add or extend tests when coverage is missing. Run the relevant suites before finishing (`npm run test`, or the narrower scripts in `package.json` when the change is scoped). If a new test file is added, register it in the appropriate `package.json` test script so CI picks it up. diff --git a/README.md b/README.md index f9001e54..175bfb7c 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ I don't come from a coding background. I'd been bouncing between the coding agen - **Terminal, Git, and review** - keep a terminal drawer beside the conversation, inspect files and diffs in Environment, edit with dirty-file protection, compare branches, commit or push checked snapshots, and open the workspace in a discovered external editor. - **macOS integration and appearance** - native menus, **Keychain**, **Parakeet**, the dictation pill, Apple **Foundation Models**, the signed **Rust** Computer Use broker, semantic themes, high contrast, reduced motion, and consistent light/dark rendering. - **Extensibility and background work** - use skills, **MCP**, **Exa** search, scheduled tasks, voice, and attachments through typed, allowlisted boundaries. +- **Aiden On The Go** - opt in to a pinned local-network connection or an explicit non-Funnel Tailscale Serve route, pair each iPhone or iPad separately, and revoke devices from [Remote Access settings](docs/aiden-on-the-go-remote-access.md). - **Updates and release safety** - signed builds use the verified GitHub release feed. Once an update is downloaded, Aiden shows the version above Profile with **Later** and **Restart now**, then follows the normal save and shutdown guards before relaunching. ## Upcoming @@ -86,6 +87,8 @@ npm install npm run dev ``` +The native Aiden On The Go iPhone and iPad client lives in [`ios/`](ios/README.md). It is not part of the Electron development command; open `ios/AidenOnTheGo.xcodeproj` or use the documented physical-device `xcodebuild` commands separately. + The development launcher prepares a cached, ad-hoc-signed **Aiden Agent Dev** runtime that can run beside the installed **Aiden Agent** app. Development uses separate Application Support, Chromium session, log, crash, and `~/.aiden-dev` roots; it does not copy production data, register global shortcuts, or check the production update feed by default. Set `AIDEN_DEV_GLOBAL_SHORTCUTS=1` only when a development run intentionally needs the global bindings. Native builds discover the newest compatible full Xcode without changing the machine-wide `xcode-select` setting; `DEVELOPER_DIR` remains available as a per-command override. diff --git a/docs/aiden-on-the-go-remote-access.md b/docs/aiden-on-the-go-remote-access.md new file mode 100644 index 00000000..c8444f0b --- /dev/null +++ b/docs/aiden-on-the-go-remote-access.md @@ -0,0 +1,52 @@ +# Aiden On The Go remote access + +Aiden Agent can expose a small authenticated API to Aiden On The Go on iPhone and iPad. Remote Access is off by default. Aiden must remain running on the Mac, although its window may be closed. + +## Local Network setup + +1. Open **Settings → Remote Access** in Aiden Agent. +2. Choose **Local Network** or **Local Network + Tailscale**. +3. Turn on **Enable Remote Access**. +4. Add only the folders the phone or iPad may explore. Selecting the entire home directory requires a second confirmation on the Mac; the filesystem root is never allowed. +5. Choose **Pair over Local Network** and scan the one-time QR code in Aiden On The Go. + +The Mac advertises `_aiden-agent._tcp` with Bonjour only while Local Network access is running. LAN traffic uses a per-install P-256 HTTPS identity. The QR contains the private CA trust anchor and the server public-key pin so the mobile client can validate the hostname, certificate chain, and pinned key. A certificate renewal keeps the server key; an identity-key change requires pairing again. + +## Tailscale setup + +Tailscale supplies reachability and network encryption, but Aiden still requires its own device credential on every request. + +1. Install Tailscale on the Mac and sign in to the intended tailnet. +2. Ensure HTTPS certificates are available for the tailnet. Aiden reports this prerequisite rather than enabling it silently. +3. In **Settings → Remote Access**, select **Tailscale** or **Local Network + Tailscale** and enable Remote Access. +4. Review the exact command-equivalent route preview, then choose **Connect**. +5. Pair with **Pair over Tailscale** after the stable `https://…ts.net/api/aiden/v1` address appears. + +Aiden owns only `/api/aiden/v1`, proxies it to the loopback-only HTTP listener's matching `/api/aiden/v1` base, and verifies the resulting route. The matching target base is required because Tailscale strips the public `--set-path` prefix before proxying. First-time connection works from an empty Serve configuration only after the node's exact Tailscale certificate domain proves HTTPS was already authorized. Aiden never enables Tailscale Funnel, never runs `tailscale serve reset`, never completes Tailscale authorization for you, and never changes unrelated Serve handlers. **Disconnect** removes only the exact route and target recorded by Aiden. A conflict is reported instead of being overwritten. + +## Devices, credentials, and revocation + +Each phone or iPad receives a separate random credential. Aiden persists only a fast lookup digest, a salted scrypt digest, and redacted device metadata—not the credential or pairing secret. Pairing QR codes expire after five minutes and work once. + +Use **Revoke** beside a paired device to invalidate it immediately. Revocation does not rotate model-provider credentials or affect other paired devices. Pair the device again to restore access. + +## Offline behavior + +Closing the Aiden window does not stop Remote Access. Quitting Aiden does. The mobile app may retain its bounded offline read cache, but it cannot start new work or mutate the Mac while Aiden is stopped or unreachable. Temporary connection loss does not grant broader access and does not make an invalid or revoked credential valid. + +## Mobile usage summary + +The **Usage** row on the Aiden On The Go home screen reads aggregate usage from the paired Mac. It reflects Aiden Agent's device-local usage store and sends only aggregate request, token, activity, and estimated-cost totals to the phone. Chat content, workspace paths, and provider credentials are not included. + +## Troubleshooting + +- **Remote Access says Off:** enable it locally in Aiden Settings. No listener or Bonjour advertisement exists while it is off. +- **Local device cannot find Aiden:** confirm both devices are on the same network, Local Network mode is selected, and local-network permission is enabled for Aiden On The Go. +- **Certificate or pin changed:** do not bypass the warning. Verify the Mac, revoke the old device record, and pair again. +- **Tailscale not found or disconnected:** open Tailscale on the Mac and confirm it reports a stable MagicDNS name. +- **Tailnet HTTPS unavailable:** complete Tailscale's HTTPS authorization flow, then retry Connect in Aiden. Aiden will not authorize it on your behalf. +- **Serve conflict:** inspect the route shown in the error. Remove or relocate the conflicting handler yourself; Aiden will not take it over. +- **A folder is missing:** add it from the Mac. The phone cannot submit an arbitrary path or approve a new browser root. +- **Port already in use:** stop the other local service or repair the saved Remote Access configuration before enabling it again. + +Remote Access diagnostics contain request IDs, route names, status codes, latency, and at most a device-ID suffix. They exclude bearer credentials, pairing secrets, provider keys, prompt bodies, and filesystem paths. diff --git a/docs/aiden-remote-api-v1.md b/docs/aiden-remote-api-v1.md new file mode 100644 index 00000000..89301213 --- /dev/null +++ b/docs/aiden-remote-api-v1.md @@ -0,0 +1,245 @@ +# Aiden Remote API v1 + +Status: Phase 0 normative contract +Base path: `/api/aiden/v1` +Transport: HTTPS REST plus resumable Server-Sent Events +Schema: `protocol/aiden-remote/v1/openapi.json` +Fixtures: `protocol/aiden-remote/v1/fixtures/contract.json` + +## 1. Contract rules + +This is Aiden's native remote protocol. It is not Hermes WebUI compatibility. Aiden Agent remains the execution, persistence, permission, filesystem, and provider authority. The remote transport adapts authenticated commands to shared main-process application services; it never calls Electron IPC handlers or impersonates a `WebContents` owner. + +The URL major version changes only for an incompatible wire break. Additive fields are forward compatible only where the schema marks an envelope extensible; new capability-gated endpoints/events do not require a new major version. Clients must ignore unknown fields on the extensible SSE envelope and unknown nonterminal SSE events. Known v1 payload and mutation DTOs remain allowlisted. Clients must fail closed and fetch an authoritative snapshot for an unknown terminal state, missing required identity, invalid sequence, or mutation-precondition mismatch. + +All timestamps are RFC 3339 UTC strings. IDs are opaque strings and must not encode filesystem paths, credentials, provider details, or user names. JSON request bodies use UTF-8 and reject duplicate keys, non-finite numbers, and unknown mutation fields. + +## 2. Authentication and capabilities + +`GET /health` is the only unauthenticated read. `POST /pairing/exchange` is available only while a local desktop pairing window is open and requires its single-use secret. Every other request, including SSE, requires: + +```http +Authorization: Bearer +Aiden-Protocol-Version: 1 +``` + +The desktop stores only a slow/strong digest of the credential plus device metadata. Revocation closes streams and rejects later requests. Credentials are installation-specific and must never be accepted by a different Aiden instance. + +Initial capability IDs: + +| Capability | Authority | +| --- | --- | +| `server:read` | Read instance/version/capability projection. | +| `chat:read` | Read projected chats and stream status. | +| `chat:write` | Create/rename/delete chats; start/cancel turns. | +| `approval:respond` | Resolve approvals owned by this device/stream. | +| `workspace:read` | Read workspace registry projection. | +| `workspace:browse` | Navigate locally approved directory roots. | +| `workspace:manage` | Create/update/unregister workspace records, including permission changes. Granted by default for the confirmed full-CRUD mobile product and disclosed during pairing. | +| `files:read` | Read the bounded file index and text documents. | +| `files:write` | Perform expected-version file writes. | +| `git:read` | Review/diff/compare/branch/worktree projection. | +| `git:write` | Confirmed commit/push/checkout/branch/worktree mutations. | +| `schedule:read` | Read scheduled tasks/settings/run history. | +| `schedule:write` | Confirmed task/settings mutations and run-now. | + +No capability can enable Computer Use, mint the reserved Assistant identity, select a hidden unattended mode, read provider/MCP credentials, execute a generic shell/Git command, or widen a workspace's tool authority. + +## 3. Error envelope + +Every non-2xx JSON response uses: + +```json +{ + "error": { + "code": "stable_machine_code", + "message": "Safe user-facing summary.", + "requestId": "req_opaque", + "retryable": false, + "details": {} + } +} +``` + +`details` is optional and endpoint-allowlisted. It never contains paths, credentials, request bodies, raw provider errors, raw Git output, or stack traces. + +Required stable codes include: + +- `invalid_request`, `payload_too_large`, `rate_limited` +- `authentication_required`, `credential_revoked`, `capability_denied` +- `pairing_closed`, `pairing_expired`, `pairing_already_used`, `server_identity_changed` +- `not_found`, `already_exists`, `revision_conflict`, `idempotency_conflict`, `idempotency_capacity`, `idempotency_in_flight` +- `workspace_unavailable`, `workspace_changing`, `permission_confirmation_required` +- `handle_invalid`, `handle_expired`, `handle_wrong_device`, `root_policy_changed`, `filesystem_identity_changed`, `path_outside_root`, `handle_capacity` +- `turn_already_active`, `stream_gone`, `approval_already_resolved`, `approval_expired` +- `operation_in_progress`, `operation_stale`, `git_capability_denied` +- `schedule_disabled`, `schedule_run_in_progress` +- `server_interrupted`, `internal_error` + +## 4. DTO allowlists + +### Server + +Allowed: instance ID/display name, app/protocol version, supported capabilities, selected connection mode, minimum client version, server time. + +Forbidden: local usernames, config/user-data paths, environment values, provider credentials, logs, process arguments. + +### Workspace + +Allowed: `id`, `name`, `permission`, `hasFolder`, `isManagedWorktree`, optional branch/repository display names, small Git summary, `createdAt`, `updatedAt`, `revision`. + +Forbidden: `folderPath`, repository/worktree/Git-admin paths, ownership token, device/inode identity, remote URL, created-from HEAD, config-store record. + +### Chat/message + +Allowed: IDs, workspace ID, visible title/provider/model selections, visible user/assistant messages, bounded attachments, safe reasoning/tool/timeline milestones, timestamps, terminal provider-failure category. + +Forbidden: Pi journals, raw diagnostics, raw tool arguments/results not already safe for renderer display, subagent private history, hidden prompts, credentials, filesystem internals. + +### File + +Allowed: opaque file handle, safe workspace-relative display path/name, kind, bounded size/language metadata, version, truncation/warning state, text content for a selected readable document. + +Forbidden: canonical/absolute path, symlink target, inode/device, recovery path, arbitrary binary bytes. + +### Git + +Allowed: safe review/diff/comparison fields, branch display names, counts, push capability/reason, managed-worktree display state, operation ID/status. + +Forbidden: absolute paths, Git admin path, ownership token, device/inode, raw command line/stdout/stderr, credential-bearing remote URL, private refs not present in the desktop projection. + +### Scheduled task + +Allowed: task ID/revision, safe name/description, workspace/provider/model IDs, schedule/timezone, mode/permission, selected MCP IDs, notification preference, validated script ID/display name, enabled/running state, next/previous dates, bounded redacted run result. + +Forbidden: provider fingerprint, resolved MCP bindings, chat ID, credentials, process environment, raw script path, unredacted stdout/stderr, internal cancellation handles. + +## 5. Endpoint inventory + +The OpenAPI document owns exact request/response shapes. This section owns behavior. + +### Bootstrap/device + +- The locally displayed QR encodes the OpenAPI `PairingBootstrap` object as canonical JSON: protocol version, instance ID, HTTPS API endpoint, P-256 SPKI SHA-256 fingerprint, high-entropy single-use secret, and expiry. The phone must decode and validate this object, configure hostname plus SPKI verification, and only then make its first network request. A fingerprint learned from `/pairing/exchange` is confirmation, never the trust bootstrap. +- `GET /health`: minimal readiness and protocol version. +- `POST /pairing/exchange`: exchange a high-entropy single-use secret for device/instance IDs, bearer credential, capability list, endpoint, and P-256 SPKI SHA-256 fingerprint. +- `GET /server`: authenticated server projection. + +Device enumeration/revocation remains desktop-local in v1 unless a later explicit capability is added. + +### Workspaces and approved-root browser + +- `GET /workspaces`, `GET /workspaces/{workspaceId}` +- `POST /workspaces`: `folderless`, `scratch`, or `selected-folder` with an idempotency key. +- `PATCH /workspaces/{workspaceId}`: revision-checked name/permission patch. Permission elevation requires explicit foreground confirmation evidence. +- `DELETE /workspaces/{workspaceId}`: revision-checked unregister only; never deletes the folder. +- `GET /workspace-browser/roots` +- `GET /workspace-browser/children?location=...&cursor=...` +- `POST /workspace-browser/selections` + +Approved roots are server-side records containing random root ID, canonical path, filesystem identity, policy revision, display label, and hidden/system policy. Nested roots are deduplicated/rejected. Home requires explicit local warning/confirmation; filesystem root is disabled by default and never remotely enabled. + +Location handles are random high-entropy nonces stored as digests and bound to instance/device/root/policy/canonical internal location/filesystem identity/expiry/depth. Cursors additionally bind the parent handle and ordering snapshot. Handle stores prune expired/consumed records, enforce a hard entry ceiling, and fail closed at capacity. Listings are deterministic, paginated, rate limited, directory-only, and nonrecursive. + +Selection nonces are a separate type. Workspace creation atomically revalidates real path, directory type, root boundary, policy revision, filesystem identity, and duplicate state, then consumes the nonce once inside one synchronous storage transaction. Async mutation callbacks are prohibited; an escaped promise fails closed with the nonce consumed. Reuse or browse-to-register replacement fails closed. Selected-folder workspaces default to `ask`. + +### Chats, turns, streams, approvals + +- `GET|POST /chats` +- `GET|PATCH|DELETE /chats/{chatId}` +- `POST /chats/{chatId}/move`: empty chat only. +- `GET /models`: configured provider/model projection without credentials. +- `GET /usage?range=7d|30d|90d|1y|all`: aggregate request, token, activity, and estimated-cost totals from the Mac's device-local usage store. Requires `server:read`; never returns content, chat/workspace identifiers, paths, raw usage records, or credentials. +- `POST /chats/{chatId}/turns`: atomic append/admit/start, idempotent by client key. +- `POST /chats/{chatId}/attachments`: validate and stage one bounded image or UTF-8 text attachment. +- `DELETE /chats/{chatId}/attachments/{attachmentId}`: discard an unused staged attachment. +- `GET /streams/{streamId}` +- `GET /streams/{streamId}/events`: SSE replay via `Last-Event-ID` or `after`. +- `POST /streams/{streamId}/cancel` +- `POST /approvals/{approvalId}/respond`: `allow` or `deny` only. + +Turn start returns `turnId`, `streamId`, accepted state, and canonical appended message. The generation owner is the authenticated device/stream, not a socket. Disconnect never resends the prompt or cancels the turn. Restart during an active remote turn records one explicit interrupted terminal state and never retries the provider call. + +### Attachments + +Uploads produce random, short-lived, single-use references bound to the authenticated device and exact chat. References expire after 10 minutes, are removed on device revocation, and are consumed atomically by a turn. A turn accepts at most 10 distinct references. The server retains at most 20 staged references per device/chat, 40 per device, 256 globally, and 64 MiB of staged representation data; capacity exhaustion fails closed. + +Image uploads accept only PNG or JPEG, at most 8 MiB decoded, at most 16,384 pixels on either axis, and at most 40 million decoded pixels. Text uploads accept only the documented plain-text/source MIME allowlist, at most 100,000 Unicode scalars and 400,000 UTF-8 bytes. Display names are bounded to 255 Unicode scalars and reject separators and control characters. Upload envelopes never accept a local or server path. Chat and message responses project attachment ID, display name, MIME type, kind, and size only; inline bytes and text are never returned. + +### Files and Git + +- `GET /workspaces/{workspaceId}/files`: one recursive snapshot with maximum 4,000 entries and depth 20 plus `truncated`. +- `GET|PUT /workspaces/{workspaceId}/files/{fileId}`: opaque file handle; write requires `expectedVersion`. +- Git review/diff/compare/comparison-diff/branches/checkout/create-branch/commit/push-capability/push/worktrees/create-worktree/delete-managed-worktree under `/workspaces/{workspaceId}/git/...`. + +File handles are separate from browser handles and bind instance/device/workspace/canonical root identity/relative file identity/index snapshot/expiry. Read/write re-resolves within the canonical root. Writes retain Aiden's atomic replacement and expected-version conflict behavior. There is no file create/rename/delete in v1. + +Git mutations reuse the workspace operation registry and mutation gate plus canonical common-directory serialization. Commit/push remain repository-root-only; nested workspaces expose a read/diff-only reason. Consequential actions carry an explicit foreground-confirmation field and return stable operation/snapshot IDs. Disconnect does not abandon an operation owner. There is no fetch, pull, stage/unstage, discard, generic Git, or terminal endpoint. + +### Scheduled tasks + +- `GET|POST /scheduled-tasks` +- `GET|PATCH|DELETE /scheduled-tasks/{taskId}` +- `POST /scheduled-tasks/{taskId}/pause|resume|run` +- `GET /scheduled-tasks/{taskId}/runs` +- `POST /scheduled-tasks/preview` +- `GET /scheduled-tasks/scripts?workspaceId=...` +- `GET|PATCH /scheduled-tasks/settings` + +Edits use `If-Match`/`expectedUpdatedAt`; settings use an equivalent revision. `run` requires an idempotency key and returns `202` with a durable `runId`. Socket loss does not cancel execution. Status is observed in run history. Cancellation follows existing remove/pause/global-disable/revocation/shutdown policy; v1 adds no bespoke remote cancel action. + +## 6. SSE envelope and ordering + +Every SSE `id` is the decimal sequence. `data` decodes as: + +```json +{ + "protocolVersion": 1, + "streamId": "stream_opaque", + "sequence": 12, + "timestamp": "2026-08-18T19:00:00.000Z", + "type": "text_delta", + "terminal": false, + "payload": { "text": "hello" } +} +``` + +Initial event types: + +- `snapshot`: authoritative projected turn/chat state and next sequence. +- `status`: `queued`, `running`, `waiting_for_approval`, or `reconciling`. +- `text_delta`, `reasoning_delta`. +- `tool_started`, `tool_finished`: safe name/status/milestone only. +- `timeline`: renderer-safe generation milestone. +- `approval_required`: approval ID, safe summary, deadline; no raw command/path/arguments. +- `done`: terminal persisted completion. +- `error`: terminal stable category and safe message. +- `cancelled`: terminal cancellation source. +- `heartbeat`: no semantic state change. + +The `terminal` bit is required. Known `done`, `error`, and `cancelled` events set it to `true`; every other known event sets it to `false`. An unknown nonterminal event is ignored for forward compatibility only after its required bounded payload object and envelope safety fields validate. An unknown terminal event fails closed and triggers authoritative reconciliation. Individual SSE frames are limited to 1 MiB before JSON decoding. Sequences are monotonically increasing and unique within a stream. Replay validates the caller's expected stream identity and includes only events after the acknowledged sequence. Duplicate/lower sequences are ignored. A stream mismatch or gap triggers snapshot/status reconciliation; it never triggers turn creation. Terminal events are immutable. Expired journals return `stream_gone` with the chat ID needed for snapshot recovery. + +## 7. Idempotency, revisions, and operation ownership + +- Create/start/run endpoints require `Idempotency-Key` (random client value, scoped to authenticated device + route + resource). The server durably stores only a bounded scope digest/request digest/state/safe-result record plus a non-secret stable operation reference; raw keys are never persisted. Fulfilled and safely classified rejected outcomes are replayable until expiry. In-flight entries never expire or evict into duplicate work and restore after restart as a fail-closed `idempotency_in_flight` state until the authoritative operation store finalizes that exact reference. Settlement starts the replay TTL; a long-running operation does not lose its replay window. Same key and same canonical request returns the original outcome; same key with different input returns `idempotency_conflict`. Capacity exhaustion fails closed rather than evicting any unexpired or in-flight record. +- Workspace, chat metadata, file, task, settings, and task pause/resume edits require server revision or content version. Stale mutation returns `revision_conflict` with current safe revision only. +- Turns and Git/file/schedule operations have stable owners independent of TCP. Revocation/shutdown/cancel policy is explicit per operation; socket close alone is never authority to mutate state. +- Approval response is idempotent by approval ID and decision. A conflicting second decision returns `approval_already_resolved`. + +## 8. Limits and logging + +The implementation must set explicit defaults and expose safe capability metadata for request bytes, JSON depth, attachment totals, active devices, streams, replay journal events/bytes/retention, browser pages/depth, file index bounds, text read/write bytes, diff bytes, schedule output bytes, rate limits, and timeouts. Ordinary JSON requests are limited to 1 MiB. Attachment uploads have a dedicated 12 MiB JSON-envelope limit. + +Logs may include request ID, route template, status, latency, instance/device ID suffix, and stable error code. Logs never include Authorization, pairing secrets, idempotency keys, opaque handles, request/response bodies, paths, prompts, message text, attachment data, tool arguments/results, provider errors, Git output, schedule output, or App Group/Keychain contents. + +## 9. Transport identity + +LAN transport uses an installation-local P-256 CA and a server-only leaf with a stable P-256 key. The server presents the leaf plus CA chain; the QR payload carries the HTTPS endpoint and `sha256/`. Certificate renewal keeps the leaf key and pin. Key rotation is explicit, invalidates the old pin, and requires recovery/re-pairing. The iOS trust path anchors only the presented local CA, validates hostname, validity, certificate signature, server-auth usage, and the expected leaf SPKI digest; a matching pin alone must not accept an expired or wrong-host certificate. + +When LAN access is enabled, discovery advertises `_aiden-agent._tcp` over Bonjour. Its TXT record may expose only `v=1` and the public Aiden instance identifier; the service port comes from Bonjour. Pairing secrets, device credentials, pins, paths, capability grants, and user content are never discovery metadata. The iOS app declares `_aiden-agent._tcp` in `NSBonjourServices` and provides `NSLocalNetworkUsageDescription`; disabling LAN access withdraws the advertisement. + +Tailscale mode uses the server-owned loopback Aiden HTTP listener behind Tailscale Serve HTTPS plus the same Aiden device credential. Because `--set-path=/api/aiden/v1` strips that public mount prefix before reverse proxying, the target must restore the exact canonical base: `http://localhost:/api/aiden/v1`, IPv4 loopback, or IPv6 loopback, with no credentials, additional path, query, or fragment. Before connect, inspect current config. An explicitly incompatible or Funnel-enabled HTTPS listener is a connect conflict. When Serve status is empty, first-connect eligibility comes from an exact normalized match between the node's stable DNS name and its Tailscale certificate domains; Aiden never completes Tailscale's HTTPS authorization flow for the user. Add one exact Aiden-owned non-Funnel route. Disconnect uses its matching route-specific `off`; never use Serve reset or replace unrelated configuration. A pre-acceptance origin-only target may be recognized solely for exact persisted-ownership cleanup before replacement with the canonical target; it is never accepted for a new connection. + +## 10. Contract change process + +Change this document, OpenAPI, TypeScript contract constants/types, shared fixtures, and Swift/TypeScript fixture tests in one phase. Additive changes increment `contractRevision`. Breaking changes require `/v2`. No handler may ship an undocumented route or field. diff --git a/docs/plans/README.md b/docs/plans/README.md index 375007c8..6b391123 100644 --- a/docs/plans/README.md +++ b/docs/plans/README.md @@ -7,6 +7,7 @@ This directory is the source of truth for Aiden's implementation plans. The engi | Plan | Status | Current state | | -------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Aiden Assistant](aiden-assistant-plan.md) | Partial | The dock, Markdown rendering, and confirmed provider-connection/model-pinned project-or-MCP automation creation/editing ship; settings tools and proactivity remain planned. | +| [Aiden On The Go](aiden-on-the-go-plan.md) | Active | Version 0.1.0 build 7 is `VALID` and `IN_BETA_TESTING`; the newer local Hermex-informed Markdown transcript fix is installed on the physical iPhone 13 Pro but intentionally not uploaded. Physical iPad/manual system-UI acceptance, privacy publication, final store assets, and external/public-release decisions remain open. | | [Compaction](compaction-plan.md) | Partial | Pi-native checkpoints, lifecycle/crash recovery, and exact audited-upstream compatibility ship; durable memory and provider-native paths remain open. | | [Designer Mode](designer-mode-plan.md) | Planned | Phase 0 validation has not started in the runtime. | | [Dynamic Model Catalog](dynamic-model-catalog-plan.md) | Partial | Stored Pi catalogs, cache-only hydration, and explicit provider refresh ship; remote overlays for otherwise-static providers remain open. | @@ -36,4 +37,3 @@ This directory is the source of truth for Aiden's implementation plans. The engi | [Pi Thinking Disclosure](completed/pi-thinking-disclosure-plan.md) | Complete | Provider-neutral readable Pi thinking, a one-second inspectable preview, and a durable local presentation toggle now match the audited Pi contract. | Move a plan to `completed/` only when its original delivery scope is complete. Keep the original plan as historical documentation; follow-on work belongs in a new active plan. - diff --git a/docs/plans/aiden-assistant-plan.md b/docs/plans/aiden-assistant-plan.md index 23b1f6d6..9844dbd0 100644 --- a/docs/plans/aiden-assistant-plan.md +++ b/docs/plans/aiden-assistant-plan.md @@ -52,7 +52,7 @@ Tailwind + semantic tokens in `renderer/styles.css`. equality_ between live broadcast sites and that list, so a missed entry fails CI. - Adding `"assistant:"` to `INVOKE_PREFIXES` fails the "every INVOKE_PREFIX has at least one live handler" test until a handler exists. Prefix and first handler land together. -- Every new test file is registered in a `package.json` test script (per `CLAUDE.md`). +- Every new test file is registered in the appropriate `package.json` test script so CI runs it. - UI work reviews `docs/chatgpt-desktop-ui-inspiration.md` and `docs/chatgpt-ui-element-specimen.html` first, and uses semantic tokens from `renderer/styles.css` / `renderer/shared/appearance.ts` — no one-off colors. diff --git a/docs/plans/aiden-on-the-go-plan.md b/docs/plans/aiden-on-the-go-plan.md new file mode 100644 index 00000000..25bb68b1 --- /dev/null +++ b/docs/plans/aiden-on-the-go-plan.md @@ -0,0 +1,712 @@ +# Aiden On The Go Plan + +Status: Active — Phases 0–4, 7, 9, 10, and 11 are complete; Phases 5/6/8 are implemented, LAN and real Tailscale are proven on a physical iPhone, and version 0.1.0 build 7 is `VALID` and `IN_BETA_TESTING` for Internal Testers; physical-iPad and external/public-release acceptance remain open +Date: 2026-08-18 +Owners: Aiden Electron main process and the SwiftUI app under `ios/` + +## Goal + +Turn the imported Hermex SwiftUI application into **Aiden On The Go**, a native iPhone and iPad client for the locally running Aiden Agent desktop application. + +The desktop app remains the execution authority. The mobile app connects over the local network or Tailscale, pairs to a specific Aiden installation, and controls Aiden through a narrow authenticated API. The complete product scope is deliberately limited to Aiden capabilities that have a real desktop equivalent: + +- Pairing and connection management. +- Aiden chats: list, open, create, rename, delete, compose, stream, cancel, and approve or deny tool requests. +- Aiden workspaces: list, create folderless or managed-scratch entries, explore approved folders on the paired Mac, register a selected folder, rename, change permission, and unregister. +- The provider/model choices required to start an Aiden chat, without exposing credentials. +- Aiden branding and the mobile-applicable Aiden appearance choices. +- Aiden workspace files, Git workflows, scheduled tasks, App Intents, Live Activities, and local voice controls, delivered after the core chat/workspace transport is stable. + +Kanban and every other Hermex screen without a deliberate Aiden contract are removed rather than left disconnected or filled with placeholder behavior. + +## Audited starting point + +### Aiden desktop + +Aiden does not currently expose an inbound application server. Its production control path is: + +```text +React renderer + -> sandboxed preload + -> allowlisted Electron IPC + -> Electron main process + -> chat/workspace services and Pi runtime +``` + +The existing chat notifications (`chat:delta`, `chat:reasoning-delta`, `chat:tool`, `chat:timeline`, `chat:approval`, `chat:done`, and `chat:error`) are delivered only to the Electron renderer that owns the generation. Workspace operations are likewise tied to a live renderer document. An HTTP handler must not impersonate or bypass that ownership model. + +Relevant sources: + +- `main/services/renderer-document-owner.ts` +- `main/services/chat-generation-owner.ts` +- `main/services/llm-client.ts` +- `main/handlers/chat.ts` +- `main/handlers/chats.ts` +- `main/handlers/workspaces.ts` +- `main/services/chat-store-core.ts` +- `main/services/config-store-core.ts` + +### Imported SwiftUI app + +Hermex already provides a strong native shell, iPhone/iPad navigation, REST client, resumable SSE client, chat transcript, reasoning/tool rendering, approvals, Keychain storage, offline cache, and workspace-registry manager. It is currently coupled to the unrelated Hermes WebUI endpoint and event vocabulary. + +Retain as foundations: + +- `ios/HermesMobile/ContentView.swift` +- `ios/HermesMobile/Features/SessionList/` +- `ios/HermesMobile/Features/Chat/` +- `ios/HermesMobile/Networking/APIClient.swift` +- `ios/HermesMobile/Networking/SSEClient.swift` +- `ios/HermesMobile/Features/Workspace/WorkspaceManagerView.swift` +- `ios/HermesMobile/Features/Workspace/WorkspaceRegistryViewModel.swift` +- `ios/HermesMobile/Persistence/` +- `ios/HermesMobile/Config/AppTheme.swift` +- `ios/HermesMobile/Features/Shared/AdaptiveGlassModifier.swift` + +The imported code remains MIT-licensed upstream work. Preserve its copyright and third-party notices while renaming and adapting it. + +## Product boundary + +### Ship in the first complete release + +1. Desktop Remote Access settings and lifecycle. +2. QR/one-time-code pairing, per-device credentials, device list, and revocation. +3. Local-network discovery plus manual host entry. +4. Tailscale-compatible connection through a stable HTTPS address. +5. Chat and workspace registry surfaces that represent Aiden's real models and rules. +6. Resumable streaming with tool activity, reasoning, terminal state, and allow/deny approvals. +7. Aiden light/dark styling, presets, icons, naming, and iPad layouts. +8. Offline display of previously fetched chats; mutations remain disabled while disconnected. +9. Normal workspace turns retain Aiden's server-side chat capabilities (including configured skills, MCP tools, and subagents where the existing permission model allows them) without adding separate mobile management screens. Computer Use remains unavailable remotely in the first release. +10. An approved-root Mac folder explorer for adding folder-backed workspaces without accepting free-form server paths. +11. Aiden's existing workspace file list/read/write and Git workflows, adapted to the retained Hermex native views. +12. Aiden scheduled-task list, create, edit, remove, pause, resume, run-now, preview, and run-history workflows. +13. App Intents, Live Activities, and local voice dictation/read-aloud behavior that never grants background authority beyond the paired device and workspace permission. + +### Remove from the mobile product + +- Kanban models, endpoints, event stream, views, tests, scripts, and navigation. +- Hermes Skills, Memory, Insights, and server-panel screens. +- Hermes projects and path-as-workspace identity. +- Hermes personalities/profiles and server-specific commands. +- Hermes goals, Btw/background controls, clarification flows, and other actions without an Aiden equivalent. +- Hermes Cloudflare onboarding, branding, URLs, copy, icons, and gold theme settings. +- Terminal and arbitrary shell controls. + +### Defer until the core client is stable + +- Share extension. +- Push notifications that require an external relay or cloud service. +- Voice-note upload and server TTS. The planned voice surface is local dictation into the composer plus optional on-device read-aloud. +- Computer Use controls. +- Subagent management UI. +- Chat copy/export, branching, pinning, archiving, and history truncation unless Aiden first ships matching service semantics. +- File create/rename/delete and Git/terminal actions without an existing Aiden desktop service contract. + +## Target architecture + +```text +Aiden On The Go (SwiftUI) + -> HTTPS REST for commands and snapshots + -> resumable SSE for generation events + -> device bearer credential from Keychain + +Aiden Agent (Electron main) + -> Remote API transport (off by default) + -> remote device/session ownership + -> shared chat/workspace application services + -> existing stores, mutation gates, llmClient, and Pi runtime +``` + +The API belongs to Aiden and is versioned at `/api/aiden/v1`. Do not permanently reproduce the entire Hermes `/api/*` protocol. Swift models and endpoint functions are converted to Aiden DTOs. Hermex code is reused for implementation patterns, not treated as the new protocol source of truth. + +## Remote API contract draft + +Before implementing handlers, add a checked-in protocol specification and JSON fixtures. The specification owns field names, nullability, limits, status codes, error codes, and event ordering. TypeScript and Swift tests consume the same fixtures. + +### Public bootstrap surface + +- `GET /health` + - Minimal availability and protocol version only. + - Must not expose paths, chats, providers, host usernames, or configuration. +- `POST /api/aiden/v1/pairing/exchange` + - Accepts a short-lived single-use pairing secret and client public metadata. + - Returns the Aiden instance ID, device ID, device credential, API capabilities, and server identity needed for pinning. + - Rate-limited; pairing must already be open in the desktop app. + +### Authenticated server/device surface + +- `GET /api/aiden/v1/server` + - Instance name, app/protocol version, capabilities, and connection mode. +- `GET /api/aiden/v1/devices` + - Desktop Settings only unless a later capability explicitly grants it remotely. +- `DELETE /api/aiden/v1/devices/:deviceId` + - Revokes the selected device and closes its streams. + +### Workspace registry + +- `GET /api/aiden/v1/workspaces` +- `GET /api/aiden/v1/workspaces/:workspaceId` +- `POST /api/aiden/v1/workspaces` +- `PATCH /api/aiden/v1/workspaces/:workspaceId` +- `DELETE /api/aiden/v1/workspaces/:workspaceId` +- `GET /api/aiden/v1/workspace-browser/roots` +- `GET /api/aiden/v1/workspace-browser/children?location=...&cursor=...` +- `POST /api/aiden/v1/workspace-browser/selections` + +Mobile workspace DTOs use Aiden IDs, names, permission (`full`, `ask`, `none`), folder-presence metadata, a small Git summary, and timestamps. Managed-worktree projection is explicitly allowlisted to display-only state such as `isManagedWorktree` and branch/repository display names. Absolute paths, worktree Git/admin paths, ownership tokens, device/inode values, remote URLs, and other config-store internals never cross the API. Absolute filesystem paths are not identifiers or writable request fields. + +The supported create modes are: + +- `folderless`: create a named Aiden context without a folder. +- `scratch`: ask Aiden to create and own a scratch directory. +- `selected-folder`: register a directory chosen through the server-controlled workspace browser. + +The phone never submits a free-form Mac path. Remote Access settings on the Mac own an explicit list of browsable roots, initially suggested from parents of already registered workspaces and extended only by a local desktop action. Each approved-root record stores a random root ID, canonical path, device/inode identity, policy revision, display label, and hidden/system-directory policy. Dedupe or reject nested roots. Broad roots such as the home directory require a specific local warning and confirmation; the filesystem root is disabled by default and cannot be enabled remotely. + +The browser returns friendly root labels, high-entropy opaque location tokens, bounded/paginated directory children, and breadcrumbs; it does not return file contents or recursively index a root. Store only token digests server-side and bind each handle to the Aiden instance, authenticated device, root ID/policy revision, canonical internal location, device/inode identity, expiry, and maximum depth. Never embed or log paths in client-visible tokens. Cursors bind to the exact root and parent-location token; listing has deterministic ordering, fixed maximum page/depth limits, hidden/system filtering, and per-device rate limits. + +Canonicalize every location with `realpath`, reject traversal and symlink escape, and re-check the approved-root boundary and device/inode identity after every navigation. A root, directory, or symlink replacement invalidates the handle and fails closed. + +`POST /workspace-browser/selections` exchanges the current location handle for a separate short-lived, single-use selection nonce bound to the authenticated device, root/policy revision, canonical directory identity, and expiration. `POST /workspaces` accepts that nonce rather than a path. Selection consumption and workspace creation are one atomic/idempotent operation: immediately before mutation, re-check `realpath`, directory type, root boundary, policy revision, device/inode identity, and duplicate-workspace state; then consume the nonce exactly once. Concurrent reuse, a policy change, or a browse-to-register filesystem race fails closed. The new workspace uses Aiden's existing default `ask` permission unless the server contract deliberately changes that default. Existing folder-backed workspaces can be read, renamed, permission-changed, and unregistered. Deleting a workspace record never deletes its folder. Managed-worktree deletion stays a separate Git operation with its own confirmation. + +Workspace mutation must reuse Aiden's existing cancellation, schedule-restoration, operation-registry, default-workspace, and safe-removal rules. A paired device receives `workspace:manage` by default to satisfy the confirmed full-CRUD behavior; pairing UI discloses that authority. Raising a workspace from `none`/`ask` to a stronger permission requires an explicit foreground confirmation and an audit event, but not a second Mac confirmation. Add authoritative workspace-created/updated/removed broadcasts for both Electron and mobile consumers; do not assume the current IPC handlers already broadcast, and do not carry over Hermex-only workspace reordering. + +### Chats + +- `GET /api/aiden/v1/chats?workspaceId=...` +- `POST /api/aiden/v1/chats` +- `GET /api/aiden/v1/chats/:chatId` +- `PATCH /api/aiden/v1/chats/:chatId` for supported metadata such as title +- `DELETE /api/aiden/v1/chats/:chatId` +- `POST /api/aiden/v1/chats/:chatId/move` only for an empty chat, matching Aiden's existing rule + +The mobile projection includes Aiden's renderer-safe chat fields only: IDs, workspace ID, provider/model IDs, timestamps, visible messages, safe reasoning, safe timeline/tool milestones, attachments, and closed provider-failure metadata. Private Pi journals, raw diagnostics, provider credentials, subagent private history, and filesystem internals never cross the API. + +Ordinary remote chat creation cannot mint Aiden's reserved Assistant workspace identity or unattended modes. Remote workspace turns follow the same main-owned capability composition and permission checks as an attended desktop workspace chat; the transport cannot request hidden modes or widen tool authority. + +### Provider/model discovery + +- `GET /api/aiden/v1/models` + +Return only connected/configured provider identities, selectable models, capabilities needed by the composer, thinking-level choices, and the server's current defaults. Never return provider tokens, base authentication headers, credential payloads, or private provider configuration. + +### Turns, streams, and approvals + +- `POST /api/aiden/v1/chats/:chatId/turns` + - Atomically validates the chat/workspace/provider/model, appends the user message, acquires the Aiden turn lease, and starts generation. + - Returns `turnId`, `streamId`, accepted state, and the canonical appended message. +- `GET /api/aiden/v1/streams/:streamId` + - Active/terminal status and authoritative chat ID. +- `GET /api/aiden/v1/streams/:streamId/events` + - SSE stream; supports `Last-Event-ID` and explicit `after` sequence. +- `POST /api/aiden/v1/streams/:streamId/cancel` +- `POST /api/aiden/v1/approvals/:approvalId/respond` + - First release accepts only `allow` or `deny`, matching Aiden's enforceable scope. + +Every event has a monotonically increasing sequence number, stream ID, event type, timestamp, and typed payload. Initial vocabulary: + +- `snapshot` +- `status` +- `text_delta` +- `reasoning_delta` +- `tool_started` +- `tool_finished` +- `timeline` +- `approval_required` +- `done` +- `error` +- `cancelled` +- `heartbeat` + +The client never resends a user message because an SSE connection dropped. It asks stream status, reconnects with its last sequence, and reconciles the final authoritative chat snapshot. + +### Attachments + +Aiden already models bounded inline image and text attachments. The remote API should use bounded multipart upload or a bounded JSON upload step that produces a short-lived attachment reference, then translate that reference into Aiden's existing attachment model during the atomic turn operation. + +Do not accept arbitrary server-side file paths from the phone. Enforce content type, byte size, decoded image size, text truncation, aggregate turn limits, expiry, and cleanup. Attachments are included only after the core text-turn path is stable. + +### Workspace files and Git + +Expose only operations already owned by Aiden's workspace and Git services: + +- `GET /api/aiden/v1/workspaces/:workspaceId/files` +- `GET /api/aiden/v1/workspaces/:workspaceId/files/:fileId` +- `PUT /api/aiden/v1/workspaces/:workspaceId/files/:fileId` +- `GET /api/aiden/v1/workspaces/:workspaceId/git/review` +- `POST /api/aiden/v1/workspaces/:workspaceId/git/diff` +- `GET /api/aiden/v1/workspaces/:workspaceId/git/branches` +- `POST /api/aiden/v1/workspaces/:workspaceId/git/checkout` +- `POST /api/aiden/v1/workspaces/:workspaceId/git/branches` +- `POST /api/aiden/v1/workspaces/:workspaceId/git/commit` +- `GET /api/aiden/v1/workspaces/:workspaceId/git/push-capability` +- `POST /api/aiden/v1/workspaces/:workspaceId/git/push` +- `POST /api/aiden/v1/workspaces/:workspaceId/git/compare` and `/comparison-diff` +- `GET|POST /api/aiden/v1/workspaces/:workspaceId/git/worktrees` +- `DELETE /api/aiden/v1/workspaces/:workspaceId/git/managed-worktree` + +Match Aiden's current file-index semantics: one bounded recursive snapshot, currently capped by the service at 4,000 entries and depth 20, with explicit `truncated` metadata. Do not silently pretend it is a complete tree or reuse workspace-browser location tokens. The index may display safe workspace-relative names, but read/write requests use separate high-entropy, server-side opaque file handles bound to instance, device, workspace, canonical root identity, relative file identity, expiry, and index snapshot. Every operation re-resolves the handle under the current canonical workspace root and fails on root/file replacement or escape. Writes require Aiden's `expectedVersion` value and return the authoritative document or a stable conflict result. The first release matches desktop file semantics—index, read, and version-checked atomic write—and does not invent file create, rename, or delete actions. + +Git DTOs use an explicit field allowlist for Aiden's review, bounded diff, comparison, branch, push-capability, and managed-worktree display results. They omit absolute paths, Git admin paths, credentials, remote URLs containing secrets, ownership tokens, device/inode values, head metadata, and raw command output. Managed-worktree deletion accepts only the persisted workspace ID and re-resolves server-owned metadata; it never accepts client-supplied filesystem/admin fields. + +Commit, push, checkout, branch creation, worktree creation, and managed-worktree deletion require an explicit foreground confirmation in the phone UI and must not be callable from App Intents. Commit and push retain Aiden's repository-root-only rule; nested workspaces surface the server's read/diff-only capability reason. Reuse Aiden's workspace operation registry, mutation gate, canonical Git-common-directory serialization, cancellation rules, worktree ownership validation, rollback, and schedule restoration. Network operations return stable `operationId`/snapshot metadata and stale-conflict errors; a TCP disconnect does not implicitly invalidate the authenticated remote operation owner. The remote transport does not expose fetch, pull, stage/unstage, discard, a generic Git command, or a shell endpoint because Aiden has no matching service contract. + +### Scheduled tasks + +Mirror Aiden's existing scheduled-task services rather than Hermes Cron payloads: + +- `GET|POST /api/aiden/v1/scheduled-tasks` +- `GET|PATCH|DELETE /api/aiden/v1/scheduled-tasks/:taskId` +- `POST /api/aiden/v1/scheduled-tasks/:taskId/pause` +- `POST /api/aiden/v1/scheduled-tasks/:taskId/resume` +- `POST /api/aiden/v1/scheduled-tasks/:taskId/run` +- `GET /api/aiden/v1/scheduled-tasks/:taskId/runs` +- `POST /api/aiden/v1/scheduled-tasks/preview` +- `GET /api/aiden/v1/scheduled-tasks/scripts?workspaceId=...` +- `GET /api/aiden/v1/scheduled-tasks/mcp-servers` +- `GET|PATCH /api/aiden/v1/scheduled-tasks/settings` + +Mobile supports list, create, edit, remove, pause, resume, run now, preview, and run history using Aiden's validated task schema, workspace/provider/model bindings, timezone rules, script inventory, global enable switch, permissions, notification preference, and selected MCP IDs. Remote task DTOs explicitly omit provider fingerprints, resolved MCP bindings, chat IDs, provider credentials, raw script paths, and other internal runtime metadata. Project run output/errors through a bounded, redacted display DTO because stored output can still contain paths or secrets. The client selects only server-inventoried script IDs; it never names a path. + +Creating or editing unattended work is a consequential foreground action with a final review screen. Edits require `If-Match`/`expectedUpdatedAt`, and settings mutations require an equivalent revision or serialized mutation gate, producing stable conflict responses for concurrent Electron/mobile changes. `POST .../:taskId/run` is an idempotent accepted operation: an idempotency key returns one `runId`, execution is owned durably by the schedule service, status is observed through run history, and a TCP disconnect does not cancel it. Cancellation follows the existing task remove/pause/global-disable/revocation/shutdown policy; do not add a bespoke mobile cancellation action unless Aiden desktop first exposes matching semantics. App Intents cannot create, edit, remove, enable, or run tasks in the first release. Remote mutations broadcast the same authoritative updates used by the Electron UI. + +## Shared desktop application services + +Do not call Electron IPC handlers from the HTTP layer. Extract or introduce main-process application services whose inputs are already parsed and whose ownership is explicit: + +- `ChatApplicationService` + - list/get/create/rename/delete/move-empty + - append-and-start as one transaction +- `WorkspaceApplicationService` + - list/get/create-folderless/create-scratch/create-from-selection/update/remove + - existing mutation and operation gates remain authoritative +- `WorkspaceBrowserService` + - approved roots, bounded directory navigation, opaque location/selection tokens, canonical-path revalidation +- `WorkspaceFileApplicationService` + - safe index/read/version-checked write over the existing file services +- `GitApplicationService` + - review/diff/compare/branches/commit/push/worktrees over the existing operation and mutation gates +- `ScheduledTaskApplicationService` + - validated list/save/remove/pause/resume/run/preview/history/settings operations and broadcasts +- `RemoteGenerationOwner` + - stable device/session identity independent of `WebContents` + - event journal and subscribers + - cancellation and approval ownership + - lifecycle invalidation on revocation or server shutdown +- `RemoteDeviceStore` + - instance identity, paired device records, credential hashes, capabilities, revocation, and last-seen metadata + +Electron IPC remains renderer-owned. Both IPC and the remote transport should call shared service operations where their semantics truly match; renderer-only actions keep their renderer requirements. + +## Stream durability and concurrency + +The remote owner is tied to a paired device and stream, not to one TCP connection. A temporary network loss must not cancel a turn. + +- Maintain a bounded per-stream sequence journal for replay. +- Persist enough terminal metadata to reconcile after app or network restart. +- Persist the assistant response through the existing chat store as the authority. +- If Aiden restarts during an active remote turn, close it with an explicit interrupted terminal state; never silently retry the model call. +- Expire terminal journals after a documented retention window while keeping chat history. +- A replayed approval retains one approval ID and deadline; duplicate responses are idempotent or return a stable already-resolved result. +- Chat deletion, workspace mutation, cancellation, provider failure, renderer activity, and multiple paired devices need explicit race tests. +- Remote-started chat metadata changes must use the existing desktop broadcasts so the Electron sidebar stays current. + +## Pairing, transport, and security + +### Desktop controls + +Add a Remote Access settings page using existing Aiden settings components and semantic tokens: + +- Master enable switch, off by default. +- Listen mode: Tailscale, local network, or both. +- Port/status and reachable addresses. +- Explicit Tailscale `Connect` / `Disconnect` controls with a preview of the exact Aiden-owned Serve route. +- Approved workspace-browser roots, editable only on the Mac. +- `Pair device` action that shows a QR code and short code with expiration. +- Paired device list with name, type, last seen, and Revoke. +- Clear warnings about remote workspace authority and what each workspace permission means. + +The service starts only after explicit enablement and valid configuration. It remains available while the Aiden process is running even when the main window is closed, and settles cleanly on app quit. + +The production QR payload carries the instance ID, selected HTTPS endpoint, short-lived high-entropy pairing secret, protocol version, and pinned server public-key fingerprint. A human-sized numeric code is not sufficient authentication on an untrusted LAN by itself: its fallback flow must use a reviewed PAKE/SAS-style exchange or a separate fingerprint confirmation. Do not silently downgrade QR pairing to numeric-code-plus-plain-HTTP. + +### Device credentials + +- Generate one random credential per device. +- Store only a strong credential digest and device metadata on desktop; protect sensitive local material with Aiden's existing safe-storage patterns. +- Store the issued credential in the iOS Keychain. +- Authenticate every REST request and SSE connection. +- Bind stream/approval ownership to the authenticated device. +- Issue explicit device capabilities for chat, workspace browsing/registry, files, Git, and schedules; reject a route when the credential lacks its capability. +- Support revocation and rotation without changing provider credentials. +- Redact credentials and pairing secrets from logs and errors. +- Apply request body limits, endpoint-specific rate limits, timeouts, and a small maximum number of clients/streams. +- Do not enable permissive browser CORS; this is a native-client API. + +### Tailscale + +Tailscale supplies reachability and network encryption, not application authorization. Aiden binds to loopback behind Tailscale Serve HTTPS. The confirmed product behavior is an explicit `Connect` / `Disconnect` flow: before changing anything, inspect the current configuration, show the exact non-Funnel Serve route and command-equivalent action, record ownership of only the route Aiden creates, and on disconnect use the matching route-specific `off` operation. Never invoke `tailscale serve reset`, replace the whole Serve config, or modify unrelated routes. If the existing Tailscale configuration conflicts, stop and explain the conflict instead of taking over. If tailnet HTTPS certificates are not enabled, explain the prerequisite and let the user complete Tailscale's own authorization flow instead of enabling it silently. The desktop UI shows the exact stable HTTPS URL used for pairing and must never enable Tailscale Funnel. + +### Local network + +- Advertise availability with Bonjour/mDNS only while local-network access is enabled. +- Discovery publishes instance identity and port, never a pairing secret or bearer token. +- Add the required iOS local-network usage description and Bonjour service declaration. +- Production LAN transport should use HTTPS with a per-install server identity pinned during QR pairing. A Phase 0 transport spike must prove certificate generation, renewal, pinning, and recovery before the API is exposed broadly. +- Pin the stable server public key rather than a short-lived certificate when feasible. Certificate renewal may retain that key; key rotation requires an explicit re-pair/recovery flow. +- Plain HTTP is limited to an explicit development configuration and must not be the production default. + +## SwiftUI conversion + +### Project identity + +Replace the imported identity throughout the main app, tests, configs, entitlements, localization, URL routes, and retained extensions: + +- Product/display name: `Aiden On The Go`. +- Swift/Xcode targets and schemes: rename from `HermesMobile` after the first green protocol slice so the rename is mechanically isolated. +- Apple development team: `5WP229CBB8`, matching the checked-in Contact Sheet Generator Xcode project. +- Signing: automatic, with a Phase 0 physical-device provisioning check for the main app, Live Activity widget, and App Group. +- Main bundle ID: `sbtbiswas.AidenOnTheGo`. +- Test bundle IDs: `sbtbiswas.AidenOnTheGoTests` and `sbtbiswas.AidenOnTheGoUITests`. +- Live Activity widget bundle ID: `sbtbiswas.AidenOnTheGo.LiveActivityWidget`. +- App Group: `group.sbtbiswas.AidenOnTheGo`. +- Keychain service: `sbtbiswas.AidenOnTheGo.pairing`. +- URL scheme: `aiden-otg`. +- App Store SKU: `aiden-on-the-go-ios`. +- New Aiden app icons and in-app brand assets; remove every Hermes/Hermex icon, banner, gold mask, and alternate icon. +- Rename Hermes-prefixed Swift types and files in bounded mechanical passes. +- Replace `ios/README.md`, `ios/PROJECT_SPEC.md`, and the Hermex-specific working agreement with Aiden-owned documentation after this plan is approved. Preserve required upstream attribution. + +The main target already declares iPhone and iPad support. Keep that device-family setting and validate real split-view behavior rather than treating iPad as a scaled iPhone. + +These values were derived from `/Users/sambitbiswas/projects/contactsheet/Contact-Sheet-Generator/ContactSheetGen.xcodeproj/project.pbxproj`, whose shipped targets use automatic signing, team `5WP229CBB8`, and the `sbtbiswas.*` namespace. The repository does not contain an App Store Connect SKU, App Group, or URL scheme, so the SKU/group/scheme above are new Aiden choices rather than copied metadata. The locally installed Apple Development certificate currently reports team `7EK65FX44E`; Phase 0 must refresh or select credentials that can provision the confirmed `5WP229CBB8` team before identity work proceeds. + +### Networking + +- Keep the generic URLSession and error-handling foundation. +- Replace cookie/password auth with pairing credential injection. +- Replace path-based Hermes endpoint definitions with Aiden `/api/aiden/v1` definitions. +- Replace Hermes session/workspace DTOs with tolerant Aiden DTOs. +- Keep the SSE parser/reconnect structure but decode the Aiden event vocabulary and sequence contract. +- Keep multi-server registry concepts only if they are reframed as multiple paired Aiden installations. +- Keep Keychain-backed per-server credentials and ensure credentials/cookies never cross between servers. +- Add Bonjour discovery and manual Tailscale URL entry. + +### App navigation + +The root shell contains only: + +- Chats +- Workspaces +- Scheduled Tasks +- Settings / paired Aiden installations + +Files and Git are workspace-scoped destinations opened from a workspace rather than new global tabs. On iPhone, use the retained navigation-stack patterns. On iPad, preserve `NavigationSplitView` with workspace/chat selection in the sidebar and the selected conversation in detail. Empty, disconnected, loading, and reconnecting states must work in compact and regular size classes. + +### Chat surface + +Retain Hermex's native components only where the Aiden contract supports them: + +- Transcript and Markdown rendering. +- User and assistant messages. +- Streaming text and safe reasoning. +- Compact tool/timeline activity. +- Allow/Deny approval overlay. +- Composer, send, stop, model/provider selection, thinking level, and bounded attachments. +- Offline read cache. + +Add a top-right ellipsis in the conversation toolbar. Its workspace-backed menu opens `Workspace Settings` and the other workspace-scoped destinations; the permission control lives inside that settings screen beside name, folder/managed-worktree status, Files, Git, and management links. It must not appear in the composer. The composer remains limited to message/voice input, attachments, model/thinking choices, send, and stop. Use the retained Hermex toolbar, menu, form, and sheet patterns rather than inventing a custom control. + +Remove Hermes goals, Btw/background mode, clarification UI, server TTS, server commands, profiles/personalities, and unsupported message actions. + +### Workspace surface + +Show Aiden workspaces by stable ID and name. Support: + +- List and refresh. +- Create folderless workspace. +- Create Aiden-managed scratch workspace. +- Explore approved folders on the paired Mac and register a selected folder. +- Rename. +- Change permission with the same `full`, `ask`, and `none` meaning as desktop, from `Workspace Settings` reached through the conversation-toolbar ellipsis or workspace management screen—not from the composer. +- Unregister with explicit confirmation and copy that the folder is not deleted. + +Build folder exploration from the existing Hermex list/navigation vocabulary: approved root picker, breadcrumb/back navigation, paginated directory rows, and a native `Use This Folder` action. Do not show an arbitrary absolute path field. A mobile mutation waits for server settlement and reconciles from the returned authoritative list. + +### Files and Git surfaces + +Retain and adapt `FileBrowserView`, `GitWorkspaceView`, `GitDiffView`, `GitCommitView`, `GitBranchPickerView`, and their existing native supporting components only where they map to Aiden's DTOs. Files support index, read, edit, save, conflict/reload, and offline read cache. Git supports repository review, diff, compare, branch switching/creation, commit, push when capability permits, worktree creation, and confirmed deletion of Aiden-managed worktrees. Consequential actions use native confirmation sheets and show authoritative success/error results. No terminal or arbitrary Git command field is introduced. + +### Scheduled Tasks surface + +Adapt the Hermex Tasks screen structure and native forms, but replace every Cron DTO, endpoint, label, and assumption with Aiden's scheduled-task model. Support list/filter, create/edit, pause/resume, remove, run now, previewed next runs, and run history. The editor exposes only server-projected provider/model/workspace, mode, permission, selected MCP, notification, timezone, schedule, prompt, and validated script choices. It must clearly label that enabled tasks can run while the phone is disconnected. + +### App Intents, Live Activities, and voice + +- Retain the imported App Intent deep-link/router idea and rename it for Aiden, but migrate away from the imported deprecated `openAppWhenRun` pattern to the current SDK's `OpenIntent`/`OpenURLIntent`/URL-representable navigation contract. Ship `New Chat`, `New Chat with Voice`, and `New Chat in Workspace` intents backed only by App Group-cached Aiden installation/workspace entities. Entity lookup is cache-only and stable-ID-only: the intent process does not read pairing credentials, call the network, create a chat, embed a path/token in a deep link, or send a prompt. A stale/revoked installation opens the connection UI. Intents otherwise open the app to the requested destination and cannot alter permissions, run Git, or mutate scheduled tasks in the first release. +- Retain the ActivityKit target and reconciliation architecture. Start a Live Activity for a turn initiated on this device; update its sub-4 KB state from authoritative status/tool/approval/stream events; mark it stale on loss; reconcile by `streamId` after relaunch using the selected paired installation's Keychain credential and pinned transport; and end on done, error, cancellation, revocation, or server interruption. The widget extension cannot access the network. With no cloud push relay in scope, it displays the last known stale state while the app is suspended/terminated and reconciles only when Aiden On The Go next runs. Default Lock Screen content to title and safe status; make assistant excerpts an explicit local privacy preference; never include paths, tool arguments, raw approval details, credentials, or provider errors. +- Retain only on-device voice dictation as composer input and the `New Chat with Voice` entry point. Phase 11 removes/disables the imported server-STT provider, audio upload code path, voice-note recording/attachment path, and hold-to-record gesture. Prefer `SpeechAnalyzer` where the deployment target permits; otherwise require an on-device-capable recognizer with `requiresOnDeviceRecognition`. Request Speech and Microphone access only when the user starts dictation. Add optional on-device read-aloud of assistant text with system speech APIs. Permission denial or unavailable on-device recognition must degrade to the text composer. Do not upload raw recordings to Aiden or Apple, and do not add an Aiden server TTS endpoint. + +## Aiden appearance on iOS/iPadOS + +Use semantic Swift theme tokens, not colors scattered through views. Port the canonical Aiden, Slate, Berry, and Moss palettes from `renderer/shared/appearance.ts` into a versioned shared appearance fixture that both platforms test. + +Mobile-applicable parity: + +- System, Light, and Dark modes. +- Aiden, Slate, Berry, and Moss presets. +- Separate light/dark accent, background, and foreground configuration if custom themes are in scope. +- System, rounded, and humanist UI font choices. +- SF Mono, Menlo, and Monaco code font choices where available. +- Contrast, reduced-motion preference, UI size, and code size. +- Sidebar translucency mapped to the iPad navigation shell. +- Diff marker preference for the retained Git diff surface. + +Desktop-only settings do not become fake mobile controls: + +- Pointer cursor mode. +- macOS dock icon selection. If alternate mobile icons ship, expose only real Aiden and Monochrome iOS icons. +- Browser font-smoothing toggle. + +Appearance is confirmed to be independent and device-local on iPhone/iPad. Do not add a desktop appearance endpoint, implicit mirroring, or silent bidirectional synchronization. A later explicit `Follow desktop` mode would require its own contract and product decision. + +## Desktop onboarding and documentation + +Remote access is a durable, setup-critical feature, so update Aiden onboarding: + +- Add a concise Remote Access explanation and opt-in path. +- Explain that Aiden must be running and that Tailscale is optional transport. +- Explain workspace permissions and paired-device revocation. +- Add a data-driven final-tour tile with its own optimized 1024 x 1024 transparent PNG. +- Keep all network actions behind the user's explicit enable/pair action. + +Document local-network setup, Tailscale Serve setup, no-Funnel policy, device revocation, offline behavior, and troubleshooting without adding a normal-startup network dependency. + +## Delivery phases and acceptance gates + +### Phase 0 — Freeze decisions and prove transport + +1. Record the confirmed product decisions below in the shared protocol and iOS project specification. +2. Write the API schema, event vocabulary, capability document, error envelope, DTO allowlists, opaque-handle claims/storage, idempotency/revision rules, remote-operation ownership, and shared fixtures. +3. Prove Tailscale Serve HTTPS against a loopback test endpoint. +4. Prove LAN HTTPS identity generation, QR fingerprint transfer, URLSession pinning, renewal, and recovery on a physical device. +5. Verify that Xcode can automatically provision `sbtbiswas.AidenOnTheGo`, its Live Activity widget, and `group.sbtbiswas.AidenOnTheGo` under team `5WP229CBB8`; resolve the currently installed `7EK65FX44E` Apple Development identity mismatch. +6. Threat-model pairing, device theft, replay, revocation, LAN interception, malicious browser requests, approved-root browsing, selection-token replay, path/symlink escape, and approval races. + +Acceptance: reviewed protocol/threat model plus a correctly team-signed physical-device transport spike; no chat/workspace production endpoint yet. + +### Phase 1 — Shared Aiden service boundary + +1. Extract chat and workspace application operations from IPC-specific handlers where semantics match. +2. Add a remote owner abstraction without weakening renderer ownership. +3. Preserve all mutation, deletion, reconciliation, and approval gates. +4. Add focused unit tests showing IPC behavior is unchanged. + +Acceptance: desktop type-check, focused tests, full `npm run test`, and normal Electron chat/workspace smoke remain green. + +### Phase 2 — Remote service, pairing, and desktop UI + +1. Add the off-by-default server lifecycle. +2. Add device store, one-time pairing, credential verification, revocation, limits, and redacted diagnostics. +3. Add health/server/capability endpoints. +4. Add explicit Aiden-owned Tailscale Serve Connect/Disconnect with preview, conflict detection, ownership tracking, and unrelated-route preservation. +5. Add Remote Access Settings, approved browser roots, onboarding, feature-tour asset, and documentation. + +Acceptance: disabled means no listener; paired test client authenticates; unpaired/revoked clients fail; packaged Aiden keeps the service alive with the window closed and stops it on quit; disconnect removes only the route Aiden created and Funnel is never enabled. + +### Phase 3 — Workspace registry and safe folder-browser API + +1. Add list/get/create-folderless/create-scratch/create-from-selection/update/remove endpoints. +2. Use existing Aiden mutation gates and default-workspace rules. +3. Add desktop-approved roots, opaque location navigation, bounded directory listing, and short-lived device-bound selection tokens. +4. Exclude raw path mutation and disk deletion; canonicalize and revalidate root membership at navigation and registration time. +5. Add concurrency, stale-ID, active-chat, schedule, managed-worktree, traversal, symlink-escape, root-policy-change, and replay tests. + +Acceptance: full supported registry CRUD and approved folder selection work from a contract test client; paths outside approved roots cannot be selected; the Electron workspace UI reconciles immediately. + +### Phase 4 — Chat CRUD and remote generation + +1. Add chat list/get/create/rename/delete and optional empty-chat move. +2. Add atomic remote turn start and stable remote ownership. +3. Add resumable SSE journal, status, cancellation, reasoning/tool/timeline events, terminal reconciliation, and allow/deny approvals. +4. Add provider/model read projection. +5. Add desktop refresh broadcasts for remote mutations. + +Acceptance: a network test client completes, reconnects to, cancels, approves, and denies real mocked turns without duplicate messages or cross-device ownership leaks. + +### Phase 5 — Swift cleanup and Aiden identity + +1. Remove every out-of-scope feature and test from the Xcode project. +2. Replace product docs and the protocol source of truth. +3. Apply the confirmed Contact Sheet-derived team/bundle namespace plus the new Aiden app-group/keychain/URL/SKU identities; rename targets, schemes, symbols, and retained resources. +4. Install Aiden icons/assets and preserve license notices. +5. Keep the project buildable after each mechanical rename slice. + +Acceptance: no Hermes/Hermex user-facing identity remains; the signed app launches on iPhone and iPad; retained unit tests are green. + +### Phase 6 — Swift connection and workspace client + +1. Add discovery, manual URL, QR/short-code pairing, Keychain credential storage, multiple Aiden installations, switching, and revoke handling. +2. Implement Aiden workspace DTOs and full supported registry CRUD. +3. Implement approved-root folder exploration and registration with breadcrumbs, pagination, selection-token expiry recovery, and no editable absolute-path field. +4. Add the conversation-toolbar ellipsis and `Workspace Settings`; keep workspace permission out of the composer. +5. Add offline/error/retry states and local-network permission copy. + +Acceptance: physical iPhone and iPad pair over LAN and Tailscale, switch installations without credential leakage, explore only approved Mac roots, register a selected folder, and perform supported workspace CRUD. + +### Phase 7 — Swift chat client + +1. Implement chat list/detail/create/rename/delete. +2. Implement atomic turn send, stream replay, status reconciliation, stop, tool/timeline rendering, reasoning, and approvals. +3. Add model/provider/thinking selection. +4. Add bounded attachments after text turns are stable. +5. Rework offline cache keys around Aiden instance and chat IDs. + +Acceptance: disconnect/reconnect never resends a prompt; terminal history matches desktop; chat/workspace changes appear in both clients; revoked devices stop receiving events. + +Completed 2026-08-19. Chat CRUD, provider/model/thinking selection, resumable SSE, approvals, cancellation, authoritative reconciliation, instance-scoped caching, and bounded attachments now ship. Attachment uploads mint ten-minute, one-use references bound to the exact device and chat; turns consume at most ten unique references and translate them into Aiden's canonical image/text attachment model. The server caps formats, bytes, dimensions, Unicode scalars, retained memory, and counts; cleans references on discard, consumption, expiry, and revocation; and projects metadata only. The native composer uses system Photos/file pickers, bounded image transcode and text-prefix reads, attachment-only turns, fail-closed reference validation, and exact-request idempotency-key reuse. The signed iPhone 13 Pro suite executes 64 tests: 59 pass, five environment-gated live proofs skip, and zero fail. Production-router HTTP tests cover attachment upload, discard, turn creation, replay, and projection. An additional opted-in physical-iPhone test proves upload, discard, one-use consumption, replay rejection, authoritative metadata, streaming reconnect, approval, duplicate-approval rejection, cancellation, and cleanup over real LAN HTTPS. Phase 12 later proves real Tailscale pairing, authenticated workspace transport, and canonical route cleanup on the same phone. Evidence is in `docs/testing/aiden-on-the-go/phase-7.md` and `docs/testing/aiden-on-the-go/phase-12.md`. + +### Phase 8 — Appearance and iPad polish + +1. Introduce Swift semantic tokens and shared preset fixtures. +2. Apply Aiden branding without redesigning retained Hermex controls. +3. Implement the confirmed independent, device-local mobile appearance options. +4. Validate Dynamic Type, VoiceOver, keyboard navigation, Reduce Motion, contrast, split view, rotation, Stage Manager sizing, and offline states. + +Acceptance: visual and accessibility matrix passes for all four presets in light/dark on iPhone and iPad; no desktop appearance setting is mutated. + +### Phase 9 — Workspace files and Git + +1. Extract Aiden workspace-file and Git application services from IPC-only ownership without changing desktop behavior. +2. Add remote index/read/version-checked-write plus review/diff/compare/branches/commit/push/worktree endpoints and device capabilities. +3. Adapt the retained Hermex file and Git views to Aiden DTOs and move their entry points into workspace navigation/settings. +4. Add device/workspace-bound opaque file handles, snapshot/operation IDs, confirmations, conflict handling, disconnect-safe ownership, bounded output, explicit DTO allowlists, worktree ownership, rollback, and multi-client race tests. + +Acceptance: mobile can inspect the bounded/truncation-aware file index, version-safely edit files, complete supported Git workflows, reconnect to operation state, and reconcile with Electron; path escape, stale writes/snapshots, cross-workspace tokens, internal Git metadata projection, and unconfirmed consequential operations fail safely. + +### Phase 10 — Scheduled Tasks + +1. Extract/share Aiden scheduled-task application operations and authenticated remote routes. +2. Adapt the Hermex Tasks list/detail structure to Aiden task DTOs, validation, settings, preview, script inventory, and run history. +3. Implement create/edit/remove/pause/resume/run-now workflows with final review for unattended execution, revision-checked edits/settings, and idempotent durable `runId` ownership across disconnects. +4. Test timezone/DST behavior, workspace deletion/permission changes, provider/MCP projection, redacted run output, global disable, concurrent desktop/mobile edits, duplicate run retries, disconnect during execution, and offline UI. + +Acceptance: the phone exposes every supported Aiden scheduled-task operation without leaking internal metadata/credentials or accepting arbitrary script paths; a duplicate retry creates one run; execution survives phone disconnect; edit conflicts are explicit; and authoritative desktop/mobile state converges. + +### Phase 11 — App Intents, Live Activities, and voice + +1. Rename and adapt App Intent entities, shortcuts, deep links, phrases, and cold/warm launch routing for installations/workspaces using the current non-deprecated SDK navigation APIs. +2. Rename/sign the Live Activity widget; adapt its bounded state, reducer, privacy controls, deep links, last-known/stale behavior, stream reconciliation, and terminal states to Aiden events. +3. Retain dictation only where on-device recognition is available, remove every server/cloud/audio-upload path and hold-to-record gesture, and add optional on-device response read-aloud with correct microphone/speech/privacy lifecycle behavior. +4. Verify background execution cannot bypass pairing, workspace permission, approval, Git confirmation, or scheduled-task review. +5. Replace Hermex privacy strings and remove the imported production HTTP exception; any plain-HTTP allowance is development-build-only and narrowly scoped. + +Acceptance: App Intents open the correct Aiden destination using cached IDs without network or credential access; voice is on-device-only and degrades safely to text; and a Live Activity shows honest last-known/stale state while disconnected, then authenticates, reconciles, and ends correctly when the app next runs, without exposing response text by default. + +Completed 2026-08-19. The shipping app now has cache-only installation/workspace App Intent entities and current-SDK deep-link navigation; a signed, embedded Aiden Live Activity widget with bounded private-by-default state and authenticated relaunch reconciliation; explicit on-device-only composer dictation; and system read-aloud. Stable IDs are separated by installation, stale or revoked destinations fail closed, response excerpts default off, and neither App Intents nor the widget receive endpoints or credentials. The final shipping bundle contains generated shortcut metadata, the required privacy strings, and no production ATS exception. The full signed physical-iPhone suite passed 56 tests with four expected environment-gated skips, the focused Phase 11 suite passed 6/6 after the final reducer cleanup, and the clean app installed and launched on the iPhone 13 Pro. No simulator or iPhone 16 Pro Max was used. Evidence is in `docs/testing/aiden-on-the-go/phase-11.md`. + +### Phase 12 — End-to-end hardening and release readiness + +1. Run Electron unit/integration/full test suites, type-check, lint, build, packaged smoke, and listener-disabled verification. +2. Run the full retained XCTest suite and signed physical-device build/launch; simulator use remains excluded by user direction. +3. Run shared contract fixtures against TypeScript and Swift decoders. +4. Run physical-device LAN and Tailscale scenarios, sleep/wake, IP change, token rotation/revocation, server restart, duplicate approval, and multi-device concurrency. +5. Complete privacy strings, Local Network/Bonjour/Speech/Microphone descriptions, ActivityKit/App Group/Keychain entitlements, team provisioning, App Store metadata, and third-party notices. + +Acceptance: release checklist is green with no Hermes-only endpoint, brand asset, localization, or dead navigation remaining. + +Progress 2026-08-19: Desktop type-check, lint, canonical tests, production build, strengthened Remote Access Electron E2E, hardened package/verification, and an isolated packaged listener-off smoke pass. The final focused Remote Access run passes 119/119 production tests plus 7/7 deterministic transport tests, including production-service revoke-and-pair-again coverage, first-time Tailscale Serve setup from an empty configuration, and cleanup-only migration of the exact origin-only target persisted by pre-acceptance builds. Real-tailnet testing exposed and fixed two production blockers: HTTPS eligibility now comes from the exact certificate domain instead of a pre-existing listener, and the loopback target restores `/api/aiden/v1` after Tailscale strips the public mount prefix. A 1.611-second physical iPhone 13 Pro proof used system trust plus the live Tailscale leaf SPKI pin, paired, authenticated, browsed an approved root, completed workspace CRUD, rejected selection replay, and removed only Aiden's route back to `{}`. Phase 7's bounded-attachment requirement is implemented end to end with one-use device/chat-bound references, canonical Aiden attachment translation, native Photos/file selection, attachment-only turns, path-free metadata, and retry-safe exact-request idempotency. The complete signed iPhone suite executes 70 tests: 65 pass, five live-environment tests are expected skips, and zero fail. One opted-in physical-iPhone proof passed the attachment upload/discard/consume/replay/history flow plus streaming reconnect, approval, duplicate-approval rejection, cancellation, and cleanup over real LAN HTTPS. A second 105.983-second proof passed authenticated reconnection after same-endpoint process replacement, typed `403 credential_revoked` durable revocation, explicit local re-pair with an independent one-use secret, replay rejection, a distinct replacement credential, and another process restart where the new credential remained authoritative and the old one remained revoked. Persisted spike state contains only the active credential SHA-256 digest and a bounded set of prior revoked SHA-256 digests; restart does not reopen pairing, and the deterministic transport suite proves atomic digest replacement plus owner-only marker/state cleanup. A fresh test-free attachment build passed strict deep code-sign verification, contained no XCTest bundle, installed, and launched on the physical iPhone 13 Pro. A fresh generic-hardware attachment Release archive passed store validation, App Intent generation, strict deep code-sign verification, and no-test-bundle inspection. It is development-signed with `get-task-allow=true`, so distribution signing/export is still open. Physical iPad, sleep/wake/address-change/multi-device acceptance, Siri/dictation/direct server-streamed Live Activity system-UI acceptance, and App Store Connect owner metadata/review assets remain open. Evidence is in `docs/testing/aiden-on-the-go/phase-12.md`. + +Release-readiness follow-up 2026-08-19: the CI mobile gate no longer references the removed Hermex project or an iOS Simulator; it compiles the shipping Aiden app/test bundle for generic iOS hardware and passes locally. The active Aiden shell now includes the reused `AppConfig` foundation and exposes native Privacy Policy and Support links. The canonical live destinations are locked by the focused physical-iPhone integration suite. Contact Sheet's owner identity plus Aiden's live site/repository resolve the public metadata values. Hermex's separate internal/external export-option, manual workflow, and App Store build-selector patterns are adapted for Aiden: separate protected environments, exact confirmation gates, main-only execution, pinned checkout, closed-version-train protection, generic-hardware archive/upload, and upload-only external behavior are locked by 20 Ruby tests/42 assertions plus three deterministic policy tests registered in `npm test`. The published privacy policy still needs mobile-specific copy and the support page needs a visible working contact; no workflow was dispatched and no distribution credential, App Store record, export, upload, tester assignment, or external-review action was created implicitly. + +ASC/metadata follow-up 2026-08-19: current Apple definitions resolve the store draft to Developer Tools / Productivity, no developer-collected data, and a conservative 13+ override matching the published under-13 policy. Four additional registered policy tests lock metadata limits/links, manifest/SDK privacy alignment, ASC credential ignores, and telemetry-off strict command guidance. The stable local Rork `asc 3.4.0` client was used read-only: the main app bundle ID and App Groups capability exist under team `5WP229CBB8`, but the active Parsely-named profile exposes no Aiden App Store record, widget identifier, or Aiden distribution profile. Aiden's live website links to an active public **Aiden - Quick AI** TestFlight beta outside that profile's scope; its page describes the existing macOS product and says it is available on iOS, but does not expose the numeric App ID or bundle ID. The correct Aiden credential must reconcile that record before any new record is created. One remote iOS Distribution certificate exists while no local Apple Distribution private-key identity does, and no Apple web session is authenticated. A provisional physical-iPhone `1170 × 2532` pairing-screen capture converted to opaque JPEG passes ASC screenshot validation, proving the real-device pipeline without using a simulator; final distribution-candidate iPhone/iPad assets remain open. No ASC mutation or Codex automation was created because no exact reviewed Aiden App ID/build exists. The owner-gated ASC and future read-only automation runbook is `ios/ASC_CLI.md`. + +ASC automation follow-up 2026-08-19: a registered `ios:asc-monitor` repository command now provides the exact future Codex-automation boundary. It requires a named Aiden Keychain profile, a numeric App Store Connect App ID, and an exact build resource ID for processing/TestFlight modes; sets telemetry off; uses strict authentication and read-only CLI verbs; and summarizes TestFlight crashes/feedback as counts, newest timestamps, and fingerprints without persisting tester identity, feedback text, screenshot URLs, or crash content. Processing uses exact `builds info --build-id` rather than an app-wide latest-build dashboard. Six focused tests lock command scope, identifier/profile validation, telemetry policy, read-only review behavior, and privacy-safe output. No automation was created because the authoritative Aiden record/build remains unresolved. + +Internal TestFlight follow-up 2026-08-19: the owner-authorized distinct Aiden On The Go App Store record, widget identifier, Apple Distribution identity, and App Store profiles are now provisioned. The train is `0.1.0`; build `1` was rejected before processing with `ITMS-90717` for an alpha-bearing icon. Build `2` replaces it with the exact opaque RayChat Icon Composer package, passes release tests plus archive/export bundle, entitlement, signing, internal-only, and compiled-icon inspection, and App Store Connect reports the exact build `VALID` and `IN_BETA_TESTING`. The internal group contains the account holder. External TestFlight, App Review, and public release were not enabled. + +Compact-navigation follow-up 2026-08-19: build `3` replaces the always-on `NavigationSplitView` shell with the retained Hermex adaptive pattern: a value-driven `NavigationStack` on compact iPhone and a split view with a stacked detail column on regular iPad layouts. Workspace taps, creation, folder registration, deep links, deletion, and compact/regular transitions now reconcile explicit selection and path state. Five focused navigation/appearance tests and nine chat tests pass on the physical iPhone 13 Pro; the exact `0.1.0 (3)` app was installed and launched without using a simulator. Its internal-only distribution IPA passed identity, entitlement, signing, widget, and opaque-icon inspection, and App Store Connect build `e5f0ae7e-35aa-451e-be87-bc039885b2de` is `VALID` and `IN_BETA_TESTING` for `Internal Testers`. + +ASC localization/privacy follow-up 2026-08-19: canonical `en-US` App Info and version-localization JSON now exist under `ios/app-store/metadata` for the shipping `1.5` draft. Rork ASC's offline validator reports zero errors and zero warnings, and registered policy tests lock the canonical files to the documented name, subtitle, privacy/marketing/support URLs, keywords, description limits, and intentionally omitted optional fields. `ios/app-store/MOBILE_PRIVACY_SUPPORT_COPY.md` supplies ready-to-review live-site replacement/addition copy for mobile pairing/transport, Keychain and device-local caches, attachments and provider forwarding, Apple permission surfaces, App Intents, Live Activities, external media, and a visible support email. Version confirmation, owner/legal approval, and publication on the separately owned website remain external gates; no metadata was applied. + +Shipping-target/package follow-up 2026-08-19: the app, test, and widget `PBXSourcesBuildPhase` memberships are now locked by a registered exact-allowlist test that also rejects imported product identity and non-Aiden `/api/*` literals in shipping Swift. At this checkpoint, unlinked packages retained from the Hermex import were removed and KeychainAccess 4.2.2 was the only resolved and linked Swift package. The later Markdown parity follow-up deliberately restored Hermex's MarkdownUI 2.4.1 rendering foundation plus its locked NetworkImage 6.0.1 and swift-cmark 0.8.0 transitive dependencies and notices. A clean generic-hardware Release compile and the focused signed integration suite on the physical iPhone 13 Pro passed after the cleanup without using a simulator. + +Native-shell refresh follow-up 2026-08-19: onboarding is now a concise Aiden welcome, Mac preparation, and secure QR-pairing flow. The adaptive home places Scheduled Tasks, privacy-safe Mac Usage, all workspaces, and globally chronological chats behind a Hermex-informed native list; its profile control opens the single App Settings surface, while per-workspace permission/files/Git remain in Workspace Settings. New Agent creates an Aiden-managed scratch workspace, the compact composer restores Hermex's attachment/model/thinking/voice rhythm with the Aiden logo, and transcript activity groups reasoning and tool calls without brain/sparkle identity. Berry dark uses an accessible berry accent shared exactly by Electron, protocol fixture, and Swift. The top-level iOS project is Aiden-owned MIT while required upstream notices remain bundled. All 215 non-shipping imported Swift/test files and their 430 isolated Xcode objects were removed; a registered gate now enforces the exact 31-file app/test tree, brand asset hash, shell ordering, composer affordances, and absence of brain/sparkle glyphs. The new `/usage` route uses existing `server:read` authority and projects aggregate Mac totals only. The full physical iPhone 13 Pro suite executed 73 tests: 68 passed, five environment-gated live proofs skipped, and zero failed. A clean `0.1.0 (5)` app installed and launched on that phone. A cold real connection proved successful `chats`, `scheduledTasks`, and `usage` loads after correcting the connected-state trigger discovered in build 4. The internal-only Apple Distribution IPA passed app/widget identity, App Group, entitlement, no-test-bundle, and strict deep-signature inspection; App Store Connect build `6173d5e2-0e58-4d0a-92fa-fc804fc82c37` is `VALID` and `IN_BETA_TESTING` for `Internal Testers`, with external testing `NOT_APPLICABLE`. + +Final shell follow-up 2026-08-19: build `6` closes the remaining locally actionable migration defects found by a fresh source and physical-device audit against `/Users/sambitbiswas/projects/opp/hermex`. New Agent now creates a managed scratch workspace before creating its chat. Cold navigation requests are retained until the coordinator is connected instead of cancelling their own task, and handoff chats have an explicit native Close action. The Live Activity extension now embeds the exact tintable Aiden sidebar artwork and uses it for starting/thinking in every size instead of brain/sparkle glyphs. The registered shipping-target test locks all four behaviors. The complete physical iPhone 13 Pro suite again executed 73 tests with 68 passes, five expected live-environment skips, and zero failures; no simulator or iPhone 16 Pro Max was used. The iOS release suite passes 20 Ruby tests/42 assertions plus 23 Node tests, and the desktop Remote Access suite passes 120 production tests plus seven transport proofs. The internal-only local IPA reports `0.1.0 (6)`, exact app/widget/App Group identities, `TFInternalTestingOnly=true`, Apple Distribution team `5WP229CBB8`, `get-task-allow=false`, no XCTest content, the widget Aiden logo resource, and a valid strict deep signature. Exact App Store Connect build `aa994233-1bf3-4482-86f5-b8b0356eee25` is `VALID`, assigned to `Internal Testers`, `IN_BETA_TESTING`, and external testing remains `NOT_APPLICABLE`. + +Interaction and streaming follow-up 2026-08-19: the retained Hermex implementation was audited and confirmed to stream through resumable Server-Sent Events, not WebSockets. Aiden On The Go keeps its stricter SSE path with event IDs, replay cursors, reconnect, reasoning/tool/approval events, and authoritative terminal reconciliation. The exact dependency-free Thinking Orbs `0.3.1` iOS SwiftUI port is vendored with its MIT notice and maps queued, reasoning, tool, approval, token, and completion phases to the same visual states used by Aiden Agent, including reduced-motion behavior. New Agent is now a native Liquid Glass action offering an existing workspace, a new reusable workspace, or a managed scratch workspace; selecting an existing workspace creates only the chat and avoids registry bloat. Chat send, rename, delete, approval, cancellation, and workspace mutations render hopeful local state immediately, then replace it with the server's canonical result or roll back/reconcile on failure. Home and workspace chat timestamps update from `just now` through minutes, hours, and days; home message counts and top-level workspace/scheduled-task badges are removed. The shipping-target suite passes 6/6, Thinking Orbs passes its 72-case/70,115-value golden-vector suite, and the signed physical iPhone 13 Pro suite executes 74 tests with 69 passes, five expected environment-gated skips, and zero failures. A separate clean test-free build passed strict deep-signature inspection, installed, and launched on that phone. No simulator, iPhone 16 Pro Max, App Store Connect mutation, archive, or TestFlight upload was used for this local refresh. + +Composer-overlay follow-up 2026-08-19: screenshot review exposed that the chat used an opaque `.bar` safe-area inset beneath a material composer, cutting the selected Aiden canvas off above the control, while the send arrow relied on the system `.background` role and lost contrast when disabled. The chat now follows the retained Hermex overlay geometry: its transcript remains the full theme canvas and scrolls beneath a bottom-aligned composer, with a measured transparent tail spacer keeping the latest content reachable above the control. The composer uses native interactive Liquid Glass on supported iOS versions, a theme-raised opaque fallback under Reduce Transparency, and regular material on older systems. Send states are explicit Aiden palette roles: active uses accent/canvas contrast, disabled uses foreground/secondary contrast, and stop uses foreground/canvas. A registered source regression rejects an opaque `.bar` return and locks the overlay/glass/theme roles. All six shipping checks and the complete physical iPhone 13 Pro suite pass: 74 total, 69 passed, five expected environment skips, and zero failed. A clean test-free app passed strict signing inspection, installed, and launched on that phone; no simulator or TestFlight upload was used. + +Composer-focus/model follow-up 2026-08-19: composer focus is owned by the chat shell so tapping anywhere in the transcript dismisses the keyboard, while interactive downward scrolling retains native swipe-to-dismiss behavior. The model menu now makes every model with declared `thinkingLevels` a submenu whose children are those exact server-projected levels; selecting a child atomically updates provider, model, and thinking level. Models without levels remain direct actions. The separate thinking sibling control is removed, and the model trigger shows the Aiden thinking mark plus the selected level when applicable, including a combined accessibility value. Registered source checks lock focus handoff, tap/swipe dismissal, nested thinking menus, and absence of the former sibling picker. All six shipping checks and the full physical iPhone 13 Pro suite pass at 74 total, 69 passed, five expected environment skips, and zero failures. A clean test-free build passed strict signing inspection, installed, and launched on that phone. No simulator, iPhone 16 Pro Max, ASC mutation, archive, or TestFlight upload was used. + +Internal TestFlight build 7 follow-up 2026-08-19: after explicit owner authorization, the project build number advanced to `7` while the internal train remained `0.1.0`. The release gate passed 20 Ruby tests/42 assertions plus 23 Node tests, and the latest physical iPhone 13 Pro suite remained green at 74 total with 69 passes, five expected environment skips, and zero failures. A generic-iOS Release archive was exported with the internal-only policy and the resulting IPA reported `TFInternalTestingOnly=true`, exact app/widget/App Group identities, Apple Distribution team `5WP229CBB8`, `get-task-allow=false` for both targets, no XCTest content, valid strict deep signing, and opaque compiled iPhone/iPad icons. The exact IPA was uploaded through telemetry-off strict authentication with the named `Parsely ASC` profile. App Store Connect build `717b6381-4dec-4cce-85d1-72b503c28590` processed as `VALID`, is related to exact group `Internal Testers` (`3f90ffa7-29bb-429e-80a4-88422eb85b6d`), reports `internalBuildState=IN_BETA_TESTING`, and keeps `externalBuildState=NOT_APPLICABLE`. No external group, Beta App Review, App Review, public availability, metadata, pricing, or screenshot mutation occurred. + +App Intent handoff follow-up 2026-08-19: a cold physical-iPhone launch through `aiden-otg://new-chat` exercised the same bounded URL emitted by the New Chat App Intent. With no paired installation, the installed app failed closed before creating or sending a chat and presented the correct connect-first action. Physical screenshot review exposed a misleading “Couldn’t Pair” alert title inherited from the pairing shell; the title is now the neutral Aiden product name, an eighth focused integration assertion locks the copy, and a repeated cold launch confirmed the corrected result. Temporary screenshots and DerivedData were moved to Trash. Siri/Shortcuts phrase invocation itself remains a manual system-UI gate. + +ActivityKit follow-up 2026-08-19: a real signed physical-iPhone lifecycle test exposed that ActivityKit's rendered `activity.content` can lag an awaited update. The production manager previously derived each event from that public rendered snapshot, so rapid tool/token/stale events could overwrite semantic state. It now maintains canonical MainActor-isolated state per Activity ID, adopts persisted state only during reconciliation, refuses updates to ended/dismissed activities, and clears terminal cache entries. One regression performs a real request, fires rapid tool → token → stale transitions, confirms private-by-default responding/stale state, and immediately removes the activity. A second physical regression releases the original manager, creates a fresh manager in the same test host, adopts the system-persisted activity, and verifies the exact bearer-authenticated, protocol-versioned stream-status request and reconciled rendering through `AidenRemoteClient`. The guarded `ios:activitykit-process-proof` command then builds once, starts a unique activity in one host process, proves that host has exited, reuses the installed destination artifacts without app reinstallation, and passes the same authenticated adoption and terminal cleanup in a distinct host process. It validates matching physical CoreDevice/Xcode identities, has failure cleanup, and is locked by four registered Node tests. The focused suite passes 11/11 and the full physical target passes 70 total with 65 passes, five intentional live-environment skips, and zero failures. Only direct system-UI observation of a real server-driven activity remains manual for this path. + +Post-proof release-gate refresh 2026-08-19: the complete canonical `npm test` lifecycle passes after the ActivityKit runner registration, including the Aiden Remote pretest matrix, iOS release policy suite, Electron tests, and native helper suites. A telemetry-off, strict-auth ASC refresh remains unchanged: only the Parsely-named Keychain profile is active; the exact Aiden bundle query exposes no App Store Connect record; filtered Developer Portal results contain the main identifier but not the Live Activity widget; no Aiden iOS App Store profile exists; and the local Keychain has no Apple Distribution identity. No ASC mutation or Codex automation was created. The owner authorizes using `Parsely ASC` for Aiden within its visible scope, with explicit profile selection and exact identifiers; its zero-app result remains insufficient authority to create a duplicate record. + +Hermex Markdown parity follow-up 2026-08-19: transcript inspection against `/Users/sambitbiswas/projects/opp/hermex` found that Aiden On The Go was parsing completed replies with inline-only `AttributedString`, leaving block headings and lists visible as punctuation. The same assistant layout used a trailing spacer that could squeeze long content and clip the stored leading `I` from `I'll explore…`. Completed and streaming replies now use the exact Hermex `swift-markdown-ui` 2.4.1 renderer, receive the full proposed transcript width, and retain Aiden palette typography. User prompts remain literal content-sized bubbles like Hermex rather than being stretched by the full-width renderer. Parsing is bounded to 80,000 characters and 2,000 lines with literal-text fallback; Markdown images are deliberately suppressed through a no-network provider. Required MarkdownUI, NetworkImage 6.0.1, and swift-cmark 0.8.0 notices are bundled and release tests lock the resolved graph. Fourteen focused chat tests pass, and the complete physical iPhone 13 Pro target passes 78 total with 73 passes, five expected environment-gated skips, and zero failures. A generic physical-iOS compile and the release policy suite (20 Ruby tests/42 assertions plus 23 Node tests) also pass. The app was launched on the phone for visual confirmation. This is a local follow-up after TestFlight build 7; no simulator, archive, ASC mutation, or upload occurred. + +## Test inventory + +### Electron + +- Remote service disabled/listen lifecycle. +- Pairing expiry, single use, rate limits, token digest, rotation, and revocation. +- Authorization on every route and SSE reconnect. +- DTO projection excludes secrets and unsafe paths. +- Workspace CRUD invariants, approved-root identity/policy changes, token-digest isolation, cursor binding, atomic selection consumption, expiry/replay, traversal/symlink/root-replacement escape, permission elevation audit, broadcasts, and mutation races. +- Chat CRUD/deletion/reconciliation races. +- Atomic append/start behavior. +- SSE ordering, replay bounds, heartbeat, terminal persistence, and restart interruption. +- Approval ownership, expiry, duplicate decision, device revocation, and desktop/remote overlap. +- Request size, attachment, malformed JSON, path injection, CORS/origin, and log-redaction tests. +- Workspace file-handle instance/device/workspace isolation, 4,000-entry/depth-20 truncation projection, root replacement, read bounds, expected-version conflicts, and save reconciliation. +- Git allowlist projection, nested-workspace capability, bounded diff, stale snapshots, canonical-repository serialization, branch/commit/push confirmation, disconnect-safe ownership, worktree ownership/rollback, and concurrent desktop/mobile operations. +- Scheduled-task schema validation, revisions/CAS, DST/timezone preview, internal-field/credential/output projection, idempotent durable run ownership, settings, lifecycle actions, run history, disconnect behavior, workspace/provider changes, and concurrent edits. +- Tailscale Serve conflict/ownership tests proving that unrelated routes and Funnel state are untouched. +- Onboarding and 1024 x 1024 asset contract. + +### Swift + +- Pairing and certificate/token storage. +- Multi-installation credential isolation. +- Aiden endpoint request and tolerant response decoding. +- SSE sequence, replay, reset/snapshot, duplicate suppression, and terminal convergence. +- Chat list/detail/mutation/send/cancel/approval. +- Workspace list/create/update/remove and authoritative reconciliation. +- Approved-root folder navigation, pagination, selection-token expiry, and registration. +- Offline cache scoping. +- Theme preset/token fixtures and accessibility settings. +- iPhone/iPad navigation-state coverage. +- Workspace-settings ellipsis routing and absence of permission controls in the composer. +- File index/read/edit/version-conflict and Git review/diff/branch/commit/push/worktree flows. +- Scheduled-task list/editor/preview/lifecycle/run-history flows. +- Cache-only App Intent entities/current-SDK navigation/deep links/cold/stale launch with assertions that the intent process makes no network/Keychain access; Live Activity sub-4 KB reducer/stale/reconciliation/privacy behavior; on-device-only dictation authorization; removal of audio upload/voice-note paths; and read-aloud lifecycle. + +Retain and adapt the useful Hermex API, auth, session, chat, SSE, cache, navigation, appearance, workspace, Git, file, Tasks, App Intents, Live Activity, and voice tests. Remove Kanban, Skills, Memory, Insights, and other deleted-feature tests from the target rather than leaving skipped suites. Hermes Cron fixtures do not survive unchanged; only the native Tasks UI/test patterns are reused against new Aiden DTOs. + +## Research inputs + +- Local Apple identity baseline: `/Users/sambitbiswas/projects/contactsheet/Contact-Sheet-Generator/ContactSheetGen.xcodeproj/project.pbxproj`. +- Current App Intents navigation APIs: [AppIntent](https://developer.apple.com/documentation/appintents/appintent) and [OpenIntent](https://developer.apple.com/documentation/appintents/openintent). +- Live Activity lifecycle and extension limits: [Displaying live data with Live Activities](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities). +- Speech authorization and privacy behavior: [Asking Permission to Use Speech Recognition](https://developer.apple.com/documentation/speech/asking-permission-to-use-speech-recognition). +- Current Serve lifecycle, status, route-specific `off`, and destructive reset semantics: [Tailscale Serve CLI](https://tailscale.com/docs/reference/tailscale-cli/serve). + +## Confirmed product decisions + +1. **Workspace scope:** Mobile has full Aiden workspace-registry CRUD, including folderless and managed-scratch creation, may explore approved folders on the paired Mac and register a selected folder, and later gains Aiden's existing file index/read/version-checked-write capabilities. It does not accept free-form Mac paths or invent file create/rename/delete operations. +2. **Remote permissions:** Mobile turns honor the workspace's saved `full`/`ask`/`none` permission and show approvals on the phone. Permission lives in `Workspace Settings` under the conversation-toolbar ellipsis, never in the composer. +3. **Workspace creation:** The phone uses a server-controlled approved-root directory browser and a short-lived opaque selection token. New selected-folder workspaces default to `ask`. +4. **Appearance:** Aiden On The Go appearance is independent and stored locally on the phone/iPad. +5. **Expanded native scope:** App Intents, Live Activities, voice, Aiden Git/files, and scheduled tasks are planned after the stable chat/workspace core. Share Extension, terminal, Computer Use, and cloud push remain deferred. +6. **Apple identity:** Use automatic signing with team `5WP229CBB8`, the `sbtbiswas.*` bundle namespace, `group.sbtbiswas.AidenOnTheGo`, URL scheme `aiden-otg`, and SKU `aiden-on-the-go-ios`, subject to the Phase 0 provisioning preflight. +7. **Tailscale ownership:** Aiden provides explicit Connect/Disconnect, previews the exact non-Funnel Serve change, records ownership, and removes only the route it created without altering unrelated routes. + +Implementation may begin with Phase 0. Later phases remain gated by the acceptance criteria above rather than unresolved product decisions. diff --git a/docs/security/aiden-remote-threat-model.md b/docs/security/aiden-remote-threat-model.md new file mode 100644 index 00000000..399a43ad --- /dev/null +++ b/docs/security/aiden-remote-threat-model.md @@ -0,0 +1,124 @@ +# Aiden Remote Access Threat Model + +Status: Phase 0 baseline +Scope: Aiden Agent desktop remote service and Aiden On The Go iPhone/iPad client +Protocol: `/api/aiden/v1` +Review trigger: any new route, capability, transport, background surface, credential store, filesystem projection, or execution owner + +## 1. Security goals + +1. A remote device cannot control Aiden until the Mac user explicitly enables Remote Access and completes pairing. +2. Network reachability is never authorization; LAN/Tailscale peers still need a valid, capability-scoped Aiden device credential. +3. The phone cannot gain more workspace/tool authority than the saved Aiden permission and explicit approvals allow. +4. No remote DTO, event, log, widget, App Intent, or error leaks credentials, absolute paths, private runtime state, or raw consequential-operation details. +5. Browser/file handles cannot escape approved canonical roots, survive policy/filesystem identity changes, cross devices, or be replayed after use/expiry. +6. Network loss/retry cannot duplicate prompts, approvals, workspace creation, Git mutations, or scheduled runs. +7. Revocation and shutdown terminate future authority predictably without corrupting persisted chats/workspaces/tasks. +8. Desktop IPC ownership remains renderer-scoped; network operations use separate explicit owners rather than forged `WebContents` identity. + +## 2. Assets + +- Aiden instance identity and LAN private key. +- Pairing secrets and issued device credentials. +- Provider/MCP credentials and provider configuration. +- Chats, messages, reasoning, attachments, and event journals. +- Workspace registry, permissions, filesystem contents, and approved-root policy. +- Git repositories, branches, commits, remotes, and managed-worktree ownership metadata. +- Scheduled task definitions, scripts, process environment, run output, and notification settings. +- Desktop process availability and user intent/approval state. +- iOS Keychain, App Group cache, offline chat cache, Live Activity, and local notification content. + +## 3. Trust boundaries + +```text +iOS UI / app process + -> Keychain credential + pinned HTTPS + -> LAN or Tailscale network + -> Aiden Remote transport parser/limits/auth + -> capability + revision/idempotency/owner gates + -> shared Aiden application services + -> stores, filesystem, Git, schedule service, Pi runtime + +App Intent process -> App Group cached IDs only -> foreground app routing +Live Activity widget -> bounded last-known state only (no network) +Electron renderer -> preload allowlist -> IPC owner (separate from remote owner) +``` + +Untrusted inputs begin at discovery packets, URLs/QR data, every HTTP header/body/query, SSE resume position, opaque handle, attachment, user-visible name/text, App Group cache, and all filesystem state that can change between calls. + +## 4. Adversaries + +- Unpaired device or malicious browser on the LAN/tailnet. +- Paired but stolen/compromised phone. +- Paired user attempting to exceed capability or workspace permission. +- Network attacker performing interception, DNS redirection, replay, downgrade, or connection disruption. +- Local unprivileged process probing the listener or logs. +- Malicious workspace containing symlink races, replacement directories, enormous trees/files/diffs, hostile Git metadata, or secret-bearing output. +- Compromised/hostile provider, MCP server, tool result, scheduled script, or prompt content attempting exfiltration through remote projections. +- Accidental user retry, concurrent Electron/mobile edits, app suspension, desktop restart, or stale offline state. + +## 5. Threats, mitigations, and required evidence + +| Threat | Mitigations | Required tests/evidence | +| --- | --- | --- | +| Listener enabled without consent | Master switch off by default; no bind/discovery until enabled; packaged lifecycle check. | Disabled port probe and packaged smoke. | +| Pairing secret guessing/replay | 256-bit random QR secret, short TTL, single-use atomic exchange, rate limit, pairing window required, digest-only storage, redacted logs. Numeric fallback requires PAKE/SAS or fingerprint confirmation. | Expiry, duplicate, concurrency, rate-limit, and log tests. | +| MITM/wrong Aiden server | HTTPS; QR carries endpoint + P-256 SPKI SHA-256 pin; hostname/time/signature + pin validation; explicit key rotation/re-pair. | Same-key renewal accepted, changed key/wrong host/expired cert rejected on macOS and physical iOS device. | +| Plain-HTTP downgrade | Production config rejects HTTP; development exception compile/config gated and clearly labeled. | Release/config contract tests. | +| Tailscale mistaken for auth | Bearer credential on every route/SSE; capability checks after auth. | Tailnet request without credential fails. | +| Tailscale config takeover | Inspect current config; Funnel-enabled listener fails connect; exact owned Serve route; matching `off`; never Funnel/reset/global replacement; conflict stops. | Fixture/parser tests plus live loopback route proof showing before/after config equality. | +| Credential theft from server storage | Store credential digest only using memory-hard/slow verifier; safe-storage-protected instance key material; restrictive file permissions. | Store format and migration tests. | +| Credential leakage from phone | Keychain only; no UserDefaults/App Group/URL/log/widget/intent; per-install isolation. | Swift storage and no-network/no-Keychain intent-process tests. | +| Stolen paired device | Desktop device list/revoke; capability scopes; last-seen audit; revoke closes streams and rejects commands. | Revocation during stream/approval/operation tests. | +| Browser cross-origin attack | No permissive CORS, cookies, or ambient auth; Authorization bearer required; reject browser origins as policy; content types/limits. | Origin/CORS/preflight and auth tests. | +| Request smuggling/parser abuse | One vetted HTTP stack; header/body/count/time limits; reject duplicate JSON keys, ambiguous transfer framing, invalid UTF-8, deep JSON. | Malformed/body/header/timeout tests. | +| Secret/path leakage | DTO allowlists; stable safe errors; log redaction; bounded sanitized Git/schedule/provider/tool projection. | Forbidden-field fixture/route/log tests. | +| Directory traversal or handle-store exhaustion | No free-form path; hashed device-bound location handles; canonical `realpath`; approved-root boundary + device/inode + policy revision checks; expired/consumed pruning and hard fail-closed capacity. | `..`, encoding, symlink, mount/root replacement, cross-device, expired/replayed handle, pruning, and capacity tests. | +| Browse-to-register TOCTOU | Separate selection nonce; synchronous atomic revalidate-and-consume with workspace creation; async callbacks prohibited and promise escapes consume/fail closed; fail on identity/policy/duplicate change. | Concurrent consume, async-callback, and filesystem replacement tests. | +| Broad-root data exposure | Root addition desktop-only; nested-root dedupe; home warning/confirmation; filesystem root disabled by default; hidden/system policy. | Settings policy tests and remote inability to add roots. | +| File handle escape/staleness | File handles separate from browser handles; bind instance/device/workspace/root/file/snapshot/expiry; re-resolve; expected-version atomic writes. | Cross-workspace/device, root/file replacement, stale version, symlink tests. | +| File/diff resource exhaustion | 4,000/depth-20 index, read/write/diff byte/time limits, streaming/backpressure where needed. | Boundary and cancellation tests. | +| Git destructive misuse | Explicit `git:write`, foreground confirmation, repository-root-only commit/push, operation snapshots/stale errors, canonical repo serialization, managed metadata server-owned. No generic Git/shell. | Nested workspace, confirmation, concurrency, rollback, metadata projection tests. | +| Managed-worktree deletion escape | Accept persisted workspace ID only; re-resolve ownership token/filesystem identity; cancel/settle operations; schedule restoration; rollback. | Replacement and partial-failure tests. | +| Duplicate prompt/model call | Atomic append/admit/start; bounded durable idempotency digest with fulfilled/rejected/in-flight states; no TTL/capacity eviction of in-flight work; stable turn/stream owner independent of socket; client never retries on SSE loss. | Disconnect/retry/concurrent start, rejected outcome, in-flight expiry, and restart tests. | +| Cross-device stream/approval | Owner bound to authenticated device/stream; approval deadline/idempotent decision; other devices denied. | Ownership and duplicate/conflicting decision tests. | +| Event replay/gap confusion | Per-stream monotonic sequence; bounded journal; Last-Event-ID/after; gap -> snapshot; immutable terminal event. | Replay, duplicate, gap, expiry, restart tests. | +| Restart silently retries provider | Persist terminal metadata; restart marks interrupted once; never retries call. | Crash/restart fixture/integration test. | +| Permission elevation/bypass | `workspace:manage`; explicit foreground confirmation + audit for stronger permission; server saved permission composes tool set; remote cannot request hidden modes. | Elevation, approval, none/ask/full tool-contract tests. | +| Scheduled task duplicate/run cancellation | Revision/CAS edits; idempotent durable `runId`; schedule service owns execution across TCP loss; bounded redacted output. | Duplicate retry, disconnect, concurrent edit, DST, output redaction tests. | +| Scheduled script exfiltration | Only existing server-inventoried script IDs; no raw paths; pairing warns about scheduled authority; no App Intent schedule mutations. | Script-ID/path rejection and DTO tests. | +| Attachment bomb/content abuse | MIME/size/count/dimensions/text limits; short-lived references; cleanup; no server paths. | Oversize, decompression dimension, expiry, aggregate tests. | +| Lock Screen/App Intent leak | Live Activity under 4 KB, safe status only by default, no path/args/errors/credentials; intents cache ID-only and foreground. | Payload size/allowlist and stale/revoked entity tests. | +| Voice privacy leak | On-device recognition required; remove server/cloud/audio upload and voice-note path; permission at first use. | Code-path absence and permission fallback tests. | +| Denial of service | Per-device/global rate limits; active stream/device caps; bounded journals/uploads/browse; timeouts; revoke/disable. | Limit and recovery tests. | + +## 6. Operation ownership and revocation policy + +- Chat turns survive socket loss. Explicit cancel, device revocation, workspace mutation/deletion, or server shutdown follows the generation owner's settlement policy. +- File writes and Git operations use stable remote operation IDs and the existing workspace operation/mutation gates. Socket loss alone does not cancel after server acceptance. +- Scheduled `run now` survives socket loss and is observed by `runId`; remove/pause/global disable/revocation/shutdown use the schedule service's documented policy. +- Pairing/device revocation immediately prevents new requests, closes SSE subscribers, resolves pending approvals as denied/expired according to service policy, and prevents reconnect. It must not blindly kill unrelated desktop-owned work. + +## 7. Privacy and logging + +Remote-access logs are metadata-minimal. Permitted fields: request ID, route template, status, duration, stable error code, and truncated instance/device ID. Forbidden fields: Authorization, pairing/idempotency secrets, opaque handles, QR contents, URLs with query data, request/response bodies, paths, prompts/messages/reasoning, attachments, tool details, provider/MCP failures, Git/schedule output, Keychain/App Group data. + +Offline caches are scoped by Aiden instance ID and use platform data protection. Revocation makes cached data read-only until the user explicitly removes the installation/cache. Lock Screen response excerpts are off by default. + +## 8. Residual risks and non-goals + +- A fully compromised paired phone can exercise granted capabilities until revoked. Pairing disclosure, least capability, foreground confirmations, and desktop revocation reduce but cannot eliminate this. +- A malicious scheduled script or agent tool already authorized on the Mac may access data available to the Aiden process. Mobile does not create new shell/script path authority, but triggering an existing task remains consequential. +- Tailscale/Bonjour availability and certificates depend on their respective system services. Aiden must fail closed and explain prerequisites. +- No external push relay means Live Activities cannot receive fresh terminal updates while the app is terminated. +- No generic terminal, Computer Use, remote provider credentials, file create/rename/delete, or arbitrary Git command is in scope. + +## 9. Review checklist + +Before each phase gate: + +- Compare new routes/DTOs to this model and the protocol allowlists. +- Identify every new bearer secret, opaque handle, owner, mutation precondition, and terminal state. +- Add negative tests before accepting a new consequential action. +- Verify logs and user-visible errors with adversarial inputs. +- Run two independent fresh-memory security/architecture reviews and clear all P0/P1 findings. diff --git a/docs/testing/aiden-on-the-go/phase-0.md b/docs/testing/aiden-on-the-go/phase-0.md new file mode 100644 index 00000000..9203cecf --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-0.md @@ -0,0 +1,130 @@ +# Aiden On The Go — Phase 0 evidence + +Date: 2026-08-18 +Status: Complete — contract, transport, signing, threat model, parser parity, durability, and physical-device gates pass. + +## Contract and threat model + +- Normative prose: `docs/aiden-remote-api-v1.md` +- Machine-readable schema: `protocol/aiden-remote/v1/openapi.json` +- Shared fixture: `protocol/aiden-remote/v1/fixtures/contract.json` +- Threat model: `docs/security/aiden-remote-threat-model.md` +- iOS implementation contract: `ios/PROJECT_SPEC.md` + +Automated desktop proof: + +```text +npm run type-check +PASS + +npm run test:aiden-remote +PASS — 43 protocol/security-semantics tests and 4 LAN transport/restart tests +``` + +The LAN spike generates a local P-256 CA plus server-only leaves (`CA:FALSE`, digital-signature key usage, server-auth EKU) and proves normal hostname/chain validation, stable installation-key restart, same-key certificate renewal, changed-key rejection and explicit re-pair recovery, wrong-pin rejection, wrong-host rejection, and expired-certificate rejection. Private keys remain in a temporary directory and are removed by the spike. + +## Swift contract and transport proof + +Current generic-hardware compile command: + +```text +xcodebuild build-for-testing -project ios/AidenOnTheGo.xcodeproj \ + -scheme AidenOnTheGo \ + -destination 'generic/platform=iOS' CODE_SIGNING_ALLOWED=NO +``` + +Result: PASS. The deterministic Swift contract suite and its environment-gated trust/Keychain cases are now run on explicitly selected physical iOS devices; hosted CI performs only the signing-disabled generic-hardware compile. The TypeScript and Swift tests decode the same checked-in fixture. The privacy tests assert that the built host declares a non-empty `NSLocalNetworkUsageDescription` and the canonical `_aiden-agent._tcp` Bonjour service while shipping no insecure HTTP or arbitrary-load ATS exception. Swift and TypeScript enforce the same known payload/error bounds using Unicode-scalar counts, exact Health allowlisting, strict full-string RFC 3339 timestamps, safe-integer stream sequences, non-null schema members, canonical encoded paths and ASCII host authorities, required unknown-event payload, forbidden wire fields, duplicate-key and unpaired-surrogate rejection, finite JSON numbers, aligned raw/parsed extensible-envelope limits, and a 1 MiB JSON/SSE body ceiling. + +Physical-device setup is non-secret and reproducible: + +1. Run `scripts/aiden-remote-physical-device-spike-server.mjs --host `. +2. Build for testing with automatic signing and the approved Aiden identifiers. +3. Copy the generated `.xctestrun` beside its `Build/Products` payload (or preserve/rewrite its relative `__TESTROOT__` paths), then add the ephemeral canonical pairing-bootstrap JSON as a test-process environment variable, modeling the QR transfer. +4. Run only `AidenRemotePhase0Tests/testPhysicalDevicePinnedURLSessionWhenConfigured` on the connected iPhone. + +The test makes a real request through `AidenPinnedServerSessionDelegate`, verifies the health response, then proves that the same endpoint is rejected with a wrong SPKI pin. No private key, endpoint, LAN address, or fingerprint is committed. + +Physical result: PASS on `Smbt16ProMax` (iPhone 16 Pro Max, iOS 27.0) — one test executed with zero failures in 0.652 seconds. The first hardware attempt usefully exposed missing Local Network/Bonjour declarations, which were added and locked by `testLocalNetworkPrivacyAndBonjourContractIsDeclared`. A second attempt exposed an incorrect hard-coded Mac interface; the successful run derived the active interface from the default route and advertised that LAN address. The final run admitted the correctly pinned health request and rejected the wrong pin through the real device URLSession trust stack. + +Signed Keychain result: PASS on the same device — the approved `sbtbiswas.AidenOnTheGo.pairing` service resolved from the signed application plist, an ephemeral probe completed write/read/delete, and the same account remained absent from a distinct service. Cleanup ran in-test and no credential or probe value was logged or committed. + +Expanded physical lifecycle result: PASS on `Sambit’s iPhone` (iPhone 13 Pro, iOS 27.0). A fresh signed payload first repeated the correct-pin health request and wrong-pin rejection, then completed this six-case matrix with one focused XCTest and zero failures per case: + +| Case | Separate server process | Expected result | Result | +| --- | --- | --- | --- | +| Original pairing | Yes | Current pin accepted; deliberately wrong pin rejected | PASS | +| Process restart | Yes | Persisted installation key remains accepted; wrong pin rejected | PASS | +| Same-key renewal | Yes | Renewed certificate remains accepted; wrong pin rejected | PASS | +| Key rotation / repair | Yes | New pin accepted only after explicit repair; original pin rejected | PASS | +| Wrong hostname | Yes | Normal hostname validation fails closed | PASS | +| Expired certificate | Yes | Normal validity validation fails closed | PASS | + +The matrix used one private installation-identity directory across genuinely separate Node processes. Every process issued a new ephemeral pairing secret and port, while only the intended key/certificate lifecycle property persisted. After the final duplicate-key/ATS remediation, a new signed payload repeated the signed Keychain proof and the complete six-case matrix with the same passing results. That exact built app reported the approved team/bundle/Keychain identity and no `NSAppTransportSecurity` exception dictionary. Injected test-run files, pairing bootstraps, private keys, certificates, build products, and logs were kept under private temporary paths and removed after the summarized result was recorded. + +## Tailscale Serve proof + +Initial `tailscale serve status --json`: `{}`. + +An ephemeral HTTP health service was bound to loopback. A single path-scoped, non-Funnel HTTPS handler was added with the equivalent of: + +```text +tailscale serve --yes --bg --https=443 \ + --set-path=/aiden-phase0-spike http://127.0.0.1:42731 +``` + +The stable tailnet HTTPS URL returned the expected JSON health payload. The handler was removed with the matching path-specific command: + +```text +tailscale serve --yes --https=443 --set-path=/aiden-phase0-spike off +``` + +Final `tailscale serve status --json`: `{}`. Funnel was never invoked and `serve reset` was never invoked. A second live proof first installed an unrelated exact-path handler, added the Aiden handler, removed only the Aiden handler, and compared the remaining status: the unrelated handler and its target were byte-for-byte unchanged. The unrelated proof handler was then removed with its own exact-path `off`, restoring `{}`. The temporary TLS certificate/key requested by the local Tailscale CLI for the proof were deleted after validation. A checked-in pure planner test additionally covers unowned matching-target conflicts, persisted Aiden ownership, idempotent reconnect/disconnect, preservation of unrelated handlers, and preservation of Funnel state. + +## Automatic-signing preflight + +The connected-device `build-for-testing` command used: + +```text +DEVELOPMENT_TEAM=5WP229CBB8 +APP_BUNDLE_IDENTIFIER=sbtbiswas.AidenOnTheGo +APP_GROUP_IDENTIFIER=group.sbtbiswas.AidenOnTheGo +CODE_SIGN_STYLE=Automatic +-allowProvisioningUpdates +-allowProvisioningDeviceRegistration +``` + +Result: PASS. Xcode provisioned the main app, test bundle, existing share target, and Live Activity widget. Inspection of the signed main app, test bundle, and Live Activity widget reports: + +```text +Identifier=sbtbiswas.AidenOnTheGo +TeamIdentifier=5WP229CBB8 +application-identifier=5WP229CBB8.sbtbiswas.AidenOnTheGo +com.apple.security.application-groups=[group.sbtbiswas.AidenOnTheGo] + +Identifier=sbtbiswas.AidenOnTheGoTests +TeamIdentifier=5WP229CBB8 + +Identifier=sbtbiswas.AidenOnTheGo.LiveActivityWidget +TeamIdentifier=5WP229CBB8 +``` + +This resolves the Phase 0 team mismatch at the built-product/provisioning boundary. Permanent target renaming and identity cleanup remain Phase 5 work. + +After the final trust-delegate remediation, a fresh generic iOS `build-for-testing` also passed. Its current `.xctestrun`, signed application, and signed test bundle were generated together; the application still reports `Identifier=sbtbiswas.AidenOnTheGo`, `TeamIdentifier=5WP229CBB8`, and App Group `group.sbtbiswas.AidenOnTheGo`, while the test bundle reports `Identifier=sbtbiswas.AidenOnTheGoTests` and the same team. The build was regenerated after adding the Local Network/Bonjour declarations and was the payload used for the passing physical run. + +## Review outcome + +The post-hardware review rounds reported no P0 and identified concrete P1 contract/lifecycle gaps. Remediation binds file handles to workspace identity, keeps selection nonces consumed after mutation errors, persists and authoritatively finalizes stable in-flight operation references, validates expected SSE stream identity, constrains Tailscale to a server-owned loopback HTTP target, rejects endpoint userinfo, aligns bounded Swift/TypeScript decoding, configures the approved Keychain service, and persists transport identity across real process restarts. Later passes closed parser parity, Release ATS, Health/timestamp/Unicode, restart, and Tailscale-capability edges. Phase 12 real-tailnet acceptance further requires that the target restore the exact canonical Aiden API base stripped by `--set-path`. The final hardening rejects duplicate/escaped-equivalent keys, non-finite values, unpaired surrogates, null schema members, unsafe stream integers, encoded endpoint variants, numeric-final-label DNS aliases, and noncanonical/ambiguous Tailscale listener authorities. OpenAPI, TypeScript, and Swift now share the same endpoint vectors and JSON-safe sequence maximum. + +Persisted idempotency envelopes and state-specific entries are exact allowlists; durable-operation arrays reject every non-dense own key. Replay values have depth, node, key, array, string, per-result, and aggregate-snapshot limits. The live operation registry is capped at 10,000 owners with owner-checked capacity release. If an action has run but its result or rejection cannot be persisted within the exact schema and byte budget, its stable operation reference remains indefinitely `in_flight` for authoritative reconciliation instead of expiring into a duplicate retry. + +At the user's direction, the final post-remediation gate was completed locally without further subagents. It included targeted adversarial restore/settlement tests, a 20,000-authority OpenAPI/runtime parity fuzz with zero mismatches, lint, schema parsing, diff checks, type-check, all 43 focused protocol/security tests, all 4 transport tests, and the complete repository lifecycle. The current signed source also ran directly on `Sambit’s iPhone` (iPhone 13 Pro): all 21 deterministic Phase 0 tests passed and the two environment-injected live transport/Keychain cases skipped as designed. Earlier signed evidence remains valid for Keychain isolation and all six certificate-lifecycle cases; the iPhone 16 Pro Max was untouched. + +## Full applicable test gate + +```text +npm run test +PASS +``` + +The repository's complete desktop test lifecycle passed after the final reviewer remediations, including the new pretest contract/transport suite, all JavaScript/TypeScript suites, native worktree-remover tests, and 41 Rust computer-use-broker tests plus formatting/clippy. The focused Aiden Remote suite, desktop type-check, and focused Swift suite were also rerun after the final contract, redirect, idempotency, selection-transaction, and Tailscale changes and passed. diff --git a/docs/testing/aiden-on-the-go/phase-1.md b/docs/testing/aiden-on-the-go/phase-1.md new file mode 100644 index 00000000..18ec2449 --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-1.md @@ -0,0 +1,42 @@ +# Aiden On The Go — Phase 1 evidence + +Date: 2026-08-18 +Status: Complete — shared chat/workspace application services preserve the Electron ownership and mutation contracts. + +## Implementation boundary + +- `main/services/chat-application-service.ts` owns chat list/get/wait/create/rename/empty-move/delete semantics and keeps inactive-renderer reconciliation, atomic create guards, Assistant identity rejection, workspace admission, and privacy-first durable deletion ordering. +- `main/services/workspace-application-service.ts` owns workspace registry list/get/create/folder-create/scratch-create/update/remove semantics and keeps canonical folder validation, renderer path-mutation rejection, mutation/operation drains, generation/schedule settlement, permission restoration, and managed-worktree deletion refusal. +- The corresponding `*-main.ts` files bind Electron-backed singletons; the core services import singleton shapes only as types and remain directly unit-testable. +- `ChatGenerationOwner` is a shared narrow interface. Remote owners retain only digests of bounded device/stream identities and survive network subscriber loss until explicit invalidation. Existing renderer owners still come from `rendererDocumentOwner`. +- `main/handlers/chats.ts` and `main/handlers/workspaces.ts` delegate matching CRUD operations. Renderer-only Assistant creation, dialogs, append/copy/export, Git/files, and approval-facing operations remain in IPC-specific handlers. + +## Review outcome + +The requested between-phase review was completed locally because the user directed that no further subagents be used. The review compared the extracted services with the removed handler implementations and checked authority lifetime, cancellation/drain order, schedule restoration, deletion durability, Assistant isolation, dialog ownership, and IPC validation. + +Two test-only issues were found: source-contract assertions still expected mutation logic inline in `chats.ts`. They were updated to prove both sides of the new boundary: the IPC handler must acquire a renderer owner and delegate, while the application service must retain the mutation and operation gates. No production issue remained after review. + +## Tests + +```text +npm run type-check +PASS + +npm run test:aiden-service-boundary +PASS — 9 tests + +npx tsx --test main/handlers/chat-create-params.test.ts main/handlers/ipc-contract.test.ts main/handlers/chat.parse.test.ts main/services/chat-deletion-gate.test.ts main/services/chat-workspace-mutation-gate.test.ts main/services/workspace-mutation-gate.test.ts main/services/workspace-operation-registry.test.ts main/services/workspace-record-removal.test.ts main/services/workspace-schedule-restoration.test.ts +PASS — 43 tests + +npm run test +PASS — complete pretest, TypeScript/JavaScript, Telegram, native worktree-remover, and Rust computer-use lifecycle. One initial parallel Git-suite run reported a single transient failure; the isolated 97-test Git suite and the complete retained-log rerun both passed. + +npm run build +PASS + +npx playwright test tests/e2e/chat-shell-interactions.spec.ts --config=playwright.config.ts +PASS — 1 isolated Electron smoke test +``` + +Targeted ESLint and `git diff --check` also pass for the Phase 1 files. diff --git a/docs/testing/aiden-on-the-go/phase-10.md b/docs/testing/aiden-on-the-go/phase-10.md new file mode 100644 index 00000000..52980772 --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-10.md @@ -0,0 +1,25 @@ +# Aiden On The Go — Phase 10 evidence + +Date: 2026-08-19 +Status: Complete on the available desktop and physical-iPhone gates. + +The Electron main process and authenticated Aiden Remote API now use one shared scheduled-task application service. Mobile supports list, search/filter, detail, create, edit, remove, pause, resume, run now, preview, run history, and revision-checked global settings. Task definitions bind only to validated Aiden workspaces, configured providers/models, enabled MCP IDs, and current server-inventoried scripts. + +Remote DTOs omit provider fingerprints, resolved MCP bindings, chat IDs, credentials, raw script paths, and execution internals. Script selections are short-lived opaque claims bound to the paired device and selected workspace. The MCP inventory contains display names and IDs only. Stored run output is bounded and redacts local paths and token-like values before projection. + +Create/edit uses a final foreground review. Lifecycle edits carry revisions into per-task serialization, settings have a serialized revision gate, and concurrent desktop/mobile edits admit only one revision. Run-now is device-scoped and idempotent: one server-minted `runId` is carried into the scheduler's stored run, duplicate retries replay it, and scheduler-owned execution continues after the caller disconnects. + +The Swift app uses native `List`, `Form`, `Picker`, `Toggle`, `TextEditor`, sheets, alerts, and confirmation dialogs. Definitions, settings, and bounded run history have an installation-scoped, file-protected offline cache; offline mutations remain disabled. + +Verification: + +- TypeScript type-check and `git diff --check` pass. +- Scheduler tests pass 84/84. +- Shared application-service boundary tests pass 18/18. +- Aiden Remote tests pass 112/112 plus 4/4 LAN transport proofs. +- The complete workspace/subagent regression command passes 650/650 plus all native helper tests. +- The complete signed physical iPhone 13 Pro XCTest suite passes 50 tests with four expected environment-gated transport/Keychain skips. Its three Phase 10 tests cover canonical routes and preconditions, strict DTO rejection, stable run idempotency, safe inventory, and installation-scoped bounded offline cache. +- The signed app builds, installs, and launches successfully on the iPhone 13 Pro. +- No simulator was used and the iPhone 16 Pro Max was untouched. + +Physical-iPad and real-Tailscale acceptance were not claimed by this phase. Phase 12 later closes real-Tailscale pairing and authenticated workspace transport on the physical iPhone; physical-iPad acceptance remains open. diff --git a/docs/testing/aiden-on-the-go/phase-11.md b/docs/testing/aiden-on-the-go/phase-11.md new file mode 100644 index 00000000..c7644258 --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-11.md @@ -0,0 +1,27 @@ +# Aiden On The Go — Phase 11 evidence + +Date: 2026-08-19 +Status: Complete on the available signed-build and physical-iPhone gates. + +Phase 11 adds Aiden-native App Intents, deep-link routing, Live Activities, on-device composer dictation, and local assistant read-aloud without widening the authenticated remote-control boundary. + +App Intents use a bounded App Group cache containing only sanitized installation/workspace display names and stable IDs. Per-installation composite entity IDs prevent collisions. The intent process does not network or read Keychain; it returns current-SDK `OpenURLIntent` results for new chat, voice chat, or an exact cached workspace. Deep links reject credentials, ports, fragments, paths, duplicate or unknown query keys, missing values, and unsafe identifiers. The app switches only to a known paired installation, authenticates through the pinned client, and resolves the exact destination before navigating. Stale and revoked destinations fail closed. + +The signed Aiden Live Activity extension renders bounded app-provided state and never receives credentials or performs network requests. The app owns start/update/terminal transitions, marks activities stale while disconnected or backgrounded, reconciles persisted stream IDs only through an authenticated pinned client after relaunch, and ends activities on installation removal or revocation. Response excerpts are controlled by a local privacy toggle and default off. + +Composer voice starts only after an explicit user action, requests Speech and Microphone permission at that point, requires an on-device-capable recognizer with `requiresOnDeviceRecognition`, and fills the draft without sending. It stops before send and on disappearance. The shipping source list has no server STT, audio upload, voice-note, or hold-to-record path. Assistant read-aloud uses the local system speech synthesizer with bounded text. + +Verification: + +- `git diff --check` passes. +- The complete signed physical iPhone 13 Pro XCTest suite passed 56 tests with zero failures and four expected environment-gated transport/Keychain skips. +- After the final Live Activity reducer cleanup, the focused `AidenNativeIntegrationTests` suite passed 6/6 on the same physical phone in 0.008 seconds. It covers cache safety/bounds, sanitization, multi-installation isolation, strict stable-ID deep links, private bounded Live Activity state, on-device voice policy, privacy keys, and absence of a production ATS exception. +- A clean signed physical-device build succeeded. Xcode embedded and validated `AidenLiveActivityWidget.appex`, generated `Metadata.appintents`, and trained the three Aiden shortcut phrases. +- Bundle inspection confirms the Aiden display/bundle identity, microphone and speech descriptions, `NSSupportsLiveActivities`, the App Group entitlement, generated App Intent assets, embedded widget, and no `NSAppTransportSecurity` exception. +- The shipping Swift source list contains no deprecated `openAppWhenRun`, plain-HTTP literal, server API client, audio-upload, voice-note, or hold-to-record marker. +- The clean app installed and launched successfully on the physical iPhone 13 Pro (`iPhone14,2`). +- No simulator was used and the iPhone 16 Pro Max was untouched. + +Manual/environment acceptance still open: invoking the shortcuts through Siri/Shortcuts, exercising the live Speech/Microphone permission UI and actual dictation, directly observing a real server-streamed Live Activity in system UI, and physical-iPad behavior. Those scenarios are not claimed here and remain part of Phase 12 plus the existing Phase 6/8 acceptance gates. Phase 12 later closes real-Tailscale pairing, authenticated workspace transport, and the independently automated ActivityKit process-boundary reconciliation proof on the physical iPhone. + +Physical ActivityKit follow-up 2026-08-19: a signed iPhone 13 Pro test now performs a real local ActivityKit request, three rapid production-manager updates, stale rendering, immediate terminal cleanup, and absence from the active activity inventory. It exposed and fixed a race where `activity.content` could lag an awaited update, causing the next event to derive from stale rendered state. The manager now owns canonical per-Activity state on the MainActor and hydrates that state only when adopting a persisted activity. A second regression releases the original manager, creates a fresh manager in the same test host, adopts the system-persisted activity, makes the exact bearer-authenticated and protocol-versioned stream-status request through `AidenRemoteClient`, reconciles the activity, and removes it. A third opt-in physical proof builds once, creates a uniquely identified activity in one test-host process, confirms that host has exited, launches a distinct host against the already-installed destination artifacts so the app is not reinstalled, then authenticates, reconciles, and ends the same system-persisted activity. Its guarded runner validates that the CoreDevice UUID and Xcode UDID identify the same physical iOS device and has a best-effort cleanup phase. The focused native suite passes 11/11 and the complete physical target passes 70 tests: 65 pass, five configured live-network/Keychain proofs skip, and zero fail. This closes local request/update/end, fresh-manager adoption, and actual process-boundary reconciliation plumbing; direct system-UI observation of a real server-streamed activity remains manual. diff --git a/docs/testing/aiden-on-the-go/phase-12.md b/docs/testing/aiden-on-the-go/phase-12.md new file mode 100644 index 00000000..172f2984 --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-12.md @@ -0,0 +1,96 @@ +# Aiden On The Go — Phase 12 evidence + +Date: 2026-08-19 +Status: In progress. All available automated, packaging, archive, and physical-iPhone gates pass; external and unavailable hardware gates remain open. + +## Desktop release gates + +- `npm run type-check` passed. +- `npm run lint` passed. +- The canonical `npm test` lifecycle passed, including its pretest matrix, Electron main tests, native Swift/Rust/helper tests, and registered suites. It was repeated successfully after registering and hardening the physical ActivityKit process-proof runner. +- `npm run build` passed. Vite emitted only its existing chunk-size advisory. +- `npm run type-check:e2e` passed. +- CI no longer points at the removed `ios/HermesMobile.xcodeproj` or requests an iOS Simulator. Its iOS gate now runs `build-for-testing` against `ios/AidenOnTheGo.xcodeproj`, the `AidenOnTheGo` scheme, and generic iOS hardware with signing disabled. The exact command passed locally without addressing or launching a simulator. +- `npm run test:aiden-remote` passed 119/119 production Remote Access tests plus 7/7 deterministic transport tests. The production-service integration proves that a fresh local pairing window issues a distinct credential after revocation, keeps the old credential at typed `403 credential_revoked`, and never serializes either raw credential. New Tailscale regressions cover first-listener creation from `{}`, exact HTTPS certificate-domain eligibility, canonical prefix restoration, conflict/Funnel preservation, cleanup-only migration of an exact persisted origin-only route, and a system-trusted loopback proof payload. +- The strengthened Remote Access lifecycle Playwright spec passed 1/1 and now proves the health endpoint is unreachable before Remote Access is enabled. +- `npm run package` and `npm run package:verify` passed for the hardened signed development package at `release/development/mac-arm64/Aiden Agent.app`. Packaging emitted the existing Foundation Models helper deprecation warning but completed successfully. +- A packaged-app listener-off smoke used isolated Aiden config and Electron user-data directories. The exact test process remained alive for five seconds, port `49220` never listened, deep code-sign verification passed, and only that test process was terminated. The already-running installed Aiden Agent was not modified or stopped. + +The TypeScript remote-protocol suite and the signed Swift `AidenRemotePhase0Tests` both consume the checked-in shared contract fixture. Their complete registered suites pass on their respective release gates. + +## Physical iPhone and iOS release gates + +- The complete signed iPhone 13 Pro suite now executes 70 tests: 65 pass, five live-environment tests are expected skips, and zero fail. This includes the final bounded-attachment, malformed-reference, server-restart, credential-revocation, installation-scoped re-pair replacement, unpaired App Intent handoff, real ActivityKit lifecycle, fresh-manager authenticated reconciliation, and opt-in actual process-boundary ActivityKit coverage. +- A separate environment-enabled test passed 1/1 on the physical iPhone 13 Pro over LAN HTTPS. It paired to the Mac's private-CA endpoint, uploaded and explicitly discarded attachment references, consumed a Markdown reference exactly once, rejected replay with `409 handle_invalid`, preserved path-free metadata in authoritative history, rejected a duplicate approval with `409 approval_expired`, and completed the reconnect/approval/cancel stream lifecycle. +- A second environment-enabled test passed 1/1 in 105.983 seconds on the same physical iPhone. After authenticated access, the Mac server process was stopped and replaced on the identical LAN address and port with the same private CA, leaf certificate, SPKI pin, instance ID, and digest-only persisted device authorization. A fresh pinned client reconnected while pairing remained closed; after durable revocation, another fresh client rejected the old credential with typed `403 credential_revoked`. An explicit local re-pair window then accepted an independent one-use secret, issued a distinct device credential, rejected secret replay with `401 pairing_closed`, restored authenticated access, and kept the old credential invalid. After one more same-identity process restart, the new credential still worked and the old credential still returned typed `403 credential_revoked`. Persisted state contains only the active credential SHA-256 digest and a bounded set of prior revoked SHA-256 digests; no raw credential is written. The deterministic transport test separately proves atomic digest replacement, owner-only state, marker cleanup, and the same restart durability. The ordinary 65-test regression suite passed afterward. +- A real Tailscale proof passed 1/1 in 1.611 seconds on the physical iPhone 13 Pro. Starting from empty Serve status, Aiden created only `/api/aiden/v1`, targeting the exact loopback API base so Tailscale's stripped mount prefix was restored. The phone used system certificate validation plus the live Tailscale leaf SPKI pin, reported `connectionMode=tailscale`, paired, authenticated, browsed an approved root, completed folderless/scratch/selected-folder workspace CRUD, rejected selection replay, and reconciled authoritative state. Route-specific disconnect returned Serve status to `{}`; Funnel and unrelated routes were untouched. The temporary backend, secret-bearing xctestrun, DerivedData, and logs were moved to Trash after the route and server stopped. +- The final focused `AidenNativeIntegrationTests` suite passed 11/11 on that same physical iPhone. It reads the installed test host's privacy manifest and third-party notice, verifies the copied Hermex MIT license, checks all required camera/local-network/microphone/speech/Live Activity declarations, confirms there is no production ATS exception, pins the canonical public Privacy Policy and Support HTTPS destinations, locks the neutral unpaired deep-link error presentation, performs an actual ActivityKit request/update/stale/end lifecycle with immediate cleanup, has a fresh manager adopt and reconcile the persisted activity through the authenticated production client, and keeps the opt-in process proof inert when its paired environment keys are absent. +- A real cold custom-URL launch used `aiden-otg://new-chat`, the same bounded handoff emitted by the New Chat App Intent. On the unpaired physical iPhone it launched the installed app, failed closed without creating or sending a chat, and displayed the actionable “Connect to Aiden Agent before opening this link” message. Physical screenshot review found and corrected the misleading pairing-specific alert title; the repeated cold launch displayed the neutral Aiden product title. Both temporary captures were moved to Trash. +- The first real ActivityKit lifecycle run exposed that `await Activity.update` does not guarantee the public `activity.content` snapshot is already current. Rapid tool, token, and stale events could therefore derive from an older rendered snapshot and overwrite newer semantic state. The production manager now keeps a MainActor-isolated canonical state per Activity ID, hydrates persisted activities during reconciliation, ignores ended/dismissed activities, and clears terminal cache entries. One physical regression sends tool → token → stale without waiting for rendered state, verifies the eventual state is responding/stale with response excerpts still off, then ends and removes the activity immediately. Another releases the original manager, instantiates a fresh manager in the same test host, adopts the system-persisted activity, verifies the exact authenticated `GET /api/aiden/v1/streams/:id` reconciliation request, and confirms the rendered status and immediate cleanup. The registered `ios:activitykit-process-proof` command goes further: it validates a matching physical CoreDevice/Xcode destination, builds once, starts the activity in one host, confirms that host is absent, switches the temporary `.xctestrun` to `UseDestinationArtifacts` so Xcode cannot reinstall the app, and passes authenticated adoption and cleanup in a distinct host process. Four registered Node assertions lock its physical-only destination and no-reinstall contract. The complete 70-test signed suite passed after this proof. +- A fresh test-free signed Debug app was built for the physical iPhone 13 Pro. It contains no XCTest plug-in, embeds and validates the Live Activity widget, generates/trains App Intent metadata, and includes the exact third-party notices used by the shipping target. +- That exact app passed deep code-sign verification, installed, and launched as `sbtbiswas.AidenOnTheGo` on the iPhone 13 Pro (`iPhone14,2`). +- The final bounded-attachment follow-up repeated that gate from a separate clean DerivedData directory: the test-free app contained no XCTest bundle, passed strict deep code-sign verification, installed, and launched on the same iPhone 13 Pro. +- A fresh generic-hardware Release archive succeeded at `/tmp/AidenOnTheGo-phase12-final-20260819.xcarchive`. Xcode performed its store-validation build step, embedded the widget, and generated App Intent metadata. +- The bounded-attachment follow-up produced a fresh generic-hardware Release archive at `/tmp/AidenOnTheGo-attachments-20260819.xcarchive`. Store validation and App Intent generation passed, the archive contains no XCTest bundle, and its app passes strict deep code-sign verification. +- Archive inspection confirms bundle `sbtbiswas.AidenOnTheGo`, marketing version `1.5`, build `1`, team `5WP229CBB8`, App Group `group.sbtbiswas.AidenOnTheGo`, no test bundle, privacy tracking disabled, no declared collected-data types, and complete third-party notices. The archived Hermex attribution is the upstream license bundled under `ThirdPartyNotices`; `ios/LICENSE` is Aiden On The Go's own MIT license. +- No simulator was used for Phase 12, and the iPhone 16 Pro Max was untouched. + +Both archives are signed with `Apple Development: Sambit Biswas (7EK65FX44E)` and have `get-task-allow=true`. They are therefore verified development archives, not App Store/TestFlight export candidates. Distribution signing and export remain open. + +## Direct product audit + +- Shipping Release Swift sources use only `/api/aiden/v1`; the imported Hermes product sources were removed from the repository rather than retained outside the target. +- The current locked Xcode package graph contains KeychainAccess 4.2.2 plus Hermex's MarkdownUI 2.4.1 rendering foundation and its NetworkImage 6.0.1 and swift-cmark 0.8.0 transitive dependencies. `Package.resolved`, the Xcode project, the bundled notice directory, and a registered source/package regression test agree on that exact graph. A clean generic-hardware Release compile and the focused physical-iPhone integration suite passed after the cleanup; no simulator was addressed or launched. +- Shipping localization has no Hermes, Hermex, Kanban, or Cloudflare product copy. +- The archive contains Aiden app icons for iPhone and iPad, the privacy manifest, ActivityKit/App Group entitlements, App Intent assets, Local Network/Bonjour/Speech/Microphone/Camera descriptions, and the licensed third-party notice folder. +- The imported Hermex implementation foundation is attributed in the bundled notice and its exact MIT license ships with the app. +- The shipping Aiden app menu now exposes the canonical public Privacy Policy and Support destinations through native `Link` actions. The previously configured GitHub privacy URL returned `404` and lived only in dead imported settings code; the active target now includes the shared link config, and the physical-iPhone test locks the corrected destinations. +- TestFlight instructions and the metadata draft now resolve developer/team name, feedback email, marketing/support/privacy URLs, and copyright from the Contact Sheet identity reference plus Aiden's live website/repository. Contact Sheet's product domain was not copied. The current public policy still needs mobile-specific copy before submission. +- Internal-only and external-capable App Store Connect export option plists plus manual upload workflows were adapted from Hermex with Aiden's project, bundle, and pinned team. Both workflows are main-only, confirmation-gated, protected by separate GitHub environments, and use pinned actions. The external path checks for a closed App Store version train before archiving and uploads only; tester assignment and Beta App Review submission remain manual. The build selector passes 20 tests/42 assertions, and three deterministic workflow/export-policy tests are registered in `npm test`. No workflow was dispatched and owner-only signing, app-record, credential, and approval gates remain intact. +- Current Apple category/privacy/age-rating definitions resolve the release draft to Developer Tools / Productivity, no developer-collected data, and a conservative 13+ override matching Aiden's published under-13 policy. The draft includes exact questionnaire reasoning and current physical-device screenshot dimensions. Four registered metadata/ASC safety tests lock public links, field limits, category, privacy manifest alignment, absence of telemetry SDKs, telemetry-off strict ASC commands, and credential ignores. +- A read-only Rork `asc 3.4.0` audit found the main Aiden bundle ID and App Groups capability under team `5WP229CBB8`, but no accessible App Store app record, widget bundle identifier, or Aiden distribution profile. Aiden's live website links to an active public **Aiden - Quick AI** TestFlight beta whose page describes the existing macOS product and says it is available on iOS; its numeric App ID and bundle ID are not public. This proves the zero-result query is a credential-scope result and makes record reconciliation with the correct Aiden profile mandatory before creation. The accessible team has one iOS Distribution certificate, while the Mac still lacks its Apple Distribution private-key identity. The default ASC profile is Parsely-named and the Apple web session is unauthenticated, so no external mutation or placeholder automation was created. +- A post-ActivityKit read-only refresh confirms that external state is unchanged: `asc auth status` still exposes only the default `Parsely ASC` Keychain profile, the exact `sbtbiswas.AidenOnTheGo` app query still returns zero visible App Store Connect records, filtered Developer Portal output contains the main universal identifier but not `sbtbiswas.AidenOnTheGo.LiveActivityWidget`, filtered iOS App Store profiles contain no Aiden entry, and `security find-identity` still reports no Apple Distribution identity. Telemetry was disabled, strict authentication was used for API queries, unrelated account records were filtered before output, and no mutation occurred. + +## Internal TestFlight evidence — 2026-08-19 + +- The owner subsequently created the distinct Aiden On The Go record (App ID `6803233275`), provisioned the widget identifier and App Store signing, and authorized the existing strict ASC profile for this release. +- The first `0.1.0` upload used build `1` and was rejected before processing with `ITMS-90717`; inspection confirmed that the compiled App Store icon source contained alpha. +- Build `2` copies the exact RayChat `AppIcon.icon` package and its opaque 1024×1024 RGB artwork. Registered release tests lock the package, artwork digest, dimensions, PNG color type, resource-phase integration, and matching asset-catalog fallback. The generic-iOS archive's compiled 120×120 iPhone and 152×152 iPad icons both report `hasAlpha: no`. +- The exported IPA is internal-only, has the exact app/widget identifiers and App Group, is signed by Apple Distribution team `5WP229CBB8`, has `get-task-allow=false` on both bundles, and passes strict deep code-sign verification. +- App Store Connect build `721aeb9d-2b33-4729-8d10-5bc1783abbef` processed as `VALID`, is assigned to the `Internal Testers` group, and reports `internalBuildState=IN_BETA_TESTING` / `externalBuildState=NOT_APPLICABLE`. The account holder is assigned to that group. No simulator was used. +- Build `3` fixes an iPhone navigation regression in which `NavigationSplitView` highlighted a workspace row without transitioning to its chats. Compact layouts now use an explicit value-driven `NavigationStack`; regular layouts retain split navigation and wrap the detail in its own stack. Creation, folder registration, deep-link handoff, deletion, and size-class transitions share reconciled selection/path behavior. +- The focused navigation/appearance suite passed 5/5 and the adjacent chat suite passed 9/9 on the physical iPhone 13 Pro. A test-free `0.1.0 (3)` Debug app was then built, installed, verified by `devicectl`, and launched on that same device. No simulator or iPhone 16 Pro Max was used. +- The build `3` internal-only IPA reports `TFInternalTestingOnly=true`, the exact app/widget/App Group identities, Apple Distribution team `5WP229CBB8`, and `get-task-allow=false`; strict deep signing verification passes and both compiled iPhone/iPad icons report no alpha. App Store Connect build `e5f0ae7e-35aa-451e-be87-bc039885b2de` processed as `VALID` and reports `internalBuildState=IN_BETA_TESTING` / `externalBuildState=NOT_APPLICABLE` for `Internal Testers`. +- Build `4` contains the Aiden-native onboarding/home/settings/composer refresh, privacy-safe Mac usage projection, accessible Berry repair, and complete dead imported-source cleanup. The full signed iPhone 13 Pro suite executed 73 tests: 68 passed, five environment-gated live proofs skipped, and zero failed. A separate clean `0.1.0 (4)` app contained no XCTest bundle, installed, and launched on that phone. Its locally exported internal-only IPA is Apple Distribution-signed for team `5WP229CBB8`, has exact app/widget/App Group identities and `get-task-allow=false`, contains no XCTest bundle, and passes strict deep signature verification. The telemetry-off strict `Parsely ASC` upload created exact build `ee5b7c23-14d6-44e7-a4ba-6a5003018758`; Apple reports `VALID`, `internalBuildState=IN_BETA_TESTING`, and `externalBuildState=NOT_APPLICABLE` after assignment to `Internal Testers`. No simulator or iPhone 16 Pro Max was used. +- A post-upload cold-launch check showed build `4` could request workspaces/server while still connecting, then fail to retry the home aggregate after the coordinator became connected. Build `5` keys that load to the connection-state transition; an installed clean build on the iPhone 13 Pro then produced successful Mac-side `chats`, `scheduledTasks`, and `usage` requests, including `/usage` status 200. The complete 73-test physical suite remained green, the shipping-shell regression locks the connected-state trigger, and the internal-only Apple Distribution IPA repeated all identity, entitlement, no-test-bundle, and signature gates. Exact App Store Connect build `6173d5e2-0e58-4d0a-92fa-fc804fc82c37` is `VALID`, `IN_BETA_TESTING` for `Internal Testers`, and external state is `NOT_APPLICABLE`. +- A fresh build `6` audit against the retained Hermex reference corrected the remaining locally actionable shell mismatches: New Agent creates a managed scratch workspace, cold navigation handoffs wait for a connected coordinator without self-cancelling, handoff chats expose a native Close action, and the Live Activity extension embeds and tints the Aiden sidebar logo for starting/thinking rather than using brain/sparkle glyphs. The registered shipping test locks those contracts. The full physical iPhone 13 Pro suite executed 73 tests with 68 passes, five expected live-environment skips, and zero failures; release policy passes 20 Ruby tests/42 assertions plus 23 Node tests; Remote Access passes 120 production plus seven transport tests. The local internal-only IPA reports `0.1.0 (6)`, exact app/widget/App Group identities, `TFInternalTestingOnly=true`, Apple Distribution team `5WP229CBB8`, `get-task-allow=false`, no XCTest content, the widget Aiden logo resource, and a valid strict deep signature. App Store Connect build `aa994233-1bf3-4482-86f5-b8b0356eee25` is `VALID`, assigned to `Internal Testers`, `IN_BETA_TESTING`, and externally `NOT_APPLICABLE`. No simulator or iPhone 16 Pro Max was used. +- The owner clarified that the `Parsely ASC` profile may be used for Aiden On The Go within the resources it can access. Future commands must still select it explicitly, remain scoped to exact Aiden identifiers, and must not treat the profile's zero visible app records as permission to duplicate the existing public Aiden beta. +- A registered `ios:asc-monitor` wrapper now gives future Codex automations a tested read-only entry point. It requires an explicit named Aiden profile and exact App/build resource IDs, disables ASC telemetry, enforces strict auth, scopes build processing to `builds info --build-id`, and emits privacy-safe TestFlight change summaries without tester identity, feedback text, screenshots, or crash content. Six focused monitor tests join the seven metadata/workflow policy tests; no monitor was dispatched without an authoritative target. +- Canonical ASC `en-US` localization files now exist for the shipping `1.5` draft. The stable CLI's offline validator scans both files with zero errors and zero warnings; registered tests keep their name, subtitle, URLs, keywords, field limits, description, and intentionally omitted optional keys aligned with the reviewed metadata document. A separate mobile privacy/support patch is ready for owner/legal review and covers direct LAN/Tailscale pairing, Keychain/cache behavior, attachments/provider forwarding, Local Network/Camera/Photos/Files/on-device Speech permissions, App Intents, Live Activities, external media requests, and a visible support email. The website lives outside this repository, so publication remains an explicit external gate. +- `xcrun devicectl` captured the shipping app's pairing screen from the physical iPhone 13 Pro at its native `1170 × 2532` resolution. An opaque JPEG conversion passed local `asc screenshots validate` for the matching display type. This proves the physical screenshot pipeline but is not retained as a final store asset because it is not the final distribution candidate and the required physical-iPad set remains unavailable. + +## Plan completion audit + +The current source, registered test commands, signed artifacts, and phase evidence were re-audited against every phase acceptance statement in `docs/plans/aiden-on-the-go-plan.md`: + +| Phases | Implementation result | Remaining acceptance | +| --- | --- | --- | +| 0–4 | Complete and covered by the protocol, service-boundary, real-HTTP, lifecycle, package, and signed transport evidence in the corresponding phase files. | None. | +| 5 | Shipping target contains the Aiden identity and launches on the physical iPhone. | Physical-iPad launch. | +| 6 | Pairing, trust, installation switching/revocation/re-pair replacement, approved-root browsing, selected-folder registration, and workspace CRUD are implemented; LAN and real Tailscale are proven on the physical iPhone. | Repeat LAN and Tailscale acceptance on a physical iPad. | +| 7 | Complete, including bounded attachments, replay-safe turns, authoritative reconciliation, duplicate-approval rejection, and physical-iPhone LAN streaming. | None specific to Phase 7. | +| 8 | Aiden appearance parity, adaptive navigation, accessibility contracts, and physical-iPhone regression coverage pass. | Physical-iPad visual/accessibility/keyboard/rotation/split-view/Stage Manager matrix. | +| 9–10 | Files/Git and Scheduled Tasks are implemented through shared desktop services and native clients with strict DTO, mutation, cache, and idempotency tests. | Their physical-iPad cross-network coverage follows the Phase 6/8 gates. | +| 11 | App Intents, the signed Live Activity widget, on-device dictation, local read-aloud, real ActivityKit lifecycle, fresh-manager adoption, and authenticated reconciliation across distinct physical-device app-host processes are implemented and tested. | Manual Siri/Shortcuts, live permission/dictation, and direct system-UI observation of a real server-streamed Live Activity. | +| 12 | All available automated, package, archive, release-policy, physical-iPhone LAN/Tailscale, attachment, duplicate-approval, same-endpoint process-restart, durable credential-revocation, and credential-rotation/re-pair gates pass. | The external and manual gates listed below. | + +This audit found no additional locally actionable missing product surface. The remaining items require unavailable hardware, physical network transitions, direct system-UI observation, a second physical client, or release-owner credentials and metadata. + +## Gates still open + +- Physical-iPad LAN/Tailscale pairing, workspace acceptance, visual, accessibility, keyboard, rotation, split-view, and Stage Manager matrix. +- Physical sleep/wake, address change, and multi-device concurrency acceptance. +- Manual Siri/Shortcuts invocation, live microphone/speech permission UI and dictation, and direct system-UI observation of a real server-streamed Live Activity. +- Mobile-specific privacy-policy publication, visible support contact, owner publication of the drafted age/privacy answers, final physical-device screenshots, review phone/environment, remaining metadata approval, external TestFlight/Beta App Review, and public-release decisions. + +Phase 12 and the overall plan must remain active until those gates are completed or deliberately re-scoped by the owner. diff --git a/docs/testing/aiden-on-the-go/phase-2.md b/docs/testing/aiden-on-the-go/phase-2.md new file mode 100644 index 00000000..fda6b916 --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-2.md @@ -0,0 +1,52 @@ +# Aiden On The Go — Phase 2 evidence + +Date: 2026-08-19 +Status: Complete — Remote Access is an explicit, off-by-default desktop capability with authenticated LAN/Tailscale transports, pairing, revocation, approved roots, Settings, onboarding, and documentation. + +## Implementation boundary + +- `AidenRemoteService` owns the app-lifetime listeners. Disabled startup creates no TLS identity, listener, Bonjour advertisement, or Tailscale route. LAN uses dual-stack HTTPS and `_aiden-agent._tcp`; Tailscale fronts a loopback-only HTTP listener at the exact `/api/aiden/v1` route. +- Installation-local P-256 CA/server identities are persisted owner-only. LAN pairing carries the private CA certificate plus the leaf SPKI pin; Tailscale pairing verifies and pins the actual system-trusted peer identity. +- Pairing opens only after a local desktop action, expires after five minutes, consumes a 256-bit secret once, stores only credential digests, and supports durable per-device revocation and capability checks. +- The HTTP router uses exact paths, bounded duplicate-key-safe JSON, strict protocol and bearer headers, no CORS/browser origin, safe error envelopes, request IDs, connection/time limits, and redacted logging. Health is the only unauthenticated endpoint. +- Tailscale Connect/Disconnect previews and owns only Aiden's exact non-Funnel Serve route. It detects conflicts, verifies the installed route, never runs `serve reset`, and preserves unrelated routes. +- Settings exposes enable/mode, endpoints, route preview, Connect/Disconnect, QR pairing, approved roots, paired devices, and revocation using existing Aiden primitives and semantic tokens. The onboarding tour includes a dedicated transparent 1024×1024 Aiden On The Go illustration. + +## Review outcome + +The between-phase review was performed locally, per the user's direction to use no subagents. It checked disabled-start side effects, TLS trust semantics, IPv4/IPv6 reachability, one-time pairing, route ownership/cleanup, authentication boundaries, QR secrecy, renderer IPC validation, window-close lifetime, quit cleanup, onboarding accessibility, and documentation. + +Review fixes included dual-stack LAN binding for Bonjour/IPv6, a versioned IPC-only QR trust envelope for the private LAN CA, targeted ESLint globals for transport spike scripts, and updates to source-contract tests after the Phase 1 application-service extraction. No production failure remains at the Phase 2 gate. + +## Tests + +```text +npm run test:aiden-remote +PASS — 76 TypeScript tests plus 4 transport spike tests + +npm run test:onboarding +PASS — 18 tests, including tile/asset/alpha contracts + +npm run type-check +PASS + +npm run type-check:e2e +PASS + +npm run lint +PASS + +npm run build +PASS + +npx playwright test tests/e2e/remote-access-lifecycle.spec.ts --config=playwright.config.ts +PASS — off by default, enable, authenticated HTTPS health, service survives main-window close + +npx playwright test tests/e2e/settings-model-picker.spec.ts --config=playwright.config.ts +PASS — Remote Access destination renders Off and unchecked by default + +npm test +PASS — complete TypeScript/JavaScript, native helper, Git safety, and Rust lifecycle gate +``` + +`git diff --check` also passes for the completed Phase 2 state. diff --git a/docs/testing/aiden-on-the-go/phase-3.md b/docs/testing/aiden-on-the-go/phase-3.md new file mode 100644 index 00000000..f7405994 --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-3.md @@ -0,0 +1,47 @@ +# Aiden On The Go — Phase 3 evidence + +Date: 2026-08-19 +Status: Complete — The authenticated remote API supports safe workspace-registry CRUD and approved-root folder selection without exposing or accepting Mac paths. + +## Implementation boundary + +- Workspace list/get/create/update/remove routes use allowlisted path-free DTOs, strict request schemas, scoped durable idempotency, exact revision preconditions, the shared workspace mutation registry, default-workspace recovery, and immediate Electron invalidation. +- Folderless and managed-scratch creation are supported. Folder-backed creation accepts only a short-lived one-use selection token issued by the server-controlled browser; it never accepts an absolute or relative client path. +- Approved-root navigation is directory-only, nonrecursive, deterministic, paginated, depth-bounded, and excludes hidden/system entries and symlinks. Location handles, cursors, and selections are opaque and bound to the instance, device, root policy, canonical directory identity, and expiry. +- Registration revalidates root approval, policy revision, real path, directory device/inode identity, and duplicate state inside the serialized workspace commit. Removing a workspace never deletes its folder, while generic deletion refuses managed worktrees. + +## Review outcome + +The between-phase review was performed locally, following the user's direction not to use subagents. It checked DTO secrecy, parser aliases, cursor isolation, replay, expiry, symlink and root replacement, root-policy removal, selected-folder TOCTOU, stale revisions, deletion side effects, idempotency restart behavior, operation snapshot secrecy, desktop reconciliation, and disabled-start side effects. + +Review fixes moved revision checks and directory revalidation inside authoritative mutation leases, prevented stale removal requests from closing a workspace terminal, sanitized control characters from browser labels, and made the idempotency store persist the in-flight admission before mutation and terminal response afterward. No production failure remains at the Phase 3 gate. + +## Tests + +```text +npm run test:aiden-remote +PASS — 88 TypeScript tests plus 4 transport spike tests + +npm run test:aiden-service-boundary +PASS — 12 tests + +npx playwright test tests/e2e/remote-access-lifecycle.spec.ts --config=playwright.config.ts +PASS — 1 test + +npm run lint +PASS + +npm run type-check +PASS + +npm run type-check:e2e +PASS + +npm run build +PASS + +npm test +PASS — complete TypeScript/JavaScript, native helper, Git safety, and Rust lifecycle gate +``` + +The real HTTP integration test browses an approved root, exchanges the opaque directory handle for a selection, creates a workspace, patches it with its revision, removes it, verifies default-workspace preservation, and asserts that no raw path appears in the response. `git diff --check` also passes for the completed Phase 3 state. diff --git a/docs/testing/aiden-on-the-go/phase-4.md b/docs/testing/aiden-on-the-go/phase-4.md new file mode 100644 index 00000000..93c0ef0f --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-4.md @@ -0,0 +1,48 @@ +# Aiden On The Go — Phase 4 evidence + +Date: 2026-08-19 +Status: Complete — The authenticated remote API supports chat CRUD, atomic remote turns, resumable server-owned generation streams, cancellation, approvals, and a privacy-safe provider/model catalog. + +## Implementation boundary + +- Chat list/get/create/rename/delete and empty-chat move use path-free DTOs, exact parsers, scoped durable idempotency, and revision assertions inside the shared serialized chat mutation boundary. Desktop and remote clients receive immediate chat invalidation after mutations. +- Remote turn creation commits one user message through the existing turn-admission lease, then transfers generation to a stable device-and-stream-bound owner. A setup failure after the append returns the accepted message plus a terminal error stream; an indeterminate append remains durably in flight rather than becoming retryable. +- The bounded SSE journal records typed status, text, reasoning, tool, timeline, approval, terminal, error, cancellation, and reconciliation events with monotonic sequence IDs. Reconnect replays from `Last-Event-ID`; future cursors fail closed and pruned gaps receive an authoritative snapshot. +- Disconnecting delivery does not cancel main-owned work. Explicit cancellation, approval expiry, device revocation, restart interruption, journal retention, and persistence settlement all preserve per-device ownership and terminal reconciliation. +- Provider/model projection includes only configured chat-capable choices and contains no credentials, endpoint URLs, authentication methods, or embedding-only entries. Remote turns cannot enable Computer Use and retain the workspace's existing permission/tool contract. + +## Review outcome + +The between-phase review was performed locally, following the user's direction not to use subagents. It covered cross-device stream and approval isolation, duplicate sends, stale revisions, post-append setup failures, unknown append outcomes, reconnect gaps, cancellation and approval replay, approval expiry, device revocation, restart recovery, model secrecy, desktop cache refresh, disabled-start side effects, and deletion ordering. + +Review fixes added automatic expiry denial for unattended approvals, coalesced stream-journal persistence, graceful quit settlement, a pre-side-effect delete revision check plus an authoritative final check, and compatibility preservation for removing an indexed chat whose payload is corrupt. No production failure remains at the Phase 4 gate. + +## Tests + +```text +npm run test:aiden-remote +PASS — 102 TypeScript tests plus 4 transport spike tests + +npm run test:aiden-service-boundary +PASS — 13 tests + +npx playwright test tests/e2e/remote-access-lifecycle.spec.ts --config=playwright.config.ts --fail-on-flaky-tests +PASS — 1 test + +npm run type-check +PASS + +npm run type-check:e2e +PASS + +npm run lint -- --no-fix +PASS + +npm run build +PASS + +npm test +PASS — complete TypeScript/JavaScript, native helper, Git safety, and Rust lifecycle gate +``` + +The real HTTP integration test starts one mocked remote turn, reconnects and replays its SSE journal without duplicating the prompt, allows and denies device-owned approvals, cancels a turn, and verifies the final append count. A signed Debug build with the approved Aiden identifiers was installed and launched on the physical iPhone 13 Pro; the iPhone 16 Pro Max and all simulators were untouched. `git diff --check` also passes for the completed Phase 4 state. diff --git a/docs/testing/aiden-on-the-go/phase-5.md b/docs/testing/aiden-on-the-go/phase-5.md new file mode 100644 index 00000000..100dd9fa --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-5.md @@ -0,0 +1,10 @@ +# Aiden On The Go — Phase 5 evidence + +Date: 2026-08-19 +Status: Implementation complete; physical-iPad launch acceptance remains open. + +The shipping target now compiles only the Aiden application shell, remote transport, pairing, workspace, chat, cache, Keychain, and shared Aiden contract sources. Retained Hermex files remain available in repository history/imported source for later deliberate adaptation, but are not members of the Aiden target. Built-product identity assertions reject Hermes, Hermex, Kanban, Cloudflare, and legacy bundle/service identifiers. + +A registered source-membership regression test now locks the exact app, test, and Live Activity widget source allowlists. It also scans only shipping Swift sources for imported product identity and legacy `/api/*` routes, and locks the shipping route family to `/api/aiden/v1`. + +Automatic signing uses team `5WP229CBB8`, bundle identifier `sbtbiswas.AidenOnTheGo`, the Aiden Keychain service, and the approved URL/app-group namespace. The signed app builds, installs, launches, and runs tests on the physical iPhone 13 Pro. No simulator was used and the iPhone 16 Pro Max was untouched. A physical iPad was not connected, so the plan's physical-iPad launch criterion is recorded as open rather than inferred from compilation. diff --git a/docs/testing/aiden-on-the-go/phase-6.md b/docs/testing/aiden-on-the-go/phase-6.md new file mode 100644 index 00000000..e00a156e --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-6.md @@ -0,0 +1,12 @@ +# Aiden On The Go — Phase 6 evidence + +Date: 2026-08-19 +Status: LAN and Tailscale implementation verified on a physical iPhone; physical-iPad acceptance remains open. + +The Swift client supports canonical QR pairing, private-CA or system trust plus the exact P-256 leaf SPKI pin, per-installation Keychain credentials, multiple Aiden installations, switching/removal/revocation, workspace registry CRUD, managed scratch creation, and opaque approved-root folder exploration/selection. Legacy installation metadata without an explicit pairing trust policy fails closed and requires secure re-pairing. + +On the physical iPhone 13 Pro, a real Wi-Fi LAN harness proved QR exchange, authenticated server discovery, folderless create/update/delete, scratch create/delete, approved roots/children, selected-folder registration, one-use selection replay rejection, and authoritative final reconciliation. The strict certificate proof separately covers wrong pin, renewal, rotation, host mismatch, and expiry. No simulator was used and the iPhone 16 Pro Max was untouched. + +The same physical iPhone then passed 1/1 in 1.611 seconds through a real Aiden-owned Tailscale Serve route. It used the tailnet's system-trusted certificate plus the exact live SPKI pin, reported `connectionMode=tailscale`, paired, authenticated, browsed the approved root, completed folderless/scratch/selected-folder workspace CRUD, rejected selection replay, and reconciled to an empty authoritative workspace list. The proof began from an empty Serve configuration and exposed two defects that are now covered by regression tests: first-connect must derive HTTPS eligibility from the exact Tailscale certificate domain rather than requiring a pre-existing 443 listener, and the loopback proxy target must restore `/api/aiden/v1` because `--set-path` strips the public mount prefix. Cleanup removed only Aiden's exact path and returned `tailscale serve status --json` to `{}`. + +The remaining Phase 6 acceptance work is explicit: repeat LAN and Tailscale pairing, browsing, and workspace CRUD from a physical iPad. No simulator was used and the iPhone 16 Pro Max was untouched. diff --git a/docs/testing/aiden-on-the-go/phase-7.md b/docs/testing/aiden-on-the-go/phase-7.md new file mode 100644 index 00000000..2b2257e9 --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-7.md @@ -0,0 +1,23 @@ +# Aiden On The Go — Phase 7 evidence + +Date: 2026-08-19 +Status: Complete. + +The Swift workspace shell now provides chat list/detail/create/rename/delete, native Aiden conversation presentation, provider/model/thinking selection, atomic text and attachment turns, reasoning, tool/timeline activity, inline approvals, stop, strict resumable SSE, terminal authoritative reconciliation, and an installation/chat-scoped protected cache. + +Attachment support is end to end rather than UI-only. The authenticated API accepts bounded JSON image or UTF-8 text uploads and returns short-lived, one-use references bound to the exact device and chat. A turn accepts at most ten unique references and atomically translates them into Aiden's existing canonical attachment model. References expire after ten minutes, are removed on discard, consumption, or device revocation, and never expose bytes or paths in remote message projections. Image admission is limited to PNG/JPEG, 8 MiB, 16,384 pixels per dimension, and 40 megapixels; text is MIME-allowlisted and limited to 400,000 UTF-8 bytes and 100,000 Unicode scalars. Store count and retained-byte ceilings bound aggregate memory. + +The native composer uses the retained system `Menu`, `PhotosPicker`, and file importer. Images are bounded before decode, dimension-checked, downscaled, and transcoded to JPEG. Text import reads only a bounded prefix, validates UTF-8, and visibly marks truncation. The client rejects malformed attachment references, allows attachment-only turns, and reuses the exact turn idempotency key after an ambiguous transport failure so a retry cannot duplicate the prompt or consume the attachment twice. + +Streaming stores the accepted `streamId`, `turnId`, and last applied sequence before consumption. Reconnect opens the existing stream after the durable cursor and never calls the turn endpoint again. Duplicate events are ignored, gaps trigger authoritative chat reconciliation, terminal events reload server history, and cached streams are restored through the status endpoint after app/view recreation. Raw SSE bytes preserve blank frame boundaries and enforce UTF-8 and frame limits before strict event decoding. + +Verification on the physical iPhone 13 Pro: + +- Signed build-for-testing succeeded. +- 64 XCTest cases executed on the iPhone 13 Pro: 59 passed, five environment-gated live proofs were expected skips, and zero failed. +- Native tests cover image transcode/dimension/byte limits, bounded text-file reads, metadata-only DTOs, fail-closed reference validation, canonical upload/discard routes, attachment-only turn projection, and exact-request idempotency-key reuse. +- The extended opted-in Phase 7 LAN test passed in 1.039 seconds. From the physical iPhone it paired through the canonical private-CA payload at the Mac's `192.168.1.228` LAN address, created/renamed a chat, uploaded a Markdown attachment, discarded a second reference through `DELETE`, consumed the first reference in a turn, verified metadata in the accepted and authoritative user message, and verified a second use failed with typed `409 handle_invalid`. It then received sequences 1–2, reconnected after sequence 2 for reasoning/tool/timeline/approval events 3–7, allowed the approval, verified a duplicate decision failed with typed `409 approval_expired`, reconciled terminal events 8–10 and the authoritative assistant message, cancelled a second turn, then removed the chat and workspace. +- Desktop service and real-HTTP tests cover valid image/text and attachment-only turns, one-use and device/chat binding, duplicate and count rejection, expiry/revocation/discard, capacity, dimension/name validation, retry replay, and path-free legacy metadata sanitization. +- Deterministic chat tests also cover canonical mutation preconditions, model/turn/cancel/approval contracts, duplicate-key and SSE identity rejection, frame bounds, cache isolation between installations, and stream-cursor restoration. + +The live LAN attachment acceptance gap is closed for the iPhone path: the signed native client performed upload, discard, one-use consumption, replay rejection, metadata reconciliation, streaming, approval, cancellation, and cleanup over HTTPS from the physical iPhone 13 Pro to the Mac. Tailscale was unavailable during this Phase 7 run; Phase 12 later closes real-Tailscale pairing and authenticated workspace transport on the same phone. No simulator was used and the iPhone 16 Pro Max was untouched. diff --git a/docs/testing/aiden-on-the-go/phase-8.md b/docs/testing/aiden-on-the-go/phase-8.md new file mode 100644 index 00000000..05bb2f49 --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-8.md @@ -0,0 +1,18 @@ +# Aiden On The Go — Phase 8 evidence + +Date: 2026-08-19 +Status: Implementation complete; physical-iPad acceptance remains open. + +The shipping Swift target now uses semantic Aiden palette tokens backed by a versioned fixture shared with Electron. It provides System, Light, and Dark modes; independent Aiden, Slate, Berry, and Moss selections for light and dark; System, Rounded, and Humanist UI fonts; SF Mono, Menlo, and Monaco code fonts; bounded UI/code sizing; per-scheme contrast and sidebar translucency; system/forced reduced-motion behavior; and a device-local Git diff-marker preference. No desktop appearance API exists or is mutated. + +Appearance is available both before pairing and from the workspace shell. Native controls retain their semantic disabled and secondary states, custom scrolling honors the resolved motion preference, code inputs use the selected scalable code font, and the composer switches its selector layout at accessibility Dynamic Type sizes. The send/stop controls have 44-point targets. `NavigationSplitView` retains compact/regular workspace selection, uses a bounded sidebar appropriate to iPad resizing, and reconciles workspace CRUD without losing a still-valid detail selection. + +Verification: + +- The Electron appearance suite passes 13/13 tests, including exact shared-fixture parity and contrast checks for every built-in palette in light and dark. +- A signed physical-iPhone build-for-testing succeeded. +- The focused physical iPhone 13 Pro appearance suite passes 3/3 tests: exact palette parity, complete device-local preference persistence/normalization, and adaptive workspace-selection reconciliation. +- The complete physical iPhone 13 Pro suite executes 43 tests with zero failures; four explicit environment-gated transport/Keychain proofs are expected skips in the ordinary run. +- No simulator was used and the iPhone 16 Pro Max was untouched. + +The target declares both iPhone and iPad families and the adaptive implementation compiles in the signed device build. A physical iPad is not connected, so VoiceOver, hardware-keyboard, rotation, Stage Manager sizing, offline-state, and all-preset light/dark visual checks on actual iPad hardware have not been claimed. Those are the remaining Phase 8 acceptance items. diff --git a/docs/testing/aiden-on-the-go/phase-9.md b/docs/testing/aiden-on-the-go/phase-9.md new file mode 100644 index 00000000..1fb446c4 --- /dev/null +++ b/docs/testing/aiden-on-the-go/phase-9.md @@ -0,0 +1,20 @@ +# Aiden On The Go — Phase 9 evidence + +Date: 2026-08-19 +Status: Complete on the available physical-iPhone and desktop gates. + +The desktop now exposes workspace Files and Git through shared application services used by both Electron IPC and authenticated Aiden Remote routes. File identities and Git snapshots are opaque, device/workspace bound, bounded, and path-free on the wire. File writes use version preconditions. Git commit, push, checkout, branch creation, managed-worktree creation, and managed-worktree deletion require explicit foreground confirmation and durable idempotency; managed worktrees retain Aiden's mutation, rollback, ownership, terminal, generation, and scheduled-task safety gates. + +The Swift app provides native Files and Git destinations from Workspace Settings. It supports bounded file search/read/edit, offline cache reads, stale-write reconciliation, Git review/diff/compare/branches/commit/push/worktrees, destructive confirmation, and retry of ambiguous Git outcomes with the original idempotency key. It does not accept or reveal Mac paths or Git administration metadata. + +Verification: + +- TypeScript type-check passes. +- Shared application-service boundary tests pass 15/15. +- Aiden Remote tests pass 107/107 plus 4/4 LAN transport proofs. +- The complete subagent and workspace-mutation regression suite passes after its source-contract assertions were updated to follow the shared worktree application service. +- The complete signed physical iPhone 13 Pro XCTest suite passes, including opaque Files/Git DTO validation, cache isolation, canonical routes and mutation preconditions, and exact idempotency-key reuse after an ambiguous disconnect. Four explicit environment-gated transport/Keychain tests remain expected skips in the ordinary run. +- The installed app launches successfully on the iPhone 13 Pro. +- No simulator was used and the iPhone 16 Pro Max was untouched. + +The physical-iPad and real-Tailscale gates belong to Phases 6 and 8 and were not claimed by this phase. Phase 12 later closes real-Tailscale pairing and authenticated workspace transport on the physical iPhone; physical-iPad acceptance remains open. From 096c56823dbef19bac61c93cb50ad27c93356c59 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Thu, 20 Aug 2026 12:57:42 -0400 Subject: [PATCH 005/266] feat(ios): refine the Aiden mobile workspace shell --- ios/AidenOnTheGo.xcodeproj/project.pbxproj | 4 + .../Features/Remote/AidenChatFeature.swift | 361 ++++- .../Remote/AidenRemoteCoordinator.swift | 181 ++- .../Remote/AidenScheduledTasksView.swift | 32 +- .../Remote/AidenWorkspaceShellView.swift | 1194 +++++++++++++++-- .../Features/Shared/AidenProviderIcon.swift | 129 ++ ios/AidenOnTheGo/Models/AidenChat.swift | 26 + .../Contents.json | 16 + .../amazon-bedrock.svg | 1 + .../Contents.json | 16 + .../ant-ling.svg | 3 + .../Contents.json | 16 + .../anthropic.svg | 1 + .../Contents.json | 16 + .../apple-foundation-models.svg | 1 + .../Contents.json | 16 + .../azure-openai-responses.svg | 1 + .../Contents.json | 16 + .../cerebras.svg | 5 + .../Contents.json | 16 + .../ProviderLogo-claude.imageset/claude.svg | 1 + .../Contents.json | 16 + .../cloudflare-ai-gateway.svg | 1 + .../Contents.json | 16 + .../cloudflare-workers-ai.svg | 1 + .../Contents.json | 16 + .../deepseek.svg | 1 + .../Contents.json | 15 + .../fireworks.svg | 14 + .../Contents.json | 16 + .../github-copilot.svg | 1 + .../Contents.json | 16 + .../google-vertex.svg | 1 + .../Contents.json | 16 + .../ProviderLogo-google.imageset/google.svg | 1 + .../ProviderLogo-grok.imageset/Contents.json | 16 + .../ProviderLogo-grok.imageset/grok.svg | 9 + .../ProviderLogo-groq.imageset/Contents.json | 15 + .../ProviderLogo-groq.imageset/groq.svg | 1 + .../Contents.json | 16 + .../huggingface.svg | 1 + .../Contents.json | 16 + .../kimi-coding.svg | 1 + .../Contents.json | 16 + .../lmstudio.svg | 1 + .../Contents.json | 16 + .../minimax-cn.svg | 1 + .../Contents.json | 16 + .../ProviderLogo-minimax.imageset/minimax.svg | 1 + .../Contents.json | 16 + .../ProviderLogo-mistral.imageset/mistral.svg | 1 + .../Contents.json | 16 + .../moonshotai-cn.svg | 1 + .../Contents.json | 16 + .../moonshotai.svg | 1 + .../Contents.json | 16 + .../ProviderLogo-nvidia.imageset/nvidia.svg | 1 + .../Contents.json | 16 + .../ProviderLogo-ollama.imageset/ollama.svg | 1 + .../Contents.json | 16 + .../openai-codex.svg | 1 + .../Contents.json | 16 + .../ProviderLogo-openai.imageset/openai.svg | 1 + .../Contents.json | 15 + .../opencode-go.svg | 7 + .../Contents.json | 15 + .../opencode.svg | 7 + .../Contents.json | 16 + .../openrouter.svg | 1 + .../Contents.json | 15 + .../together.svg | 18 + .../Contents.json | 16 + .../vercel-ai-gateway.svg | 1 + .../ProviderLogo-xai.imageset/Contents.json | 16 + .../ProviderLogo-xai.imageset/xai.svg | 7 + .../Contents.json | 16 + .../xiaomi-token-plan-ams.svg | 1 + .../Contents.json | 16 + .../xiaomi-token-plan-cn.svg | 1 + .../Contents.json | 16 + .../xiaomi-token-plan-sgp.svg | 1 + .../Contents.json | 16 + .../ProviderLogo-xiaomi.imageset/xiaomi.svg | 1 + .../Contents.json | 15 + .../zai-coding-cn.svg | 219 +++ .../ProviderLogo-zai.imageset/Contents.json | 15 + .../ProviderLogo-zai.imageset/zai.svg | 219 +++ .../Resources/ThirdPartyNotices/NOTICE.txt | 2 + .../ThirdPartyNotices/ProviderLogos-NOTICE.md | 31 + 89 files changed, 2967 insertions(+), 164 deletions(-) create mode 100644 ios/AidenOnTheGo/Features/Shared/AidenProviderIcon.swift create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-amazon-bedrock.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-amazon-bedrock.imageset/amazon-bedrock.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ant-ling.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ant-ling.imageset/ant-ling.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-anthropic.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-anthropic.imageset/anthropic.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-apple-foundation-models.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-apple-foundation-models.imageset/apple-foundation-models.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-azure-openai-responses.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-azure-openai-responses.imageset/azure-openai-responses.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cerebras.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cerebras.imageset/cerebras.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-claude.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-claude.imageset/claude.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-ai-gateway.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-ai-gateway.imageset/cloudflare-ai-gateway.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-workers-ai.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-workers-ai.imageset/cloudflare-workers-ai.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-deepseek.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-deepseek.imageset/deepseek.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-fireworks.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-fireworks.imageset/fireworks.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-github-copilot.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-github-copilot.imageset/github-copilot.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google-vertex.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google-vertex.imageset/google-vertex.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google.imageset/google.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-grok.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-grok.imageset/grok.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-groq.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-groq.imageset/groq.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-huggingface.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-huggingface.imageset/huggingface.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-kimi-coding.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-kimi-coding.imageset/kimi-coding.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-lmstudio.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-lmstudio.imageset/lmstudio.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax-cn.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax-cn.imageset/minimax-cn.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax.imageset/minimax.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-mistral.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-mistral.imageset/mistral.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai-cn.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai-cn.imageset/moonshotai-cn.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai.imageset/moonshotai.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-nvidia.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-nvidia.imageset/nvidia.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ollama.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ollama.imageset/ollama.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai-codex.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai-codex.imageset/openai-codex.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai.imageset/openai.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode-go.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode-go.imageset/opencode-go.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode.imageset/opencode.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openrouter.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openrouter.imageset/openrouter.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-together.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-together.imageset/together.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-vercel-ai-gateway.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-vercel-ai-gateway.imageset/vercel-ai-gateway.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xai.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xai.imageset/xai.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-ams.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-ams.imageset/xiaomi-token-plan-ams.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-cn.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-cn.imageset/xiaomi-token-plan-cn.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-sgp.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-sgp.imageset/xiaomi-token-plan-sgp.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi.imageset/xiaomi.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai-coding-cn.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai-coding-cn.imageset/zai-coding-cn.svg create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai.imageset/Contents.json create mode 100644 ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai.imageset/zai.svg create mode 100644 ios/AidenOnTheGo/Resources/ThirdPartyNotices/ProviderLogos-NOTICE.md diff --git a/ios/AidenOnTheGo.xcodeproj/project.pbxproj b/ios/AidenOnTheGo.xcodeproj/project.pbxproj index eb299ecf..f76ecc5a 100644 --- a/ios/AidenOnTheGo.xcodeproj/project.pbxproj +++ b/ios/AidenOnTheGo.xcodeproj/project.pbxproj @@ -64,6 +64,7 @@ A0E000000000000000000008 /* Strands.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E000000000000000000018 /* Strands.swift */; }; A0E000000000000000000009 /* ThinkingOrb.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E000000000000000000019 /* ThinkingOrb.swift */; }; A0E00000000000000000000A /* Web.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0E00000000000000000001A /* Web.swift */; }; + A0F000000000000000000001 /* AidenProviderIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0F000000000000000000011 /* AidenProviderIcon.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -156,6 +157,7 @@ A0E000000000000000000018 /* Strands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strands.swift; sourceTree = ""; }; A0E000000000000000000019 /* ThinkingOrb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThinkingOrb.swift; sourceTree = ""; }; A0E00000000000000000001A /* Web.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Web.swift; sourceTree = ""; }; + A0F000000000000000000011 /* AidenProviderIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AidenProviderIcon.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -322,6 +324,7 @@ C09300000000000000000010 /* Shared */ = { isa = PBXGroup; children = ( + A0F000000000000000000011 /* AidenProviderIcon.swift */, A0E000000000000000000020 /* ThinkingOrbsKit */, ); path = Shared; @@ -607,6 +610,7 @@ A0E000000000000000000008 /* Strands.swift in Sources */, A0E000000000000000000009 /* ThinkingOrb.swift in Sources */, A0E00000000000000000000A /* Web.swift in Sources */, + A0F000000000000000000001 /* AidenProviderIcon.swift in Sources */, 1A2B3C4D5E6F700000000338 /* AidenAppIntents.swift in Sources */, A04500000000000000000001 /* AgentRunActivityAttributes.swift in Sources */, A04500000000000000000002 /* AidenDeepLink.swift in Sources */, diff --git a/ios/AidenOnTheGo/Features/Remote/AidenChatFeature.swift b/ios/AidenOnTheGo/Features/Remote/AidenChatFeature.swift index 191d69ae..fcbf0a04 100644 --- a/ios/AidenOnTheGo/Features/Remote/AidenChatFeature.swift +++ b/ios/AidenOnTheGo/Features/Remote/AidenChatFeature.swift @@ -188,6 +188,24 @@ struct AidenTurnAttemptTracker { } } +enum AidenTurnRequestBuilder { + static func make( + text: String, + providerId: String?, + modelId: String?, + thinkingLevel: String?, + attachments: [AidenAttachmentReference] + ) -> AidenTurnStart { + AidenTurnStart( + text: text, + providerId: providerId, + modelId: modelId, + thinkingLevel: thinkingLevel, + attachmentIds: attachments.isEmpty ? nil : attachments.map(\.id) + ) + } +} + @MainActor @Observable final class AidenWorkspaceChatsModel { @@ -395,6 +413,13 @@ final class AidenChatViewModel { let text = draft.trimmingCharacters(in: .whitespacesAndNewlines) guard canSend else { return } let submittedAttachments = pendingAttachments + let request = AidenTurnRequestBuilder.make( + text: text, + providerId: selectedProviderId, + modelId: selectedModelId, + thinkingLevel: selectedThinkingLevel, + attachments: submittedAttachments + ) let previousUpdatedAt = chat.updatedAt let optimisticID = "local-\(UUID().uuidString.lowercased())" let now = Date() @@ -422,13 +447,6 @@ final class AidenChatViewModel { chat.messages.append(optimisticMessage) chat.updatedAt = now streamState = .queued - let request = AidenTurnStart( - text: text, - providerId: selectedProviderId, - modelId: selectedModelId, - thinkingLevel: selectedThinkingLevel, - attachmentIds: pendingAttachments.isEmpty ? nil : pendingAttachments.map(\.id) - ) let idempotencyKey = turnAttempts.key(for: request) do { let response = try await coordinator.remoteClient().startTurn( @@ -968,6 +986,22 @@ private struct AidenMessageView: View { } } .frame(maxWidth: .infinity) + .contextMenu { + if let copyText = AidenMessageActionContent.copyText(for: message) { + Button { + UIPasteboard.general.string = copyText + } label: { + Label("Copy", systemImage: "doc.on.doc") + } + } + } + .accessibilityActions { + if let copyText = AidenMessageActionContent.copyText(for: message) { + Button("Copy response") { + UIPasteboard.general.string = copyText + } + } + } .accessibilityElement(children: message.role == .assistant ? .contain : .combine) .accessibilityLabel(message.role == .user ? "You" : "Aiden") } @@ -1009,6 +1043,13 @@ private struct AidenMessageView: View { } } +enum AidenMessageActionContent { + static func copyText(for message: AidenChatMessage) -> String? { + guard message.role == .assistant, !message.text.isEmpty else { return nil } + return message.text + } +} + struct AidenMessageTextView: View { let role: AidenChatRole let content: String @@ -1017,6 +1058,7 @@ struct AidenMessageTextView: View { var body: some View { if role == .assistant { AidenMarkdownView(content: content) + .frame(maxWidth: .infinity, alignment: .leading) } else { Text(verbatim: content) .font(.body) @@ -1076,10 +1118,12 @@ struct AidenMarkdownView: View { } else if AidenMarkdownRenderingPolicy.fallbackReason(for: content) != nil { Text(verbatim: content) .font(.body) + .fixedSize(horizontal: false, vertical: true) } else { MarkdownUI.Markdown(content) .markdownTheme(.aidenChat(colorScheme: colorScheme)) .markdownImageProvider(AidenMarkdownNoNetworkImageProvider()) + .markdownCodeSyntaxHighlighter(.plainText) .markdownTextStyle { ForegroundColor(.primary) BackgroundColor(nil) @@ -1097,8 +1141,6 @@ struct AidenMarkdownView: View { } } } - .frame(maxWidth: .infinity, alignment: .leading) - .fixedSize(horizontal: false, vertical: true) .textSelection(.enabled) } } @@ -1173,30 +1215,138 @@ private struct AidenLiveResponseView: View { } if let approval = model.pendingApproval { - VStack(alignment: .leading, spacing: 12) { - Label("Approval needed", systemImage: "hand.raised") - .font(.headline) - Text(approval.summary).font(.callout) - HStack { - Button("Deny", role: .destructive) { - Task { await model.respondToApproval(.deny) } + AidenApprovalCard( + summary: approval.summary, + onDeny: { Task { await model.respondToApproval(.deny) } }, + onAllow: { Task { await model.respondToApproval(.allow) } } + ) + } + + if !model.liveText.isEmpty { + AidenMarkdownView(content: model.liveText) + .contextMenu { + Button { + UIPasteboard.general.string = model.liveText + } label: { + Label("Copy", systemImage: "doc.on.doc") } - .buttonStyle(.bordered) - Button("Allow") { - Task { await model.respondToApproval(.allow) } + } + .accessibilityActions { + Button("Copy response") { + UIPasteboard.general.string = model.liveText } - .buttonStyle(.borderedProminent) } + } + } + .frame(maxWidth: .infinity, alignment: .leading) + } +} + +private struct AidenApprovalCard: View { + @Environment(\.aidenPalette) private var palette + + let summary: String + let onDeny: () -> Void + let onAllow: () -> Void + + private let shape = RoundedRectangle(cornerRadius: 14, style: .continuous) + + var body: some View { + VStack(alignment: .leading, spacing: 10) { + HStack(alignment: .top, spacing: 10) { + Image(systemName: "shield") + .font(.system(size: 15, weight: .semibold)) + .foregroundStyle(palette.warning) + .frame(width: 32, height: 32) + .background(palette.warning.opacity(0.12), in: Circle()) + .accessibilityHidden(true) + + VStack(alignment: .leading, spacing: 2) { + Text("Approval needed") + .font(.subheadline.weight(.semibold)) + .foregroundStyle(palette.foreground) + + Text("Review this one action before Aiden continues.") + .font(.caption) + .foregroundStyle(palette.secondary) + .fixedSize(horizontal: false, vertical: true) } - .padding() - .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16, style: .continuous)) } - if !model.liveText.isEmpty { - AidenMarkdownView(content: model.liveText) + Text(summary) + .font(.caption.monospaced()) + .foregroundStyle(palette.foreground) + .textSelection(.enabled) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 10) + .padding(.vertical, 8) + .background(palette.canvas, in: RoundedRectangle(cornerRadius: 10, style: .continuous)) + + HStack(spacing: 8) { + Spacer(minLength: 0) + + Button(action: onDeny) { + Text("Deny") + .font(.caption.weight(.semibold)) + .foregroundStyle(palette.foreground) + .padding(.horizontal, 13) + .frame(height: 34) + .aidenApprovalActionGlass() + } + .buttonStyle(.plain) + .padding(.vertical, 5) + + Button(action: onAllow) { + Text("Allow once") + .font(.caption.weight(.semibold)) + .foregroundStyle(palette.canvas) + .padding(.horizontal, 13) + .frame(height: 34) + .aidenApprovalActionGlass(tint: palette.accent) + } + .buttonStyle(.plain) + .padding(.vertical, 5) } } - .frame(maxWidth: .infinity, alignment: .leading) + .padding(12) + .background(palette.raised, in: shape) + .overlay(shape.stroke(palette.foreground.opacity(0.08), lineWidth: 0.5)) + .shadow(color: palette.foreground.opacity(0.08), radius: 8, y: 3) + .accessibilityElement(children: .contain) + } +} + +private struct AidenApprovalActionGlassModifier: ViewModifier { + @Environment(\.accessibilityReduceTransparency) private var reduceTransparency + @Environment(\.aidenPalette) private var palette + + let tint: Color? + + @ViewBuilder + func body(content: Content) -> some View { + if #available(iOS 26, *), !reduceTransparency { + if let tint { + content.glassEffect(.regular.tint(tint).interactive(), in: Capsule()) + } else { + content.glassEffect(.regular.interactive(), in: Capsule()) + } + } else if let tint { + content.background(tint, in: Capsule()) + } else if reduceTransparency { + content + .background(palette.canvas, in: Capsule()) + .overlay(Capsule().stroke(palette.foreground.opacity(0.14), lineWidth: 0.5)) + } else { + content + .background(.regularMaterial, in: Capsule()) + .overlay(Capsule().stroke(palette.foreground.opacity(0.10), lineWidth: 0.5)) + } + } +} + +private extension View { + func aidenApprovalActionGlass(tint: Color? = nil) -> some View { + modifier(AidenApprovalActionGlassModifier(tint: tint)) } } @@ -1312,6 +1462,7 @@ private struct AidenComposerView: View { @State private var voiceInput = ComposerVoiceInputController() @State private var didAutoStartVoice = false @State private var selectedPhoto: PhotosPickerItem? + @State private var isPhotoPickerPresented = false @State private var isFileImporterPresented = false var body: some View { @@ -1354,16 +1505,7 @@ private struct AidenComposerView: View { } HStack(alignment: .center, spacing: 10) { - Menu { - PhotosPicker(selection: $selectedPhoto, matching: .images) { - Label("Photo Library", systemImage: "photo.on.rectangle") - } - Button { - isFileImporterPresented = true - } label: { - Label("Choose File", systemImage: "doc") - } - } label: { + AidenUIKitMenuButton { if model.isUploadingAttachment { ProgressView().controlSize(.small).frame(width: 44, height: 44) } else { @@ -1371,15 +1513,22 @@ private struct AidenComposerView: View { .font(.title3.weight(.medium)) .frame(width: 44, height: 44) } + } menu: { + attachmentMenu() } .disabled(!model.isConnected || model.isStreaming || model.isUploadingAttachment || model.pendingAttachments.count >= 10) .accessibilityLabel("Add attachment") .accessibilityHint("Attach an image or bounded text file") + .photosPicker( + isPresented: $isPhotoPickerPresented, + selection: $selectedPhoto, + matching: .images + ) if let catalog = model.catalog, !catalog.providers.isEmpty { Menu { ForEach(catalog.providers) { provider in - Section(provider.label) { + Section { ForEach(provider.models) { candidate in if let levels = candidate.thinkingLevels, !levels.isEmpty { Menu { @@ -1417,12 +1566,29 @@ private struct AidenComposerView: View { } } } + } header: { + Label { + Text(provider.label) + } icon: { + AidenProviderIcon( + providerID: provider.id, + providerLabel: provider.label, + size: 16, + color: palette.secondary + ) + } } } } label: { HStack(spacing: 4) { - if model.selectedModel?.thinkingLevels?.isEmpty == false { - AidenSidebarLogo(size: 15, color: palette.secondary) + if let provider = model.selectedProvider { + AidenProviderIcon( + providerID: provider.id, + providerLabel: provider.label, + modelID: model.selectedModel?.id, + size: 15, + color: palette.secondary + ) } Text(model.selectedModel?.label ?? "Model").lineLimit(1) if let level = model.selectedThinkingLevel, @@ -1548,6 +1714,29 @@ private struct AidenComposerView: View { .onDisappear { voiceInput.stopKeepingTranscript() } } + private func attachmentMenu() -> UIMenu { + UIMenu(children: [ + UIAction( + title: String(localized: "Photo Library"), + image: UIImage(systemName: "photo.on.rectangle") + ) { _ in + Task { @MainActor in + composerFocus.wrappedValue = false + isPhotoPickerPresented = true + } + }, + UIAction( + title: String(localized: "Choose File"), + image: UIImage(systemName: "doc") + ) { _ in + Task { @MainActor in + composerFocus.wrappedValue = false + isFileImporterPresented = true + } + }, + ]) + } + private var sendButtonBackground: Color { if model.canSend { return palette.accent } return palette.foreground.opacity(colorScheme == .dark ? 0.18 : 0.12) @@ -1583,6 +1772,102 @@ private struct AidenComposerView: View { } } +private struct AidenUIKitMenuButton: View { + @Environment(\.isEnabled) private var isEnabled + + private let menu: () -> UIMenu + private let label: Label + + init( + @ViewBuilder label: () -> Label, + menu: @escaping () -> UIMenu + ) { + self.label = label() + self.menu = menu + } + + var body: some View { + label + .opacity(isEnabled ? 1 : 0.62) + .overlay { + AidenUIKitMenuButtonBacker(menu: menu) + } + .accessibilityElement(children: .combine) + .accessibilityAddTraits(.isButton) + } +} + +private struct AidenUIKitMenuButtonBacker: UIViewControllerRepresentable { + @Environment(\.isEnabled) private var isEnabled + let menu: () -> UIMenu + + func makeCoordinator() -> Coordinator { + Coordinator(menu: menu) + } + + func makeUIViewController(context: Context) -> AidenMenuButtonHostController { + let controller = AidenMenuButtonHostController() + let button = controller.button + button.menu = UIMenu(children: [ + UIDeferredMenuElement.uncached { completion in + completion(context.coordinator.menu().children) + }, + ]) + button.isEnabled = isEnabled + button.isAccessibilityElement = false + return controller + } + + func updateUIViewController(_ controller: AidenMenuButtonHostController, context: Context) { + context.coordinator.menu = menu + controller.button.isEnabled = isEnabled + } + + func sizeThatFits( + _ proposal: ProposedViewSize, + uiViewController: AidenMenuButtonHostController, + context: Context + ) -> CGSize? { + CGSize( + width: proposal.width ?? UIView.noIntrinsicMetric, + height: proposal.height ?? UIView.noIntrinsicMetric + ) + } + + final class Coordinator { + var menu: () -> UIMenu + + init(menu: @escaping () -> UIMenu) { + self.menu = menu + } + } +} + +private final class AidenMenuButtonHostController: UIViewController { + let button = UIButton(type: .custom) + + override func loadView() { + let container = UIView() + container.backgroundColor = .clear + container.isOpaque = false + view = container + + button.showsMenuAsPrimaryAction = true + button.backgroundColor = .clear + button.setTitle(nil, for: .normal) + button.setImage(nil, for: .normal) + button.translatesAutoresizingMaskIntoConstraints = false + + container.addSubview(button) + NSLayoutConstraint.activate([ + button.leadingAnchor.constraint(equalTo: container.leadingAnchor), + button.trailingAnchor.constraint(equalTo: container.trailingAnchor), + button.topAnchor.constraint(equalTo: container.topAnchor), + button.bottomAnchor.constraint(equalTo: container.bottomAnchor), + ]) + } +} + private struct AidenComposerGlassModifier: ViewModifier { @Environment(\.accessibilityReduceTransparency) private var reduceTransparency @Environment(\.aidenPalette) private var palette diff --git a/ios/AidenOnTheGo/Features/Remote/AidenRemoteCoordinator.swift b/ios/AidenOnTheGo/Features/Remote/AidenRemoteCoordinator.swift index 39917b58..af48963f 100644 --- a/ios/AidenOnTheGo/Features/Remote/AidenRemoteCoordinator.swift +++ b/ios/AidenOnTheGo/Features/Remote/AidenRemoteCoordinator.swift @@ -19,28 +19,35 @@ final class AidenRemoteCoordinator { private(set) var connectionState: AidenRemoteConnectionState private(set) var server: AidenServer? private(set) var workspaces: [AidenWorkspace] = [] + private(set) var workspaceSnapshotRevision = 0 private(set) var isMutating = false var presentedError: String? + let workspaceArchiveStore: AidenWorkspaceArchiveStore private var pendingManagedWorktreeDeletionKeys: [String: UUID] = [:] + private var connectionGeneration = 0 init() { let installationStore = AidenInstallationStore() self.installationStore = installationStore + workspaceArchiveStore = AidenWorkspaceArchiveStore() clientFactory = { try AidenRemoteClient(installation: $0, credential: $1) } connectionState = installationStore.activeInstallation == nil ? .needsPairing : .connecting } init( installationStore: AidenInstallationStore, + workspaceArchiveStore: AidenWorkspaceArchiveStore? = nil, clientFactory: @escaping ClientFactory = { try AidenRemoteClient(installation: $0, credential: $1) } ) { self.installationStore = installationStore + self.workspaceArchiveStore = workspaceArchiveStore ?? AidenWorkspaceArchiveStore() self.clientFactory = clientFactory connectionState = installationStore.activeInstallation == nil ? .needsPairing : .connecting } func start() async { guard installationStore.activeInstallation != nil else { + connectionGeneration &+= 1 connectionState = .needsPairing updateIntentCatalog(for: nil) return @@ -71,7 +78,12 @@ final class AidenRemoteCoordinator { trust: payload.trust, name: temporaryName ) - try await load(installation: installation, credential: exchange.credential) + connectionGeneration &+= 1 + try await load( + installation: installation, + credential: exchange.credential, + generation: connectionGeneration + ) } catch { connectionState = installationStore.activeInstallation == nil ? .needsPairing : .offline(message: error.localizedDescription) presentedError = error.localizedDescription @@ -79,6 +91,8 @@ final class AidenRemoteCoordinator { } func connectActiveInstallation() async { + connectionGeneration &+= 1 + let generation = connectionGeneration guard let installation = installationStore.activeInstallation else { connectionState = .needsPairing server = nil @@ -93,13 +107,15 @@ final class AidenRemoteCoordinator { !credential.isEmpty else { throw AidenRemoteClientError.missingCredential } - try await load(installation: installation, credential: credential) + try await load(installation: installation, credential: credential, generation: generation) } catch { + guard isCurrentContext(installationId: installation.id, generation: generation) else { return } await handleConnectionError(error, installationId: installation.id) } } func switchInstallation(to installationId: String) async { + guard !isMutating else { return } do { try installationStore.setActive(installationId) server = nil @@ -112,6 +128,7 @@ final class AidenRemoteCoordinator { } func removeInstallation(_ installationId: String) async { + guard !isMutating else { return } do { await AidenRemoteLiveActivityManager.shared.endAll(forInstanceID: installationId) try installationStore.remove(installationId) @@ -125,16 +142,20 @@ final class AidenRemoteCoordinator { } func refreshWorkspaces() async { - guard let client = try? activeClient() else { + guard let installationId = activeInstanceId, + let client = try? activeClient() else { connectionState = .needsPairing return } + let generation = connectionGeneration do { - workspaces = try await client.workspaces() + let refreshed = try await client.workspaces() + guard isCurrentContext(installationId: installationId, generation: generation) else { return } + applyWorkspaceSnapshot(refreshed, instanceId: installationId) connectionState = .connected - updateIntentCatalog(for: activeInstanceId) } catch { - await handleConnectionError(error, installationId: installationStore.activeInstallationId) + guard isCurrentContext(installationId: installationId, generation: generation) else { return } + await handleConnectionError(error, installationId: installationId) } } @@ -152,22 +173,44 @@ final class AidenRemoteCoordinator { presentedError = nil defer { isMutating = false } + guard let installationId = activeInstanceId else { return nil } + let generation = connectionGeneration + let client: AidenRemoteClient + do { + client = try activeClient() + } catch { + await handleConnectionError(error, installationId: installationId) + return nil + } + var optimistic = workspace if let name { optimistic.name = name } if let permission { optimistic.permission = permission } upsert(optimistic) do { - let updated = try await activeClient().updateWorkspace( + let updated = try await client.updateWorkspace( id: workspace.id, revision: workspace.revision, patch: AidenWorkspacePatch(name: name, permission: permission) ) + guard isCurrentContext(installationId: installationId, generation: generation) else { return nil } upsert(updated) return updated } catch { - upsert(workspace) - await handleConnectionError(error, installationId: installationStore.activeInstallationId) + guard isCurrentContext(installationId: installationId, generation: generation) else { return nil } + if let canonical = try? await client.workspaces(), + isCurrentContext(installationId: installationId, generation: generation) { + applyWorkspaceSnapshot(canonical, instanceId: installationId) + if let reconciled = canonical.first(where: { $0.id == workspace.id }), + (name == nil || reconciled.name == name), + (permission == nil || reconciled.permission == permission) { + return reconciled + } + } else { + upsert(workspace) + } + await handleConnectionError(error, installationId: installationId) return nil } } @@ -177,15 +220,42 @@ final class AidenRemoteCoordinator { isMutating = true presentedError = nil defer { isMutating = false } + + guard let installationId = activeInstanceId else { return false } + let generation = connectionGeneration + let client: AidenRemoteClient + do { + client = try activeClient() + } catch { + await handleConnectionError(error, installationId: installationId) + return false + } workspaces.removeAll { $0.id == workspace.id } - updateIntentCatalog(for: activeInstanceId) + updateIntentCatalog(for: installationId) do { - let client = try activeClient() try await client.removeWorkspace(id: workspace.id, revision: workspace.revision) + guard isCurrentContext(installationId: installationId, generation: generation) else { return false } + // The desktop guarantees at least one registry workspace and may + // seed a replacement when the last record is removed. Canonicalize + // after the confirmed delete without rolling the delete back if + // this follow-up read happens to fail. + if let canonicalWorkspaces = try? await client.workspaces() { + guard isCurrentContext(installationId: installationId, generation: generation) else { return false } + applyWorkspaceSnapshot(canonicalWorkspaces, instanceId: installationId) + } return true } catch { - upsert(workspace) - await handleConnectionError(error, installationId: installationStore.activeInstallationId) + guard isCurrentContext(installationId: installationId, generation: generation) else { return false } + if let canonical = try? await client.workspaces(), + isCurrentContext(installationId: installationId, generation: generation) { + applyWorkspaceSnapshot(canonical, instanceId: installationId) + if !canonical.contains(where: { $0.id == workspace.id }) { + return true + } + } else { + upsert(workspace) + } + await handleConnectionError(error, installationId: installationId) return false } } @@ -195,26 +265,46 @@ final class AidenRemoteCoordinator { isMutating = true presentedError = nil defer { isMutating = false } - let scope = "\(installationStore.activeInstallationId ?? ""):\(workspace.id)" + + guard let installationId = activeInstanceId else { return false } + let generation = connectionGeneration + let client: AidenRemoteClient + do { + client = try activeClient() + } catch { + await handleConnectionError(error, installationId: installationId) + return false + } + let scope = "\(installationId):\(workspace.id)" let key = pendingManagedWorktreeDeletionKeys[scope] ?? UUID() pendingManagedWorktreeDeletionKeys[scope] = key workspaces.removeAll { $0.id == workspace.id } updateIntentCatalog(for: activeInstanceId) do { - let client = try activeClient() _ = try await client.deleteManagedGitWorktree( workspaceId: workspace.id, revision: workspace.revision, idempotencyKey: key ) + guard isCurrentContext(installationId: installationId, generation: generation) else { return false } pendingManagedWorktreeDeletionKeys[scope] = nil return true } catch { - upsert(workspace) + guard isCurrentContext(installationId: installationId, generation: generation) else { return false } + if let canonical = try? await client.workspaces(), + isCurrentContext(installationId: installationId, generation: generation) { + applyWorkspaceSnapshot(canonical, instanceId: installationId) + if !canonical.contains(where: { $0.id == workspace.id }) { + pendingManagedWorktreeDeletionKeys[scope] = nil + return true + } + } else { + upsert(workspace) + } if !Self.isAmbiguousMutationError(error) { pendingManagedWorktreeDeletionKeys[scope] = nil } - await handleConnectionError(error, installationId: installationStore.activeInstallationId) + await handleConnectionError(error, installationId: installationId) return false } } @@ -232,6 +322,8 @@ final class AidenRemoteCoordinator { isMutating = true presentedError = nil defer { isMutating = false } + guard let installationId = activeInstanceId else { return nil } + let generation = connectionGeneration do { let client = try activeClient() let selection = try await client.createWorkspaceSelection(location: location) @@ -241,10 +333,12 @@ final class AidenRemoteCoordinator { let workspace = try await client.createWorkspace( .selectedFolder(selection: selection.selection, name: name) ) + guard isCurrentContext(installationId: installationId, generation: generation) else { return nil } upsert(workspace) return workspace } catch { - await handleConnectionError(error, installationId: installationStore.activeInstallationId) + guard isCurrentContext(installationId: installationId, generation: generation) else { return nil } + await handleConnectionError(error, installationId: installationId) return nil } } @@ -253,11 +347,21 @@ final class AidenRemoteCoordinator { installationStore.activeInstallationId } + func setDeviceArchivedWorkspaceIDs(_ workspaceIDs: Set, for instanceId: String?) { + guard let instanceId, !instanceId.isEmpty else { return } + _ = workspaceIDs + updateIntentCatalog(for: instanceId) + } + func remoteClient() throws -> AidenRemoteClient { try activeClient() } - private func load(installation: AidenInstallation, credential: String) async throws { + private func load( + installation: AidenInstallation, + credential: String, + generation: Int + ) async throws { let client = try clientFactory(installation, credential) async let serverRequest = client.server() async let workspaceRequest = client.workspaces() @@ -265,11 +369,11 @@ final class AidenRemoteCoordinator { guard server.instanceId == installation.instanceId else { throw AidenRemoteContractError.invalidPairingExchange } + guard isCurrentContext(installationId: installation.id, generation: generation) else { return } try installationStore.updateServer(server) self.server = server - self.workspaces = workspaces + applyWorkspaceSnapshot(workspaces, instanceId: installation.id) connectionState = .connected - updateIntentCatalog(for: installation.id) } private func activeClient() throws -> AidenRemoteClient { @@ -290,16 +394,36 @@ final class AidenRemoteCoordinator { isMutating = true presentedError = nil defer { isMutating = false } + guard let installationId = activeInstanceId else { return nil } + let generation = connectionGeneration do { - let workspace = try await operation(activeClient()) + let client = try activeClient() + let workspace = try await operation(client) + guard isCurrentContext(installationId: installationId, generation: generation) else { return nil } upsert(workspace) return workspace } catch { - await handleConnectionError(error, installationId: installationStore.activeInstallationId) + guard isCurrentContext(installationId: installationId, generation: generation) else { return nil } + await handleConnectionError(error, installationId: installationId) return nil } } + private func applyWorkspaceSnapshot(_ workspaces: [AidenWorkspace], instanceId: String) { + guard activeInstanceId == instanceId else { return } + self.workspaces = workspaces + workspaceArchiveStore.prune( + instanceID: instanceId, + validWorkspaceIDs: Set(workspaces.map(\.id)) + ) + workspaceSnapshotRevision &+= 1 + updateIntentCatalog(for: instanceId) + } + + private func isCurrentContext(installationId: String, generation: Int) -> Bool { + activeInstanceId == installationId && connectionGeneration == generation + } + private func upsert(_ workspace: AidenWorkspace) { workspaces.removeAll { $0.id == workspace.id } workspaces.append(workspace) @@ -308,6 +432,7 @@ final class AidenRemoteCoordinator { } private func handleConnectionError(_ error: Error, installationId: String?) async { + guard installationId == nil || installationId == activeInstanceId else { return } if let clientError = error as? AidenRemoteClientError, clientError.isCredentialRevoked, let installationId { @@ -324,7 +449,12 @@ final class AidenRemoteCoordinator { presentedError = revocationMessage return } - connectionState = .offline(message: error.localizedDescription) + if let clientError = error as? AidenRemoteClientError, + case .server = clientError { + connectionState = .connected + } else { + connectionState = .offline(message: error.localizedDescription) + } presentedError = error.localizedDescription } @@ -334,7 +464,8 @@ final class AidenRemoteCoordinator { } let cachedWorkspaces: [AidenIntentWorkspaceRecord] if let instanceId, instanceId == activeInstanceId { - cachedWorkspaces = workspaces.map { + let archivedWorkspaceIDs = workspaceArchiveStore.archivedWorkspaceIDs(for: instanceId) + cachedWorkspaces = workspaces.filter { !archivedWorkspaceIDs.contains($0.id) }.map { AidenIntentWorkspaceRecord(id: $0.id, instanceId: instanceId, name: $0.name) } } else { diff --git a/ios/AidenOnTheGo/Features/Remote/AidenScheduledTasksView.swift b/ios/AidenOnTheGo/Features/Remote/AidenScheduledTasksView.swift index f81f8755..4a58c362 100644 --- a/ios/AidenOnTheGo/Features/Remote/AidenScheduledTasksView.swift +++ b/ios/AidenOnTheGo/Features/Remote/AidenScheduledTasksView.swift @@ -484,7 +484,8 @@ private struct AidenScheduledTaskEditor: View { } private var providers: [AidenProvider] { model.catalog?.providers ?? [] } - private var models: [AidenModel] { providers.first { $0.id == draft.providerId }?.models ?? [] } + private var selectedProvider: AidenProvider? { providers.first { $0.id == draft.providerId } } + private var models: [AidenModel] { selectedProvider?.models ?? [] } var body: some View { NavigationStack { @@ -514,11 +515,36 @@ private struct AidenScheduledTaskEditor: View { TextEditor(text: $draft.prompt).frame(minHeight: 120) Picker("Provider", selection: $draft.providerId) { Text("Default").tag(String?.none) - ForEach(providers) { Text($0.label).tag(Optional($0.id)) } + ForEach(providers) { provider in + Label { + Text(provider.label) + } icon: { + AidenProviderIcon( + providerID: provider.id, + providerLabel: provider.label, + size: 16 + ) + } + .tag(Optional(provider.id)) + } } Picker("Model", selection: $draft.modelId) { Text("Default").tag(String?.none) - ForEach(models) { Text($0.label).tag(Optional($0.id)) } + ForEach(models) { candidate in + Label { + Text(candidate.label) + } icon: { + if let provider = selectedProvider { + AidenProviderIcon( + providerID: provider.id, + providerLabel: provider.label, + modelID: candidate.id, + size: 16 + ) + } + } + .tag(Optional(candidate.id)) + } } } if !model.mcpServers.isEmpty { diff --git a/ios/AidenOnTheGo/Features/Remote/AidenWorkspaceShellView.swift b/ios/AidenOnTheGo/Features/Remote/AidenWorkspaceShellView.swift index 3b2b2387..3526a8d9 100644 --- a/ios/AidenOnTheGo/Features/Remote/AidenWorkspaceShellView.swift +++ b/ios/AidenOnTheGo/Features/Remote/AidenWorkspaceShellView.swift @@ -40,10 +40,102 @@ private struct AidenLiquidGlassCapsuleModifier: ViewModifier { } } +private struct AidenChromeGlassModifier: ViewModifier { + @Environment(\.accessibilityReduceTransparency) private var reduceTransparency + @Environment(\.aidenPalette) private var palette + + let isInteractive: Bool + let shape: GlassShape + + @ViewBuilder + func body(content: Content) -> some View { + if #available(iOS 26, *), !reduceTransparency { + if isInteractive { + content.glassEffect(.regular.interactive(), in: shape) + } else { + content.glassEffect(.regular, in: shape) + } + } else if reduceTransparency { + content + .background(palette.raised, in: shape) + .overlay(shape.stroke(palette.foreground.opacity(0.14), lineWidth: 0.5)) + } else { + content + .background(.ultraThinMaterial, in: shape) + .overlay(shape.stroke(palette.foreground.opacity(0.10), lineWidth: 0.5)) + } + } +} + +private struct AidenProminentGlassButtonModifier: ViewModifier { + @Environment(\.accessibilityReduceTransparency) private var reduceTransparency + @Environment(\.aidenPalette) private var palette + + @ViewBuilder + func body(content: Content) -> some View { + if #available(iOS 26, *), !reduceTransparency { + content.buttonStyle(.glass) + } else if reduceTransparency { + content + .buttonStyle(.plain) + .background(palette.raised, in: Capsule()) + .overlay(Capsule().stroke(palette.foreground.opacity(0.16), lineWidth: 0.5)) + } else { + content + .buttonStyle(.plain) + .background(.regularMaterial, in: Capsule()) + .overlay(Capsule().stroke(palette.foreground.opacity(0.10), lineWidth: 0.5)) + } + } +} + private extension View { func aidenLiquidGlassCapsule(tint: Color) -> some View { modifier(AidenLiquidGlassCapsuleModifier(tint: tint)) } + + func aidenChromeGlass( + isInteractive: Bool = false, + in shape: GlassShape + ) -> some View { + modifier(AidenChromeGlassModifier(isInteractive: isInteractive, shape: shape)) + } + + func aidenProminentGlassButton() -> some View { + modifier(AidenProminentGlassButtonModifier()) + } +} + +enum AidenNewAgentChoice: String, CaseIterable, Identifiable { + case existingWorkspace + case newWorkspace + case scratchWorkspace + + var id: String { rawValue } + + var title: String { + switch self { + case .existingWorkspace: "Existing Workspace" + case .newWorkspace: "New Workspace" + case .scratchWorkspace: "Managed Scratch Workspace" + } + } + + var detail: String { + switch self { + case .existingWorkspace: "Start another chat without creating a folder." + case .newWorkspace: "Create a reusable workspace for ongoing work." + case .scratchWorkspace: "Use an isolated workspace Aiden can clean up later." + } + } + + var symbol: String { + switch self { + case .existingWorkspace: "folder" + case .newWorkspace: "folder.badge.plus" + case .scratchWorkspace: "hammer.fill" + } + } } @MainActor @@ -108,6 +200,95 @@ enum AidenWorkspaceNavigation { } } +@MainActor +@Observable +final class AidenWorkspaceArchiveStore { + private struct Snapshot: Codable { + var workspaceIDsByInstance: [String: [String]] + } + + private static let snapshotKey = "aiden.deviceArchivedWorkspaces.v1" + private static let disclosureKey = "aiden.deviceArchivedWorkspaces.disclosureSeen.v1" + + @ObservationIgnored private let defaults: UserDefaults + private var workspaceIDsByInstance: [String: Set] + private(set) var hasAcknowledgedDeviceOnlyArchive: Bool + + init(defaults: UserDefaults = .standard) { + self.defaults = defaults + hasAcknowledgedDeviceOnlyArchive = defaults.bool(forKey: Self.disclosureKey) + + if let data = defaults.data(forKey: Self.snapshotKey), + let snapshot = try? JSONDecoder().decode(Snapshot.self, from: data) { + workspaceIDsByInstance = snapshot.workspaceIDsByInstance.reduce(into: [:]) { result, entry in + result[entry.key] = Set(entry.value.filter { !$0.isEmpty }) + } + } else { + workspaceIDsByInstance = [:] + } + } + + func archivedWorkspaceIDs(for instanceID: String?) -> Set { + guard let instanceID, !instanceID.isEmpty else { return [] } + return workspaceIDsByInstance[instanceID] ?? [] + } + + func isArchived(workspaceID: String, instanceID: String?) -> Bool { + archivedWorkspaceIDs(for: instanceID).contains(workspaceID) + } + + func acknowledgeDeviceOnlyArchive() { + guard !hasAcknowledgedDeviceOnlyArchive else { return } + hasAcknowledgedDeviceOnlyArchive = true + defaults.set(true, forKey: Self.disclosureKey) + } + + func archive(workspaceID: String, instanceID: String?) { + guard let instanceID, !instanceID.isEmpty, !workspaceID.isEmpty else { return } + var workspaceIDs = workspaceIDsByInstance[instanceID] ?? [] + guard workspaceIDs.insert(workspaceID).inserted else { return } + workspaceIDsByInstance[instanceID] = workspaceIDs + persist() + } + + func unarchive(workspaceID: String, instanceID: String?) { + guard let instanceID, !instanceID.isEmpty, + var workspaceIDs = workspaceIDsByInstance[instanceID], + workspaceIDs.remove(workspaceID) != nil else { return } + if workspaceIDs.isEmpty { + workspaceIDsByInstance.removeValue(forKey: instanceID) + } else { + workspaceIDsByInstance[instanceID] = workspaceIDs + } + persist() + } + + func forget(workspaceID: String, instanceID: String?) { + unarchive(workspaceID: workspaceID, instanceID: instanceID) + } + + func prune(instanceID: String?, validWorkspaceIDs: Set) { + guard let instanceID, !instanceID.isEmpty, + let current = workspaceIDsByInstance[instanceID] else { return } + let pruned = current.intersection(validWorkspaceIDs) + guard pruned != current else { return } + if pruned.isEmpty { + workspaceIDsByInstance.removeValue(forKey: instanceID) + } else { + workspaceIDsByInstance[instanceID] = pruned + } + persist() + } + + private func persist() { + let snapshot = Snapshot( + workspaceIDsByInstance: workspaceIDsByInstance.mapValues { $0.sorted() } + ) + guard let data = try? JSONEncoder().encode(snapshot) else { return } + defaults.set(data, forKey: Self.snapshotKey) + } +} + struct AidenWorkspaceShellView: View { @Bindable var coordinator: AidenRemoteCoordinator @Binding private var navigationRequest: AidenNavigationRequest? @@ -133,6 +314,7 @@ struct AidenWorkspaceShellView: View { @State private var isCreatingAgent = false @State private var agentCreationStatus = "" @FocusState private var searchFieldIsFocused: Bool + @Namespace private var newAgentTransition @AppStorage("aiden.defaults.workspacePermission") private var defaultWorkspacePermissionRaw = AidenWorkspacePermission.ask.rawValue init( @@ -147,6 +329,22 @@ struct AidenWorkspaceShellView: View { horizontalSizeClass == .regular } + private var archivedWorkspaceIDs: Set { + workspaceArchiveStore.archivedWorkspaceIDs(for: coordinator.activeInstanceId) + } + + private var workspaceArchiveStore: AidenWorkspaceArchiveStore { + coordinator.workspaceArchiveStore + } + + private var activeWorkspaces: [AidenWorkspace] { + coordinator.workspaces.filter { !archivedWorkspaceIDs.contains($0.id) } + } + + private var activeWorkspaceIDs: [String] { + activeWorkspaces.map(\.id) + } + var body: some View { Group { if usesSplitNavigation { @@ -212,11 +410,13 @@ struct AidenWorkspaceShellView: View { .sheet(isPresented: $isShowingUsage) { if let usage = homeModel.usage { AidenUsageView(usage: usage) + .presentationDetents([.large]) + .presentationDragIndicator(.visible) } } .sheet(isPresented: $isShowingExistingWorkspacePicker) { NavigationStack { - List(coordinator.workspaces) { workspace in + List(activeWorkspaces) { workspace in Button { isShowingExistingWorkspacePicker = false Task { await createNewAgent(in: workspace) } @@ -271,38 +471,29 @@ struct AidenWorkspaceShellView: View { } message: { Text("Aiden will create a reusable workspace, then open a new chat in it.") } - .confirmationDialog( - "Where should this agent work?", - isPresented: $isShowingNewAgentChoices, - titleVisibility: .visible - ) { - Button("Existing Workspace") { isShowingExistingWorkspacePicker = true } - .disabled(coordinator.workspaces.isEmpty) - Button("New Workspace") { isShowingNewAgentWorkspacePrompt = true } - Button("Managed Scratch Workspace") { - Task { await createNewAgentInScratchWorkspace() } - } - Button("Cancel", role: .cancel) {} - } message: { - Text("Reuse a workspace to avoid creating unnecessary folders, or choose an isolated scratch workspace for temporary work.") + .onChange(of: coordinator.workspaces.map(\.id)) { _, _ in + syncArchivedWorkspaceProjection() + reconcileNavigation(workspaceIDs: activeWorkspaceIDs) } - .onChange(of: coordinator.workspaces.map(\.id)) { _, ids in - selectedWorkspaceId = AidenWorkspaceNavigation.reconciledSelection( - current: selectedWorkspaceId, - workspaceIDs: ids - ) - compactWorkspacePath = AidenWorkspaceNavigation.reconciledCompactPath( - current: compactWorkspacePath, - workspaceIDs: ids - ) + .onChange(of: coordinator.workspaceSnapshotRevision) { _, _ in + syncArchivedWorkspaceProjection() + reconcileNavigation(workspaceIDs: activeWorkspaceIDs) + } + .onChange(of: archivedWorkspaceIDs) { _, _ in + syncArchivedWorkspaceProjection() + reconcileNavigation(workspaceIDs: activeWorkspaceIDs) + } + .onChange(of: coordinator.activeInstanceId) { _, _ in + syncArchivedWorkspaceProjection() + reconcileNavigation(workspaceIDs: activeWorkspaceIDs) } .onChange(of: compactWorkspacePath) { _, path in guard let workspaceID = path.last, - coordinator.workspaces.contains(where: { $0.id == workspaceID }) else { return } + activeWorkspaceIDs.contains(workspaceID) else { return } selectedWorkspaceId = workspaceID } .onChange(of: usesSplitNavigation) { wasSplit, isSplit in - let workspaceIDs = coordinator.workspaces.map(\.id) + let workspaceIDs = activeWorkspaceIDs if isSplit { if let compactWorkspaceID = compactWorkspacePath.last, workspaceIDs.contains(compactWorkspaceID) { @@ -318,15 +509,8 @@ struct AidenWorkspaceShellView: View { } } .task { - let workspaceIDs = coordinator.workspaces.map(\.id) - selectedWorkspaceId = AidenWorkspaceNavigation.reconciledSelection( - current: selectedWorkspaceId, - workspaceIDs: workspaceIDs - ) - compactWorkspacePath = AidenWorkspaceNavigation.reconciledCompactPath( - current: compactWorkspacePath, - workspaceIDs: workspaceIDs - ) + syncArchivedWorkspaceProjection() + reconcileNavigation(workspaceIDs: activeWorkspaceIDs) } .task(id: AidenNavigationResolutionID( request: navigationRequest, @@ -345,8 +529,14 @@ struct AidenWorkspaceShellView: View { Button { intentChat = chat } label: { homeChatRow(chat) } .buttonStyle(.plain) } - newAgentButton.padding(.trailing, 24).padding(.bottom, 22) + if !isSearching { + newAgentButton + .padding(.trailing, 24) + .padding(.bottom, 22) + .transition(.move(edge: .bottom).combined(with: .opacity)) + } } + .animation(.smooth(duration: 0.24, extraBounce: 0), value: isSearching) } private var compactWorkspaceSidebar: some View { @@ -358,13 +548,21 @@ struct AidenWorkspaceShellView: View { homeChatRow(chat) } } - newAgentButton.padding(.trailing, 24).padding(.bottom, 22) + if !isSearching { + newAgentButton + .padding(.trailing, 24) + .padding(.bottom, 22) + .transition(.move(edge: .bottom).combined(with: .opacity)) + } } + .animation(.smooth(duration: 0.24, extraBounce: 0), value: isSearching) } private var filteredChats: [AidenChat] { - guard !searchText.isEmpty else { return homeModel.chats } - return homeModel.chats.filter { $0.title.localizedCaseInsensitiveContains(searchText) } + let activeWorkspaceIDSet = Set(activeWorkspaceIDs) + let visibleChats = homeModel.chats.filter { activeWorkspaceIDSet.contains($0.workspaceId) } + guard !searchText.isEmpty else { return visibleChats } + return visibleChats.filter { $0.title.localizedCaseInsensitiveContains(searchText) } } private func homeList( @@ -416,11 +614,17 @@ struct AidenWorkspaceShellView: View { } } - Color.clear.frame(height: 90).listRowSeparator(.hidden) } .listStyle(.plain) .scrollContentBackground(.hidden) - .background(palette.canvas) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(palette.canvas.ignoresSafeArea()) + .safeAreaInset(edge: .bottom, spacing: 0) { + Color.clear + .frame(height: 92) + .background(palette.canvas) + .allowsHitTesting(false) + } .refreshable { await coordinator.refreshWorkspaces() await homeModel.load(coordinator: coordinator) @@ -454,6 +658,7 @@ struct AidenWorkspaceShellView: View { } label: { Image(systemName: "magnifyingglass") .font(.system(size: 22, weight: .semibold)) + .foregroundStyle(isSearching ? palette.secondary : palette.foreground) .frame(width: 44, height: 44) .contentShape(Rectangle()) } @@ -481,6 +686,7 @@ struct AidenWorkspaceShellView: View { Button { isShowingAppSettings = true } label: { Image(systemName: "person.crop.circle.fill") .font(.system(size: 28, weight: .medium)) + .foregroundStyle(palette.accent) .frame(width: 44, height: 44) .contentShape(Rectangle()) } @@ -490,7 +696,7 @@ struct AidenWorkspaceShellView: View { } .padding(.vertical, 2) .frame(maxWidth: isSearching ? .infinity : nil, alignment: .trailing) - .background(.thinMaterial, in: Capsule()) + .aidenChromeGlass(isInteractive: true, in: Capsule()) .clipShape(Capsule()) .contentShape(Capsule()) } @@ -513,7 +719,10 @@ struct AidenWorkspaceShellView: View { .disabled(homeModel.usage == nil) NavigationLink { - AidenWorkspacesDirectoryView(coordinator: coordinator) + AidenWorkspacesDirectoryView( + coordinator: coordinator, + archiveStore: workspaceArchiveStore + ) } label: { homeNavigationRow( title: "Workspaces", @@ -610,16 +819,43 @@ struct AidenWorkspaceShellView: View { private var newAgentButton: some View { Button { isShowingNewAgentChoices = true } label: { - Label("New Agent", systemImage: "square.and.pencil") - .font(.headline.weight(.semibold)) - .padding(.horizontal, 20) - .frame(height: 56) - .foregroundStyle(palette.canvas) - .aidenLiquidGlassCapsule(tint: palette.foreground) - .contentShape(Capsule()) + Image(systemName: "square.and.pencil") + .font(.title2.weight(.semibold)) + .foregroundStyle(palette.foreground) + .frame(width: 42, height: 50) + .contentShape(Rectangle()) } - .buttonStyle(.plain) + .aidenProminentGlassButton() .disabled(coordinator.connectionState != .connected || coordinator.isMutating || isCreatingAgent) + .accessibilityLabel("New Agent") + .accessibilityHint("Choose where the new agent should work.") + .matchedTransitionSource(id: "AidenNewAgentOptions", in: newAgentTransition) + .popover(isPresented: $isShowingNewAgentChoices, arrowEdge: .bottom) { + AidenNewAgentPopover( + canUseExistingWorkspace: !activeWorkspaces.isEmpty, + onSelect: presentNewAgentChoice + ) + .frame(width: 320) + .presentationCompactAdaptation(.popover) + .navigationTransition(.zoom(sourceID: "AidenNewAgentOptions", in: newAgentTransition)) + } + } + + @MainActor + private func presentNewAgentChoice(_ choice: AidenNewAgentChoice) { + isShowingNewAgentChoices = false + Task { @MainActor in + await Task.yield() + switch choice { + case .existingWorkspace: + isShowingExistingWorkspacePicker = true + case .newWorkspace: + newAgentWorkspaceName = "" + isShowingNewAgentWorkspacePrompt = true + case .scratchWorkspace: + await createNewAgentInScratchWorkspace() + } + } } @MainActor @@ -698,6 +934,7 @@ struct AidenWorkspaceShellView: View { @ViewBuilder private func workspaceDetail(workspaceID: String?) -> some View { if let workspaceID, + !archivedWorkspaceIDs.contains(workspaceID), let workspace = coordinator.workspaces.first(where: { $0.id == workspaceID }) { AidenWorkspaceDetailView( coordinator: coordinator, @@ -715,7 +952,7 @@ struct AidenWorkspaceShellView: View { } private func navigate(to workspaceID: String) { - guard coordinator.workspaces.contains(where: { $0.id == workspaceID }) else { return } + guard activeWorkspaceIDs.contains(workspaceID) else { return } selectedWorkspaceId = workspaceID if !usesSplitNavigation { compactWorkspacePath = [workspaceID] @@ -729,6 +966,24 @@ struct AidenWorkspaceShellView: View { compactWorkspacePath.removeAll { $0 == workspaceID } } + private func reconcileNavigation(workspaceIDs: [String]) { + selectedWorkspaceId = AidenWorkspaceNavigation.reconciledSelection( + current: selectedWorkspaceId, + workspaceIDs: workspaceIDs + ) + compactWorkspacePath = AidenWorkspaceNavigation.reconciledCompactPath( + current: compactWorkspacePath, + workspaceIDs: workspaceIDs + ) + } + + private func syncArchivedWorkspaceProjection() { + coordinator.setDeviceArchivedWorkspaceIDs( + archivedWorkspaceIDs, + for: coordinator.activeInstanceId + ) + } + @MainActor private func resolveNavigationRequest() async { guard let request = navigationRequest, @@ -743,9 +998,14 @@ struct AidenWorkspaceShellView: View { let chat: AidenChat switch request.destination { case .newChat: - let workspaceId = request.workspaceId ?? selectedWorkspaceId ?? coordinator.workspaces.first?.id + let workspaceId = request.workspaceId ?? selectedWorkspaceId ?? activeWorkspaces.first?.id guard let workspaceId, - coordinator.workspaces.contains(where: { $0.id == workspaceId }) else { + activeWorkspaceIDs.contains(workspaceId) else { + if let requestedWorkspaceID = request.workspaceId, + archivedWorkspaceIDs.contains(requestedWorkspaceID) { + coordinator.presentedError = String(localized: "That workspace is archived on this device. Unarchive it from Workspaces to start a chat.") + return + } coordinator.presentedError = String(localized: "The requested workspace is unavailable. Choose or add a workspace first.") return } @@ -753,7 +1013,11 @@ struct AidenWorkspaceShellView: View { chat = try await coordinator.remoteClient().createChat(workspaceId: workspaceId) case .chat(let chatId): chat = try await coordinator.remoteClient().chat(id: chatId) - guard coordinator.workspaces.contains(where: { $0.id == chat.workspaceId }) else { + guard activeWorkspaceIDs.contains(chat.workspaceId) else { + if archivedWorkspaceIDs.contains(chat.workspaceId) { + coordinator.presentedError = String(localized: "That chat belongs to a workspace archived on this device. Unarchive it from Workspaces to open the chat.") + return + } coordinator.presentedError = String(localized: "The chat's workspace is no longer available.") return } @@ -767,18 +1031,115 @@ struct AidenWorkspaceShellView: View { } } +private struct AidenNewAgentPopover: View { + @Environment(\.aidenPalette) private var palette + + let canUseExistingWorkspace: Bool + let onSelect: (AidenNewAgentChoice) -> Void + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + VStack(alignment: .leading, spacing: 5) { + Text("New Agent") + .font(.title3.bold()) + .foregroundStyle(palette.foreground) + + Text("Choose where Aiden should work.") + .font(.subheadline) + .foregroundStyle(palette.secondary) + } + .padding(.horizontal, 18) + .padding(.top, 18) + .padding(.bottom, 10) + + ForEach(Array(AidenNewAgentChoice.allCases.enumerated()), id: \.element.id) { index, choice in + Button { + onSelect(choice) + } label: { + HStack(alignment: .center, spacing: 13) { + Image(systemName: choice.symbol) + .font(.system(size: 17, weight: .semibold)) + .foregroundStyle(palette.accent) + .frame(width: 38, height: 38) + .background(palette.accent.opacity(0.12), in: Circle()) + + VStack(alignment: .leading, spacing: 3) { + Text(choice.title) + .font(.body.weight(.semibold)) + .foregroundStyle(palette.foreground) + .lineLimit(1) + + Text(detail(for: choice)) + .font(.caption) + .foregroundStyle(palette.secondary) + .fixedSize(horizontal: false, vertical: true) + } + + Spacer(minLength: 6) + + Image(systemName: "chevron.forward") + .font(.caption.weight(.semibold)) + .foregroundStyle(palette.secondary) + } + .padding(.horizontal, 18) + .padding(.vertical, 10) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .disabled(choice == .existingWorkspace && !canUseExistingWorkspace) + .opacity(choice == .existingWorkspace && !canUseExistingWorkspace ? 0.45 : 1) + .accessibilityHint(detail(for: choice)) + + if index < AidenNewAgentChoice.allCases.count - 1 { + Divider() + .overlay(palette.secondary.opacity(0.18)) + .padding(.leading, 69) + } + } + } + .padding(.bottom, 8) + .background(palette.canvas.opacity(0.001)) + .accessibilityElement(children: .contain) + } + + private func detail(for choice: AidenNewAgentChoice) -> String { + if choice == .existingWorkspace, !canUseExistingWorkspace { + return String(localized: "Add a workspace before using this option.") + } + return choice.detail + } +} + private struct AidenWorkspacesDirectoryView: View { @Bindable var coordinator: AidenRemoteCoordinator + @Bindable var archiveStore: AidenWorkspaceArchiveStore @State private var searchText = "" @State private var isShowingFolderBrowser = false @State private var isShowingNewWorkspace = false @State private var isConfirmingScratch = false @State private var newWorkspaceName = "" + @State private var workspacePendingRename: AidenWorkspace? + @State private var workspacePendingFirstArchive: AidenWorkspace? + @State private var workspacePendingRemoval: AidenWorkspace? + @State private var renameText = "" + @State private var retainedRenameDraftWorkspaceID: String? @AppStorage("aiden.defaults.workspacePermission") private var defaultWorkspacePermissionRaw = AidenWorkspacePermission.ask.rawValue + private var archivedWorkspaceIDs: Set { + archiveStore.archivedWorkspaceIDs(for: coordinator.activeInstanceId) + } + + private var activeWorkspaces: [AidenWorkspace] { + coordinator.workspaces.filter { !archivedWorkspaceIDs.contains($0.id) } + } + + private var archivedWorkspaces: [AidenWorkspace] { + coordinator.workspaces.filter { archivedWorkspaceIDs.contains($0.id) } + } + private var filteredWorkspaces: [AidenWorkspace] { - guard !searchText.isEmpty else { return coordinator.workspaces } - return coordinator.workspaces.filter { workspace in + guard !searchText.isEmpty else { return activeWorkspaces } + return activeWorkspaces.filter { workspace in workspace.name.localizedCaseInsensitiveContains(searchText) || workspace.repositoryName?.localizedCaseInsensitiveContains(searchText) == true || workspace.branchName?.localizedCaseInsensitiveContains(searchText) == true @@ -798,14 +1159,23 @@ private struct AidenWorkspacesDirectoryView: View { .listRowBackground(Color.clear) } else { ForEach(filteredWorkspaces) { workspace in + interactiveRow(workspace, isArchived: false) + } + } + + if searchText.isEmpty { + Section { NavigationLink { - AidenDirectoryWorkspaceDetail( + AidenArchivedWorkspacesView( coordinator: coordinator, - workspace: workspace + workspaces: archivedWorkspaces, + row: interactiveRow ) } label: { - AidenWorkspaceRow(workspace: workspace) + Label("Archived Workspaces", systemImage: "archivebox") } + } footer: { + Text("Archived workspaces and their chats are hidden only on this device.") } } } @@ -878,6 +1248,118 @@ private struct AidenWorkspacesDirectoryView: View { } message: { Text("Aiden Agent will create and manage the worktree on your Mac.") } + .alert( + "Rename Workspace", + isPresented: Binding( + get: { workspacePendingRename != nil }, + set: { if !$0 { workspacePendingRename = nil } } + ) + ) { + TextField("Workspace name", text: $renameText) + Button("Cancel", role: .cancel) { + retainedRenameDraftWorkspaceID = nil + workspacePendingRename = nil + } + Button("Rename") { + guard let workspace = workspacePendingRename else { return } + let trimmedName = renameText.trimmingCharacters(in: .whitespacesAndNewlines) + Task { + if await coordinator.updateWorkspace(workspace, name: trimmedName) != nil { + retainedRenameDraftWorkspaceID = nil + } + workspacePendingRename = nil + } + } + .disabled( + renameText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + || renameText.trimmingCharacters(in: .whitespacesAndNewlines) == workspacePendingRename?.name + || coordinator.connectionState != .connected + || coordinator.isMutating + ) + } message: { + Text("This updates the workspace name in Aiden Agent on your Mac and paired clients. It does not rename the folder on disk.") + } + .alert( + "Archive on This Device?", + isPresented: Binding( + get: { workspacePendingFirstArchive != nil }, + set: { if !$0 { workspacePendingFirstArchive = nil } } + ) + ) { + Button("Cancel", role: .cancel) { workspacePendingFirstArchive = nil } + Button("Archive on This Device") { + guard let workspace = workspacePendingFirstArchive else { return } + archiveStore.acknowledgeDeviceOnlyArchive() + archiveStore.archive( + workspaceID: workspace.id, + instanceID: coordinator.activeInstanceId + ) + workspacePendingFirstArchive = nil + } + } message: { + Text("This hides the workspace and its chats only on this iPhone or iPad. It stays available in Aiden Agent on your Mac and on other devices.") + } + .alert( + "Remove from Aiden Agent?", + isPresented: Binding( + get: { workspacePendingRemoval != nil }, + set: { if !$0 { workspacePendingRemoval = nil } } + ) + ) { + Button("Cancel", role: .cancel) { workspacePendingRemoval = nil } + Button("Remove from Aiden Agent", role: .destructive) { + guard let workspace = workspacePendingRemoval else { return } + workspacePendingRemoval = nil + Task { + if await coordinator.removeWorkspace(workspace) { + archiveStore.forget( + workspaceID: workspace.id, + instanceID: coordinator.activeInstanceId + ) + } + } + } + .disabled(coordinator.connectionState != .connected || coordinator.isMutating) + } message: { + Text("This unregisters the workspace from Aiden Agent and paired clients. Its folder, files, and chats stay on your Mac, but its chats will no longer be listed. Delete the folder separately in Finder if you no longer need it.") + } + } + + private func interactiveRow(_ workspace: AidenWorkspace, isArchived: Bool) -> some View { + AidenWorkspaceInteractiveRow( + coordinator: coordinator, + workspace: workspace, + isArchived: isArchived, + onRename: { requestRename(workspace) }, + onToggleArchive: { toggleArchive(workspace, isArchived: isArchived) }, + onRemove: workspace.isManagedWorktree || coordinator.workspaces.count <= 1 + ? nil + : { workspacePendingRemoval = workspace } + ) + } + + private func requestRename(_ workspace: AidenWorkspace) { + if retainedRenameDraftWorkspaceID != workspace.id { + renameText = workspace.name + } + retainedRenameDraftWorkspaceID = workspace.id + workspacePendingRename = workspace + } + + private func toggleArchive(_ workspace: AidenWorkspace, isArchived: Bool) { + if isArchived { + archiveStore.unarchive( + workspaceID: workspace.id, + instanceID: coordinator.activeInstanceId + ) + } else if archiveStore.hasAcknowledgedDeviceOnlyArchive { + archiveStore.archive( + workspaceID: workspace.id, + instanceID: coordinator.activeInstanceId + ) + } else { + workspacePendingFirstArchive = workspace + } } @MainActor @@ -888,6 +1370,131 @@ private struct AidenWorkspacesDirectoryView: View { } } +private struct AidenArchivedWorkspacesView: View { + @Bindable var coordinator: AidenRemoteCoordinator + let workspaces: [AidenWorkspace] + @ViewBuilder let row: (AidenWorkspace, Bool) -> Row + @State private var searchText = "" + + private var filteredWorkspaces: [AidenWorkspace] { + guard !searchText.isEmpty else { return workspaces } + return workspaces.filter { workspace in + workspace.name.localizedCaseInsensitiveContains(searchText) + || workspace.repositoryName?.localizedCaseInsensitiveContains(searchText) == true + || workspace.branchName?.localizedCaseInsensitiveContains(searchText) == true + } + } + + var body: some View { + List { + if filteredWorkspaces.isEmpty { + ContentUnavailableView( + searchText.isEmpty ? "No Archived Workspaces" : "No Matching Workspaces", + systemImage: searchText.isEmpty ? "archivebox" : "magnifyingglass", + description: Text(searchText.isEmpty + ? "Workspaces archived on this device appear here." + : "Try a different search term.") + ) + .listRowBackground(Color.clear) + } else { + ForEach(filteredWorkspaces) { workspace in + row(workspace, true) + } + } + } + .listStyle(.plain) + .navigationTitle("Archived Workspaces") + .navigationBarTitleDisplayMode(.inline) + .searchable(text: $searchText, prompt: "Search archived workspaces") + .refreshable { await coordinator.refreshWorkspaces() } + } +} + +private struct AidenWorkspaceInteractiveRow: View { + @Environment(\.aidenPalette) private var palette + @Bindable var coordinator: AidenRemoteCoordinator + let workspace: AidenWorkspace + let isArchived: Bool + let onRename: () -> Void + let onToggleArchive: () -> Void + let onRemove: (() -> Void)? + + private var serverMutationDisabled: Bool { + coordinator.connectionState != .connected || coordinator.isMutating + } + + var body: some View { + Group { + if isArchived { + Button(action: onToggleArchive) { + AidenWorkspaceRow(workspace: workspace) + } + .buttonStyle(.plain) + .accessibilityHint("Unarchives this workspace on this device.") + } else { + NavigationLink { + AidenDirectoryWorkspaceDetail( + coordinator: coordinator, + workspace: workspace + ) + } label: { + AidenWorkspaceRow(workspace: workspace) + } + } + } + .swipeActions(edge: .leading, allowsFullSwipe: false) { + Button(action: onRename) { + Label("Rename", systemImage: "pencil") + } + .disabled(serverMutationDisabled) + .tint(palette.accent) + } + .swipeActions(edge: .trailing, allowsFullSwipe: false) { + if let onRemove { + Button(role: .destructive, action: onRemove) { + Label("Remove from Agent", systemImage: "minus.circle") + } + .disabled(serverMutationDisabled) + .tint(palette.danger) + } + + Button(action: onToggleArchive) { + Label( + isArchived ? "Unarchive" : "Archive", + systemImage: isArchived ? "tray.and.arrow.up" : "archivebox" + ) + } + .tint(palette.warning) + } + .contextMenu { + Button(action: onRename) { + Label("Rename", systemImage: "pencil") + } + .disabled(serverMutationDisabled) + + Button(action: onToggleArchive) { + Label( + isArchived ? "Unarchive on This Device" : "Archive on This Device", + systemImage: isArchived ? "tray.and.arrow.up" : "archivebox" + ) + } + + if let onRemove { + Divider() + Button(role: .destructive, action: onRemove) { + Label("Remove from Aiden Agent", systemImage: "minus.circle") + } + .disabled(serverMutationDisabled) + } + } + .accessibilityAction(named: Text("Rename workspace"), onRename) + .accessibilityAction( + named: Text(isArchived ? "Unarchive on this device" : "Archive on this device"), + onToggleArchive + ) + } +} + private struct AidenDirectoryWorkspaceDetail: View { @Environment(\.dismiss) private var dismiss @Bindable var coordinator: AidenRemoteCoordinator @@ -998,13 +1605,17 @@ private struct AidenWorkspaceSettingsView: View { var body: some View { NavigationStack { Form { - Section("Workspace") { + Section { TextField("Name", text: $name) LabeledContent("Folder", value: workspace.hasFolder ? "Connected" : "None") LabeledContent("Managed worktree", value: workspace.isManagedWorktree ? "Yes" : "No") if let repository = workspace.repositoryName { LabeledContent("Repository", value: repository) } + } header: { + Text("Workspace") + } footer: { + Text("Renaming updates Aiden Agent and paired clients. It does not rename the folder on disk.") } Section { @@ -1035,17 +1646,19 @@ private struct AidenWorkspaceSettingsView: View { .disabled(!workspace.hasFolder) } - Section { - Button( - workspace.isManagedWorktree ? "Delete Managed Worktree" : "Unregister Workspace", - role: .destructive - ) { - isConfirmingRemoval = true + if workspace.isManagedWorktree || coordinator.workspaces.count > 1 { + Section { + Button( + workspace.isManagedWorktree ? "Delete Managed Worktree" : "Remove from Aiden Agent", + role: .destructive + ) { + isConfirmingRemoval = true + } + } footer: { + Text(workspace.isManagedWorktree + ? "Deleting an Aiden-managed worktree removes its checkout and may remove its branch when safe." + : "Removing unregisters this workspace from Aiden Agent and paired clients. Its folder, files, and chats stay on your Mac, but its chats will no longer be listed.") } - } footer: { - Text(workspace.isManagedWorktree - ? "Deleting an Aiden-managed worktree removes its checkout and may remove its branch when safe." - : "Unregistering removes this workspace from Aiden. It does not delete its folder.") } } .navigationTitle("Workspace Settings") @@ -1071,11 +1684,11 @@ private struct AidenWorkspaceSettingsView: View { } } .confirmationDialog( - workspace.isManagedWorktree ? "Delete \(workspace.name)?" : "Unregister \(workspace.name)?", + workspace.isManagedWorktree ? "Delete \(workspace.name)?" : "Remove \(workspace.name) from Aiden Agent?", isPresented: $isConfirmingRemoval, titleVisibility: .visible ) { - Button(workspace.isManagedWorktree ? "Delete Managed Worktree" : "Unregister Workspace", role: .destructive) { + Button(workspace.isManagedWorktree ? "Delete Managed Worktree" : "Remove from Aiden Agent", role: .destructive) { Task { let removed = workspace.isManagedWorktree ? await coordinator.removeManagedWorktree(workspace) @@ -1089,7 +1702,7 @@ private struct AidenWorkspaceSettingsView: View { } message: { Text(workspace.isManagedWorktree ? "This destructive Git operation is performed by Aiden Agent using its persisted worktree ownership record." - : "The folder and its files remain untouched on your Mac.") + : "The folder, its files, and chats remain on your Mac, but the chats will no longer be listed. Delete the folder separately in Finder if you no longer need it.") } } } @@ -1097,36 +1710,48 @@ private struct AidenWorkspaceSettingsView: View { private struct AidenUsageView: View { @Environment(\.dismiss) private var dismiss + @Environment(\.aidenPalette) private var palette let usage: AidenUsageSummary - var body: some View { - NavigationStack { - List { - Section("Last 30 days") { - LabeledContent("Requests", value: usage.totals.requests.formatted()) - LabeledContent("Total tokens", value: usage.totals.tokens.total.formatted()) - LabeledContent("Input", value: usage.totals.tokens.input.formatted()) - LabeledContent("Output", value: usage.totals.tokens.output.formatted()) - LabeledContent("Reasoning", value: usage.totals.tokens.reasoning.formatted()) - LabeledContent("Cache read", value: usage.totals.tokens.cacheRead.formatted()) - } + private var heatmapDays: [AidenUsageHeatmapDay] { + AidenUsagePresentation.heatmapDays(for: usage) + } - Section("Activity") { - LabeledContent("Active days", value: usage.totals.activeDays.formatted()) - LabeledContent("Current streak", value: "\(usage.totals.currentStreak) days") - LabeledContent("Local requests", value: usage.totals.localRequests.formatted()) - LabeledContent( - "Hosted cost", - value: usage.totals.hostedCostUsd.formatted(.currency(code: "USD")) - ) - } + private var maximumDailyTokens: Int { + max(heatmapDays.map(\.tokens).max() ?? 0, 1) + } - Section { - Text("These are privacy-safe aggregates recorded by Aiden Agent on your Mac. Prompts, responses, chat IDs, workspace IDs, and file paths are not part of usage data.") - .font(.footnote) - .foregroundStyle(.secondary) + private var completionRate: Double { + AidenUsagePresentation.ratio( + usage.totals.completedRequests, + of: usage.totals.requests + ) + } + + private var localRequestShare: Double { + AidenUsagePresentation.ratio( + usage.totals.localRequests, + of: usage.totals.requests + ) + } + + var body: some View { + NavigationStack { + ScrollView { + LazyVStack(alignment: .leading, spacing: 28) { + usageHero + overviewGrid + tokenActivitySection + activityInsightsSection + modelSection + privacyNote } + .padding(.horizontal, 20) + .padding(.top, 12) + .padding(.bottom, 36) } + .scrollContentBackground(.hidden) + .background(palette.canvas.ignoresSafeArea()) .navigationTitle("Usage") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -1136,6 +1761,377 @@ private struct AidenUsageView: View { } } } + + private var usageHero: some View { + VStack(alignment: .leading, spacing: 6) { + HStack(spacing: 10) { + AidenSidebarLogo(size: 28, color: palette.accent) + Text("Aiden activity") + .font(.title2.weight(.bold)) + .foregroundStyle(palette.foreground) + } + + Text(AidenUsagePresentation.dateRangeText(for: usage)) + .font(.subheadline) + .foregroundStyle(palette.secondary) + } + .accessibilityElement(children: .combine) + } + + private var overviewGrid: some View { + LazyVGrid( + columns: [GridItem(.flexible(), spacing: 12), GridItem(.flexible(), spacing: 12)], + spacing: 12 + ) { + metricCard( + value: usage.totals.requests.formatted(), + label: "Requests", + symbol: "bolt.fill" + ) + metricCard( + value: usage.totals.activeDays.formatted(), + label: "Active days", + symbol: "calendar" + ) + metricCard( + value: AidenUsagePresentation.dayCount(usage.totals.currentStreak), + label: "Current streak", + symbol: "flame" + ) + metricCard( + value: AidenUsagePresentation.dayCount(usage.totals.longestStreak), + label: "Longest streak", + symbol: "trophy" + ) + + HStack(alignment: .center, spacing: 14) { + Image(systemName: "circle.hexagongrid.fill") + .font(.title2) + .foregroundStyle(palette.accent) + .frame(width: 34, height: 34) + + VStack(alignment: .leading, spacing: 3) { + Text(usage.totals.tokens.total.formatted()) + .font(.title2.weight(.bold)) + .foregroundStyle(palette.foreground) + .contentTransition(.numericText()) + Text("Total tokens") + .font(.subheadline) + .foregroundStyle(palette.secondary) + } + Spacer(minLength: 0) + } + .padding(18) + .frame(maxWidth: .infinity, minHeight: 92, alignment: .leading) + .gridCellColumns(2) + .aidenUsageCard(palette: palette) + .accessibilityElement(children: .combine) + } + } + + private func metricCard(value: String, label: String, symbol: String) -> some View { + VStack(alignment: .leading, spacing: 12) { + Image(systemName: symbol) + .font(.body.weight(.semibold)) + .foregroundStyle(palette.accent) + .frame(width: 30, height: 30) + .background(palette.accent.opacity(0.12), in: RoundedRectangle(cornerRadius: 9, style: .continuous)) + + VStack(alignment: .leading, spacing: 2) { + Text(value) + .font(.title3.weight(.bold)) + .foregroundStyle(palette.foreground) + .contentTransition(.numericText()) + Text(label) + .font(.subheadline) + .foregroundStyle(palette.secondary) + } + } + .padding(16) + .frame(maxWidth: .infinity, minHeight: 118, alignment: .leading) + .aidenUsageCard(palette: palette) + .accessibilityElement(children: .combine) + } + + private var tokenActivitySection: some View { + usageSection(title: "Token activity") { + VStack(alignment: .leading, spacing: 18) { + HStack { + Text("Daily totals") + .font(.subheadline.weight(.semibold)) + .foregroundStyle(palette.foreground) + Spacer() + Text("Last 30 days") + .font(.caption.weight(.medium)) + .foregroundStyle(palette.secondary) + .padding(.horizontal, 10) + .padding(.vertical, 6) + .background(palette.sidebar, in: Capsule()) + } + + LazyVGrid( + columns: Array(repeating: GridItem(.flexible(), spacing: 6), count: 10), + spacing: 6 + ) { + ForEach(heatmapDays) { day in + RoundedRectangle(cornerRadius: 5, style: .continuous) + .fill(activityColor(tokens: day.tokens)) + .aspectRatio(1, contentMode: .fit) + .accessibilityElement() + .accessibilityLabel("\(day.date), \(day.tokens.formatted()) tokens") + } + } + + HStack(spacing: 6) { + Text("Less") + ForEach(0..<5, id: \.self) { level in + RoundedRectangle(cornerRadius: 3, style: .continuous) + .fill(activityColor(level: level)) + .frame(width: 14, height: 14) + } + Text("More") + } + .font(.caption2) + .foregroundStyle(palette.secondary) + + Divider().overlay(palette.secondary.opacity(0.18)) + + tokenBreakdown + } + .padding(18) + .aidenUsageCard(palette: palette) + } + } + + private var tokenBreakdown: some View { + VStack(spacing: 14) { + usageValueRow("Input", value: usage.totals.tokens.input.formatted(), color: palette.accent) + usageValueRow("Output", value: usage.totals.tokens.output.formatted(), color: palette.success) + usageValueRow("Reasoning", value: usage.totals.tokens.reasoning.formatted(), color: palette.warning) + usageValueRow("Cache read", value: usage.totals.tokens.cacheRead.formatted(), color: palette.secondary) + } + } + + private var activityInsightsSection: some View { + usageSection(title: "Activity insights") { + VStack(spacing: 0) { + insightRow("Completed requests", value: completionRate.formatted(.percent.precision(.fractionLength(0)))) + insightDivider + insightRow("Local model share", value: localRequestShare.formatted(.percent.precision(.fractionLength(0)))) + insightDivider + insightRow("Failed requests", value: usage.totals.failedRequests.formatted()) + insightDivider + insightRow( + "Hosted cost", + value: usage.totals.hostedCostUsd.formatted(.currency(code: "USD")) + ) + } + .padding(.horizontal, 18) + .aidenUsageCard(palette: palette) + } + } + + @ViewBuilder + private var modelSection: some View { + if !usage.models.isEmpty { + usageSection(title: "Most used models") { + VStack(spacing: 0) { + ForEach(Array(usage.models.prefix(5).enumerated()), id: \.element.id) { index, model in + HStack(spacing: 12) { + AidenProviderIcon( + providerID: model.providerId, + providerLabel: model.providerLabel, + modelID: model.modelId, + size: 20, + color: palette.accent + ) + .frame(width: 34, height: 34) + .background(palette.sidebar, in: RoundedRectangle(cornerRadius: 10, style: .continuous)) + + VStack(alignment: .leading, spacing: 2) { + Text(model.modelLabel) + .font(.body.weight(.semibold)) + .foregroundStyle(palette.foreground) + .lineLimit(1) + Text(model.local ? "\(model.providerLabel) · Local" : model.providerLabel) + .font(.caption) + .foregroundStyle(palette.secondary) + .lineLimit(1) + } + + Spacer(minLength: 8) + + Text("\(model.requests.formatted()) runs") + .font(.subheadline) + .foregroundStyle(palette.secondary) + .fixedSize(horizontal: true, vertical: false) + } + .padding(.vertical, 14) + + if index < min(usage.models.count, 5) - 1 { + insightDivider + } + } + } + .padding(.horizontal, 18) + .aidenUsageCard(palette: palette) + } + } + } + + private var privacyNote: some View { + HStack(alignment: .top, spacing: 12) { + Image(systemName: "lock.shield") + .font(.body.weight(.semibold)) + .foregroundStyle(palette.accent) + .frame(width: 28) + + Text("Privacy-safe aggregates are recorded by Aiden Agent on your Mac. Prompts, responses, chat IDs, workspace IDs, and file paths are not included.") + .font(.footnote) + .foregroundStyle(palette.secondary) + .fixedSize(horizontal: false, vertical: true) + } + .padding(16) + .frame(maxWidth: .infinity, alignment: .leading) + .background(palette.accent.opacity(0.08), in: RoundedRectangle(cornerRadius: 18, style: .continuous)) + .accessibilityElement(children: .combine) + } + + private func usageSection( + title: String, + @ViewBuilder content: () -> Content + ) -> some View { + VStack(alignment: .leading, spacing: 12) { + Text(title) + .font(.title3.weight(.semibold)) + .foregroundStyle(palette.secondary) + .padding(.leading, 4) + content() + } + } + + private func usageValueRow(_ label: String, value: String, color: Color) -> some View { + HStack(spacing: 10) { + Circle() + .fill(color) + .frame(width: 8, height: 8) + Text(label) + .foregroundStyle(palette.foreground) + Spacer() + Text(value) + .foregroundStyle(palette.secondary) + .monospacedDigit() + } + .font(.subheadline) + .accessibilityElement(children: .combine) + } + + private func insightRow(_ label: String, value: String) -> some View { + HStack(spacing: 12) { + Text(label) + .foregroundStyle(palette.foreground) + Spacer(minLength: 12) + Text(value) + .foregroundStyle(palette.secondary) + .monospacedDigit() + } + .font(.body) + .padding(.vertical, 16) + .accessibilityElement(children: .combine) + } + + private var insightDivider: some View { + Divider().overlay(palette.secondary.opacity(0.18)) + } + + private func activityColor(tokens: Int) -> Color { + guard tokens > 0 else { return palette.sidebar } + let normalized = min(Double(tokens) / Double(maximumDailyTokens), 1) + return palette.accent.opacity(0.22 + (0.78 * normalized.squareRoot())) + } + + private func activityColor(level: Int) -> Color { + guard level > 0 else { return palette.sidebar } + return palette.accent.opacity(0.18 + (Double(level) * 0.205)) + } +} + +struct AidenUsageHeatmapDay: Identifiable, Equatable { + let date: String + let tokens: Int + + var id: String { date } +} + +enum AidenUsagePresentation { + private static let dateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .gregorian) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.dateFormat = "yyyy-MM-dd" + return formatter + }() + + private static let displayFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .gregorian) + formatter.locale = .current + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.setLocalizedDateFormatFromTemplate("MMM d") + return formatter + }() + + static func ratio(_ value: Int, of total: Int) -> Double { + guard total > 0 else { return 0 } + return min(max(Double(value) / Double(total), 0), 1) + } + + static func dayCount(_ value: Int) -> String { + value == 1 ? "1 day" : "\(value) days" + } + + static func dateRangeText(for usage: AidenUsageSummary) -> String { + guard let start = dateFormatter.date(from: usage.startDate), + let end = dateFormatter.date(from: usage.endDate) else { + return String(localized: "Last 30 days") + } + return "\(displayFormatter.string(from: start))–\(displayFormatter.string(from: end))" + } + + static func heatmapDays(for usage: AidenUsageSummary) -> [AidenUsageHeatmapDay] { + let totalsByDate = Dictionary(uniqueKeysWithValues: usage.days.map { ($0.date, $0.tokens.total) }) + guard let start = dateFormatter.date(from: usage.startDate), + let end = dateFormatter.date(from: usage.endDate), + start <= end else { + return usage.days.map { AidenUsageHeatmapDay(date: $0.date, tokens: $0.tokens.total) } + } + + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(secondsFromGMT: 0)! + var date = start + var result: [AidenUsageHeatmapDay] = [] + while date <= end, result.count < 366 { + let key = dateFormatter.string(from: date) + result.append(AidenUsageHeatmapDay(date: key, tokens: totalsByDate[key] ?? 0)) + guard let next = calendar.date(byAdding: .day, value: 1, to: date) else { break } + date = next + } + return result + } +} + +private extension View { + func aidenUsageCard(palette: AidenPalette) -> some View { + background( + palette.raised, + in: RoundedRectangle(cornerRadius: 24, style: .continuous) + ) + .overlay { + RoundedRectangle(cornerRadius: 24, style: .continuous) + .stroke(palette.foreground.opacity(0.06), lineWidth: 0.5) + } + } } private struct AidenAppSettingsView: View { @@ -1245,8 +2241,10 @@ private struct AidenInstallationsView: View { } } } + .disabled(coordinator.isMutating || coordinator.connectionState == .connecting) .swipeActions { Button("Forget", role: .destructive) { installationToRemove = installation } + .disabled(coordinator.isMutating) } } } diff --git a/ios/AidenOnTheGo/Features/Shared/AidenProviderIcon.swift b/ios/AidenOnTheGo/Features/Shared/AidenProviderIcon.swift new file mode 100644 index 00000000..f14ef63d --- /dev/null +++ b/ios/AidenOnTheGo/Features/Shared/AidenProviderIcon.swift @@ -0,0 +1,129 @@ +import SwiftUI + +enum AidenProviderIconResolver { + static let supportedSlugs: Set = [ + "amazon-bedrock", + "ant-ling", + "anthropic", + "apple-foundation-models", + "azure-openai-responses", + "cerebras", + "claude", + "cloudflare-ai-gateway", + "cloudflare-workers-ai", + "deepseek", + "fireworks", + "github-copilot", + "google", + "google-vertex", + "grok", + "groq", + "huggingface", + "kimi-coding", + "lmstudio", + "minimax", + "minimax-cn", + "mistral", + "moonshotai", + "moonshotai-cn", + "nvidia", + "ollama", + "openai", + "openai-codex", + "opencode", + "opencode-go", + "openrouter", + "together", + "vercel-ai-gateway", + "xai", + "xiaomi", + "xiaomi-token-plan-ams", + "xiaomi-token-plan-cn", + "xiaomi-token-plan-sgp", + "zai", + "zai-coding-cn", + ] + + static let multicolorSlugs: Set = [ + "fireworks", + "groq", + "opencode", + "opencode-go", + "together", + "zai", + "zai-coding-cn", + ] + + private static let aliases = [ + "gemini": "google", + "lm-studio": "lmstudio", + "moonshot": "moonshotai", + ] + + static func slug(providerID: String, modelID: String? = nil) -> String? { + let provider = providerID.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() + let model = modelID?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() ?? "" + + if provider == "anthropic", model.contains("claude") { return "claude" } + if provider == "xai", model.contains("grok") { return "grok" } + if matchesNumberedCustomProvider(provider, base: "custom:lmstudio") { return "lmstudio" } + if matchesNumberedCustomProvider(provider, base: "custom:ollama") { return "ollama" } + if let alias = aliases[provider] { return alias } + return supportedSlugs.contains(provider) ? provider : nil + } + + private static func matchesNumberedCustomProvider(_ provider: String, base: String) -> Bool { + if provider == base { return true } + guard provider.hasPrefix(base + "-") else { return false } + let suffix = provider.dropFirst(base.count + 1) + guard !suffix.isEmpty, + suffix.first != "0", + let number = Int(suffix) + else { return false } + return number >= 2 + } +} + +struct AidenProviderIcon: View { + @Environment(\.aidenPalette) private var palette + + let providerID: String + let providerLabel: String + var modelID: String? = nil + var size: CGFloat = 20 + var color: Color? = nil + + private var slug: String? { + AidenProviderIconResolver.slug(providerID: providerID, modelID: modelID) + } + + var body: some View { + Group { + if let slug { + Image("ProviderLogo-\(slug)") + .resizable() + .renderingMode( + AidenProviderIconResolver.multicolorSlugs.contains(slug) + ? .original + : .template + ) + .foregroundStyle(color ?? palette.foreground) + .scaledToFit() + } else { + Text(providerInitial) + .font(.system(size: size * 0.48, weight: .semibold, design: .rounded)) + .foregroundStyle(color ?? palette.secondary) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(palette.sidebar) + .clipShape(RoundedRectangle(cornerRadius: size * 0.28, style: .continuous)) + } + } + .frame(width: size, height: size) + .accessibilityHidden(true) + } + + private var providerInitial: String { + providerLabel.trimmingCharacters(in: .whitespacesAndNewlines).first + .map { String($0).uppercased() } ?? "?" + } +} diff --git a/ios/AidenOnTheGo/Models/AidenChat.swift b/ios/AidenOnTheGo/Models/AidenChat.swift index 581c9e76..fe552ac6 100644 --- a/ios/AidenOnTheGo/Models/AidenChat.swift +++ b/ios/AidenOnTheGo/Models/AidenChat.swift @@ -160,11 +160,37 @@ struct AidenUsageTotals: Codable, Equatable, Sendable { let tokens: AidenUsageTokens } +struct AidenUsageDay: Codable, Equatable, Sendable { + let date: String + let requests: Int + let reportedTokenRequests: Int + let unmeteredRequests: Int + let tokens: AidenUsageTokens + let hostedCostUsd: Double +} + +struct AidenUsageModel: Codable, Equatable, Identifiable, Sendable { + let providerId: String + let providerLabel: String + let modelId: String + let modelLabel: String + let local: Bool + let requests: Int + let reportedTokenRequests: Int + let unmeteredRequests: Int + let tokens: AidenUsageTokens + let hostedCostUsd: Double + + var id: String { "\(providerId):\(modelId):\(local)" } +} + struct AidenUsageSummary: Codable, Equatable, Sendable { let range: String let startDate: String let endDate: String let totals: AidenUsageTotals + let days: [AidenUsageDay] + let models: [AidenUsageModel] } struct AidenTurnStart: Encodable, Equatable, Sendable { diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-amazon-bedrock.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-amazon-bedrock.imageset/Contents.json new file mode 100644 index 00000000..440f66fc --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-amazon-bedrock.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "amazon-bedrock.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-amazon-bedrock.imageset/amazon-bedrock.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-amazon-bedrock.imageset/amazon-bedrock.svg new file mode 100644 index 00000000..70316bab --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-amazon-bedrock.imageset/amazon-bedrock.svg @@ -0,0 +1 @@ +Amazon \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ant-ling.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ant-ling.imageset/Contents.json new file mode 100644 index 00000000..1445a6f4 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ant-ling.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "ant-ling.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ant-ling.imageset/ant-ling.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ant-ling.imageset/ant-ling.svg new file mode 100644 index 00000000..3253266f --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ant-ling.imageset/ant-ling.svg @@ -0,0 +1,3 @@ + + + diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-anthropic.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-anthropic.imageset/Contents.json new file mode 100644 index 00000000..ee77b5e1 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-anthropic.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "anthropic.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-anthropic.imageset/anthropic.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-anthropic.imageset/anthropic.svg new file mode 100644 index 00000000..c917480d --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-anthropic.imageset/anthropic.svg @@ -0,0 +1 @@ +Anthropic \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-apple-foundation-models.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-apple-foundation-models.imageset/Contents.json new file mode 100644 index 00000000..26216090 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-apple-foundation-models.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "apple-foundation-models.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-apple-foundation-models.imageset/apple-foundation-models.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-apple-foundation-models.imageset/apple-foundation-models.svg new file mode 100644 index 00000000..de4b0d17 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-apple-foundation-models.imageset/apple-foundation-models.svg @@ -0,0 +1 @@ +Apple \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-azure-openai-responses.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-azure-openai-responses.imageset/Contents.json new file mode 100644 index 00000000..042f9556 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-azure-openai-responses.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "azure-openai-responses.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-azure-openai-responses.imageset/azure-openai-responses.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-azure-openai-responses.imageset/azure-openai-responses.svg new file mode 100644 index 00000000..69a264e2 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-azure-openai-responses.imageset/azure-openai-responses.svg @@ -0,0 +1 @@ +Microsoft Azure \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cerebras.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cerebras.imageset/Contents.json new file mode 100644 index 00000000..98c01ddb --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cerebras.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "cerebras.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cerebras.imageset/cerebras.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cerebras.imageset/cerebras.svg new file mode 100644 index 00000000..9562aff9 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cerebras.imageset/cerebras.svg @@ -0,0 +1,5 @@ + + Cerebras + + + diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-claude.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-claude.imageset/Contents.json new file mode 100644 index 00000000..2ab0af9c --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-claude.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "claude.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-claude.imageset/claude.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-claude.imageset/claude.svg new file mode 100644 index 00000000..1beee861 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-claude.imageset/claude.svg @@ -0,0 +1 @@ +Claude \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-ai-gateway.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-ai-gateway.imageset/Contents.json new file mode 100644 index 00000000..2b4ad463 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-ai-gateway.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "cloudflare-ai-gateway.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-ai-gateway.imageset/cloudflare-ai-gateway.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-ai-gateway.imageset/cloudflare-ai-gateway.svg new file mode 100644 index 00000000..a1cf2d6d --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-ai-gateway.imageset/cloudflare-ai-gateway.svg @@ -0,0 +1 @@ +Cloudflare \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-workers-ai.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-workers-ai.imageset/Contents.json new file mode 100644 index 00000000..9db13a9e --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-workers-ai.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "cloudflare-workers-ai.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-workers-ai.imageset/cloudflare-workers-ai.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-workers-ai.imageset/cloudflare-workers-ai.svg new file mode 100644 index 00000000..a1cf2d6d --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-cloudflare-workers-ai.imageset/cloudflare-workers-ai.svg @@ -0,0 +1 @@ +Cloudflare \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-deepseek.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-deepseek.imageset/Contents.json new file mode 100644 index 00000000..7250436b --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-deepseek.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "deepseek.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-deepseek.imageset/deepseek.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-deepseek.imageset/deepseek.svg new file mode 100644 index 00000000..e19a4597 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-deepseek.imageset/deepseek.svg @@ -0,0 +1 @@ +DeepSeek \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-fireworks.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-fireworks.imageset/Contents.json new file mode 100644 index 00000000..cfe55ecc --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-fireworks.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images": [ + { + "filename": "fireworks.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-fireworks.imageset/fireworks.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-fireworks.imageset/fireworks.svg new file mode 100644 index 00000000..cfbfcfd4 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-fireworks.imageset/fireworks.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-github-copilot.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-github-copilot.imageset/Contents.json new file mode 100644 index 00000000..d6ee362a --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-github-copilot.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "github-copilot.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-github-copilot.imageset/github-copilot.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-github-copilot.imageset/github-copilot.svg new file mode 100644 index 00000000..f064947d --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-github-copilot.imageset/github-copilot.svg @@ -0,0 +1 @@ +GitHub Copilot \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google-vertex.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google-vertex.imageset/Contents.json new file mode 100644 index 00000000..0a8f2413 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google-vertex.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "google-vertex.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google-vertex.imageset/google-vertex.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google-vertex.imageset/google-vertex.svg new file mode 100644 index 00000000..765adb08 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google-vertex.imageset/google-vertex.svg @@ -0,0 +1 @@ +Google Gemini \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google.imageset/Contents.json new file mode 100644 index 00000000..b0b767a3 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "google.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google.imageset/google.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google.imageset/google.svg new file mode 100644 index 00000000..765adb08 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-google.imageset/google.svg @@ -0,0 +1 @@ +Google Gemini \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-grok.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-grok.imageset/Contents.json new file mode 100644 index 00000000..bada30df --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-grok.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "grok.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-grok.imageset/grok.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-grok.imageset/grok.svg new file mode 100644 index 00000000..741c54e9 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-grok.imageset/grok.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-groq.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-groq.imageset/Contents.json new file mode 100644 index 00000000..5dc3c765 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-groq.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images": [ + { + "filename": "groq.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-groq.imageset/groq.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-groq.imageset/groq.svg new file mode 100644 index 00000000..e00197c1 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-groq.imageset/groq.svg @@ -0,0 +1 @@ + diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-huggingface.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-huggingface.imageset/Contents.json new file mode 100644 index 00000000..b893d38e --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-huggingface.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "huggingface.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-huggingface.imageset/huggingface.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-huggingface.imageset/huggingface.svg new file mode 100644 index 00000000..dd2db93b --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-huggingface.imageset/huggingface.svg @@ -0,0 +1 @@ +Hugging Face \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-kimi-coding.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-kimi-coding.imageset/Contents.json new file mode 100644 index 00000000..49bbb929 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-kimi-coding.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "kimi-coding.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-kimi-coding.imageset/kimi-coding.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-kimi-coding.imageset/kimi-coding.svg new file mode 100644 index 00000000..3d254a1a --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-kimi-coding.imageset/kimi-coding.svg @@ -0,0 +1 @@ +KIMI \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-lmstudio.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-lmstudio.imageset/Contents.json new file mode 100644 index 00000000..a2647e25 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-lmstudio.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "lmstudio.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-lmstudio.imageset/lmstudio.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-lmstudio.imageset/lmstudio.svg new file mode 100644 index 00000000..ff243d99 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-lmstudio.imageset/lmstudio.svg @@ -0,0 +1 @@ +LM Studio \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax-cn.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax-cn.imageset/Contents.json new file mode 100644 index 00000000..41fb1ae6 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax-cn.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "minimax-cn.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax-cn.imageset/minimax-cn.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax-cn.imageset/minimax-cn.svg new file mode 100644 index 00000000..cca72298 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax-cn.imageset/minimax-cn.svg @@ -0,0 +1 @@ +MiniMax \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax.imageset/Contents.json new file mode 100644 index 00000000..891663ea --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "minimax.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax.imageset/minimax.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax.imageset/minimax.svg new file mode 100644 index 00000000..cca72298 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-minimax.imageset/minimax.svg @@ -0,0 +1 @@ +MiniMax \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-mistral.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-mistral.imageset/Contents.json new file mode 100644 index 00000000..5a6fe72d --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-mistral.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "mistral.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-mistral.imageset/mistral.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-mistral.imageset/mistral.svg new file mode 100644 index 00000000..6388f65a --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-mistral.imageset/mistral.svg @@ -0,0 +1 @@ +Mistral AI \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai-cn.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai-cn.imageset/Contents.json new file mode 100644 index 00000000..d3d1e2ac --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai-cn.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "moonshotai-cn.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai-cn.imageset/moonshotai-cn.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai-cn.imageset/moonshotai-cn.svg new file mode 100644 index 00000000..3d254a1a --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai-cn.imageset/moonshotai-cn.svg @@ -0,0 +1 @@ +KIMI \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai.imageset/Contents.json new file mode 100644 index 00000000..f5348b3d --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "moonshotai.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai.imageset/moonshotai.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai.imageset/moonshotai.svg new file mode 100644 index 00000000..3d254a1a --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-moonshotai.imageset/moonshotai.svg @@ -0,0 +1 @@ +KIMI \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-nvidia.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-nvidia.imageset/Contents.json new file mode 100644 index 00000000..d77a7b25 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-nvidia.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "nvidia.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-nvidia.imageset/nvidia.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-nvidia.imageset/nvidia.svg new file mode 100644 index 00000000..2c7ff66f --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-nvidia.imageset/nvidia.svg @@ -0,0 +1 @@ +NVIDIA \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ollama.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ollama.imageset/Contents.json new file mode 100644 index 00000000..684f53c3 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ollama.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "ollama.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ollama.imageset/ollama.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ollama.imageset/ollama.svg new file mode 100644 index 00000000..bc368e99 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-ollama.imageset/ollama.svg @@ -0,0 +1 @@ +Ollama \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai-codex.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai-codex.imageset/Contents.json new file mode 100644 index 00000000..3a2aa15e --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai-codex.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "openai-codex.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai-codex.imageset/openai-codex.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai-codex.imageset/openai-codex.svg new file mode 100644 index 00000000..ebbdab0e --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai-codex.imageset/openai-codex.svg @@ -0,0 +1 @@ +OpenAI \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai.imageset/Contents.json new file mode 100644 index 00000000..f9c116ea --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "openai.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai.imageset/openai.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai.imageset/openai.svg new file mode 100644 index 00000000..ebbdab0e --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openai.imageset/openai.svg @@ -0,0 +1 @@ +OpenAI \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode-go.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode-go.imageset/Contents.json new file mode 100644 index 00000000..2ba4d636 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode-go.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images": [ + { + "filename": "opencode-go.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode-go.imageset/opencode-go.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode-go.imageset/opencode-go.svg new file mode 100644 index 00000000..157edc4d --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode-go.imageset/opencode-go.svg @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode.imageset/Contents.json new file mode 100644 index 00000000..bc6ee1e9 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images": [ + { + "filename": "opencode.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode.imageset/opencode.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode.imageset/opencode.svg new file mode 100644 index 00000000..157edc4d --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-opencode.imageset/opencode.svg @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openrouter.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openrouter.imageset/Contents.json new file mode 100644 index 00000000..7276fb1d --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openrouter.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "openrouter.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openrouter.imageset/openrouter.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openrouter.imageset/openrouter.svg new file mode 100644 index 00000000..83ec807b --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-openrouter.imageset/openrouter.svg @@ -0,0 +1 @@ +OpenRouter \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-together.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-together.imageset/Contents.json new file mode 100644 index 00000000..3c34c015 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-together.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images": [ + { + "filename": "together.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-together.imageset/together.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-together.imageset/together.svg new file mode 100644 index 00000000..73f80f71 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-together.imageset/together.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-vercel-ai-gateway.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-vercel-ai-gateway.imageset/Contents.json new file mode 100644 index 00000000..cb72002d --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-vercel-ai-gateway.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "vercel-ai-gateway.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-vercel-ai-gateway.imageset/vercel-ai-gateway.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-vercel-ai-gateway.imageset/vercel-ai-gateway.svg new file mode 100644 index 00000000..821ecfff --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-vercel-ai-gateway.imageset/vercel-ai-gateway.svg @@ -0,0 +1 @@ +Vercel \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xai.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xai.imageset/Contents.json new file mode 100644 index 00000000..bf041557 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xai.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "xai.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xai.imageset/xai.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xai.imageset/xai.svg new file mode 100644 index 00000000..74af63dd --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xai.imageset/xai.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-ams.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-ams.imageset/Contents.json new file mode 100644 index 00000000..b256c996 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-ams.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "xiaomi-token-plan-ams.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-ams.imageset/xiaomi-token-plan-ams.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-ams.imageset/xiaomi-token-plan-ams.svg new file mode 100644 index 00000000..f08eae5b --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-ams.imageset/xiaomi-token-plan-ams.svg @@ -0,0 +1 @@ +Xiaomi \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-cn.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-cn.imageset/Contents.json new file mode 100644 index 00000000..2c8bd6b9 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-cn.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "xiaomi-token-plan-cn.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-cn.imageset/xiaomi-token-plan-cn.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-cn.imageset/xiaomi-token-plan-cn.svg new file mode 100644 index 00000000..f08eae5b --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-cn.imageset/xiaomi-token-plan-cn.svg @@ -0,0 +1 @@ +Xiaomi \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-sgp.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-sgp.imageset/Contents.json new file mode 100644 index 00000000..e03da649 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-sgp.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "xiaomi-token-plan-sgp.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-sgp.imageset/xiaomi-token-plan-sgp.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-sgp.imageset/xiaomi-token-plan-sgp.svg new file mode 100644 index 00000000..f08eae5b --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi-token-plan-sgp.imageset/xiaomi-token-plan-sgp.svg @@ -0,0 +1 @@ +Xiaomi \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi.imageset/Contents.json new file mode 100644 index 00000000..ab138fa0 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "xiaomi.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi.imageset/xiaomi.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi.imageset/xiaomi.svg new file mode 100644 index 00000000..f08eae5b --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-xiaomi.imageset/xiaomi.svg @@ -0,0 +1 @@ +Xiaomi \ No newline at end of file diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai-coding-cn.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai-coding-cn.imageset/Contents.json new file mode 100644 index 00000000..92664fa1 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai-coding-cn.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images": [ + { + "filename": "zai-coding-cn.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai-coding-cn.imageset/zai-coding-cn.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai-coding-cn.imageset/zai-coding-cn.svg new file mode 100644 index 00000000..4f511bd7 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai-coding-cn.imageset/zai-coding-cn.svg @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai.imageset/Contents.json new file mode 100644 index 00000000..aeb04652 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images": [ + { + "filename": "zai.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai.imageset/zai.svg b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai.imageset/zai.svg new file mode 100644 index 00000000..4f511bd7 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/ProviderLogo-zai.imageset/zai.svg @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/AidenOnTheGo/Resources/ThirdPartyNotices/NOTICE.txt b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/NOTICE.txt index 264a11ef..c800b8bb 100644 --- a/ios/AidenOnTheGo/Resources/ThirdPartyNotices/NOTICE.txt +++ b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/NOTICE.txt @@ -15,3 +15,5 @@ with the iOS app. See NetworkImage-LICENSE.txt. - swift-cmark 0.8.0 (transitive dependency of MarkdownUI) See swift-cmark-COPYING.txt. +- Provider logos (vector marks shared with Aiden Agent for provider identity) + See ProviderLogos-NOTICE.md for sources and trademark ownership. diff --git a/ios/AidenOnTheGo/Resources/ThirdPartyNotices/ProviderLogos-NOTICE.md b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/ProviderLogos-NOTICE.md new file mode 100644 index 00000000..e6f8a99a --- /dev/null +++ b/ios/AidenOnTheGo/Resources/ThirdPartyNotices/ProviderLogos-NOTICE.md @@ -0,0 +1,31 @@ +# Provider logo assets + +These SVGs are the compact provider marks shared with Aiden Agent's +`ProviderIcon` system. Aiden On The Go copies the reviewed vector sources into +its Xcode asset catalog so native provider surfaces use the same identity map. + +- Root Simple Icons assets remain monochrome template images so they follow + Aiden's semantic palette in light, dark, and custom appearances. +- Multicolor or opaque-square assets preserve their original rendering so + their negative space and brand colors remain intact. +- Fireworks, Together, and Grok retain their original official vector paths + with the viewBox cropped to the logomark portion of the supplied wordmark. +- The supplied `cerebras.svg` was an OpenAI wordmark. It was replaced with the + compact Cerebras mark from `@lobehub/icons-static-svg`. +- Radius intentionally has no asset. Unknown, custom, Radius, and future Pi + providers use the native neutral-initial fallback in `AidenProviderIcon`. + +## Provenance + +- Simple Icons via jsDelivr: OpenAI, Anthropic/Claude, Google Gemini, DeepSeek, + NVIDIA, OpenRouter, Hugging Face, Mistral, Cloudflare, Vercel, Amazon, Azure, + GitHub Copilot, MiniMax, Xiaomi, LM Studio, Ollama, and their provider aliases. +- Official provider sites: Ant Ling, Fireworks, Groq, Together, Z.AI, OpenCode, + Kimi/Moonshot, and their regional or product aliases. +- Wikimedia Commons: xAI and Grok. +- Lobe Icons static SVG package: Cerebras, replacing an incorrect OpenAI asset + in the supplied folder. + +The corresponding acquisition notes remain with Aiden Agent's canonical +`renderer/assets/provider-logos/README.md` inventory. +All marks remain the property of their respective owners. From 8ab986fddd126c3846e184f4016b84a63008ea38 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Thu, 20 Aug 2026 12:57:48 -0400 Subject: [PATCH 006/266] test(ios): cover mobile shell edge cases --- ios/AidenOnTheGoTests/AidenChatTests.swift | 178 +++++++++++++++ .../AidenRemoteClientTests.swift | 210 +++++++++++++++++- .../aiden-remote-workspace-http.test.ts | 3 + scripts/check-ios-shipping-target.test.mjs | 105 ++++++++- 4 files changed, 488 insertions(+), 8 deletions(-) diff --git a/ios/AidenOnTheGoTests/AidenChatTests.swift b/ios/AidenOnTheGoTests/AidenChatTests.swift index 525ad59e..5b6cb771 100644 --- a/ios/AidenOnTheGoTests/AidenChatTests.swift +++ b/ios/AidenOnTheGoTests/AidenChatTests.swift @@ -18,6 +18,68 @@ final class AidenChatTests: XCTestCase { XCTAssertEqual(AidenRelativeTimestamp.text(for: now.addingTimeInterval(30), now: now), "just now") } + func testNewAgentPopoverKeepsTheThreeReviewedWorkspaceChoices() { + XCTAssertEqual( + AidenNewAgentChoice.allCases, + [.existingWorkspace, .newWorkspace, .scratchWorkspace] + ) + XCTAssertEqual( + AidenNewAgentChoice.allCases.map(\.title), + ["Existing Workspace", "New Workspace", "Managed Scratch Workspace"] + ) + XCTAssertEqual( + AidenNewAgentChoice.allCases.map(\.symbol), + ["folder", "folder.badge.plus", "hammer.fill"] + ) + XCTAssertTrue(AidenNewAgentChoice.allCases.allSatisfy { !$0.detail.isEmpty }) + } + + func testProviderIconResolverMatchesDesktopAliasesAndFallbackRules() { + XCTAssertEqual(AidenProviderIconResolver.slug(providerID: "openai"), "openai") + XCTAssertEqual(AidenProviderIconResolver.slug(providerID: "gemini"), "google") + XCTAssertEqual(AidenProviderIconResolver.slug(providerID: "moonshot"), "moonshotai") + XCTAssertEqual( + AidenProviderIconResolver.slug(providerID: "anthropic", modelID: "claude-sonnet-4"), + "claude" + ) + XCTAssertEqual( + AidenProviderIconResolver.slug(providerID: "xai", modelID: "grok-4-fast"), + "grok" + ) + XCTAssertEqual(AidenProviderIconResolver.slug(providerID: "custom:lmstudio-2"), "lmstudio") + XCTAssertEqual(AidenProviderIconResolver.slug(providerID: "custom:ollama-42"), "ollama") + XCTAssertNil(AidenProviderIconResolver.slug(providerID: "custom:lmstudio-1")) + XCTAssertNil(AidenProviderIconResolver.slug(providerID: "future-provider")) + } + + func testAgentReplyCopyKeepsOriginalMarkdownAndRejectsNonReplies() { + let assistant = AidenChatMessage( + id: "assistant-1", + role: .assistant, + text: "## Result\n\nUse `xcodebuild test`.", + createdAt: Date(timeIntervalSince1970: 1) + ) + let user = AidenChatMessage( + id: "user-1", + role: .user, + text: "Please test it", + createdAt: Date(timeIntervalSince1970: 2) + ) + let emptyAssistant = AidenChatMessage( + id: "assistant-2", + role: .assistant, + text: "", + createdAt: Date(timeIntervalSince1970: 3) + ) + + XCTAssertEqual( + AidenMessageActionContent.copyText(for: assistant), + "## Result\n\nUse `xcodebuild test`." + ) + XCTAssertNil(AidenMessageActionContent.copyText(for: user)) + XCTAssertNil(AidenMessageActionContent.copyText(for: emptyAssistant)) + } + func testMarkdownDocumentParsesHeadingsListsAndInlineEmphasis() { let markdown = """ I'll explore the repository to understand what it is. @@ -94,6 +156,35 @@ final class AidenChatTests: XCTestCase { XCTAssertEqual(assistantSize.width, proposal.width, accuracy: 0.5) } + @MainActor + func testAssistantMarkdownKeepsTheFirstGlyphInsideItsRenderedBounds() throws { + let renderer = ImageRenderer(content: AidenMessageTextView( + role: .assistant, + content: "Sounds good — the first letter must remain visible." + ).frame(width: 320, alignment: .leading)) + renderer.scale = 3 + + let image = try XCTUnwrap(renderer.cgImage) + let width = image.width + let height = image.height + var pixels = [UInt8](repeating: 0, count: width * height * 4) + let context = try XCTUnwrap(CGContext( + data: &pixels, + width: width, + height: height, + bitsPerComponent: 8, + bytesPerRow: width * 4, + space: CGColorSpaceCreateDeviceRGB(), + bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue + )) + context.draw(image, in: CGRect(x: 0, y: 0, width: width, height: height)) + + let firstPaintedColumn = (0.. 8 } + } + XCTAssertGreaterThan(try XCTUnwrap(firstPaintedColumn), 0) + } + func testSSEParserAcceptsCanonicalFrameAndRejectsIdentityMismatches() throws { let json = eventJSON(sequence: 1, type: "text_delta", payload: "{\"text\":\"Hello\"}") var parser = AidenSSEParser() @@ -300,6 +391,46 @@ final class AidenChatTests: XCTestCase { XCTAssertNotEqual(tracker.key(for: request), first) } + func testTurnRequestBuilderPreservesUploadedAttachmentReferences() { + let firstID = "att_\(String(repeating: "A", count: 43))" + let secondID = "att_\(String(repeating: "B", count: 43))" + let attachments = [ + AidenAttachmentReference( + id: firstID, + name: "photo.jpg", + mimeType: "image/jpeg", + kind: .image, + size: 128, + expiresAt: Date(timeIntervalSince1970: 2_000_000_000) + ), + AidenAttachmentReference( + id: secondID, + name: "notes.md", + mimeType: "text/markdown", + kind: .text, + size: 64, + expiresAt: Date(timeIntervalSince1970: 2_000_000_000) + ), + ] + + let request = AidenTurnRequestBuilder.make( + text: "Review these", + providerId: "provider", + modelId: "model", + thinkingLevel: "high", + attachments: attachments + ) + + XCTAssertEqual(request.attachmentIds, [firstID, secondID]) + XCTAssertNil(AidenTurnRequestBuilder.make( + text: "No files", + providerId: nil, + modelId: nil, + thinkingLevel: nil, + attachments: [] + ).attachmentIds) + } + private func eventJSON(sequence: Int, type: String, payload: String) -> String { """ {"protocolVersion":1,"streamId":"stream-1","sequence":\(sequence), @@ -526,4 +657,51 @@ final class AidenAppearanceTests: XCTestCase { [] ) } + + @MainActor + func testWorkspaceArchivesAreDeviceLocalPersistentAndInstallationScoped() throws { + let suiteName = "AidenWorkspaceArchiveTests.\(UUID().uuidString)" + let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) + defer { defaults.removePersistentDomain(forName: suiteName) } + + let store = AidenWorkspaceArchiveStore(defaults: defaults) + XCTAssertFalse(store.hasAcknowledgedDeviceOnlyArchive) + XCTAssertEqual(store.archivedWorkspaceIDs(for: "mac-one"), []) + + store.acknowledgeDeviceOnlyArchive() + store.archive(workspaceID: "workspace-a", instanceID: "mac-one") + store.archive(workspaceID: "workspace-b", instanceID: "mac-one") + store.archive(workspaceID: "workspace-a", instanceID: "mac-two") + + let restored = AidenWorkspaceArchiveStore(defaults: defaults) + XCTAssertTrue(restored.hasAcknowledgedDeviceOnlyArchive) + XCTAssertEqual(restored.archivedWorkspaceIDs(for: "mac-one"), ["workspace-a", "workspace-b"]) + XCTAssertEqual(restored.archivedWorkspaceIDs(for: "mac-two"), ["workspace-a"]) + XCTAssertEqual(restored.archivedWorkspaceIDs(for: nil), []) + + restored.unarchive(workspaceID: "workspace-a", instanceID: "mac-one") + XCTAssertEqual(restored.archivedWorkspaceIDs(for: "mac-one"), ["workspace-b"]) + XCTAssertEqual(restored.archivedWorkspaceIDs(for: "mac-two"), ["workspace-a"]) + } + + @MainActor + func testWorkspaceArchivePruningOnlyDropsMissingServerRecordsForActiveInstallation() throws { + let suiteName = "AidenWorkspaceArchivePruneTests.\(UUID().uuidString)" + let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) + defer { defaults.removePersistentDomain(forName: suiteName) } + + let store = AidenWorkspaceArchiveStore(defaults: defaults) + store.archive(workspaceID: "keep", instanceID: "mac-one") + store.archive(workspaceID: "removed", instanceID: "mac-one") + store.archive(workspaceID: "other-installation", instanceID: "mac-two") + + store.prune(instanceID: "mac-one", validWorkspaceIDs: ["keep", "active"]) + + XCTAssertEqual(store.archivedWorkspaceIDs(for: "mac-one"), ["keep"]) + XCTAssertEqual(store.archivedWorkspaceIDs(for: "mac-two"), ["other-installation"]) + + let restored = AidenWorkspaceArchiveStore(defaults: defaults) + XCTAssertEqual(restored.archivedWorkspaceIDs(for: "mac-one"), ["keep"]) + XCTAssertEqual(restored.archivedWorkspaceIDs(for: "mac-two"), ["other-installation"]) + } } diff --git a/ios/AidenOnTheGoTests/AidenRemoteClientTests.swift b/ios/AidenOnTheGoTests/AidenRemoteClientTests.swift index 459586c0..53ab346a 100644 --- a/ios/AidenOnTheGoTests/AidenRemoteClientTests.swift +++ b/ios/AidenOnTheGoTests/AidenRemoteClientTests.swift @@ -106,7 +106,18 @@ final class AidenRemoteClientTests: XCTestCase { "activeDays":4,"currentStreak":2,"longestStreak":3, "tokens":{"input":100,"output":50,"cacheRead":10,"cacheWrite":2,"cacheWrite1h":1,"reasoning":8,"total":170} }, - "days":[],"models":[] + "days":[{ + "date":"2026-08-19","requests":4,"reportedTokenRequests":3,"unmeteredRequests":1, + "tokens":{"input":40,"output":20,"cacheRead":4,"cacheWrite":1,"cacheWrite1h":0,"reasoning":3,"total":67}, + "hostedCostUsd":0.45 + }], + "models":[{ + "providerId":"openai","providerLabel":"OpenAI","modelId":"gpt-5.6", + "modelLabel":"GPT-5.6","local":false,"requests":8,"reportedTokenRequests":7, + "unmeteredRequests":1, + "tokens":{"input":80,"output":40,"cacheRead":8,"cacheWrite":2,"cacheWrite1h":1,"reasoning":6,"total":136}, + "hostedCostUsd":1.05 + }] } """ ) @@ -117,6 +128,19 @@ final class AidenRemoteClientTests: XCTestCase { XCTAssertEqual(usage.totals.requests, 12) XCTAssertEqual(usage.totals.tokens.total, 170) XCTAssertEqual(usage.totals.hostedCostUsd, 1.25) + XCTAssertEqual(usage.days.first?.date, "2026-08-19") + XCTAssertEqual(usage.days.first?.tokens.total, 67) + XCTAssertEqual(usage.models.first?.modelLabel, "GPT-5.6") + XCTAssertEqual(usage.models.first?.requests, 8) + + let heatmap = AidenUsagePresentation.heatmapDays(for: usage) + XCTAssertEqual(heatmap.count, 30) + XCTAssertEqual(heatmap.first?.date, "2026-07-21") + XCTAssertEqual(heatmap.first?.tokens, 0) + XCTAssertEqual(heatmap.last?.date, "2026-08-19") + XCTAssertEqual(heatmap.last?.tokens, 67) + XCTAssertEqual(AidenUsagePresentation.ratio(3, of: 12), 0.25) + XCTAssertEqual(AidenUsagePresentation.ratio(1, of: 0), 0) } func testWorkspaceCreateUpdateAndDeleteCarryMutationPreconditions() async throws { @@ -853,6 +877,7 @@ final class AidenRemoteClientTests: XCTestCase { _ = try store.savePairing(exchange, trust: makeSystemTrust(), name: "Unverified Mac") let session = makeSession() + var workspaceListRequests = 0 AidenRemoteMockURLProtocol.handler = { request in switch (request.httpMethod, request.url?.path) { case ("GET", "/api/aiden/v1/server"): @@ -866,6 +891,7 @@ final class AidenRemoteClientTests: XCTestCase { """ ) case ("GET", "/api/aiden/v1/workspaces"): + workspaceListRequests += 1 return Self.response( for: request, status: 200, @@ -916,6 +942,188 @@ final class AidenRemoteClientTests: XCTestCase { let removed = await coordinator.removeWorkspace(updated) XCTAssertTrue(removed) XCTAssertFalse(coordinator.workspaces.contains(where: { $0.id == updated.id })) + XCTAssertEqual(workspaceListRequests, 2, "Confirmed removal should reload the canonical registry in case the Mac seeded a default workspace") + } + + @MainActor + func testRejectedWorkspaceRemovalReconcilesWithoutLosingDeviceArchiveState() async throws { + let keychain = AidenRemoteMemoryKeychain() + let store = AidenInstallationStore(keychain: keychain) + _ = try store.savePairing( + makeExchange(instanceId: "instance-1", deviceId: "device-1", credential: "credential-one"), + trust: makeSystemTrust(), + name: "Home Mac" + ) + let suiteName = "AidenRejectedRemovalArchiveTests.\(UUID().uuidString)" + let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) + defer { defaults.removePersistentDomain(forName: suiteName) } + let archiveStore = AidenWorkspaceArchiveStore(defaults: defaults) + archiveStore.archive(workspaceID: "workspace-a", instanceID: "instance-1") + + let session = makeSession() + AidenRemoteMockURLProtocol.handler = { request in + switch (request.httpMethod, request.url?.path) { + case ("GET", "/api/aiden/v1/server"): + return Self.response( + for: request, + status: 200, + json: """ + {"protocolVersion":1,"instanceId":"instance-1","name":"Home Mac", + "appVersion":"1.0.0","capabilities":["server:read","workspace:read","workspace:manage"], + "connectionMode":"lan","serverTime":"2026-08-19T07:00:00.000Z"} + """ + ) + case ("GET", "/api/aiden/v1/workspaces"): + return Self.response( + for: request, + status: 200, + json: """ + {"workspaces":[ + {"id":"workspace-a","name":"Archive Me","permission":"ask","hasFolder":true, + "isManagedWorktree":false,"createdAt":"2026-08-19T07:00:00.000Z", + "updatedAt":"2026-08-19T07:01:00.000Z","revision":"rev-current"}, + {"id":"workspace-b","name":"Keep Me","permission":"ask","hasFolder":true, + "isManagedWorktree":false,"createdAt":"2026-08-19T07:00:00.000Z", + "updatedAt":"2026-08-19T07:01:00.000Z","revision":"rev-b"}]} + """ + ) + case ("DELETE", "/api/aiden/v1/workspaces/workspace-a"): + return Self.response( + for: request, + status: 409, + json: """ + {"error":{"code":"revision_conflict","message":"The workspace changed.", + "requestId":"request-conflict","retryable":false}} + """ + ) + default: + XCTFail("Unexpected rejected-removal request: \(request.httpMethod ?? "nil") \(request.url?.path ?? "nil")") + return Self.response(for: request, status: 500, json: "{}") + } + } + + let coordinator = AidenRemoteCoordinator( + installationStore: store, + workspaceArchiveStore: archiveStore, + clientFactory: { installation, credential in + AidenRemoteClient(endpoint: installation.endpoint, credential: credential, session: session) + } + ) + await coordinator.start() + let workspace = try XCTUnwrap(coordinator.workspaces.first(where: { $0.id == "workspace-a" })) + + let removed = await coordinator.removeWorkspace(workspace) + XCTAssertFalse(removed) + XCTAssertTrue(coordinator.workspaces.contains(where: { $0.id == workspace.id })) + XCTAssertTrue(archiveStore.isArchived(workspaceID: workspace.id, instanceID: "instance-1")) + XCTAssertEqual(coordinator.connectionState, .connected) + XCTAssertEqual(coordinator.workspaceSnapshotRevision, 2) + XCTAssertEqual(coordinator.presentedError, "The workspace changed.") + } + + @MainActor + func testAuthoritativeEmptyWorkspaceSnapshotPrunesStaleDeviceArchives() async throws { + let keychain = AidenRemoteMemoryKeychain() + let store = AidenInstallationStore(keychain: keychain) + _ = try store.savePairing( + makeExchange(instanceId: "instance-empty", deviceId: "device-empty", credential: "credential-empty"), + trust: makeSystemTrust(), + name: "Empty Mac" + ) + let suiteName = "AidenEmptySnapshotArchiveTests.\(UUID().uuidString)" + let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) + defer { defaults.removePersistentDomain(forName: suiteName) } + let archiveStore = AidenWorkspaceArchiveStore(defaults: defaults) + archiveStore.archive(workspaceID: "stale", instanceID: "instance-empty") + + let session = makeSession() + AidenRemoteMockURLProtocol.handler = { request in + if request.url?.path == "/api/aiden/v1/server" { + return Self.response( + for: request, + status: 200, + json: """ + {"protocolVersion":1,"instanceId":"instance-empty","name":"Empty Mac", + "appVersion":"1.0.0","capabilities":["server:read","workspace:read"], + "connectionMode":"lan","serverTime":"2026-08-19T07:00:00.000Z"} + """ + ) + } + return Self.response(for: request, status: 200, json: "{\"workspaces\":[]}") + } + let coordinator = AidenRemoteCoordinator( + installationStore: store, + workspaceArchiveStore: archiveStore, + clientFactory: { installation, credential in + AidenRemoteClient(endpoint: installation.endpoint, credential: credential, session: session) + } + ) + + await coordinator.start() + + XCTAssertEqual(coordinator.workspaceSnapshotRevision, 1) + XCTAssertEqual(archiveStore.archivedWorkspaceIDs(for: "instance-empty"), []) + } + + @MainActor + func testSlowerPreviousInstallationLoadCannotOverwriteNewActiveMac() async throws { + let keychain = AidenRemoteMemoryKeychain() + let store = AidenInstallationStore(keychain: keychain) + _ = try store.savePairing( + makeExchange(instanceId: "instance-1", deviceId: "device-1", credential: "credential-one"), + trust: makeSystemTrust(), + name: "Slow Mac" + ) + _ = try store.savePairing( + makeExchange(instanceId: "instance-2", deviceId: "device-2", credential: "credential-two"), + trust: makeSystemTrust(), + name: "Fast Mac" + ) + try store.setActive("instance-1") + + let session = makeSession() + AidenRemoteMockURLProtocol.handler = { request in + let isSlowMac = request.value(forHTTPHeaderField: "Authorization") == "Bearer credential-one" + let instanceID = isSlowMac ? "instance-1" : "instance-2" + if request.url?.path == "/api/aiden/v1/server" { + return Self.response( + for: request, + status: 200, + json: """ + {"protocolVersion":1,"instanceId":"\(instanceID)","name":"\(isSlowMac ? "Slow Mac" : "Fast Mac")", + "appVersion":"1.0.0","capabilities":["server:read","workspace:read"], + "connectionMode":"lan","serverTime":"2026-08-19T07:00:00.000Z"} + """ + ) + } + if isSlowMac { Thread.sleep(forTimeInterval: 0.2) } + return Self.response( + for: request, + status: 200, + json: """ + {"workspaces":[{"id":"workspace-\(instanceID)","name":"Workspace \(instanceID)", + "permission":"ask","hasFolder":false,"isManagedWorktree":false, + "createdAt":"2026-08-19T07:00:00.000Z","updatedAt":"2026-08-19T07:00:00.000Z", + "revision":"rev-\(instanceID)"}]} + """ + ) + } + let coordinator = AidenRemoteCoordinator( + installationStore: store, + clientFactory: { installation, credential in + AidenRemoteClient(endpoint: installation.endpoint, credential: credential, session: session) + } + ) + + let slowLoad = Task { await coordinator.connectActiveInstallation() } + try await Task.sleep(for: .milliseconds(25)) + await coordinator.switchInstallation(to: "instance-2") + await slowLoad.value + + XCTAssertEqual(coordinator.activeInstanceId, "instance-2") + XCTAssertEqual(coordinator.server?.instanceId, "instance-2") + XCTAssertEqual(coordinator.workspaces.map(\.id), ["workspace-instance-2"]) + XCTAssertEqual(coordinator.connectionState, .connected) } @MainActor diff --git a/main/services/aiden-remote-workspace-http.test.ts b/main/services/aiden-remote-workspace-http.test.ts index 944cd841..8f5c20c6 100644 --- a/main/services/aiden-remote-workspace-http.test.ts +++ b/main/services/aiden-remote-workspace-http.test.ts @@ -18,6 +18,8 @@ test("HTTP client completes approved-folder selection and revision-checked works await fs.mkdir(approvedDirectory); const approvedPath = await fs.realpath(approvedDirectory); await fs.mkdir(path.join(approvedPath, "Selected")); + const selectedMarker = path.join(approvedPath, "Selected", "kept-after-unregister.txt"); + await fs.writeFile(selectedMarker, "workspace files stay on disk"); const approvedIdentity = await fs.stat(approvedPath, { bigint: true }); let sequence = 0; let workspaces: Workspace[] = [{ @@ -172,6 +174,7 @@ test("HTTP client completes approved-folder selection and revision-checked works assert.equal(deleteResponse.status, 204); const listed = await (await fetch(`${base}/workspaces`, { headers })).json(); assert.deepEqual(listed.workspaces.map((workspace: { id: string }) => workspace.id), ["default"]); + assert.equal(await fs.readFile(selectedMarker, "utf8"), "workspace files stay on disk"); } finally { await new Promise((resolve) => server.close(() => resolve())); await fs.rm(temporary, { recursive: true, force: true }); diff --git a/scripts/check-ios-shipping-target.test.mjs b/scripts/check-ios-shipping-target.test.mjs index fb2ae254..8d0c8e3e 100644 --- a/scripts/check-ios-shipping-target.test.mjs +++ b/scripts/check-ios-shipping-target.test.mjs @@ -33,6 +33,12 @@ const sidebarLogoPath = fileURLToPath( import.meta.url, ), ); +const desktopProviderLogoDirectory = fileURLToPath( + new URL("../renderer/assets/provider-logos/", import.meta.url), +); +const iosAssetCatalogDirectory = fileURLToPath( + new URL("../ios/AidenOnTheGo/Resources/Assets.xcassets/", import.meta.url), +); const appSourcePaths = [ "AidenOnTheGo/AidenOnTheGoApp.swift", @@ -48,6 +54,7 @@ const appSourcePaths = [ "AidenOnTheGo/Features/Remote/AidenScheduledTasksView.swift", "AidenOnTheGo/Features/Remote/AidenWorkspaceEnvironmentView.swift", "AidenOnTheGo/Features/Remote/AidenWorkspaceShellView.swift", + "AidenOnTheGo/Features/Shared/AidenProviderIcon.swift", "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Core.swift", "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Lattice.swift", "AidenOnTheGo/Features/Shared/ThinkingOrbsKit/Morph.swift", @@ -131,11 +138,33 @@ test("the iOS tree contains no orphan imported Swift sources", async () => { ); }); -test("the Aiden home, onboarding, composer, and activity retain the reviewed product shell", async () => { - const [shell, pairing, chat, widget, project, logoDefinition, logoArtwork] = await Promise.all([ +test("iOS bundles every reviewed Aiden provider logo", async () => { + const desktopLogos = (await readdir(desktopProviderLogoDirectory)) + .filter((path) => path.endsWith(".svg")) + .map((path) => path.replace(/\.svg$/u, "")) + .sort(); + const iosLogos = (await readdir(iosAssetCatalogDirectory)) + .filter((path) => path.startsWith("ProviderLogo-") && path.endsWith(".imageset")) + .map((path) => path.replace(/^ProviderLogo-/u, "").replace(/\.imageset$/u, "")) + .sort(); + + assert.deepEqual(iosLogos, desktopLogos); + assert.equal(iosLogos.length, 40); + await Promise.all(iosLogos.map(async (slug) => { + const [desktopArtwork, iosArtwork] = await Promise.all([ + readFile(`${desktopProviderLogoDirectory}${slug}.svg`), + readFile(`${iosAssetCatalogDirectory}ProviderLogo-${slug}.imageset/${slug}.svg`), + ]); + assert.deepEqual(iosArtwork, desktopArtwork, `${slug} artwork diverged from Aiden Agent`); + })); +}); + +test("the Aiden home, onboarding, composer, schedules, and activity retain the reviewed product shell", async () => { + const [shell, pairing, chat, scheduledTasks, widget, project, logoDefinition, logoArtwork] = await Promise.all([ readFile(`${iosRoot}AidenOnTheGo/Features/Remote/AidenWorkspaceShellView.swift`, "utf8"), readFile(`${iosRoot}AidenOnTheGo/Features/Remote/AidenPairingView.swift`, "utf8"), readFile(`${iosRoot}AidenOnTheGo/Features/Remote/AidenChatFeature.swift`, "utf8"), + readFile(`${iosRoot}AidenOnTheGo/Features/Remote/AidenScheduledTasksView.swift`, "utf8"), readFile(`${iosRoot}AidenLiveActivityWidget/AgentRunLiveActivityWidget.swift`, "utf8"), readFile(projectPath, "utf8"), readFile(`${sidebarLogoPath}Contents.json`, "utf8"), @@ -153,12 +182,30 @@ test("the Aiden home, onboarding, composer, and activity retain the reviewed pro assert.match(shell, /Image\("AidenAppIcon"\)[\s\S]*?searchChrome/u); assert.match(shell, /Image\(systemName: "magnifyingglass"\)[\s\S]*?person\.crop\.circle\.fill/u); assert.match(shell, /AidenWorkspacesDirectoryView[\s\S]*?Label\("New Workspace"[\s\S]*?Label\("Add Mac Folder"/u); - assert.match(shell, /Label\("New Agent", systemImage: "square\.and\.pencil"\)/u); + assert.match(shell, /Image\(systemName: "square\.and\.pencil"\)[\s\S]*?aidenProminentGlassButton\(\)[\s\S]*?accessibilityLabel\("New Agent"\)/u); + assert.match(shell, /content\.buttonStyle\(\.glass\)/u); assert.match(shell, /glassEffect\(\.regular\.tint\(tint\)\.interactive\(\), in: Capsule\(\)\)/u); assert.match( shell, - /Button\("Existing Workspace"\)[\s\S]*?Button\("New Workspace"\)[\s\S]*?Button\("Managed Scratch Workspace"\)/u, + /case \.existingWorkspace: "Existing Workspace"[\s\S]*?case \.newWorkspace: "New Workspace"[\s\S]*?case \.scratchWorkspace: "Managed Scratch Workspace"/u, + ); + assert.match(shell, /aidenChromeGlass\(isInteractive: true, in: Capsule\(\)\)/u); + assert.match(shell, /glassEffect\(\.regular\.interactive\(\), in: shape\)/u); + assert.match( + shell, + /safeAreaInset\(edge: \.bottom, spacing: 0\)[\s\S]*?frame\(height: 92\)[\s\S]*?background\(palette\.canvas\)/u, + ); + assert.doesNotMatch(shell, /Color\.clear\.frame\(height: 90\)\.listRowSeparator/u); + assert.match( + shell, + /AidenUsageView[\s\S]*?overviewGrid[\s\S]*?Token activity[\s\S]*?Activity insights[\s\S]*?Most used models/u, ); + assert.match(shell, /AidenUsagePresentation\.heatmapDays[\s\S]*?usage\.days/u); + assert.match( + shell, + /popover\(isPresented: \$isShowingNewAgentChoices, arrowEdge: \.bottom\)[\s\S]*?AidenNewAgentPopover[\s\S]*?presentationCompactAdaptation\(\.popover\)/u, + ); + assert.doesNotMatch(shell, /confirmationDialog\([\s\S]{0,120}"Where should this agent work\?"/u); assert.doesNotMatch(shell, /messageLabel|chat\.messages\.count/u); assert.doesNotMatch(shell, /count: homeModel\.scheduledTasks\.count|count: coordinator\.workspaces\.count/u); assert.match(shell, /task\(id: coordinator\.connectionState\)[\s\S]*?homeModel\.load/u); @@ -175,12 +222,51 @@ test("the Aiden home, onboarding, composer, and activity retain the reviewed pro /func createNewAgentInScratchWorkspace\(\)[\s\S]*?workspaceCreate: \.scratch[\s\S]*?func createNewAgent\([\s\S]*?createChat\(workspaceId: workspace\.id\)/u, ); assert.match(shell, /Button\("Close"\) \{ intentChat = nil \}/u); + assert.match(shell, /final class AidenWorkspaceArchiveStore[\s\S]*?workspaceIDsByInstance/u); + assert.match(shell, /Archived Workspaces[\s\S]*?hidden only on this device/u); + assert.match(shell, /swipeActions\(edge: \.leading, allowsFullSwipe: false\)/u); + assert.match(shell, /swipeActions\(edge: \.trailing, allowsFullSwipe: false\)/u); + assert.match(shell, /Archive on This Device\?[\s\S]*?stays available in Aiden Agent on your Mac and on other devices/u); + assert.match(shell, /onRemove: workspace\.isManagedWorktree \|\| coordinator\.workspaces\.count <= 1/u); + assert.match(shell, /if isArchived \{[\s\S]*?Button\(action: onToggleArchive\)/u); + assert.match(shell, /activeWorkspaceIDSet[\s\S]*?visibleChats = homeModel\.chats\.filter/u); + assert.match(shell, /archivedWorkspaceIDs\.contains\(chat\.workspaceId\)[\s\S]*?Unarchive it from Workspaces/u); assert.match(pairing, /Aiden, wherever you are\./u); assert.match(pairing, /task\(id: step\)[\s\S]*?if step == 2[\s\S]*?discovery\.start\(\)/u); - assert.match(chat, /PhotosPicker/u); + assert.match(chat, /AidenUIKitMenuButton[\s\S]*?\.photosPicker\(\s*isPresented: \$isPhotoPickerPresented/u); + assert.doesNotMatch(chat, /PhotosPicker\(selection:/u); assert.match(chat, /\.fileImporter\(/u); - assert.match(chat, /AidenSidebarLogo\(size: 15/u); + assert.match( + chat, + /AidenTurnRequestBuilder\.make\([\s\S]*?attachments: submittedAttachments[\s\S]*?pendingAttachments = \[\]/u, + ); + assert.match( + chat, + /if let provider = model\.selectedProvider[\s\S]*?AidenProviderIcon\([\s\S]*?modelID: model\.selectedModel\?\.id/u, + ); + assert.match(chat, /Section \{[\s\S]*?header: \{[\s\S]*?AidenProviderIcon/u); + assert.match(chat, /contextMenu[\s\S]*?Label\("Copy", systemImage: "doc\.on\.doc"\)/u); + assert.match(chat, /if !model\.liveText\.isEmpty[\s\S]*?contextMenu[\s\S]*?UIPasteboard\.general\.string = model\.liveText/u); + assert.match( + scheduledTasks, + /Picker\("Provider"[\s\S]*?AidenProviderIcon\([\s\S]*?providerID: provider\.id/u, + ); + assert.match( + scheduledTasks, + /Picker\("Model"[\s\S]*?AidenProviderIcon\([\s\S]*?modelID: candidate\.id/u, + ); assert.match(chat, /ThinkingOrb\(state: activity\.orb, size: \.px20\)/u); + assert.match( + chat, + /AidenApprovalCard[\s\S]*?Image\(systemName: "shield"\)[\s\S]*?Text\("Approval needed"\)[\s\S]*?font\(\.subheadline\.weight\(\.semibold\)\)/u, + ); + assert.match(chat, /Text\("Review this one action before Aiden continues\."\)[\s\S]*?font\(\.caption\)/u); + assert.match(chat, /Text\(summary\)[\s\S]*?font\(\.caption\.monospaced\(\)\)/u); + assert.match(chat, /Text\("Deny"\)[\s\S]*?Text\("Allow once"\)/u); + assert.match(chat, /glassEffect\(\.regular\.interactive\(\), in: Capsule\(\)\)/u); + assert.match(chat, /glassEffect\(\.regular\.tint\(tint\)\.interactive\(\), in: Capsule\(\)\)/u); + assert.doesNotMatch(chat, /Label\("Approval needed", systemImage: "hand\.raised"\)/u); + assert.doesNotMatch(chat, /Button\("Deny", role: \.destructive\)/u); assert.match(chat, /ZStack\(alignment: \.bottom\)[\s\S]*?frame\(height: max\(96, composerHeight \+ 12\)\)[\s\S]*?AidenComposerView/u); assert.match(chat, /glassEffect\(\.regular\.interactive\(\), in: shape\)/u); assert.match(chat, /sendButtonBackground[\s\S]*?palette\.accent[\s\S]*?sendButtonForeground[\s\S]*?palette\.canvas/u); @@ -208,7 +294,10 @@ test("shipping Swift literals contain only the Aiden API and no imported product const nonAidenAPI = /"\/api\/(?!aiden\/v1)[^"\n]*"/gu; for (const [path, source] of sources) { - assert.doesNotMatch(source, forbiddenIdentity, `${path} contains imported product copy`); + const identityPattern = path.endsWith("AidenProviderIcon.swift") + ? /"[^"\n]*(?:hermes|hermex|kanban|cloudflared)[^"\n]*"/giu + : forbiddenIdentity; + assert.doesNotMatch(source, identityPattern, `${path} contains imported product copy`); assert.doesNotMatch(source, nonAidenAPI, `${path} contains a non-Aiden API endpoint`); } }); @@ -250,6 +339,7 @@ test("the Aiden MIT license, package graph, and bundled notices retain required "MarkdownUI-LICENSE.txt", "NOTICE.txt", "NetworkImage-LICENSE.txt", + "ProviderLogos-NOTICE.md", "ThinkingOrbs-LICENSE.txt", "swift-cmark-COPYING.txt", ]); @@ -262,6 +352,7 @@ test("the Aiden MIT license, package graph, and bundled notices retain required assert.match(notice, /NetworkImage 6\.0\.1/u); assert.match(notice, /swift-cmark 0\.8\.0/u); assert.match(notice, /Thinking Orbs 0\.3\.1/u); + assert.match(notice, /Provider logos/u); assert.doesNotMatch(notice, /swift-eventsource|Splash|Highlightr|SwiftMath|Lucide/u); }); From b5dbdea4ce7a363a790131e86ea0ddfa7e6b3cf6 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Thu, 20 Aug 2026 12:57:53 -0400 Subject: [PATCH 007/266] docs(ios): record native shell validation --- docs/plans/README.md | 2 +- docs/plans/aiden-on-the-go-plan.md | 26 +++++++++++++++ docs/testing/aiden-on-the-go/phase-12.md | 40 ++++++++++++++++++++++++ ios/CHANGELOG.md | 6 ++++ 4 files changed, 73 insertions(+), 1 deletion(-) diff --git a/docs/plans/README.md b/docs/plans/README.md index 6b391123..06ee6be2 100644 --- a/docs/plans/README.md +++ b/docs/plans/README.md @@ -7,7 +7,7 @@ This directory is the source of truth for Aiden's implementation plans. The engi | Plan | Status | Current state | | -------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Aiden Assistant](aiden-assistant-plan.md) | Partial | The dock, Markdown rendering, and confirmed provider-connection/model-pinned project-or-MCP automation creation/editing ship; settings tools and proactivity remain planned. | -| [Aiden On The Go](aiden-on-the-go-plan.md) | Active | Version 0.1.0 build 7 is `VALID` and `IN_BETA_TESTING`; the newer local Hermex-informed Markdown transcript fix is installed on the physical iPhone 13 Pro but intentionally not uploaded. Physical iPad/manual system-UI acceptance, privacy publication, final store assets, and external/public-release decisions remain open. | +| [Aiden On The Go](aiden-on-the-go-plan.md) | Active | Version 0.1.0 build 7 is `VALID` and `IN_BETA_TESTING`; newer local Markdown/attachment, glass navigation/approval, device-only workspace management, themed usage, provider-identity, and reply-copy changes compile and pass focused tests on the physical iPhone but are intentionally not uploaded. Physical iPad/manual system-UI acceptance, privacy publication, final store assets, and external/public-release decisions remain open. | | [Compaction](compaction-plan.md) | Partial | Pi-native checkpoints, lifecycle/crash recovery, and exact audited-upstream compatibility ship; durable memory and provider-native paths remain open. | | [Designer Mode](designer-mode-plan.md) | Planned | Phase 0 validation has not started in the runtime. | | [Dynamic Model Catalog](dynamic-model-catalog-plan.md) | Partial | Stored Pi catalogs, cache-only hydration, and explicit provider refresh ship; remote overlays for otherwise-static providers remain open. | diff --git a/docs/plans/aiden-on-the-go-plan.md b/docs/plans/aiden-on-the-go-plan.md index 25bb68b1..3ad0e006 100644 --- a/docs/plans/aiden-on-the-go-plan.md +++ b/docs/plans/aiden-on-the-go-plan.md @@ -652,6 +652,12 @@ Post-proof release-gate refresh 2026-08-19: the complete canonical `npm test` li Hermex Markdown parity follow-up 2026-08-19: transcript inspection against `/Users/sambitbiswas/projects/opp/hermex` found that Aiden On The Go was parsing completed replies with inline-only `AttributedString`, leaving block headings and lists visible as punctuation. The same assistant layout used a trailing spacer that could squeeze long content and clip the stored leading `I` from `I'll explore…`. Completed and streaming replies now use the exact Hermex `swift-markdown-ui` 2.4.1 renderer, receive the full proposed transcript width, and retain Aiden palette typography. User prompts remain literal content-sized bubbles like Hermex rather than being stretched by the full-width renderer. Parsing is bounded to 80,000 characters and 2,000 lines with literal-text fallback; Markdown images are deliberately suppressed through a no-network provider. Required MarkdownUI, NetworkImage 6.0.1, and swift-cmark 0.8.0 notices are bundled and release tests lock the resolved graph. Fourteen focused chat tests pass, and the complete physical iPhone 13 Pro target passes 78 total with 73 passes, five expected environment-gated skips, and zero failures. A generic physical-iOS compile and the release policy suite (20 Ruby tests/42 assertions plus 23 Node tests) also pass. The app was launched on the phone for visual confirmation. This is a local follow-up after TestFlight build 7; no simulator, archive, ASC mutation, or upload occurred. +Markdown/attachment correction follow-up 2026-08-20: fresh device screenshots showed a first assistant glyph still touching the renderer boundary and exposed that the `+` menu embedded a `PhotosPicker` directly inside `Menu`, unlike Hermex's state-driven external presentation. The assistant row now owns full width while the MarkdownUI renderer keeps its intrinsic block layout without a root `fixedSize`; both completed and live text continue through the same locked MarkdownUI 2.4.1 path. The composer now uses Hermex's deferred UIKit menu bridge to open external Photos and Files presentations. A separate send-path defect was also corrected: the turn request is built from the captured uploaded references before hopeful UI clearing, so attachment IDs reach the Aiden server and remain single-use. Source checks reject nested `PhotosPicker` wiring and lock request construction order. Sixteen focused chat tests pass, including rendered first-glyph bounds and attachment-reference preservation, and the complete physical iPhone 13 Pro suite passes 80 total with 75 passes, five expected environment-gated skips, and zero failures. No simulator, archive, ASC mutation, or TestFlight upload was used. + +Home glass-navigation follow-up 2026-08-20: the provided `/Users/sambitbiswas/Downloads/CustomTB/CustomTB` reference established a separate prominent bottom action with an anchored popover, while Hermex established native interactive Liquid Glass with reduced-transparency and pre-iOS-26 fallbacks. Aiden's New Agent action now opens an anchored, compact-popover-preserving three-row chooser for an existing workspace, reusable new workspace, or managed scratch workspace; its existing creation and server-confirmation flows are unchanged. The chooser hides during search and uses a zoom transition from the prominent action. The search/settings capsule now uses native interactive `.glassEffect(.regular.interactive())` on supported devices, ultra-thin material on older systems, and an opaque palette-raised surface under Reduce Transparency. Registered source coverage locks the popover and glass path, and XCTest locks the exact three choices. The iOS release suite passes 20 Ruby tests/42 assertions plus 23 Node tests, and the complete physical iPhone 13 Pro suite passes 81 total with 76 passes, five expected live-environment skips, and zero failures. No simulator, archive, ASC mutation, or TestFlight upload was used. + +Compact approval follow-up 2026-08-20: the live iOS approval card now mirrors Aiden Agent Mac's reviewed hierarchy instead of using oversized default SwiftUI controls. It uses the `shield` SF Symbol in a 32-point warning badge, a small-semibold approval title, caption helper copy, a compact monospaced summary well, and right-aligned `Deny` / `Allow once` actions. Both actions render as native interactive Liquid Glass on supported devices; older and Reduce Transparency modes retain themed material/opaque fallbacks. Visible action capsules are 34 points tall while outer padding preserves a 44-point touch target. Registered source coverage locks the shield, typography, labels, and both native glass paths. The iOS release suite passes 20 Ruby tests/42 assertions plus 23 Node tests, and the complete physical iPhone 13 Pro suite remains 81 total with 76 passes, five expected live-environment skips, and zero failures. No simulator, archive, ASC mutation, or TestFlight upload was used. + ## Test inventory ### Electron @@ -699,6 +705,26 @@ Retain and adapt the useful Hermex API, auth, session, chat, SSE, cache, navigat - Speech authorization and privacy behavior: [Asking Permission to Use Speech Recognition](https://developer.apple.com/documentation/speech/asking-permission-to-use-speech-recognition). - Current Serve lifecycle, status, route-specific `off`, and destructive reset semantics: [Tailscale Serve CLI](https://tailscale.com/docs/reference/tailscale-cli/serve). +## Device-only workspace archive follow-up — 2026-08-20 + +Workspace rows now use Hermex's reviewed long-press plus bidirectional swipe pattern with full-swipe disabled. Archive state is local to each paired installation on the current iPhone/iPad, persists across relaunch, and presents a one-time disclosure before the first archive. Active and archived workspaces have separate searchable views; archived workspace chats, New Agent choices, adaptive navigation destinations, deep links, and cached App Intent entities are hidden on that device until unarchived. + +Rename keeps the existing hopeful revision-checked server mutation, retains a failed quick-rename draft, and explicitly distinguishes the shared Aiden display name from the unchanged disk-folder name. Remove from Aiden Agent reconciles revision conflicts and ambiguous completions against a fresh canonical registry and explains that folders, files, and chat data remain on the Mac while removed-workspace chats become unlisted. Managed scratch workspaces retain the separate managed deletion path because generic record removal would discard crash-recovery authority, and ordinary removal is hidden for the sole registry workspace to match the desktop invariant. + +Fresh-memory iOS, persistence, and server reviews closed cross-installation response races, optimistic-pruning rollback loss, authoritative-empty pruning, archived-row navigation, pre-shell App Intent filtering, sole-workspace removal, and operational-server-error state gaps. Coordinator loads and mutations are installation-generation-bound; archive pruning happens only inside a confirmed authoritative snapshot. Eleven focused archive/navigation/coordinator tests pass on the physical iPhone 13 Pro, including rejected-removal rollback, empty-snapshot pruning, and reversed installation completion. The filesystem-boundary HTTP test now proves unregister leaves a real file intact. No simulator was used. + +## Usage dashboard and home continuity follow-up — 2026-08-20 + +The privacy-safe Mac Usage destination now presents the server's real 30-day aggregate as an Aiden-native dashboard: request/token summaries, active-day and streak metrics, a token heatmap, token and activity breakdowns, and top-model rows. Every surface uses the selected appearance palette and accessible semantic labeling. Reference-only fields that Aiden does not expose—such as longest task, skills, and plugins—remain absent rather than being fabricated. + +The home list now owns a palette-backed bottom safe-area inset for New Agent instead of adding a synthetic clear list row. This preserves the action's scroll clearance without exposing a mismatched white band after the final chat. Shipping source checks pass 6/6, the signed app/test bundle builds for the connected physical iPhone 13 Pro, and focused tests cover usage decoding and presentation math. No simulator, archive, App Store Connect mutation, or TestFlight upload was used. + +## Provider identity and reply-copy follow-up — 2026-08-20 + +The iOS asset catalog now mirrors all 40 reviewed provider SVGs from Aiden Agent. A shared resolver preserves desktop aliases, model-specific Claude/Grok identities, numbered local-provider fallbacks, multicolor rendering, and a neutral initial fallback. Usage model rows, the chat composer model menu, and scheduled-task provider/model selectors now present provider icons with provider-grouped model names. + +Completed and streaming assistant replies expose a Hermex-style long-press Copy action plus an accessibility action; the clipboard receives the original Markdown rather than rendered text. A requested GPT-5.6 Terra xhigh review verified byte-for-byte asset parity and found the scheduled-task, streaming-copy, and iOS-notice gaps; all three were corrected. The registered source/asset gate passes 7/7, a signed build-for-testing succeeds, and 23 focused chat, scheduling, and usage tests pass on the physical iPhone 13 Pro. No simulator, archive, ASC mutation, or TestFlight upload was used. + ## Confirmed product decisions 1. **Workspace scope:** Mobile has full Aiden workspace-registry CRUD, including folderless and managed-scratch creation, may explore approved folders on the paired Mac and register a selected folder, and later gains Aiden's existing file index/read/version-checked-write capabilities. It does not accept free-form Mac paths or invent file create/rename/delete operations. diff --git a/docs/testing/aiden-on-the-go/phase-12.md b/docs/testing/aiden-on-the-go/phase-12.md index 172f2984..10a740de 100644 --- a/docs/testing/aiden-on-the-go/phase-12.md +++ b/docs/testing/aiden-on-the-go/phase-12.md @@ -69,6 +69,46 @@ Both archives are signed with `Apple Development: Sambit Biswas (7EK65FX44E)` an - Canonical ASC `en-US` localization files now exist for the shipping `1.5` draft. The stable CLI's offline validator scans both files with zero errors and zero warnings; registered tests keep their name, subtitle, URLs, keywords, field limits, description, and intentionally omitted optional keys aligned with the reviewed metadata document. A separate mobile privacy/support patch is ready for owner/legal review and covers direct LAN/Tailscale pairing, Keychain/cache behavior, attachments/provider forwarding, Local Network/Camera/Photos/Files/on-device Speech permissions, App Intents, Live Activities, external media requests, and a visible support email. The website lives outside this repository, so publication remains an explicit external gate. - `xcrun devicectl` captured the shipping app's pairing screen from the physical iPhone 13 Pro at its native `1170 × 2532` resolution. An opaque JPEG conversion passed local `asc screenshots validate` for the matching display type. This proves the physical screenshot pipeline but is not retained as a final store asset because it is not the final distribution candidate and the required physical-iPad set remains unavailable. +## Markdown and attachment correction — 2026-08-20 + +Aiden already pins the same MarkdownUI 2.4.1 foundation used by Hermex, so no package change was needed. The renderer no longer applies its own root width/fixed-size pair; the assistant row supplies full transcript width, and a physical-device pixel regression proves the first painted glyph stays within its bounds. The `+` menu now follows Hermex's UIKit-menu/state-driven external presentation for Photos and Files instead of nesting `PhotosPicker` in `Menu`. Turn construction captures uploaded attachment IDs before hopeful composer clearing; a focused regression and registered source guard prevent those IDs from being silently dropped. + +Shipping source checks pass 6/6. The focused chat suite passes 16/16, and the complete physical iPhone 13 Pro suite reports 80 total, 75 passed, five expected live-environment skips, and zero failed. No simulator, archive, ASC mutation, or TestFlight upload was used. + +## Home glass navigation correction — 2026-08-20 + +The provided CustomTB sample was used as an interaction reference for the prominent New Agent action: the button now owns an anchored popover that stays a popover on compact iPhone layouts and exposes the existing-workspace, reusable-workspace, and managed-scratch choices with concise explanations. The former confirmation dialog was removed, and presentation handoff yields after popover dismissal before opening a picker, alert, or starting scratch creation. Hermex's session-list chrome was used as the glass reference: the search/settings capsule now selects native interactive Liquid Glass on iOS 26+, ultra-thin material on older systems, and an opaque palette-raised fallback when Reduce Transparency is enabled. + +The registered shipping-source suite passes 6/6 and locks both native glass and compact-popover adaptation. The focused physical-iPhone chat suite passes 17/17. The iOS release suite passes 20 Ruby tests/42 assertions plus 23 Node tests, and the complete signed physical iPhone 13 Pro suite reports 81 total, 76 passed, five expected live-environment skips, and zero failed. No simulator, archive, App Store Connect mutation, or TestFlight upload was used. + +## Compact approval correction — 2026-08-20 + +The iOS live approval card now follows the Aiden Agent Mac approval hierarchy: a 32-point warning badge contains the 15-point `shield` SF Symbol; the title uses small-semibold emphasis; helper and monospaced summary copy use caption sizing; and compact `Deny` / `Allow once` controls align to the trailing edge. Native iOS 26 interactive Liquid Glass renders both actions, with the allow action using the active Aiden accent; older systems and Reduce Transparency keep material or opaque themed fallbacks. Each visible capsule is 34 points tall with enough outer padding to retain a 44-point touch target. + +The registered shell checks pass 6/6 and reject the previous hand icon, destructive-role button, and oversized hierarchy. The iOS release suite passes 20 Ruby tests/42 assertions plus 23 Node tests. The complete signed physical iPhone 13 Pro suite remains green at 81 total: 76 passed, five expected live-environment skips, and zero failed. No simulator, archive, App Store Connect mutation, or TestFlight upload was used. + +## Device-only workspace archive — 2026-08-20 + +Hermex's session interaction was re-read before implementation. Aiden workspace rows now use the same safe interaction shape: a context menu plus leading/trailing swipe actions with `allowsFullSwipe: false`. Rename and safe unregister continue through Aiden's revision-checked hopeful coordinator paths; unregister confirmation explicitly says the registry entry disappears from Aiden Agent and paired clients while the folder/files remain untouched on the Mac. Managed scratch records do not expose generic unregister because the desktop contract intentionally requires managed-worktree recovery authority. + +Archive is a phone/tablet-only projection keyed by paired installation. The first confirmed use displays the device-only scope; subsequent archive/unarchive actions are immediate. Archived workspaces have a searchable directory and are removed from home chats, New Agent's existing-workspace picker, adaptive selection/path state, cold/warm deep-link resolution, and the App Intent workspace cache. The archive store prunes only after a connected authoritative workspace refresh so temporary offline/empty states cannot erase local preferences. + +Fresh-memory iOS, persistence, and server reviews were completed after implementation. Their concrete findings were fixed: authoritative snapshots—not optimistic row changes—own pruning; empty registries prune correctly; loads and mutations are bound to the active installation generation; archived rows require unarchive before chat navigation; persisted archives filter App Intents before the shell mounts; the sole workspace cannot expose ineffective removal; and conflicts/ambiguous completions reconcile from the Mac without falsely marking a healthy server offline. Failed quick renames retain their draft. + +The signed iPhoneOS build-for-testing succeeds. Eleven focused tests pass on the physical iPhone 13 Pro, including first-use acknowledgement, relaunch persistence, installation isolation, unarchive, rejected-removal rollback, authoritative-empty pruning, reversed installation-load completion, and canonical CRUD reconciliation. The focused server suite passes 16/16, including a real-file assertion proving unregister leaves Mac content intact. No simulator or iPhone 16 Pro Max was addressed, and no archive, App Store Connect mutation, or TestFlight upload occurred. + +## Usage dashboard and home continuity — 2026-08-20 + +The iOS usage DTO now retains the canonical daily and per-model aggregates returned by the privacy-safe Mac endpoint. The dashboard derives active days, current/longest activity streaks, a fixed 30-day heatmap, completion/local-use ratios, token categories, and top models from that data while retaining Aiden palette and accessibility semantics. Missing server capabilities are not represented with placeholder statistics. + +The home list's former transparent tail row was replaced with a palette-backed bottom safe-area inset, so the New Agent control keeps appropriate scroll clearance without producing a white band after the last chat. The registered shipping-source checks pass 6/6. A signed build-for-testing succeeds for the physical iPhone 13 Pro, and the focused usage test passes on that device. No simulator, iPhone 16 Pro Max, archive, ASC mutation, or TestFlight upload was used. + +## Provider identity and reply copying — 2026-08-20 + +The iOS asset catalog contains the complete 40-logo provider inventory from Aiden Agent, and the shared resolver retains the desktop alias, model-specific, local-provider, multicolor, and fallback rules. Usage, chat model selection, and scheduled-task provider/model selection now show the same provider identity without adding network-backed artwork. + +Completed and in-flight assistant replies expose a long-press Copy action and accessibility action. In keeping with Hermex, the copied value is the original Markdown source. A requested GPT-5.6 Terra xhigh review found and closed missing scheduled-task icons, missing streaming Copy, and inaccurate desktop-only notice language. The signed iPhoneOS build-for-testing succeeds, the registered shipping/asset suite passes 7/7, and 23 focused chat, scheduled-task, and usage tests pass on the connected physical iPhone 13 Pro. No simulator, iPhone 16 Pro Max, archive, App Store Connect mutation, or TestFlight upload was used. + ## Plan completion audit The current source, registered test commands, signed artifacts, and phase evidence were re-audited against every phase acceptance statement in `docs/plans/aiden-on-the-go-plan.md`: diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index d40c8022..5ae2747b 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -7,3 +7,9 @@ - Removed the imported Hermes WebUI product surface and compatibility configuration from the build. - Completed the Hermex-informed Aiden shell with managed-scratch New Agent creation, reliable connected-state navigation handoff, explicit handoff-chat dismissal, and Aiden-logo starting/thinking states across chat and Live Activity UI. - Rendered completed and streaming assistant replies with bounded block Markdown, full transcript width, and content-sized literal user prompts. +- Matched Hermex's external Photos/Files picker presentation, preserved uploaded attachment references through optimistic composer clearing, and removed the root Markdown sizing constraint that could crop a reply's first glyph. +- Replaced the New Agent confirmation sheet with a CustomTB-inspired anchored glass popover for existing, reusable, and managed-scratch workspaces, and upgraded the home search/settings capsule to Hermex-style native interactive Liquid Glass with accessible fallbacks. +- Matched Aiden Agent's compact approval hierarchy with a shield badge, smaller title/helper/summary typography, and Liquid Glass Deny and Allow Once actions that retain accessible tap targets. +- Added device-only workspace archiving with a first-use privacy explanation, a searchable archived directory, Hermex-style long-press and swipe actions, hidden archived chats/App Intent choices, generation-bound server-authoritative rename, and canonically reconciled safe removal from Aiden Agent without deleting Mac files or chats. +- Rebuilt Mac Usage as an Aiden-themed aggregate dashboard with real daily/model data, streaks, a 30-day token heatmap, token/activity breakdowns, and top models; replaced the home list's transparent tail row with a palette-backed safe-area inset to remove the bottom white band. +- Mirrored Aiden Agent's complete provider-logo catalog in the iOS asset catalog, added provider identity to Usage, chat, and scheduled-task model controls, and added Hermex-style raw-Markdown Copy actions to completed and streaming assistant replies. From 33d9904fed5a24a5fbdb964447ba5010554fd539 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Thu, 20 Aug 2026 14:10:38 -0400 Subject: [PATCH 008/266] feat(models): add visibility and custom provider artwork Persist bounded per-provider model visibility, apply it across Mac and Telegram selection surfaces, and keep existing hidden selections executable. Normalize custom provider PNG/SVG artwork on the Mac and project only bounded PNG data through the remote model catalog. --- main/handlers/providers.ts | 21 ++ main/services/aiden-remote-models.test.ts | 101 ++++- main/services/aiden-remote-models.ts | 115 ++++-- main/services/config-store-core.test.ts | 65 +++- main/services/config-store-core.ts | 71 +++- .../portable-config-core.roundtrip.test.ts | 5 + main/services/portable-config-core.ts | 13 +- main/services/provider-artwork-core.test.ts | 53 +++ main/services/provider-artwork-core.ts | 50 +++ main/services/provider-artwork.ts | 40 ++ main/services/schedule-execution.ts | 110 ++---- main/services/scheduled-chat-creation.test.ts | 22 +- .../telegram/telegram-controls.test.ts | 50 ++- main/services/telegram/telegram-controls.ts | 197 ++++++---- .../telegram/telegram-service-core.test.ts | 183 ++++++--- main/services/telegram/telegram-service.ts | 354 +++++++++++++----- main/services/types.ts | 14 +- package.json | 6 +- protocol/aiden-remote/v1/openapi.json | 3 +- .../assistant/use-assistant-chat.ts | 28 +- renderer/components/command-palette.tsx | 16 +- renderer/components/model-picker.tsx | 12 +- renderer/components/provider-icon.tsx | 18 + .../settings/builtin-provider-editor.tsx | 2 + .../settings/model-pad-settings.tsx | 15 +- .../components/settings/provider-editor.tsx | 116 +++++- .../settings/provider-model-visibility.tsx | 134 +++++++ .../settings/providers-settings.tsx | 8 +- .../components/settings/telegram-settings.tsx | 200 +++++++--- .../components/usage/model-scoreboard.tsx | 11 +- renderer/lib/ipc.ts | 6 + renderer/lib/model-picker-data.test.ts | 91 +++++ renderer/lib/model-picker-data.ts | 11 + renderer/lib/types.ts | 3 + renderer/lib/use-model-selection.test.ts | 55 ++- renderer/lib/use-model-selection.ts | 77 +++- renderer/main/chat-pane.tsx | 37 +- renderer/main/profile-view.tsx | 5 +- renderer/shared/model-visibility.ts | 98 +++++ renderer/shared/provider-artwork.ts | 50 +++ 40 files changed, 1989 insertions(+), 477 deletions(-) create mode 100644 main/services/provider-artwork-core.test.ts create mode 100644 main/services/provider-artwork-core.ts create mode 100644 main/services/provider-artwork.ts create mode 100644 renderer/components/settings/provider-model-visibility.tsx create mode 100644 renderer/shared/model-visibility.ts create mode 100644 renderer/shared/provider-artwork.ts diff --git a/main/handlers/providers.ts b/main/handlers/providers.ts index b1530346..c2c8bcae 100644 --- a/main/handlers/providers.ts +++ b/main/handlers/providers.ts @@ -47,6 +47,8 @@ import { normalizeAppearanceConfig, parseAppearanceConfig, } from "../../renderer/shared/appearance.js"; +import { normalizeProviderArtwork } from "../../renderer/shared/provider-artwork.js"; +import { normalizeProviderArtworkInput } from "../services/provider-artwork.js"; const appearancePreview = new AppearancePreviewState(); @@ -134,6 +136,7 @@ function parseProvider(value: unknown): StoredProvider { id: asProviderId(p.id), kind, label: asString(p.label, "label"), + artwork: normalizeProviderArtwork(p.artwork), baseUrl, models, modelMetadata, @@ -205,6 +208,10 @@ export function registerProviderHandlers(): void { ipcMain.handle("providers:list", listProviders); + ipcMain.handle("providers:normalizeArtwork", (_event, input: unknown) => + normalizeProviderArtworkInput(input), + ); + ipcMain.handle("providers:auth:status", async (_event, providerId: unknown) => providerAuthFlow.status(parseProviderAuthProviderId(providerId)), ); @@ -344,6 +351,20 @@ export function registerProviderHandlers(): void { return configStore.setAnthropicThinkingLevel(selection.modelId, selection.level); }, ); + ipcMain.handle( + "settings:setModelVisibility", + async (_event, providerIdValue: unknown, modelIdValue: unknown, hiddenValue: unknown) => { + const providerId = asProviderId(providerIdValue); + const modelId = asString(modelIdValue, "modelId"); + if (modelId.length > MAX_CONFIG_ID_LENGTH || typeof hiddenValue !== "boolean") { + throw new Error("Invalid model visibility request."); + } + return configStore.setModelVisibility(providerId, modelId, hiddenValue); + }, + ); + ipcMain.handle("settings:showAllProviderModels", async (_event, providerIdValue: unknown) => { + return configStore.showAllProviderModels(asProviderId(providerIdValue)); + }); ipcMain.handle("settings:set", async (_event, patch: unknown) => { if (typeof patch !== "object" || patch === null) throw new Error("Invalid settings patch."); const p = patch as Record; diff --git a/main/services/aiden-remote-models.test.ts b/main/services/aiden-remote-models.test.ts index eee48a06..4a403c14 100644 --- a/main/services/aiden-remote-models.test.ts +++ b/main/services/aiden-remote-models.test.ts @@ -11,7 +11,12 @@ function provider(overrides: Partial = {}): Provider { baseUrl: "https://secret-endpoint.example/v1", models: ["chat-model", "embedding-model"], modelMetadata: { - "chat-model": { source: "provider", name: "Chat Model", type: "llm", thinkingLevels: ["low", "high"] }, + "chat-model": { + source: "provider", + name: "Chat Model", + type: "llm", + thinkingLevels: ["low", "high"], + }, "embedding-model": { source: "provider", type: "embedding" }, }, needsKey: true, @@ -29,16 +34,30 @@ test("model projection includes only configured chat models and no connection se const projection = await service.list(); assert.deepEqual(projection.defaults, { providerId: "provider-1", modelId: "chat-model" }); assert.equal(projection.providers.length, 1); - assert.deepEqual(projection.providers[0]?.models, [{ - id: "chat-model", - label: "Chat Model", - thinkingLevels: ["low", "high"], - }]); + assert.deepEqual(projection.providers[0]?.models, [ + { + id: "chat-model", + label: "Chat Model", + thinkingLevels: ["low", "high"], + }, + ]); const serialized = JSON.stringify(projection); assert.equal(serialized.includes("secret-endpoint"), false); assert.equal(serialized.includes("authMethods"), false); }); +test("custom provider artwork crosses the remote catalog only as bounded normalized PNG data", async () => { + const artwork = { + mimeType: "image/png" as const, + dataBase64: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=", + }; + const service = new AidenRemoteModelService({ + listProviders: async () => [provider({ artwork })], + getSettings: async () => ({}), + }); + assert.deepEqual((await service.list()).providers[0]?.artwork, artwork); +}); + test("model selection rejects missing providers and models", async () => { const service = new AidenRemoteModelService({ listProviders: async () => [provider()], @@ -54,3 +73,73 @@ test("model selection rejects missing providers and models", async () => { (error: unknown) => (error as { code?: string }).code === "invalid_request", ); }); + +test("hidden models are projected for clients, skipped by defaults, and remain resolvable", async () => { + const service = new AidenRemoteModelService({ + listProviders: async () => [ + provider({ + models: ["hidden-model", "visible-model"], + defaultModel: "hidden-model", + modelMetadata: { + "hidden-model": { source: "provider", name: "Hidden Model", type: "llm" }, + "visible-model": { source: "provider", name: "Visible Model", type: "llm" }, + }, + }), + ], + getSettings: async () => ({ + lastProviderId: "provider-1", + lastModel: "hidden-model", + hiddenModelsByProvider: { "provider-1": ["hidden-model"] }, + }), + }); + + const projection = await service.list(); + assert.deepEqual(projection.defaults, { + providerId: "provider-1", + modelId: "visible-model", + }); + assert.deepEqual(projection.providers[0]?.models, [ + { id: "hidden-model", label: "Hidden Model", hidden: true }, + { id: "visible-model", label: "Visible Model" }, + ]); + assert.deepEqual(await service.resolve("provider-1", "hidden-model"), { + providerId: "provider-1", + modelId: "hidden-model", + thinkingLevels: [], + }); +}); + +test("a provider with every model hidden has no remote default", async () => { + const service = new AidenRemoteModelService({ + listProviders: async () => [provider({ models: ["chat-model"] })], + getSettings: async () => ({ + hiddenModelsByProvider: { "provider-1": ["chat-model"] }, + }), + }); + + assert.deepEqual((await service.list()).defaults, {}); +}); + +test("remote catalog omits oversized model identities instead of truncating or colliding", async () => { + const prefix = "x".repeat(256); + const service = new AidenRemoteModelService({ + listProviders: async () => [provider({ models: [`${prefix}a`, `${prefix}b`, "safe"] })], + getSettings: async () => ({}), + }); + + assert.deepEqual((await service.list()).providers[0]?.models.map((model) => model.id), ["safe"]); +}); + +test("remote model projection remains below the generic iOS response ceiling", async () => { + const models = Array.from({ length: 20_000 }, (_, index) => + `model-${index.toString().padStart(5, "0")}-${"x".repeat(120)}`, + ); + const service = new AidenRemoteModelService({ + listProviders: async () => [provider({ models })], + getSettings: async () => ({}), + }); + + const projection = await service.list(); + assert.ok(projection.providers[0]!.models.length < models.length); + assert.ok(Buffer.byteLength(JSON.stringify(projection), "utf8") <= 900 * 1024); +}); diff --git a/main/services/aiden-remote-models.ts b/main/services/aiden-remote-models.ts index b6e369b1..1558bcd1 100644 --- a/main/services/aiden-remote-models.ts +++ b/main/services/aiden-remote-models.ts @@ -1,15 +1,23 @@ import { AidenRemoteServiceError } from "./aiden-remote-errors.js"; import type { AppSettings, Provider } from "./types.js"; +import { isModelHidden } from "../../renderer/shared/model-visibility.js"; +import { normalizeProviderArtwork } from "../../renderer/shared/provider-artwork.js"; + +const MAX_REMOTE_MODEL_ID_LENGTH = 256; +const MAX_REMOTE_MODEL_CATALOG_BYTES = 900 * 1024; +const REMOTE_MODEL_CATALOG_RESERVE_BYTES = 4 * 1024; export interface AidenRemoteModelProjection { id: string; label: string; thinkingLevels?: string[]; + hidden?: boolean; } export interface AidenRemoteProviderProjection { id: string; label: string; + artwork?: { mimeType: "image/png"; dataBase64: string }; models: AidenRemoteModelProjection[]; } @@ -35,36 +43,89 @@ export class AidenRemoteModelService { this.options.listProviders(), this.options.getSettings(), ]); - const providers = configured - .filter((provider) => provider.hasKey || !provider.needsKey) - .map((provider) => ({ - id: bounded(provider.id, 256), + let serializedBytes = Buffer.byteLength('{"providers":[],"defaults":{}}', "utf8") + + REMOTE_MODEL_CATALOG_RESERVE_BYTES; + const providers: AidenRemoteProviderProjection[] = []; + for (const provider of configured) { + if ( + (!provider.hasKey && provider.needsKey) || + provider.id.length === 0 || + provider.id.length > MAX_REMOTE_MODEL_ID_LENGTH + ) { + continue; + } + const projected: AidenRemoteProviderProjection = { + id: provider.id, label: bounded(provider.label, 256), - models: provider.models - .filter((id) => provider.modelMetadata?.[id]?.type !== "embedding") - .map((id) => { - const metadata = provider.modelMetadata?.[id]; - return { - id: bounded(id, 256), - label: bounded(metadata?.name ?? id, 256), - ...(metadata?.thinkingLevels?.length - ? { thinkingLevels: [...metadata.thinkingLevels] } - : {}), - }; - }), - })) - .filter((provider) => provider.models.length > 0); - const selectedProvider = providers.find((provider) => provider.id === settings.lastProviderId) - ?? providers[0]; - const selectedModel = selectedProvider?.models.find((model) => model.id === settings.lastModel) - ?? selectedProvider?.models[0]; - return { + models: [], + }; + const baseBytes = Buffer.byteLength(JSON.stringify(projected), "utf8") + 1; + if (serializedBytes + baseBytes > MAX_REMOTE_MODEL_CATALOG_BYTES) break; + serializedBytes += baseBytes; + + const candidateArtwork = normalizeProviderArtwork(provider.artwork); + if (candidateArtwork) { + const artworkBytes = Buffer.byteLength(JSON.stringify(candidateArtwork), "utf8") + 12; + if (serializedBytes + artworkBytes <= MAX_REMOTE_MODEL_CATALOG_BYTES) { + projected.artwork = candidateArtwork; + serializedBytes += artworkBytes; + } + } + + for (const id of provider.models) { + if ( + id.length === 0 || + id.length > MAX_REMOTE_MODEL_ID_LENGTH || + provider.modelMetadata?.[id]?.type === "embedding" + ) { + continue; + } + const metadata = provider.modelMetadata?.[id]; + const thinkingLevels = metadata?.thinkingLevels + ?.slice(0, 8) + .map((level) => bounded(level, 32)); + const model: AidenRemoteModelProjection = { + id, + label: bounded(metadata?.name ?? id, 256), + ...(isModelHidden(settings.hiddenModelsByProvider, provider.id, id) + ? { hidden: true } + : {}), + ...(thinkingLevels?.length ? { thinkingLevels } : {}), + }; + const modelBytes = Buffer.byteLength(JSON.stringify(model), "utf8") + 1; + if (serializedBytes + modelBytes > MAX_REMOTE_MODEL_CATALOG_BYTES) break; + projected.models.push(model); + serializedBytes += modelBytes; + } + + if (projected.models.length > 0) providers.push(projected); + else serializedBytes -= baseBytes + (projected.artwork + ? Buffer.byteLength(JSON.stringify(projected.artwork), "utf8") + 12 + : 0); + } + const selectedProvider = + providers.find( + (provider) => + provider.id === settings.lastProviderId && provider.models.some((model) => !model.hidden), + ) ?? providers.find((provider) => provider.models.some((model) => !model.hidden)); + const selectedModel = + selectedProvider?.models.find((model) => model.id === settings.lastModel && !model.hidden) ?? + selectedProvider?.models.find((model) => !model.hidden); + const projection = { providers, defaults: { ...(selectedProvider ? { providerId: selectedProvider.id } : {}), ...(selectedModel ? { modelId: selectedModel.id } : {}), }, }; + if (Buffer.byteLength(JSON.stringify(projection), "utf8") > MAX_REMOTE_MODEL_CATALOG_BYTES) { + throw new AidenRemoteServiceError( + "internal_error", + "The configured model catalog is too large for a paired device.", + 503, + ); + } + return projection; } async resolve( @@ -75,9 +136,11 @@ export class AidenRemoteModelService { const provider = providerId ? projection.providers.find((candidate) => candidate.id === providerId) : projection.providers.find((candidate) => candidate.id === projection.defaults.providerId); - const model = provider && (modelId - ? provider.models.find((candidate) => candidate.id === modelId) - : provider.models.find((candidate) => candidate.id === projection.defaults.modelId)); + const model = + provider && + (modelId + ? provider.models.find((candidate) => candidate.id === modelId) + : provider.models.find((candidate) => candidate.id === projection.defaults.modelId)); if (!provider || !model) { throw new AidenRemoteServiceError( "invalid_request", diff --git a/main/services/config-store-core.test.ts b/main/services/config-store-core.test.ts index f0225fa4..1bca7716 100644 --- a/main/services/config-store-core.test.ts +++ b/main/services/config-store-core.test.ts @@ -232,6 +232,63 @@ test("removing a provider clears its cache entry and its key", async (t) => { assert.equal(h.secrets.keys[provider.id], undefined); }); +test("model visibility updates are atomic and provider-scoped", async (t) => { + const h = await harness(t); + await Promise.all([ + h.store.setModelVisibility("google", "gemini-pro", true), + h.store.setModelVisibility("anthropic", "claude-sonnet", true), + h.store.setModelVisibility("google", "gemini-flash", true), + ]); + + assert.deepEqual((await h.store.getSettings()).hiddenModelsByProvider, { + anthropic: ["claude-sonnet"], + google: ["gemini-flash", "gemini-pro"], + }); + + await h.store.setModelVisibility("google", "gemini-pro", false); + assert.deepEqual((await h.store.getSettings()).hiddenModelsByProvider, { + anthropic: ["claude-sonnet"], + google: ["gemini-flash"], + }); + await h.store.showAllProviderModels("google"); + assert.deepEqual((await h.store.getSettings()).hiddenModelsByProvider, { + anthropic: ["claude-sonnet"], + }); +}); + +test("removing a provider clears its model visibility preferences", async (t) => { + const h = await harness(t); + await h.store.saveProvider(provider); + await h.store.setModelVisibility(provider.id, provider.models[0], true); + await h.store.setModelVisibility("other", "other-model", true); + + await h.store.removeProvider(provider.id); + + assert.deepEqual((await h.store.getSettings()).hiddenModelsByProvider, { + other: ["other-model"], + }); +}); + +test("admitted provider removal finishes cache and visibility cleanup after renderer navigation", async (t) => { + const h = await harness(t); + await h.store.saveProvider(provider); + await h.store.setModelVisibility(provider.id, provider.models[0], true); + h.secrets.keys[provider.id] = "ciphertext"; + let validityChecks = 0; + + await h.store.removeProvider(provider.id, () => { + validityChecks += 1; + return validityChecks <= 5; + }); + + assert.equal(validityChecks, 5); + assert.deepEqual(await h.store.listProviders(), []); + assert.equal((await h.store.getSettings()).hiddenModelsByProvider?.[provider.id], undefined); + assert.equal(h.secrets.keys[provider.id], undefined); + const cache = await readJson<{ byProvider: Record }>(h.cacheFile); + assert.equal(cache.byProvider[provider.id], undefined); +}); + // ── Store placement ────────────────────────────────────────────────────────── test("MCP servers and skills are portable; workspaces and settings are not", async (t) => { @@ -428,7 +485,10 @@ test("released onboarding local identity migration re-homes cache and remembered defaultModel: "qwen3-8b", }, ], - settings: { lastProviderId: releasedId }, + settings: { + lastProviderId: releasedId, + hiddenModelsByProvider: { [releasedId]: ["qwen3-8b"] }, + }, seeded: true, }); @@ -439,6 +499,9 @@ test("released onboarding local identity migration re-homes cache and remembered assert.deepEqual(listed.models, provider.models); assert.deepEqual(listed.modelMetadata, provider.modelMetadata); assert.equal((await h.store.getSettings()).lastProviderId, "custom:lmstudio"); + assert.deepEqual((await h.store.getSettings()).hiddenModelsByProvider, { + "custom:lmstudio": ["qwen3-8b"], + }); const cache = await readJson<{ byProvider: Record }>(h.cacheFile); assert.equal(cache.byProvider[releasedId], undefined); assert.deepEqual(cache.byProvider["custom:lmstudio"], { diff --git a/main/services/config-store-core.ts b/main/services/config-store-core.ts index 350233fb..ecc9c87b 100644 --- a/main/services/config-store-core.ts +++ b/main/services/config-store-core.ts @@ -47,6 +47,11 @@ import { type AnthropicThinkingLevel, } from "../../renderer/shared/anthropic-thinking.js"; import { migrateLegacyPiProviderId } from "../../renderer/shared/google-provider.js"; +import { + remapHiddenModelProvider, + withModelVisibility, + withoutProviderVisibility, +} from "../../renderer/shared/model-visibility.js"; import { ASSISTANT_WORKSPACE_ID } from "../../renderer/shared/assistant.js"; import type { AppSettings, @@ -321,6 +326,7 @@ export function createConfigStore( const cacheBefore = (await modelCache.load()).byProvider; const currentSettings = runtimeSettingsFrom((await settingsStore.load()).settings); let lastProviderId = currentSettings.lastProviderId; + let hiddenModelsByProvider = currentSettings.hiddenModelsByProvider; let migrated: StoredProvider[] = []; let providersChanged = false; let aliasRoutesForMigration: Record = {}; @@ -373,11 +379,6 @@ export function createConfigStore( if (!seeded) { await localStore.update((config) => void (config.seeded = true)); } - if (lastProviderId !== currentSettings.lastProviderId) { - await settingsStore.update( - (config) => void (config.settings.lastProviderId = lastProviderId), - ); - } const activeProviderIds = new Set(config.providers.map((provider) => provider.id)); const cacheAliasEntries = providerAliasRoutes(aliasRoutesForMigration).filter( ([legacyId, targetId]) => @@ -386,6 +387,23 @@ export function createConfigStore( activeProviderIds.has(targetId), ); secretMigrationAliases = cacheAliasEntries; + for (const [legacyId, targetId] of cacheAliasEntries) { + hiddenModelsByProvider = remapHiddenModelProvider( + hiddenModelsByProvider, + legacyId, + targetId, + ); + } + if ( + lastProviderId !== currentSettings.lastProviderId || + JSON.stringify(hiddenModelsByProvider) !== + JSON.stringify(currentSettings.hiddenModelsByProvider) + ) { + await settingsStore.update((config) => { + config.settings.lastProviderId = lastProviderId; + config.settings.hiddenModelsByProvider = hiddenModelsByProvider; + }); + } secretMigrationTargets = [ ...new Map( cacheAliasEntries.flatMap(([, targetId]) => { @@ -622,8 +640,17 @@ export function createConfigStore( await mutatePortable((config) => { config.providers = config.providers.filter((p) => p.id !== id); }, isCurrent); - await modelCache.update((draft) => void delete draft.byProvider[id], isCurrent); - await secrets.deleteKey(id, isCurrent); + // Once portable deletion commits, finish every dependent cleanup even if + // the requesting renderer navigates away. Otherwise recreating this ID + // can revive stale cache or visibility state. + await modelCache.update((draft) => void delete draft.byProvider[id]); + await mutateSettings((config) => { + config.settings.hiddenModelsByProvider = withoutProviderVisibility( + config.settings.hiddenModelsByProvider, + id, + ); + }); + await secrets.deleteKey(id); }, /** Resolve a historical provider identity without ever falling through to a new Pi provider. */ @@ -666,6 +693,36 @@ export function createConfigStore( return runtimeSettingsFrom(saved); }, + /** Atomically update one presentation-only model visibility preference. */ + async setModelVisibility( + providerId: string, + modelId: string, + hidden: boolean, + ): Promise { + const saved = await mutateSettings((config) => { + config.settings.hiddenModelsByProvider = withModelVisibility( + config.settings.hiddenModelsByProvider, + providerId, + modelId, + hidden, + ); + return structuredClone(config.settings); + }); + return runtimeSettingsFrom(saved); + }, + + /** Atomically restore every model for one provider to picker visibility. */ + async showAllProviderModels(providerId: string): Promise { + const saved = await mutateSettings((config) => { + config.settings.hiddenModelsByProvider = withoutProviderVisibility( + config.settings.hiddenModelsByProvider, + providerId, + ); + return structuredClone(config.settings); + }); + return runtimeSettingsFrom(saved); + }, + /** Atomically merge one validated native-Google preference into current settings. */ async setGoogleThinkingLevel( modelId: string, diff --git a/main/services/portable-config-core.roundtrip.test.ts b/main/services/portable-config-core.roundtrip.test.ts index fbbdcf08..4ac3f3bf 100644 --- a/main/services/portable-config-core.roundtrip.test.ts +++ b/main/services/portable-config-core.roundtrip.test.ts @@ -39,6 +39,11 @@ const populated: PortableConfigShape = { id: "custom-vllm", kind: "openai", label: "vLLM", + artwork: { + mimeType: "image/png", + dataBase64: + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=", + }, baseUrl: "http://10.0.0.4:8000/v1", needsKey: false, deployment: "local", diff --git a/main/services/portable-config-core.ts b/main/services/portable-config-core.ts index 347f7c14..abcf463f 100644 --- a/main/services/portable-config-core.ts +++ b/main/services/portable-config-core.ts @@ -35,6 +35,8 @@ import type { import { MAX_CONFIG_ID_LENGTH, MAX_PROVIDER_BASE_URL_LENGTH } from "./types.js"; import { decodeUtf8, readRegularFile } from "./regular-file-read.js"; import { isConfiguredSkill, isConfiguredSkillList } from "./skill-config-limits.js"; +import { normalizeHiddenModelsByProvider } from "../../renderer/shared/model-visibility.js"; +import { normalizeProviderArtwork } from "../../renderer/shared/provider-artwork.js"; /** A provider minus the caches that model discovery refills. */ export type PortableProvider = Omit; @@ -357,6 +359,7 @@ export function isPortableProvider(value: unknown): value is PortableProvider { (provider.kind === "openai" || provider.kind === "anthropic") && typeof provider.label === "string" && provider.label.trim().length > 0 && + (provider.artwork === undefined || normalizeProviderArtwork(provider.artwork) !== undefined) && isProviderBaseUrl(provider.baseUrl) && typeof provider.needsKey === "boolean" && (provider.defaultModel === undefined || typeof provider.defaultModel === "string") && @@ -430,9 +433,11 @@ function normalizeStoredProvider(value: unknown): StoredProvider | undefined { const raw = value as Record; const models = normalizeModelIds(raw.models) ?? []; const modelMetadata = normalizeProviderModelMetadataMap(raw.modelMetadata); - const { modelMetadata: _metadata, models: _models, ...intent } = raw; + const { artwork: rawArtwork, modelMetadata: _metadata, models: _models, ...intent } = raw; + const artwork = normalizeProviderArtwork(rawArtwork); return { ...(intent as PortableProvider), + ...(artwork ? { artwork } : {}), models, ...(modelMetadata !== undefined ? { modelMetadata } : {}), }; @@ -551,6 +556,9 @@ function normalizeSettingsShape(value: unknown): SettingsShape { ] as const) { if (settings[key] !== undefined && !isRecord(settings[key])) delete normalized[key]; } + const hiddenModelsByProvider = normalizeHiddenModelsByProvider(settings.hiddenModelsByProvider); + if (hiddenModelsByProvider) normalized.hiddenModelsByProvider = hiddenModelsByProvider; + else delete normalized.hiddenModelsByProvider; return { ...rest, settings: normalized as AppSettings, @@ -560,6 +568,9 @@ function normalizeSettingsShape(value: unknown): SettingsShape { /** Safe projection for consumers; persistence retains unknown nested future data. */ export function runtimeSettingsFrom(settings: AppSettings): AppSettings { const runtime = structuredClone(settings); + const hiddenModelsByProvider = normalizeHiddenModelsByProvider(settings.hiddenModelsByProvider); + if (hiddenModelsByProvider) runtime.hiddenModelsByProvider = hiddenModelsByProvider; + else delete runtime.hiddenModelsByProvider; const retainKnownValue = (key: keyof AppSettings, allowed: readonly string[]): void => { const value = settings[key]; if (value !== undefined && (typeof value !== "string" || !allowed.includes(value))) { diff --git a/main/services/provider-artwork-core.test.ts b/main/services/provider-artwork-core.test.ts new file mode 100644 index 00000000..290a12ed --- /dev/null +++ b/main/services/provider-artwork-core.test.ts @@ -0,0 +1,53 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { decodeProviderArtworkSource } from "./provider-artwork-core.js"; + +test("provider artwork accepts PNG and inert SVG sources", () => { + assert.equal( + decodeProviderArtworkSource({ + name: "icon.png", + dataBase64: + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=", + }).kind, + "png", + ); + assert.equal( + decodeProviderArtworkSource({ + name: "icon.svg", + dataBase64: Buffer.from('').toString("base64"), + }).kind, + "svg", + ); +}); + +test("provider artwork rejects active or externally-referenced SVG content", () => { + for (const source of [ + '', + '', + '', + ]) { + assert.throws( + () => decodeProviderArtworkSource({ + name: "icon.svg", + dataBase64: Buffer.from(source).toString("base64"), + }), + /cannot contain scripts or external resources/u, + ); + } +}); + +test("provider artwork rejects malformed base64 and oversized PNG dimensions before decoding", () => { + assert.throws( + () => decodeProviderArtworkSource({ name: "icon.svg", dataBase64: "not base64!" }), + /valid/u, + ); + const pngHeader = Buffer.alloc(24); + Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]).copy(pngHeader); + Buffer.from("IHDR", "ascii").copy(pngHeader, 12); + pngHeader.writeUInt32BE(8_193, 16); + pngHeader.writeUInt32BE(64, 20); + assert.throws( + () => decodeProviderArtworkSource({ name: "icon.png", dataBase64: pngHeader.toString("base64") }), + /dimensions/u, + ); +}); diff --git a/main/services/provider-artwork-core.ts b/main/services/provider-artwork-core.ts new file mode 100644 index 00000000..3a6ef8df --- /dev/null +++ b/main/services/provider-artwork-core.ts @@ -0,0 +1,50 @@ +export const PROVIDER_ARTWORK_MAX_SOURCE_BYTES = 512 * 1024; + +export function decodeProviderArtworkSource(value: unknown): { + bytes: Buffer; + kind: "png" | "svg"; + safeSvg?: string; + pixelSize?: { width: number; height: number }; +} { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Choose a PNG or SVG provider icon."); + } + const record = value as Record; + const name = typeof record.name === "string" ? record.name.toLowerCase() : ""; + const dataBase64 = typeof record.dataBase64 === "string" ? record.dataBase64 : ""; + if (!dataBase64 || dataBase64.length > Math.ceil(PROVIDER_ARTWORK_MAX_SOURCE_BYTES / 3) * 4 + 4) { + throw new Error("Provider artwork must be 512 KB or smaller."); + } + if (dataBase64.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/u.test(dataBase64)) { + throw new Error("Choose a valid .png or .svg file."); + } + const bytes = Buffer.from(dataBase64, "base64"); + if (bytes.length === 0 || bytes.length > PROVIDER_ARTWORK_MAX_SOURCE_BYTES) { + throw new Error("Provider artwork must be 512 KB or smaller."); + } + if ( + name.endsWith(".png") && + bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])) + ) { + if (bytes.length < 24 || bytes.subarray(12, 16).toString("ascii") !== "IHDR") { + throw new Error("The PNG file is invalid."); + } + const width = bytes.readUInt32BE(16); + const height = bytes.readUInt32BE(20); + if (width === 0 || height === 0 || width > 8_192 || height > 8_192) { + throw new Error("Provider artwork dimensions are invalid."); + } + return { bytes, kind: "png", pixelSize: { width, height } }; + } + if (!name.endsWith(".svg")) throw new Error("Choose a valid .png or .svg file."); + const source = bytes.toString("utf8").trim(); + if (source.includes("\uFFFD") || !/)/iu.test(source)) { + throw new Error("The SVG file is invalid."); + } + if ( + / 8_192 || source.pixelSize.height > 8_192) + ) { + throw new Error("Provider artwork dimensions are invalid."); + } + const image = source.kind === "png" + ? nativeImage.createFromBuffer(source.bytes, { scaleFactor: 1 }) + : nativeImage.createFromDataURL( + `data:image/svg+xml;base64,${Buffer.from(source.safeSvg!, "utf8").toString("base64")}`, + ); + if (image.isEmpty()) throw new Error("Aiden could not decode that provider icon."); + const size = image.getSize(); + if (size.width <= 0 || size.height <= 0 || size.width > 8_192 || size.height > 8_192) { + throw new Error("Provider artwork dimensions are invalid."); + } + const scale = Math.min(1, TARGET_EDGE / Math.max(size.width, size.height)); + const normalized = scale < 1 + ? image.resize({ + width: Math.max(1, Math.round(size.width * scale)), + height: Math.max(1, Math.round(size.height * scale)), + quality: "best", + }) + : image; + const png = normalized.toPNG(); + if (png.length === 0 || png.length > PROVIDER_ARTWORK_MAX_PNG_BYTES) { + throw new Error("The normalized provider icon is too complex. Choose a simpler image."); + } + return { mimeType: "image/png", dataBase64: png.toString("base64") }; +} diff --git a/main/services/schedule-execution.ts b/main/services/schedule-execution.ts index f52bc5c2..194f8798 100644 --- a/main/services/schedule-execution.ts +++ b/main/services/schedule-execution.ts @@ -6,10 +6,7 @@ import { chatStore } from "./chat-store.js"; import { configStore } from "./config-store.js"; import { llmClient } from "./llm-client.js"; import { providerRegistry } from "./provider-registry.js"; -import { - resolveScheduledScript, - runScheduledScript, -} from "./schedule-script.js"; +import { resolveScheduledScript, runScheduledScript } from "./schedule-script.js"; import { scheduleStore, type ScheduleStore } from "./schedule-store.js"; import { SCHEDULE_TOOL_NAME } from "./schedule-tool.js"; import { @@ -18,16 +15,12 @@ import { scheduledTaskGenerationMode, } from "./schedule-guard.js"; import { showScheduledNotification } from "./schedule-notification.js"; -import type { - ChatDone, - ChatError, - ScheduledRun, - ScheduledTask, -} from "./types.js"; +import type { ChatDone, ChatError, ScheduledRun, ScheduledTask } from "./types.js"; import type { ChatGenerationOwner } from "./chat-generation-owner.js"; import type { NotificationChannel } from "../../renderer/preload-channels.js"; import { assertManagedWorktreeAdmission } from "./managed-worktree-admission.js"; import { createScheduledChatClaim } from "./scheduled-chat-creation.js"; +import { firstVisibleModelForProvider } from "../../renderer/shared/model-visibility.js"; function createBackgroundOwner(streamId: string): { owner: ChatGenerationOwner; @@ -44,8 +37,7 @@ function createBackgroundOwner(streamId: string): { documentId: `scheduled:${streamId}`, isDestroyed: () => destroyed, send: (channel: NotificationChannel, payload: unknown) => { - if (destroyed) - throw new Error("The scheduled generation is no longer active."); + if (destroyed) throw new Error("The scheduled generation is no longer active."); if (channel === "chat:done" || channel === "chat:error") settle?.(payload as ChatDone | ChatError); }, @@ -71,10 +63,7 @@ function scheduledTurnId(taskId: string, kind: string): string { return `scheduled-${kind}-${taskId}-${Date.now().toString(36)}-${scheduledTurnSequence.toString(36)}`; } -async function appendClaimedChatMessage( - chatId: string, - content: string, -): Promise { +async function appendClaimedChatMessage(chatId: string, content: string): Promise { const chat = await chatStore.appendMessage(chatId, { role: "assistant", content, @@ -105,11 +94,7 @@ async function appendSerializedChatMessage( } } -function notify( - task: ScheduledTask, - body: string, - chatId: string | undefined, -): void { +function notify(task: ScheduledTask, body: string, chatId: string | undefined): void { showScheduledNotification(task, body, chatId, { isSupported: () => Notification.isSupported(), create: (options) => new Notification(options), @@ -140,10 +125,7 @@ export function createScheduleExecution(store: ScheduleStore = scheduleStore) { await store.clearChatId(task.id, chatId); chatId = await store.ensureChatId(task.id, create); chat = await chatStore.get(chatId); - if (!chat) - throw new Error( - "Could not create the scheduled task's dedicated chat.", - ); + if (!chat) throw new Error("Could not create the scheduled task's dedicated chat."); } if (chat) { ipcMain.broadcast("chats:metadata-updated", { @@ -173,25 +155,17 @@ export function createScheduleExecution(store: ScheduleStore = scheduleStore) { const workspace = task.workspaceId ? await configStore.getWorkspace(task.workspaceId) : undefined; - if (task.workspaceId && !workspace) - throw new Error("The task workspace no longer exists."); - if (workspace?.permission === "none") - throw new Error("The task workspace has No Access."); + if (task.workspaceId && !workspace) throw new Error("The task workspace no longer exists."); + if (workspace?.permission === "none") throw new Error("The task workspace has No Access."); if (workspace) await assertManagedWorktreeAdmission(workspace); const script = await resolveScheduledScript({ script: task.script ?? "", workspaceRoot: workspace?.folderPath, }); const turnId = scheduledTurnId(task.id, "script"); - const turn = llmClient.beginChatTurn( - chatId, - turnId, - `scheduled-script:${task.id}`, - ); + const turn = llmClient.beginChatTurn(chatId, turnId, `scheduled-script:${task.id}`); if (!turn) { - throw new Error( - "The scheduled task's dedicated chat already has a turn in progress.", - ); + throw new Error("The scheduled task's dedicated chat already has a turn in progress."); } try { const processResult = await runScheduledScript(script, { @@ -205,18 +179,12 @@ export function createScheduleExecution(store: ScheduleStore = scheduleStore) { } if (processResult.timedOut) { const error = "Script timed out after 60 seconds."; - await appendClaimedChatMessage( - chatId, - `Scheduled task failed: ${error}`, - ); + await appendClaimedChatMessage(chatId, `Scheduled task failed: ${error}`); return { result: "error", output: processResult.stdout, error }; } if (processResult.outputLimitExceeded) { const error = "Script exceeded the 1 MB output limit."; - await appendClaimedChatMessage( - chatId, - `Scheduled task failed: ${error}`, - ); + await appendClaimedChatMessage(chatId, `Scheduled task failed: ${error}`); return { result: "error", output: processResult.stdout, error }; } if (processResult.exitCode !== 0) { @@ -224,10 +192,7 @@ export function createScheduleExecution(store: ScheduleStore = scheduleStore) { processResult.stderr.trim() || `Process exited with code ${String(processResult.exitCode)}.`; const error = detail.slice(0, 4_096); - await appendClaimedChatMessage( - chatId, - `Scheduled task failed: ${error}`, - ); + await appendClaimedChatMessage(chatId, `Scheduled task failed: ${error}`); return { result: "error", output: processResult.stdout, error }; } if (!processResult.stdout.trim()) return { result: "silent", output: "" }; @@ -252,26 +217,23 @@ export function createScheduleExecution(store: ScheduleStore = scheduleStore) { const workspace = task.workspaceId ? await configStore.getWorkspace(task.workspaceId) : undefined; - if (task.workspaceId && !workspace) - throw new Error("The task workspace no longer exists."); - if (workspace?.permission === "none") - throw new Error("The task workspace has No Access."); + if (task.workspaceId && !workspace) throw new Error("The task workspace no longer exists."); + if (workspace?.permission === "none") throw new Error("The task workspace has No Access."); if (workspace) await assertManagedWorktreeAdmission(workspace); const settings = await configStore.getSettings(); const providerId = task.providerId ?? settings.lastProviderId; - if (!providerId) - throw new Error("Choose a provider before running this scheduled task."); + if (!providerId) throw new Error("Choose a provider before running this scheduled task."); const provider = (await providerRegistry.selectionProvider(providerId)) ?? (await configStore.getProvider(providerId)); if (!provider) throw new Error("The task provider no longer exists."); const model = task.model ?? - settings.lastModel ?? - provider.defaultModel ?? - provider.models[0]; - if (!model) - throw new Error("Choose a model before running this scheduled task."); + firstVisibleModelForProvider(settings.hiddenModelsByProvider, providerId, provider.models, [ + settings.lastProviderId === providerId ? settings.lastModel : undefined, + provider.defaultModel, + ]); + if (!model) throw new Error("Choose a model before running this scheduled task."); const prompt = task.prompt?.trim(); if (!prompt) throw new Error("The scheduled task prompt is empty."); if (signal.aborted) throw new Error("Scheduled task was cancelled."); @@ -286,18 +248,11 @@ export function createScheduleExecution(store: ScheduleStore = scheduleStore) { task.permission === "full"; const mcpServerIds = task.mcpServerIds ?? (legacyAllMcp ? undefined : []); const allowMcpTools = - task.permission === "full" && - (legacyAllMcp || (mcpServerIds?.length ?? 0) > 0); + task.permission === "full" && (legacyAllMcp || (mcpServerIds?.length ?? 0) > 0); const background = createBackgroundOwner(streamId); - const turn = llmClient.beginChatTurn( - chatId, - streamId, - background.owner.documentId, - ); + const turn = llmClient.beginChatTurn(chatId, streamId, background.owner.documentId); if (!turn) { - throw new Error( - "The scheduled task's dedicated chat already has a turn in progress.", - ); + throw new Error("The scheduled task's dedicated chat already has a turn in progress."); } activeStreams.set(task.id, streamId); try { @@ -337,27 +292,18 @@ export function createScheduleExecution(store: ScheduleStore = scheduleStore) { turnId: streamId, }, ); - if (!started) - throw new Error( - "The scheduled generation was cancelled before it started.", - ); + if (!started) throw new Error("The scheduled generation was cancelled before it started."); const terminal = await background.terminal; if (signal.aborted) { const error = "Scheduled task was cancelled."; await llmClient.waitForChatIdle(chatId); - await appendSerializedChatMessage( - chatId, - error, - scheduledTurnId(task.id, "cancelled"), - ); + await appendSerializedChatMessage(chatId, error, scheduledTurnId(task.id, "cancelled")); return { result: "blocked", output: "", error }; } if ("message" in terminal) { const error = terminal.message; return { - result: /\bblocked\b|\bread-only\b|\bdenied\b/iu.test(error) - ? "blocked" - : "error", + result: /\bblocked\b|\bread-only\b|\bdenied\b/iu.test(error) ? "blocked" : "error", output: terminal.content ?? "", error, }; diff --git a/main/services/scheduled-chat-creation.test.ts b/main/services/scheduled-chat-creation.test.ts index 4a303983..ef8f063a 100644 --- a/main/services/scheduled-chat-creation.test.ts +++ b/main/services/scheduled-chat-creation.test.ts @@ -30,19 +30,15 @@ class FailingTaskPersistence extends MemoryPersistence { const result = await mutation(draft); const failure = this.fail; this.fail = null; - if (failure === "before") - throw new Error("task mapping pre-commit failure"); + if (failure === "before") throw new Error("task mapping pre-commit failure"); this.data = draft; - if (failure === "after") - throw new Error("task mapping post-commit failure"); + if (failure === "after") throw new Error("task mapping post-commit failure"); return result; } } test("an indeterminate scheduled create preserves its exact recovered chat identity", async (t) => { - const directory = await fs.mkdtemp( - path.join(os.tmpdir(), "aiden-scheduled-chat-claim-"), - ); + const directory = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-scheduled-chat-claim-")); t.after(() => fs.rm(directory, { recursive: true, force: true })); let failIndexWrites = true; const interrupted = createChatStore(async () => directory, undefined, { @@ -93,12 +89,11 @@ test("an indeterminate scheduled create preserves its exact recovered chat ident [claimedChatId], ); - const execution = await fs.readFile( - new URL("./schedule-execution.ts", import.meta.url), - "utf8", - ); + const execution = await fs.readFile(new URL("./schedule-execution.ts", import.meta.url), "utf8"); assert.match(execution, /createScheduledChatClaim\(claimedChatId, \(\) =>/u); assert.match(execution, /store\.ensureChatId\(task\.id, create\)/u); + assert.match(execution, /task\.model \?\?[\s\S]*firstVisibleModelForProvider/u); + assert.match(execution, /settings\.hiddenModelsByProvider/u); }); test("schedule mapping failures happen before chat creation and cannot orphan a chat", async () => { @@ -123,10 +118,7 @@ test("schedule mapping failures happen before chat creation and cannot orphan a creates += 1; return { id: claimedChatId }; }), - new RegExp( - `task mapping ${failure === "before" ? "pre" : "post"}-commit failure`, - "u", - ), + new RegExp(`task mapping ${failure === "before" ? "pre" : "post"}-commit failure`, "u"), ); assert.equal(creates, 0); } diff --git a/main/services/telegram/telegram-controls.test.ts b/main/services/telegram/telegram-controls.test.ts index eb320736..01d7e2c7 100644 --- a/main/services/telegram/telegram-controls.test.ts +++ b/main/services/telegram/telegram-controls.test.ts @@ -10,8 +10,21 @@ import { buildThinkingMenu, commandArgument, commandName, + visibleTelegramModelChoices, } from "./telegram-controls.js"; +test("Telegram model choices omit hidden models without invalidating explicit execution state", () => { + const choices = [ + { providerId: "google", providerLabel: "Google", model: "pro", reasoning: true }, + { providerId: "google", providerLabel: "Google", model: "flash", reasoning: false }, + ]; + assert.deepEqual( + visibleTelegramModelChoices(choices, { google: ["pro"] }).map((choice) => choice.model), + ["flash"], + ); + assert.equal(choices[0]?.model, "pro"); +}); + test("command catalog exposes the first-class operator controls", () => { assert.deepEqual( TELEGRAM_COMMANDS.slice(0, 6).map((command) => command.command), @@ -33,23 +46,44 @@ test("main menu projects model, thinking, queue, workspace, and active controls" workspaceLabel: "Aiden", }); const callbacks = menu.inline_keyboard.flat().map((button) => button.callback_data); - for (const expected of ["menu:model", "menu:thinking", "menu:queue", "menu:workspace", "turn:abort"]) { + for (const expected of [ + "menu:model", + "menu:thinking", + "menu:queue", + "menu:workspace", + "turn:abort", + ]) { assert.ok(callbacks.includes(expected)); } }); test("model, thinking, and queue menus carry short callback identities", () => { - const model = buildModelMenu([ - { providerId: "p", providerLabel: "Provider", model: "m", reasoning: true }, - ], "p", "m", 0); - assert.equal(model.markup.inline_keyboard[model.markup.inline_keyboard.length - 1]?.[0]?.callback_data, "model:set:0"); + const model = buildModelMenu( + [{ providerId: "p", providerLabel: "Provider", model: "m", reasoning: true }], + "p", + "m", + 0, + ); + assert.equal( + model.markup.inline_keyboard[model.markup.inline_keyboard.length - 1]?.[0]?.callback_data, + "model:set:0", + ); const thinking = buildThinkingMenu("medium", ["off", "medium", "high"]); - assert.equal(thinking.markup.inline_keyboard[thinking.markup.inline_keyboard.length - 1]?.[0]?.callback_data, "thinking:set:high"); + assert.equal( + thinking.markup.inline_keyboard[thinking.markup.inline_keyboard.length - 1]?.[0]?.callback_data, + "thinking:set:high", + ); const item = { id: 9, lane: "default" as const, text: "Review this", chatId: 1, ownerUserId: 2 }; - assert.equal(buildQueueMenu([item]).markup.inline_keyboard[2]?.[0]?.callback_data, "queue:item:9"); - assert.equal(buildQueueItemMenu(item).markup.inline_keyboard[1]?.[1]?.callback_data, "queue:delete:9"); + assert.equal( + buildQueueMenu([item]).markup.inline_keyboard[2]?.[0]?.callback_data, + "queue:item:9", + ); + assert.equal( + buildQueueItemMenu(item).markup.inline_keyboard[1]?.[1]?.callback_data, + "queue:delete:9", + ); }); test("Telegram settings expose native rendering and voice policy controls", () => { diff --git a/main/services/telegram/telegram-controls.ts b/main/services/telegram/telegram-controls.ts index 3d0c08c9..dc2c474d 100644 --- a/main/services/telegram/telegram-controls.ts +++ b/main/services/telegram/telegram-controls.ts @@ -4,12 +4,13 @@ // effects stay in telegram-service-core so this module remains a leaf in the // Telegram domain DAG. -import type { - TelegramBotCommand, - TelegramInlineKeyboardMarkup, -} from "./telegram-bot-api.js"; +import type { TelegramBotCommand, TelegramInlineKeyboardMarkup } from "./telegram-bot-api.js"; import type { QueuedTelegramTurn } from "./telegram-queue.js"; import type { GenerationThinkingLevel } from "../../../renderer/shared/generation-thinking.js"; +import { + isModelHidden, + type HiddenModelsByProvider, +} from "../../../renderer/shared/model-visibility.js"; export const TELEGRAM_COMMANDS = [ { command: "start", description: "Open the Aiden operator menu" }, @@ -38,6 +39,15 @@ export interface TelegramModelChoice { thinkingLevels?: readonly GenerationThinkingLevel[]; } +export function visibleTelegramModelChoices( + models: readonly TelegramModelChoice[], + hiddenModelsByProvider: HiddenModelsByProvider | undefined, +): readonly TelegramModelChoice[] { + return models.filter( + (choice) => !isModelHidden(hiddenModelsByProvider, choice.providerId, choice.model), + ); +} + export interface TelegramControlStatus { botUsername?: string; allowedUserId?: number; @@ -95,23 +105,37 @@ export function buildMainMenu(status: TelegramControlStatus): TelegramInlineKeyb }, ], [{ text: `🧠 Thinking: ${status.thinkingLevel}`, callback_data: "menu:thinking" }], - [{ text: `${status.queueCount ? "⏳" : "⌛"} Queue: ${status.queueCount}`, callback_data: "menu:queue" }], - [{ text: `🗂 Workspace: ${truncate(status.workspaceLabel, 40)}`, callback_data: "menu:workspace" }], + [ + { + text: `${status.queueCount ? "⏳" : "⌛"} Queue: ${status.queueCount}`, + callback_data: "menu:queue", + }, + ], + [ + { + text: `🗂 Workspace: ${truncate(status.workspaceLabel, 40)}`, + callback_data: "menu:workspace", + }, + ], [ { text: "🗜 Compact", callback_data: "compact:ask" }, { text: "⚙️ Settings", callback_data: "menu:settings" }, ], ...(status.active - ? [[ - { text: "⏭ Next", callback_data: "turn:next" }, - { text: "⏹ Abort", callback_data: "turn:abort" }, - { text: "🛑 Stop all", callback_data: "turn:stop" }, - ]] + ? [ + [ + { text: "⏭ Next", callback_data: "turn:next" }, + { text: "⏹ Abort", callback_data: "turn:abort" }, + { text: "🛑 Stop all", callback_data: "turn:stop" }, + ], + ] : []), - ...(status.extensionSections ?? []).map((section) => [{ - text: section.label, - callback_data: section.callbackData, - }]), + ...(status.extensionSections ?? []).map((section) => [ + { + text: section.label, + callback_data: section.callbackData, + }, + ]), ], }; } @@ -128,10 +152,12 @@ export function buildModelMenu( const start = safePage * pageSize; const rows = models.slice(start, start + pageSize).map((choice, offset) => { const selected = choice.providerId === activeProviderId && choice.model === activeModel; - return [{ - text: `${selected ? "🟢" : "⚫"} ${truncate(`${choice.providerLabel}/${choice.modelLabel ?? choice.model}`, 48)}`, - callback_data: `model:set:${start + offset}`, - }]; + return [ + { + text: `${selected ? "🟢" : "⚫"} ${truncate(`${choice.providerLabel}/${choice.modelLabel ?? choice.model}`, 48)}`, + callback_data: `model:set:${start + offset}`, + }, + ]; }); return { text: `🤖 Choose a model\n\n${models.length ? "The selected model is used for future Telegram turns." : "No configured models are available. Add a provider in Aiden Settings."}`, @@ -139,11 +165,16 @@ export function buildModelMenu( inline_keyboard: [ [{ text: "⬆️ Main menu", callback_data: "menu:back" }], ...(totalPages > 1 - ? [[ - { text: "⬅️", callback_data: `model:page:${Math.max(0, safePage - 1)}` }, - { text: `${safePage + 1}/${totalPages}`, callback_data: "noop" }, - { text: "➡️", callback_data: `model:page:${Math.min(totalPages - 1, safePage + 1)}` }, - ]] + ? [ + [ + { text: "⬅️", callback_data: `model:page:${Math.max(0, safePage - 1)}` }, + { text: `${safePage + 1}/${totalPages}`, callback_data: "noop" }, + { + text: "➡️", + callback_data: `model:page:${Math.min(totalPages - 1, safePage + 1)}`, + }, + ], + ] : []), ...rows, ], @@ -162,10 +193,12 @@ export function buildThinkingMenu( markup: { inline_keyboard: [ [{ text: "⬆️ Main menu", callback_data: "menu:back" }], - ...levels.map((level) => [{ - text: `${level === current ? "🟢" : "⚫"} ${level}`, - callback_data: `thinking:set:${level}`, - }]), + ...levels.map((level) => [ + { + text: `${level === current ? "🟢" : "⚫"} ${level}`, + callback_data: `thinking:set:${level}`, + }, + ]), ], }, }; @@ -175,10 +208,12 @@ export function buildQueueMenu(items: readonly QueuedTelegramTurn[]): { text: string; markup: TelegramInlineKeyboardMarkup; } { - const rows = items.map((item, index) => [{ - text: `${index + 1}. ${item.lane === "priority" ? "⚡ " : ""}${truncate(item.text.replace(/\s+/gu, " "), 42)}`, - callback_data: `queue:item:${item.id}`, - }]); + const rows = items.map((item, index) => [ + { + text: `${index + 1}. ${item.lane === "priority" ? "⚡ " : ""}${truncate(item.text.replace(/\s+/gu, " "), 42)}`, + callback_data: `queue:item:${item.id}`, + }, + ]); return { text: items.length ? `⏳ Queue\n\n${items.length} prompt${items.length === 1 ? "" : "s"} waiting.` @@ -204,7 +239,10 @@ export function buildQueueItemMenu(item: QueuedTelegramTurn): { inline_keyboard: [ [{ text: "⬆️ Back", callback_data: "menu:queue" }], [ - { text: item.lane === "priority" ? "🟡 Priority" : "⚡ Make priority", callback_data: `queue:priority:${item.id}` }, + { + text: item.lane === "priority" ? "🟡 Priority" : "⚡ Make priority", + callback_data: `queue:priority:${item.id}`, + }, { text: "🗑 Delete", callback_data: `queue:delete:${item.id}` }, ], ], @@ -219,20 +257,24 @@ export function confirmationMenu( return { text: `${escapeHtml(title)}`, markup: { - inline_keyboard: [[ - { text: "✅ Confirm", callback_data: confirmData }, - { text: "❌ Cancel", callback_data: "menu:back" }, - ]], + inline_keyboard: [ + [ + { text: "✅ Confirm", callback_data: confirmData }, + { text: "❌ Cancel", callback_data: "menu:back" }, + ], + ], }, }; } -export function buildSettingsMenu(options: { - draftPreviews?: boolean; - activity?: "quiet" | "thinking" | "tools" | "verbose"; - rendering?: "rich" | "html"; - voiceMode?: "hidden" | "mirror" | "always"; -} = {}): { text: string; markup: TelegramInlineKeyboardMarkup } { +export function buildSettingsMenu( + options: { + draftPreviews?: boolean; + activity?: "quiet" | "thinking" | "tools" | "verbose"; + rendering?: "rich" | "html"; + voiceMode?: "hidden" | "mirror" | "always"; + } = {}, +): { text: string; markup: TelegramInlineKeyboardMarkup } { return { text: [ "⚙️ Telegram agent settings", @@ -245,22 +287,30 @@ export function buildSettingsMenu(options: { [{ text: "🤖 Model", callback_data: "menu:model" }], [{ text: "🧠 Thinking", callback_data: "menu:thinking" }], [{ text: "🗂 Workspace", callback_data: "menu:workspace" }], - [{ - text: `${options.draftPreviews ? "🟢" : "⚫"} Draft previews`, - callback_data: "settings:drafts:toggle", - }], - [{ - text: `🔧 Activity: ${options.activity ?? "quiet"}`, - callback_data: "settings:activity:next", - }], - [{ - text: `📝 Rendering: ${options.rendering ?? "rich"}`, - callback_data: "settings:rendering:toggle", - }], - [{ - text: `👄 Voice: ${options.voiceMode ?? "hidden"}`, - callback_data: "settings:voice:next", - }], + [ + { + text: `${options.draftPreviews ? "🟢" : "⚫"} Draft previews`, + callback_data: "settings:drafts:toggle", + }, + ], + [ + { + text: `🔧 Activity: ${options.activity ?? "quiet"}`, + callback_data: "settings:activity:next", + }, + ], + [ + { + text: `📝 Rendering: ${options.rendering ?? "rich"}`, + callback_data: "settings:rendering:toggle", + }, + ], + [ + { + text: `👄 Voice: ${options.voiceMode ?? "hidden"}`, + callback_data: "settings:voice:next", + }, + ], ], }, }; @@ -277,21 +327,30 @@ export function buildWorkspaceMenu( "", `Current: ${escapeHtml(selected?.name ?? (selectedWorkspaceId ? "Unavailable" : "Assistant only"))}`, "", - ...workspaces.map((workspace, index) => `${index + 1}. ${escapeHtml(workspace.name)}\n ${escapeHtml(workspace.folderPath)}`), - ...(workspaces.length ? ["", "You can also use /workspace <number>."] : []), + ...workspaces.map( + (workspace, index) => + `${index + 1}. ${escapeHtml(workspace.name)}\n ${escapeHtml(workspace.folderPath)}`, + ), + ...(workspaces.length + ? ["", "You can also use /workspace <number>."] + : []), "Workspace authority is captured when each prompt enters the queue.", ].join("\n"), markup: { inline_keyboard: [ [{ text: "⬆️ Main menu", callback_data: "menu:back" }], - [{ - text: `${selectedWorkspaceId === undefined ? "🟢" : "⚫"} Assistant only`, - callback_data: "workspace:set:off", - }], - ...workspaces.map((workspace, index) => [{ - text: `${workspace.id === selectedWorkspaceId ? "🟢" : "⚫"} ${truncate(workspace.name, 44)}`, - callback_data: `workspace:set:${index}`, - }]), + [ + { + text: `${selectedWorkspaceId === undefined ? "🟢" : "⚫"} Assistant only`, + callback_data: "workspace:set:off", + }, + ], + ...workspaces.map((workspace, index) => [ + { + text: `${workspace.id === selectedWorkspaceId ? "🟢" : "⚫"} ${truncate(workspace.name, 44)}`, + callback_data: `workspace:set:${index}`, + }, + ]), ], }, }; diff --git a/main/services/telegram/telegram-service-core.test.ts b/main/services/telegram/telegram-service-core.test.ts index d6cd7a23..4aab1aa4 100644 --- a/main/services/telegram/telegram-service-core.test.ts +++ b/main/services/telegram/telegram-service-core.test.ts @@ -147,11 +147,24 @@ function createMockApi(opts: MockApiOptions) { text: p.text, }; }, - async sendRichMessage(p: { chatId: number; threadId?: number; markdown: string }): Promise { + async sendRichMessage(p: { + chatId: number; + threadId?: number; + markdown: string; + }): Promise { richMessages.push(p); - return { message_id: richMessages.length, chat: { id: p.chatId, type: "private" }, date: 0, text: p.markdown }; + return { + message_id: richMessages.length, + chat: { id: p.chatId, type: "private" }, + date: 0, + text: p.markdown, + }; }, - async sendVoice(p: { chatId: number; threadId?: number; bytes: Uint8Array }): Promise { + async sendVoice(p: { + chatId: number; + threadId?: number; + bytes: Uint8Array; + }): Promise { voiceMessages.push(p); return { message_id: voiceMessages.length, chat: { id: p.chatId, type: "private" }, date: 0 }; }, @@ -279,7 +292,12 @@ function createMockTurn(opts: MockTurnOptions = {}) { const pendingStart = new EventEmitter(); let pendingOwner: TurnOwner | undefined; const createdChats: Array<{ id: string; workspaceId?: string }> = []; - const startedParams: Array<{ chatId: string; workspaceId?: string; mode?: string; content?: string }> = []; + const startedParams: Array<{ + chatId: string; + workspaceId?: string; + mode?: string; + content?: string; + }> = []; const llmClient = { beginChatTurn() { if (opts.busy) return null; @@ -294,7 +312,12 @@ function createMockTurn(opts: MockTurnOptions = {}) { }, async start( streamId: string, - _params: { chatId: string; workspaceId?: string; mode?: string; messages?: Array<{ content: string }> }, + _params: { + chatId: string; + workspaceId?: string; + mode?: string; + messages?: Array<{ content: string }>; + }, owner: TurnOwner, _options: unknown, ): Promise { @@ -441,7 +464,9 @@ interface HarnessOptions { resolveThreadWorkspace?: (threadId: number) => Promise; clearThreadTargets?: () => Promise; listModels?: () => Promise; - applyModelSelection?: (choice: import("./telegram-controls.js").TelegramModelChoice) => Promise; + applyModelSelection?: ( + choice: import("./telegram-controls.js").TelegramModelChoice, + ) => Promise; abortChat?: (chatId: string) => Promise; mediaGroupDebounceMs?: number; handleExtensionUpdate?: import("./telegram-service-core.js").TelegramServiceDeps["handleExtensionUpdate"]; @@ -950,7 +975,11 @@ test("always voice mode intercepts automatic text and falls back only when synth telegramRendering: "rich", telegramVoiceMode: "always", batches: [[makeUpdate(1, makeMessage(10, owner, "Speak"))]], - synthesizeVoice: async () => ({ bytes: new Uint8Array([1]), name: "voice.ogg", mimeType: "audio/ogg" }), + synthesizeVoice: async () => ({ + bytes: new Uint8Array([1]), + name: "voice.ogg", + mimeType: "audio/ogg", + }), }); await voiced.service.start(); await waitFor(() => voiced.api.voiceMessages.length === 1); @@ -1039,13 +1068,20 @@ test("thumbs-down reaction removes the matching queued prompt", async () => { pendingTurn: true, delayAfterFirstBatch: true, batches: [ - [makeUpdate(1, makeMessage(10, owner, "active")), makeUpdate(2, makeMessage(11, owner, "queued"))], + [ + makeUpdate(1, makeMessage(10, owner, "active")), + makeUpdate(2, makeMessage(11, owner, "queued")), + ], [reaction], ], autoStop: false, }); await service.start(); - await waitFor(() => turnMock.startCalls() === 1 && api.sentMessages.some(({ text }) => text.includes("Queued prompt removed"))); + await waitFor( + () => + turnMock.startCalls() === 1 && + api.sentMessages.some(({ text }) => text.includes("Queued prompt removed")), + ); assert.equal(service.queueSize, 0); turnMock.completePendingTurn(); service.stop(); @@ -1060,18 +1096,23 @@ test("reaction shortcut variants normalize variation selectors and promote queue autoStop: false, delayAfterFirstBatch: true, batches: [ - [makeUpdate(1, makeMessage(10, owner, "active")), makeUpdate(2, makeMessage(11, owner, "waiting"))], - [{ - update_id: 3, - message_reaction: { - chat: { id: 100, type: "private" }, - message_id: 11, - user: owner, - old_reaction: [], - new_reaction: [{ type: "emoji", emoji: "❤️" }], - date: 0, + [ + makeUpdate(1, makeMessage(10, owner, "active")), + makeUpdate(2, makeMessage(11, owner, "waiting")), + ], + [ + { + update_id: 3, + message_reaction: { + chat: { id: 100, type: "private" }, + message_id: 11, + user: owner, + old_reaction: [], + new_reaction: [{ type: "emoji", emoji: "❤️" }], + date: 0, + }, }, - }], + ], ], }); await result.service.start(); @@ -1088,7 +1129,11 @@ test("thread provisioning reports the BotFather capability prerequisite", async }); await result.service.start(); await assert.rejects(result.service.ensureThreads(), /BotFather/u); - assert.ok(result.service.getStatus().recentDiagnostics.some(({ message }) => message.includes("BotFather"))); + assert.ok( + result.service + .getStatus() + .recentDiagnostics.some(({ message }) => message.includes("BotFather")), + ); }); test("reaction updates are offered to registered extension routing first", async () => { @@ -1097,17 +1142,21 @@ test("reaction updates are offered to registered extension routing first", async const { service } = harness({ enabled: true, allowedUserId: 42, - batches: [[{ - update_id: 1, - message_reaction: { - chat: { id: 100, type: "private" }, - message_id: 10, - user: owner, - old_reaction: [], - new_reaction: [{ type: "emoji", emoji: "👍" }], - date: 0, - }, - }]], + batches: [ + [ + { + update_id: 1, + message_reaction: { + chat: { id: 100, type: "private" }, + message_id: 10, + user: owner, + old_reaction: [], + new_reaction: [{ type: "emoji", emoji: "👍" }], + date: 0, + }, + }, + ], + ], handleExtensionUpdate: async () => { extensionCalls += 1; return true; @@ -1126,7 +1175,7 @@ test("thread messages capture their durable workspace and replies stay in the th allowedUserId: 42, telegramRendering: "rich", workspaces: [{ id: "project", name: "Project", folderPath: "/tmp/project" }], - resolveThreadWorkspace: async (threadId) => threadId === 77 ? "project" : undefined, + resolveThreadWorkspace: async (threadId) => (threadId === 77 ? "project" : undefined), batches: [[makeUpdate(1, message)]], }); await service.start(); @@ -1140,7 +1189,9 @@ test("pairing reset clears queued work and durable thread bindings", async () => const { service, config } = harness({ enabled: true, allowedUserId: 42, - clearThreadTargets: async () => { cleared += 1; }, + clearThreadTargets: async () => { + cleared += 1; + }, batches: [], autoStop: false, }); @@ -1171,8 +1222,14 @@ test("offset persistence failure is diagnostic and polling remains live", async await originalPersist(offset); }; await service.start(); - await waitFor(() => api.sentMessages.filter(({ text }) => text.includes("Aiden Telegram Agent")).length === 2); - assert.ok(service.getStatus().recentDiagnostics.some(({ message }) => message.includes("disk unavailable"))); + await waitFor( + () => api.sentMessages.filter(({ text }) => text.includes("Aiden Telegram Agent")).length === 2, + ); + assert.ok( + service + .getStatus() + .recentDiagnostics.some(({ message }) => message.includes("disk unavailable")), + ); }); test("active-run model switching persists first, aborts safely, and queues a continuation", async () => { @@ -1195,13 +1252,22 @@ test("active-run model switching persists first, aborts safely, and queues a con autoStop: false, batches: [ [makeUpdate(1, makeMessage(10, owner, "long task"))], - [{ - update_id: 2, - callback_query: { id: "model", from: owner, message: controlMessage, data: "model:set:0" }, - }], + [ + { + update_id: 2, + callback_query: { + id: "model", + from: owner, + message: controlMessage, + data: "model:set:0", + }, + }, + ], ], listModels: async () => [choice], - applyModelSelection: async (selected) => { applied.push(selected); }, + applyModelSelection: async (selected) => { + applied.push(selected); + }, abortChat: async () => { aborts += 1; finishActive(); @@ -1211,6 +1277,39 @@ test("active-run model switching persists first, aborts safely, and queues a con await result.service.start(); await waitFor(() => applied.length === 1 && aborts === 1 && result.turnMock.startCalls() === 2); assert.deepEqual(applied, [choice]); - assert.match(result.turnMock.startedParams()[1]?.content ?? "", /Continue the interrupted task using Next\/next-model/); + assert.match( + result.turnMock.startedParams()[1]?.content ?? "", + /Continue the interrupted task using Next\/next-model/, + ); + result.service.stop(); +}); + +test("a stale model callback cannot select a model omitted by the current visible catalog", async () => { + const owner = person(42); + const applied: unknown[] = []; + const result = harness({ + enabled: true, + allowedUserId: 42, + batches: [ + [ + { + update_id: 1, + callback_query: { + id: "hidden-model", + from: owner, + message: makeMessage(20, BOT, "model menu"), + data: "model:set:0", + }, + }, + ], + ], + listModels: async () => [], + applyModelSelection: async (selected) => { + applied.push(selected); + }, + }); + await result.service.start(); + await waitFor(() => result.api.answerCallbackQueryCalls() > 0); + assert.deepEqual(applied, []); result.service.stop(); }); diff --git a/main/services/telegram/telegram-service.ts b/main/services/telegram/telegram-service.ts index 2f8fb07b..00df1ecd 100644 --- a/main/services/telegram/telegram-service.ts +++ b/main/services/telegram/telegram-service.ts @@ -33,7 +33,7 @@ import { mkdir, readFile, readdir, realpath, stat, unlink, writeFile } from "nod import path from "node:path"; import { randomUUID } from "node:crypto"; import type { TelegramModelChoice } from "./telegram-controls.js"; -import { TELEGRAM_COMMANDS } from "./telegram-controls.js"; +import { TELEGRAM_COMMANDS, visibleTelegramModelChoices } from "./telegram-controls.js"; import { getTelegramExtensions } from "./telegram-extension-registry.js"; import { DEFAULT_TELEGRAM_PROFILE, @@ -47,8 +47,13 @@ import { } from "./telegram-profile-config.js"; import { createTelegramThreadStore } from "./telegram-thread-store.js"; import { createTelegramOwnershipLease } from "./telegram-ownership.js"; -import { chunkForTelegram, chunkRichMarkdown, markdownToTelegramHtml } from "./telegram-markdown.js"; +import { + chunkForTelegram, + chunkRichMarkdown, + markdownToTelegramHtml, +} from "./telegram-markdown.js"; import { registerTelegramDirectRuntime } from "./telegram-direct-runtime.js"; +import { firstVisibleModelForProvider } from "../../../renderer/shared/model-visibility.js"; export const TELEGRAM_PROVIDER_ID = "telegram"; let profileSettingsMutation = Promise.resolve(); @@ -57,7 +62,10 @@ async function getProfileSettings(profile: string) { return projectTelegramProfile(await configStore.getSettings(), profile); } -async function setProfileSettings(profile: string, patch: Partial) { +async function setProfileSettings( + profile: string, + patch: Partial, +) { let result: import("../types.js").AppSettings | undefined; const operation = profileSettingsMutation.then(async () => { const current = await configStore.getSettings(); @@ -66,7 +74,10 @@ async function setProfileSettings(profile: string, patch: Partial undefined, () => undefined); + profileSettingsMutation = operation.then( + () => undefined, + () => undefined, + ); await operation; return result!; } @@ -85,7 +96,11 @@ async function resolveProvider(profile = DEFAULT_TELEGRAM_PROFILE): Promise<{ (await configStore.getProvider(providerId)); if (!provider) return null; const model = - settings.telegramModel ?? settings.lastModel ?? provider.defaultModel ?? provider.models[0]; + settings.telegramModel ?? + firstVisibleModelForProvider(settings.hiddenModelsByProvider, providerId, provider.models, [ + settings.lastProviderId === providerId ? settings.lastModel : undefined, + provider.defaultModel, + ]); if (!model) return null; return { providerId, model, provider }; } @@ -101,10 +116,11 @@ async function resolveWorkspace(workspaceId?: string): Promise { - const [builtin, custom, codex] = await Promise.all([ + const [builtin, custom, codex, settings] = await Promise.all([ providerRegistry.listBuiltinProviders(), listProvidersWithLegacyPiCredentialMigration(), providerRegistry.codex.snapshot().catch(() => null), + configStore.getSettings(), ]); const byId = new Map(); for (const provider of [...builtin, ...custom]) { @@ -118,15 +134,18 @@ async function listTelegramModels(): Promise { baseUrl: "", models: codex.models.map((model) => model.id), modelMetadata: Object.fromEntries( - codex.models.map((model) => [model.id, { - source: "provider" as const, - name: model.name, - type: "llm" as const, - vision: model.vision, - reasoning: model.reasoning, - thinkingLevels: model.thinkingLevels, - contextLength: model.contextWindow, - }]), + codex.models.map((model) => [ + model.id, + { + source: "provider" as const, + name: model.name, + type: "llm" as const, + vision: model.vision, + reasoning: model.reasoning, + thinkingLevels: model.thinkingLevels, + contextLength: model.contextWindow, + }, + ]), ), defaultModel: codex.models[0]?.id, needsKey: true, @@ -134,7 +153,7 @@ async function listTelegramModels(): Promise { isBuiltin: true, }); } - return [...byId.values()].flatMap((provider) => + const models = [...byId.values()].flatMap((provider) => provider.models.map((model) => { const metadata = provider.modelMetadata?.[model]; return { @@ -147,28 +166,44 @@ async function listTelegramModels(): Promise { }; }), ); + return visibleTelegramModelChoices(models, settings.hiddenModelsByProvider); } async function readWorkspaceAttachment(workspaceId: string | undefined, requestedPath: string) { if (!workspaceId) throw new Error("Choose a folder workspace before attaching local files."); const workspace = await configStore.getWorkspace(workspaceId); - if (!isTelegramFolderWorkspace(workspace) || !workspace.folderPath) throw new Error("The selected workspace is unavailable."); + if (!isTelegramFolderWorkspace(workspace) || !workspace.folderPath) + throw new Error("The selected workspace is unavailable."); const folderPath = workspace.folderPath; const root = await realpath(folderPath); - const candidate = path.isAbsolute(requestedPath) ? requestedPath : path.resolve(root, requestedPath); + const candidate = path.isAbsolute(requestedPath) + ? requestedPath + : path.resolve(root, requestedPath); const resolved = await realpath(candidate); const relative = path.relative(root, resolved); - if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error("Attachments must stay inside the selected workspace."); + if (relative.startsWith("..") || path.isAbsolute(relative)) + throw new Error("Attachments must stay inside the selected workspace."); const metadata = await stat(resolved); if (!metadata.isFile()) throw new Error("The attachment is not a regular file."); if (metadata.size > 50 * 1024 * 1024) throw new Error("Telegram documents are limited to 50 MB."); const extension = path.extname(resolved).toLowerCase(); - const mimeType = ({ - ".pdf": "application/pdf", ".json": "application/json", ".txt": "text/plain", - ".md": "text/markdown", ".png": "image/png", ".jpg": "image/jpeg", - ".jpeg": "image/jpeg", ".webp": "image/webp", ".zip": "application/zip", - ".csv": "text/csv", ".mp4": "video/mp4", ".mp3": "audio/mpeg", - } as Record)[extension] ?? "application/octet-stream"; + const mimeType = + ( + { + ".pdf": "application/pdf", + ".json": "application/json", + ".txt": "text/plain", + ".md": "text/markdown", + ".png": "image/png", + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".webp": "image/webp", + ".zip": "application/zip", + ".csv": "text/csv", + ".mp4": "video/mp4", + ".mp3": "audio/mpeg", + } as Record + )[extension] ?? "application/octet-stream"; return { bytes: await readFile(resolved), name: path.basename(resolved), mimeType }; } @@ -182,7 +217,12 @@ function validateVoiceResult( throw new Error("Telegram voice messages are limited to 20 MB."); } const name = voice.name ?? "aiden-voice.ogg"; - if (!/\.(?:ogg|opus)$/iu.test(name) || (voice.mimeType !== undefined && voice.mimeType !== "audio/ogg" && voice.mimeType !== "audio/opus")) { + if ( + !/\.(?:ogg|opus)$/iu.test(name) || + (voice.mimeType !== undefined && + voice.mimeType !== "audio/ogg" && + voice.mimeType !== "audio/opus") + ) { throw new Error("Telegram-native voice providers must return OGG/Opus audio."); } return { ...voice, name }; @@ -258,15 +298,24 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { const settings = await getProfileSettings(profile); if (!settings.telegramThreadedMode) return; const workspaces = (await configStore.listWorkspaces()).filter(isTelegramFolderWorkspace); - const removed = await threadStore.retainWorkspaces(new Set(workspaces.map(({ id }) => id)), chatId); + const removed = await threadStore.retainWorkspaces( + new Set(workspaces.map(({ id }) => id)), + chatId, + ); for (const target of removed) { await api.deleteForumTopic(target.chatId, target.threadId).catch((cause) => { - logger.warn("telegram", `[${profile}] Could not remove stale Telegram thread ${target.threadId}: ${cause instanceof Error ? cause.message : String(cause)}`); + logger.warn( + "telegram", + `[${profile}] Could not remove stale Telegram thread ${target.threadId}: ${cause instanceof Error ? cause.message : String(cause)}`, + ); }); } for (const workspace of workspaces) { if (await threadStore.findWorkspace(workspace.id)) continue; - const topic = await api.createForumTopic(chatId, `Aiden · ${workspace.name}`.slice(0, 128)); + const topic = await api.createForumTopic( + chatId, + `Aiden · ${workspace.name}`.slice(0, 128), + ); await threadStore.upsert({ threadId: topic.message_thread_id, chatId, @@ -281,14 +330,20 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { }); } }); - threadProvisioning = operation.then(() => undefined, () => undefined); + threadProvisioning = operation.then( + () => undefined, + () => undefined, + ); return operation; }, clearThreadTargets: async () => { const targets = await threadStore.list(); for (const target of targets) { await api.deleteForumTopic(target.chatId, target.threadId).catch((cause) => { - logger.warn("telegram", `[${profile}] Could not remove Telegram thread ${target.threadId}: ${cause instanceof Error ? cause.message : String(cause)}`); + logger.warn( + "telegram", + `[${profile}] Could not remove Telegram thread ${target.threadId}: ${cause instanceof Error ? cause.message : String(cause)}`, + ); }); } await threadStore.clear(); @@ -302,16 +357,18 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { openSession: (id) => piCompactionSessionStore.openChat(id), resolveProvider: () => resolveProvider(profile), resolveRuntime: resolveModelRuntime, - resolveThinkingLevel: async () => (await getProfileSettings(profile)).telegramThinkingLevel, + resolveThinkingLevel: async () => + (await getProfileSettings(profile)).telegramThinkingLevel, }, chatId, ), transcribeAudio: transcribe, storeInboundFile: async ({ bytes, name, workspaceId }) => { const workspace = workspaceId ? await configStore.getWorkspace(workspaceId) : undefined; - const workspaceRoot = isTelegramFolderWorkspace(workspace) && workspace.folderPath - ? await realpath(workspace.folderPath) - : undefined; + const workspaceRoot = + isTelegramFolderWorkspace(workspace) && workspace.folderPath + ? await realpath(workspace.folderPath) + : undefined; const root = workspaceRoot ? path.join(workspaceRoot, ".aiden", "telegram-inbox", profile) : path.join(app.getPath("userData"), "telegram-inbox", profile); @@ -323,7 +380,11 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { const metadata = await stat(candidate).catch(() => undefined); if (metadata && metadata.mtimeMs < cutoff) await unlink(candidate).catch(() => undefined); } - const safeName = path.basename(name).replace(/[^A-Za-z0-9._-]+/gu, "_").slice(-120) || "telegram-file"; + const safeName = + path + .basename(name) + .replace(/[^A-Za-z0-9._-]+/gu, "_") + .slice(-120) || "telegram-file"; const destination = path.join(root, `${randomUUID()}-${safeName}`); await writeFile(destination, bytes, { flag: "wx", mode: 0o600 }); return destination; @@ -345,7 +406,10 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { }); if (result) return validateVoiceResult(result); } catch (cause) { - logger.warn("telegram", `[${profile}] Voice provider ${extension.id} failed: ${cause instanceof Error ? cause.message : String(cause)}`); + logger.warn( + "telegram", + `[${profile}] Voice provider ${extension.id} failed: ${cause instanceof Error ? cause.message : String(cause)}`, + ); } } return undefined; @@ -354,7 +418,7 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { const rows: string[] = []; const sections: Array<{ label: string; callbackData: string }> = []; for (const extension of getTelegramExtensions()) { - if (extension.statusRows) rows.push(...await extension.statusRows()); + if (extension.statusRows) rows.push(...(await extension.statusRows())); for (const section of extension.sections ?? []) { sections.push({ label: section.label, callbackData: section.callbackData }); } @@ -363,13 +427,19 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { }, handleExtensionCallback: async (data, context) => { const match = /^ext:([a-z][a-z0-9_-]{0,31}):/u.exec(data); - const extension = match ? getTelegramExtensions().find(({ id }) => id === match[1]) : undefined; - if (!extension?.handleCallback) throw new Error("This extension control is no longer available."); + const extension = match + ? getTelegramExtensions().find(({ id }) => id === match[1]) + : undefined; + if (!extension?.handleCallback) + throw new Error("This extension control is no longer available."); return extension.handleCallback(data, { profile, ...context }); }, handleExtensionUpdate: async (update, context) => { for (const extension of getTelegramExtensions()) { - if (extension.handleUpdate && await extension.handleUpdate(update, { profile, ...context })) { + if ( + extension.handleUpdate && + (await extension.handleUpdate(update, { profile, ...context })) + ) { return true; } } @@ -379,7 +449,10 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { let transformed = content; for (const extension of getTelegramExtensions()) { if (extension.transformInbound) { - transformed = await extension.transformInbound(transformed, message, { profile, ...context }); + transformed = await extension.transformInbound(transformed, message, { + profile, + ...context, + }); } } return transformed; @@ -399,12 +472,17 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { (extension.commands ?? []).flatMap((command) => { if (used.has(command.name)) return []; used.add(command.name); - return [{ - command: command.name, - description: command.description.replace(/\s+/gu, " ").slice(0, 256), - handle: (argument: string, message: Parameters[0]["message"], context: Omit[0]["context"], "profile">) => - command.handler({ argument, message, context: { profile, ...context } }), - }]; + return [ + { + command: command.name, + description: command.description.replace(/\s+/gu, " ").slice(0, 256), + handle: ( + argument: string, + message: Parameters[0]["message"], + context: Omit[0]["context"], "profile">, + ) => command.handler({ argument, message, context: { profile, ...context } }), + }, + ]; }), ); if (!workspaceId) return extensionCommands; @@ -417,16 +495,24 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { .slice(0, 32); if (!command || !/^[a-z]/u.test(command) || used.has(command)) return []; used.add(command); - return [{ - command, - description: (skill.description || `Run ${skill.name}`).replace(/\s+/gu, " ").slice(0, 256), - expand: (argument: string) => formatSkillInvocation({ - name: skill.name, - description: skill.description, - content: skill.instructions, - filePath: skill.path ?? "/Aiden/Configured Skills/SKILL.md", - }, argument), - }]; + return [ + { + command, + description: (skill.description || `Run ${skill.name}`) + .replace(/\s+/gu, " ") + .slice(0, 256), + expand: (argument: string) => + formatSkillInvocation( + { + name: skill.name, + description: skill.description, + content: skill.instructions, + filePath: skill.path ?? "/Aiden/Configured Skills/SKILL.md", + }, + argument, + ), + }, + ]; }); return [...extensionCommands, ...skillCommands]; }, @@ -467,11 +553,18 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { if (chatId === undefined) throw new Error(`Telegram profile ${profile} is not paired.`); if (thread === undefined) return { chatId, workspaceId: settings.telegramWorkspaceId }; const targets = await threadStore.list(); - const target = typeof thread === "number" - ? targets.find((candidate) => candidate.threadId === thread) - : targets.find((candidate) => candidate.name.toLowerCase() === thread.trim().toLowerCase()); - if (!target || target.chatId !== chatId) throw new Error(`Unknown live Telegram thread target: ${String(thread)}`); - return { chatId, threadId: target.threadId, workspaceId: target.workspaceId, name: target.name }; + const target = + typeof thread === "number" + ? targets.find((candidate) => candidate.threadId === thread) + : targets.find((candidate) => candidate.name.toLowerCase() === thread.trim().toLowerCase()); + if (!target || target.chatId !== chatId) + throw new Error(`Unknown live Telegram thread target: ${String(thread)}`); + return { + chatId, + threadId: target.threadId, + workspaceId: target.workspaceId, + name: target.name, + }; } return Object.assign(core, { @@ -485,21 +578,27 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { const richChunks = chunkRichMarkdown(input.text); for (let index = 0; index < richChunks.length; index += 1) { try { - sent.push(await api.sendRichMessage({ - chatId: target.chatId, - threadId: target.threadId, - markdown: richChunks[index]!, - })); - } catch (cause) { - if (!(cause instanceof TelegramApiError) || cause.code !== 400) throw cause; - for (const text of chunkForTelegram(markdownToTelegramHtml(richChunks.slice(index).join("\n\n")))) { - sent.push(await api.sendMessage({ + sent.push( + await api.sendRichMessage({ chatId: target.chatId, threadId: target.threadId, - text, - parseMode: "HTML", - disablePreview: true, - })); + markdown: richChunks[index]!, + }), + ); + } catch (cause) { + if (!(cause instanceof TelegramApiError) || cause.code !== 400) throw cause; + for (const text of chunkForTelegram( + markdownToTelegramHtml(richChunks.slice(index).join("\n\n")), + )) { + sent.push( + await api.sendMessage({ + chatId: target.chatId, + threadId: target.threadId, + text, + parseMode: "HTML", + disablePreview: true, + }), + ); } break; } @@ -508,22 +607,38 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { } const sent = []; for (const text of chunkForTelegram(markdownToTelegramHtml(input.text))) { - sent.push(await api.sendMessage({ - chatId: target.chatId, - threadId: target.threadId, - text, - parseMode: "HTML", - disablePreview: true, - })); + sent.push( + await api.sendMessage({ + chatId: target.chatId, + threadId: target.threadId, + text, + parseMode: "HTML", + disablePreview: true, + }), + ); } return sent; }, - async sendDirectAttachment(input: { path: string; caption?: string; thread?: string | number }) { + async sendDirectAttachment(input: { + path: string; + caption?: string; + thread?: string | number; + }) { const target = await resolveDirectTarget(input.thread); const file = await readWorkspaceAttachment(target.workspaceId, input.path); - return api.sendDocument({ chatId: target.chatId, threadId: target.threadId, ...file, caption: input.caption }); + return api.sendDocument({ + chatId: target.chatId, + threadId: target.threadId, + ...file, + caption: input.caption, + }); }, - async sendDirectVoice(input: { text: string; lang?: string; rate?: string; thread?: string | number }) { + async sendDirectVoice(input: { + text: string; + lang?: string; + rate?: string; + thread?: string | number; + }) { const target = await resolveDirectTarget(input.thread); for (const extension of getTelegramExtensions()) { try { @@ -538,9 +653,17 @@ export function createTelegramService(profileName = DEFAULT_TELEGRAM_PROFILE) { workspaceId: target.workspaceId, }, }); - if (voice) return api.sendVoice({ chatId: target.chatId, threadId: target.threadId, ...validateVoiceResult(voice) }); + if (voice) + return api.sendVoice({ + chatId: target.chatId, + threadId: target.threadId, + ...validateVoiceResult(voice), + }); } catch (cause) { - logger.warn("telegram", `[${profile}] Voice provider ${extension.id} failed: ${cause instanceof Error ? cause.message : String(cause)}`); + logger.warn( + "telegram", + `[${profile}] Voice provider ${extension.id} failed: ${cause instanceof Error ? cause.message : String(cause)}`, + ); } } throw new Error("No Telegram voice synthesis provider is registered."); @@ -567,7 +690,8 @@ export function createTelegramProfileManager() { const settings = await configStore.getSettings(); const profiles = listTelegramProfileNames(settings); const requested = settings.telegramActiveProfile; - activeProfile = requested && profiles.includes(requested) ? requested : DEFAULT_TELEGRAM_PROFILE; + activeProfile = + requested && profiles.includes(requested) ? requested : DEFAULT_TELEGRAM_PROFILE; return profiles; } @@ -597,12 +721,14 @@ export function createTelegramProfileManager() { async listProfiles() { const settings = await configStore.getSettings(); const profiles = listTelegramProfileNames(settings); - return Promise.all(profiles.map(async (profile) => ({ - name: profile, - settings: telegramProfileFromSettings(settings, profile), - hasToken: await secrets.hasKey(telegramProfileTokenKey(profile)), - status: serviceFor(profile).getStatus(), - }))); + return Promise.all( + profiles.map(async (profile) => ({ + name: profile, + settings: telegramProfileFromSettings(settings, profile), + hasToken: await secrets.hasKey(telegramProfileTokenKey(profile)), + status: serviceFor(profile).getStatus(), + })), + ); }, async selectProfile(value: string): Promise { const profile = normalizeTelegramProfileName(value); @@ -615,7 +741,8 @@ export function createTelegramProfileManager() { async createProfile(value: string): Promise { const profile = normalizeTelegramProfileName(value); const settings = await configStore.getSettings(); - if (listTelegramProfileNames(settings).includes(profile)) throw new Error(`Telegram profile already exists: ${profile}`); + if (listTelegramProfileNames(settings).includes(profile)) + throw new Error(`Telegram profile already exists: ${profile}`); if (Object.keys(settings.telegramProfiles ?? {}).length >= 16) { throw new Error("Aiden supports up to 16 named Telegram profiles."); } @@ -629,7 +756,8 @@ export function createTelegramProfileManager() { }, async deleteProfile(value: string): Promise { const profile = normalizeTelegramProfileName(value); - if (profile === DEFAULT_TELEGRAM_PROFILE) throw new Error("The default Telegram profile cannot be deleted."); + if (profile === DEFAULT_TELEGRAM_PROFILE) + throw new Error("The default Telegram profile cannot be deleted."); const service = services.get(profile); await service?.stopAndSettle(); await service?.resetPairing(); @@ -639,7 +767,10 @@ export function createTelegramProfileManager() { const profiles = { ...(settings.telegramProfiles ?? {}) }; delete profiles[profile]; activeProfile = DEFAULT_TELEGRAM_PROFILE; - await configStore.setSettings({ telegramProfiles: profiles, telegramActiveProfile: activeProfile }); + await configStore.setSettings({ + telegramProfiles: profiles, + telegramActiveProfile: activeProfile, + }); }, async getActiveSettings() { await refreshProfiles(); @@ -671,15 +802,32 @@ export function createTelegramProfileManager() { return serviceFor(targetProfile).listTargets(); }, async sendDirectMessage(input: { profile?: string; thread?: string | number; text: string }) { - const targetProfile = input.profile ? normalizeTelegramProfileName(input.profile) : activeProfile; + const targetProfile = input.profile + ? normalizeTelegramProfileName(input.profile) + : activeProfile; return serviceFor(targetProfile).sendDirectMessage(input); }, - async sendDirectAttachment(input: { profile?: string; thread?: string | number; path: string; caption?: string }) { - const targetProfile = input.profile ? normalizeTelegramProfileName(input.profile) : activeProfile; + async sendDirectAttachment(input: { + profile?: string; + thread?: string | number; + path: string; + caption?: string; + }) { + const targetProfile = input.profile + ? normalizeTelegramProfileName(input.profile) + : activeProfile; return serviceFor(targetProfile).sendDirectAttachment(input); }, - async sendDirectVoice(input: { profile?: string; thread?: string | number; text: string; lang?: string; rate?: string }) { - const targetProfile = input.profile ? normalizeTelegramProfileName(input.profile) : activeProfile; + async sendDirectVoice(input: { + profile?: string; + thread?: string | number; + text: string; + lang?: string; + rate?: string; + }) { + const targetProfile = input.profile + ? normalizeTelegramProfileName(input.profile) + : activeProfile; return serviceFor(targetProfile).sendDirectVoice(input); }, }; diff --git a/main/services/types.ts b/main/services/types.ts index 005b09eb..753eda74 100644 --- a/main/services/types.ts +++ b/main/services/types.ts @@ -11,6 +11,7 @@ import type { SubagentMessageReferenceV1 } from "../../renderer/shared/subagent- import type { SkillProvenanceV1 } from "../../renderer/shared/slash-commands.js"; import type { ProviderFailureV1 } from "../../renderer/shared/provider-failure.js"; import type { AssistantMessage } from "@earendil-works/pi-ai"; +import type { ProviderArtwork } from "../../renderer/shared/provider-artwork.js"; export type ProviderKind = "openai" | "anthropic"; @@ -40,6 +41,8 @@ export interface StoredProvider { id: string; kind: ProviderKind; label: string; + /** Optional normalized artwork for custom providers. Never contains a filesystem path. */ + artwork?: ProviderArtwork; /** Base URL including the version segment, e.g. https://api.openai.com/v1 */ baseUrl: string; /** Suggested / cached model ids for the picker. */ @@ -215,7 +218,11 @@ export interface ModelRanking { } export type ModelMetadataSource = - "local" | "provider" | "artificial-analysis" | "models-dev" | "fallback"; + | "local" + | "provider" + | "artificial-analysis" + | "models-dev" + | "fallback"; /** Normalized model metadata after applying local and bundled-source precedence. */ export interface ModelInfo { @@ -394,8 +401,7 @@ export interface DiscoveredSkill { export type VoiceProvider = "openai" | "gemini" | "local"; -export type ChatTitleProviderId = - "automatic" | "apple-foundation-models" | "chat-model"; +export type ChatTitleProviderId = "automatic" | "apple-foundation-models" | "chat-model"; export type FoundationModelsConnectionState = | "ready" @@ -458,6 +464,8 @@ export interface AssistantConfigSnapshot { export interface AppSettings { lastProviderId?: string; lastModel?: string; + /** Presentation-only chat models hidden from Mac and paired mobile selection UI. */ + hiddenModelsByProvider?: Record; exaEnabled?: boolean; voiceProvider?: VoiceProvider; voiceModel?: string; diff --git a/package.json b/package.json index 1fae1bf8..dfe39bda 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "test:artificial-analysis": "tsx --test main/services/artificial-analysis-action-core.test.ts main/services/artificial-analysis-cache.test.ts main/services/artificial-analysis-runtime-core.test.ts main/services/artificial-analysis-catalog-core.test.ts main/services/provider-model-info-core.test.ts renderer/lib/artificial-analysis-query-state.test.ts renderer/lib/model-data-control.test.ts renderer/lib/settings-section.test.ts", "test:model-pad": "tsx --test renderer/lib/google-provider-migration.test.ts renderer/lib/model-pad-layout.test.ts renderer/lib/model-picker-data.test.ts renderer/lib/pi-provider-display.test.ts", "test:command-system": "tsx --test main/services/native-menu-command-contract.test.ts main/services/renderer-readiness-core.test.ts main/services/shortcut-registration-core.test.ts main/services/shortcut-transaction-core.test.ts main/services/superseding-task-core.test.ts renderer/lib/appearance-intent.test.ts renderer/lib/command-palette-contract.test.ts renderer/lib/command-palette-recent.test.ts renderer/lib/command-system-core.test.ts renderer/lib/shortcut-settings-contract.test.ts renderer/lib/use-model-selection.test.ts renderer/shared/keybindings.test.ts", - "test:slash-commands": "tsx --test main/handlers/attachments.contract.test.ts main/handlers/chat.parse.test.ts main/handlers/chat-create-params.test.ts main/handlers/chat-session-params.test.ts main/handlers/worktree-create-params.test.ts main/services/chat-workspace-authority.test.ts main/handlers/chats.append.contract.test.ts main/services/attachment-contract.test.ts main/services/attachments.test.ts main/services/chat-append-commit.test.ts main/services/chat-export.test.ts main/services/chat-message-contract.test.ts main/services/chat-session-copy.test.ts main/services/chat-store-core.test.ts main/services/chat-turn-admission.test.ts main/services/generation-messages.test.ts main/services/provider-auth-flow-core.test.ts main/services/provider-list-core.test.ts main/services/scheduled-chat-creation.test.ts main/services/skill-invocation-flow.integration.test.ts main/services/skill-invocation-turn.test.ts main/services/skill-registry-core.test.ts main/services/skill-registry.test.ts main/services/skill-tools.test.ts main/services/skills-discovery.test.ts renderer/components/assistant/assistant-ui.test.tsx renderer/components/assistant/use-assistant-chat.test.ts renderer/components/composer.test.tsx renderer/components/message-bubble.test.tsx renderer/lib/chat-copy-view.test.ts renderer/lib/codex-provider-cache.test.ts renderer/lib/computer-use-control.test.ts renderer/lib/ipc-stream.test.ts renderer/lib/skill-catalog-workspace.test.ts renderer/lib/slash-command-actions.test.ts renderer/lib/slash-command-core.test.ts renderer/lib/slash-command-performance.test.ts renderer/main/chat-transition.test.tsx renderer/shared/attachment-contract.test.ts renderer/shared/chat-message-contract.test.ts renderer/shared/slash-commands.test.ts", + "test:slash-commands": "tsx --test main/handlers/attachments.contract.test.ts main/handlers/chat.parse.test.ts main/handlers/chat-create-params.test.ts main/handlers/chat-session-params.test.ts main/handlers/worktree-create-params.test.ts main/services/chat-workspace-authority.test.ts main/handlers/chats.append.contract.test.ts main/services/attachment-contract.test.ts main/services/attachments.test.ts main/services/chat-append-commit.test.ts main/services/chat-export.test.ts main/services/chat-message-contract.test.ts main/services/chat-session-copy.test.ts main/services/chat-store-core.test.ts main/services/chat-turn-admission.test.ts main/services/generation-messages.test.ts main/services/provider-auth-flow-core.test.ts main/services/provider-list-core.test.ts main/services/provider-artwork-core.test.ts main/services/scheduled-chat-creation.test.ts main/services/skill-invocation-flow.integration.test.ts main/services/skill-invocation-turn.test.ts main/services/skill-registry-core.test.ts main/services/skill-registry.test.ts main/services/skill-tools.test.ts main/services/skills-discovery.test.ts renderer/components/assistant/assistant-ui.test.tsx renderer/components/assistant/use-assistant-chat.test.ts renderer/components/composer.test.tsx renderer/components/message-bubble.test.tsx renderer/lib/chat-copy-view.test.ts renderer/lib/codex-provider-cache.test.ts renderer/lib/computer-use-control.test.ts renderer/lib/ipc-stream.test.ts renderer/lib/skill-catalog-workspace.test.ts renderer/lib/slash-command-actions.test.ts renderer/lib/slash-command-core.test.ts renderer/lib/slash-command-performance.test.ts renderer/main/chat-transition.test.tsx renderer/shared/attachment-contract.test.ts renderer/shared/chat-message-contract.test.ts renderer/shared/slash-commands.test.ts", "test:google-provider": "tsx --test main/services/anthropic-provider.test.ts main/services/google-provider.test.ts main/services/model-runtime-core.test.ts main/services/models.test.ts main/services/provider-config-migration-core.test.ts main/services/chat-store-core.test.ts main/services/schedule-store.test.ts renderer/lib/google-provider-migration.test.ts", "test:config-recovery": "tsx --test main/services/secret-map-core.test.ts main/services/provider-credential-rotation-core.test.ts main/services/legacy-pi-credential-migration-core.test.ts main/services/mcp-credential-cleanup-core.test.ts main/services/mcp-oauth-operation.test.ts main/services/mcp-oauth-session.test.ts main/services/mcp-oauth-store-core.test.ts", "pretest:subagents": "npm run build:worktree-remover && npm run build:subagent-run-store && node scripts/build-subagent-run-store.mjs --test && npm run build:subagent-file-mutator && node scripts/build-subagent-file-mutator.mjs --test && npm run build:subagent-shell-runner && node scripts/build-subagent-shell-runner.mjs --test && npm run test:subagents:inventory && npm run test:subagents:workspace-write && npm run test:subagents:phase5a && npm run test:subagents:phase5b && npm run test:subagents:phase5c && npm run test:subagents:phase5d && npm run test:subagents:phase5e && npm run test:subagents:phase6a && npm run test:subagents:phase6b && npm run test:subagents:phase7a && npm run test:subagents:soak:contracts", @@ -80,8 +80,8 @@ "test:provider-failure": "tsx --test main/services/provider-failure.test.ts", "test:compaction": "tsx --test main/services/pi-runtime-events.test.ts main/services/pi-agent-runtime-harness.test.ts main/services/pi-compaction-core.test.ts main/services/pi-runtime-effect-store.test.ts main/services/generation-context.test.ts main/services/subagents/agent-compatibility.test.ts renderer/lib/ipc-stream.test.ts renderer/main/chat-transition.test.tsx", "test:telegram": "tsx --test main/services/telegram/telegram-controls.test.ts main/services/telegram/telegram-inbound.test.ts main/services/telegram/telegram-outbound.test.ts main/services/telegram/telegram-queue.test.ts main/services/telegram/telegram-markdown.test.ts main/services/telegram/telegram-bot-api.test.ts main/services/telegram/telegram-turn.test.ts main/services/telegram/telegram-service-core.test.ts main/services/telegram/telegram-workspace-core.test.ts main/services/telegram/telegram-activity.test.ts main/services/telegram/telegram-profile-config.test.ts main/services/telegram/telegram-extension-registry.test.ts main/services/telegram/telegram-thread-store.test.ts main/services/telegram/telegram-ownership.test.ts main/services/telegram/telegram-agent-tools.test.ts renderer/lib/telegram-workspace-options.test.ts", - "test": "tsx --test main/handlers/assistant-parse.test.ts main/services/assistant/system-prompt.test.ts main/services/chat-activity-core.test.ts main/services/chat-generation-start.test.ts main/services/chat-title-policy.test.ts main/services/chat-title-routing.test.ts main/services/chat-store-core.test.ts main/services/codex-provider.test.ts main/services/coding-tools.test.ts main/services/config-store-core.test.ts main/services/computer-use/computer-use-foundation.test.ts main/services/computer-use/computer-use-tool.test.ts main/services/computer-use/generation-gate.test.ts main/services/computer-use/safety.test.ts main/services/computer-use/settings-core.test.ts main/services/computer-use/status-core.test.ts main/services/data-store.test.ts main/services/data-store.resilience.test.ts main/services/terminal.test.ts main/services/terminal-history.test.ts main/services/aiden-config-dir.test.ts main/services/portable-config-core.test.ts main/services/portable-config-core.roundtrip.test.ts main/services/portable-config-watch-core.test.ts main/services/secret-map-core.test.ts main/services/dev-log.test.ts main/services/process-diagnostics.test.ts main/services/dictation-coordinator.test.ts main/services/dictation-paste.test.ts main/services/foundation-models-connection.test.ts main/services/foundation-models-connection-core.test.ts main/services/gemini-context-cache.test.ts main/services/generation-bound-connection-cache.test.ts main/services/generation-context.test.ts main/services/generation-messages.test.ts main/services/generation-runtime.test.ts main/services/external-editors.test.ts main/services/git.test.ts main/services/model-runtime-core.test.ts main/services/models.test.ts main/services/mcp-oauth-operation.test.ts main/services/mcp-oauth-session.test.ts main/services/mcp-presets.test.ts main/services/pi-credential-store-core.test.ts main/services/pi-provider-contract.test.ts main/services/profile-share-core.test.ts main/services/profile-share-files.test.ts main/services/profile.test.ts main/services/provider-auth-flow-core.test.ts main/services/provider-auth-owner.test.ts main/services/provider-key-policy.test.ts main/services/provider-list-core.test.ts main/services/quit-barrier.test.ts main/services/scratch-workspace.test.ts main/services/skills-discovery.test.ts main/services/tool-approval.test.ts main/services/local-runtime-status.test.ts main/services/usage-store-core.test.ts main/services/workspace-files.test.ts main/windows/pill-window-security.test.ts renderer/components/assistant/use-assistant-chat.test.ts renderer/components/assistant/assistant-ui.test.tsx renderer/components/activity-feed.test.tsx renderer/components/environment-subagents-contract.test.ts renderer/components/subagents-panel.test.tsx renderer/components/chat-sidebar.test.tsx renderer/components/composer.test.tsx renderer/main/chat-transition.test.tsx renderer/components/usage/profile-share-card.test.tsx renderer/lib/accessibility-refresh.test.ts renderer/lib/agent-activity.test.ts renderer/lib/chat-activity.test.ts renderer/lib/assistant-dock.test.ts renderer/lib/assistant-motion-contract.test.ts renderer/lib/dialog-motion-contract.test.ts renderer/lib/chat-deletion-cache.test.ts renderer/lib/chat-terminal-sync.test.ts renderer/lib/ipc-stream.test.ts renderer/lib/chat-title-reveal.test.ts renderer/lib/codex-auth-session.test.ts renderer/lib/codex-provider-cache.test.ts renderer/lib/composer-placeholder.test.ts renderer/lib/computer-use-notice.test.ts renderer/lib/dictation-operation-gate.test.ts renderer/lib/editor-preference.test.ts renderer/lib/environment-panel-layout.test.ts renderer/lib/subagent-view-state.test.ts renderer/lib/truncate-path.test.ts renderer/lib/mcp-preset-state.test.ts renderer/lib/model-display.test.ts renderer/lib/model-picker-data.test.ts renderer/lib/profile-share-data.test.ts renderer/lib/sidebar-chat-shortcuts.test.ts renderer/lib/usage-profile-data.test.ts renderer/shared/appearance.test.ts renderer/shared/provider-deployment.test.ts main/handlers/ipc-contract.test.ts main/handlers/chat.parse.test.ts main/handlers/voice-codec.test.ts main/handlers/phase2-parse.test.ts scripts/apple-developer-tools.test.mjs scripts/check-macos-release.test.mjs scripts/computer-use-packaged-acceptance.test.mjs scripts/configure-electron-fuses.test.mjs scripts/model-snapshot-core.test.mjs scripts/prepare-macos-dev-runtime.test.mjs scripts/prepare-macos-package-output.test.mjs scripts/run-macos-distribution.test.mjs scripts/sign-macos.test.mjs scripts/hide-dmg-support-files.test.mjs scripts/update-model-capabilities.test.mjs scripts/vendor-cua-driver.test.mjs scripts/verify-macos-package.test.mjs && npm run test:telegram && npm run test:worktree-remover:native && npm run test:computer-use:native", - "test:coverage": "tsx --test --experimental-test-coverage main/handlers/assistant-parse.test.ts main/services/assistant/system-prompt.test.ts main/services/chat-activity-core.test.ts main/services/chat-generation-start.test.ts main/services/chat-title-policy.test.ts main/services/chat-title-routing.test.ts main/services/chat-store-core.test.ts main/services/codex-provider.test.ts main/services/coding-tools.test.ts main/services/config-store-core.test.ts main/services/computer-use/computer-use-foundation.test.ts main/services/computer-use/computer-use-tool.test.ts main/services/computer-use/generation-gate.test.ts main/services/computer-use/safety.test.ts main/services/computer-use/settings-core.test.ts main/services/computer-use/status-core.test.ts main/services/data-store.test.ts main/services/data-store.resilience.test.ts main/services/terminal.test.ts main/services/terminal-history.test.ts main/services/aiden-config-dir.test.ts main/services/portable-config-core.test.ts main/services/portable-config-core.roundtrip.test.ts main/services/portable-config-watch-core.test.ts main/services/dev-log.test.ts main/services/process-diagnostics.test.ts main/services/dictation-coordinator.test.ts main/services/dictation-paste.test.ts main/services/foundation-models-connection.test.ts main/services/foundation-models-connection-core.test.ts main/services/generation-bound-connection-cache.test.ts main/services/generation-context.test.ts main/services/generation-messages.test.ts main/services/generation-runtime.test.ts main/services/external-editors.test.ts main/services/git.test.ts main/services/model-runtime-core.test.ts main/services/pi-compaction-core.test.ts main/services/models.test.ts main/services/mcp-oauth-operation.test.ts main/services/mcp-oauth-session.test.ts main/services/mcp-presets.test.ts main/services/pi-credential-store-core.test.ts main/services/pi-provider-contract.test.ts main/services/profile-share-core.test.ts main/services/profile-share-files.test.ts main/services/profile.test.ts main/services/provider-auth-flow-core.test.ts main/services/provider-auth-owner.test.ts main/services/provider-key-policy.test.ts main/services/provider-list-core.test.ts main/services/quit-barrier.test.ts main/services/scratch-workspace.test.ts main/services/skills-discovery.test.ts main/services/tool-approval.test.ts main/services/local-runtime-status.test.ts main/services/usage-store-core.test.ts main/services/workspace-files.test.ts main/windows/pill-window-security.test.ts renderer/components/assistant/use-assistant-chat.test.ts renderer/components/assistant/assistant-ui.test.tsx renderer/components/environment-subagents-contract.test.ts renderer/components/subagents-panel.test.tsx renderer/components/chat-sidebar.test.tsx renderer/components/composer.test.tsx renderer/main/chat-transition.test.tsx renderer/components/usage/profile-share-card.test.tsx renderer/lib/accessibility-refresh.test.ts renderer/lib/agent-activity.test.ts renderer/lib/chat-activity.test.ts renderer/lib/assistant-dock.test.ts renderer/lib/assistant-motion-contract.test.ts renderer/lib/dialog-motion-contract.test.ts renderer/lib/chat-deletion-cache.test.ts renderer/lib/chat-terminal-sync.test.ts renderer/lib/ipc-stream.test.ts renderer/lib/chat-title-reveal.test.ts renderer/lib/codex-auth-session.test.ts renderer/lib/codex-auth-view-state.test.ts renderer/lib/codex-provider-cache.test.ts renderer/lib/composer-placeholder.test.ts renderer/lib/computer-use-notice.test.ts renderer/lib/dictation-operation-gate.test.ts renderer/lib/editor-preference.test.ts renderer/lib/environment-panel-layout.test.ts renderer/lib/subagent-view-state.test.ts renderer/lib/truncate-path.test.ts renderer/lib/mcp-preset-state.test.ts renderer/lib/model-display.test.ts renderer/lib/profile-share-data.test.ts renderer/lib/sidebar-chat-shortcuts.test.ts renderer/lib/usage-profile-data.test.ts renderer/shared/appearance.test.ts renderer/shared/provider-deployment.test.ts main/handlers/ipc-contract.test.ts main/handlers/chat.parse.test.ts main/handlers/voice-codec.test.ts main/handlers/phase2-parse.test.ts scripts/apple-developer-tools.test.mjs scripts/check-macos-release.test.mjs scripts/computer-use-packaged-acceptance.test.mjs scripts/configure-electron-fuses.test.mjs scripts/model-snapshot-core.test.mjs scripts/prepare-macos-dev-runtime.test.mjs scripts/prepare-macos-package-output.test.mjs scripts/run-macos-distribution.test.mjs scripts/sign-macos.test.mjs scripts/update-model-capabilities.test.mjs scripts/vendor-cua-driver.test.mjs scripts/verify-macos-package.test.mjs", + "test": "tsx --test main/handlers/assistant-parse.test.ts main/services/assistant/system-prompt.test.ts main/services/chat-activity-core.test.ts main/services/chat-generation-start.test.ts main/services/chat-title-policy.test.ts main/services/chat-title-routing.test.ts main/services/chat-store-core.test.ts main/services/codex-provider.test.ts main/services/coding-tools.test.ts main/services/config-store-core.test.ts main/services/computer-use/computer-use-foundation.test.ts main/services/computer-use/computer-use-tool.test.ts main/services/computer-use/generation-gate.test.ts main/services/computer-use/safety.test.ts main/services/computer-use/settings-core.test.ts main/services/computer-use/status-core.test.ts main/services/data-store.test.ts main/services/data-store.resilience.test.ts main/services/terminal.test.ts main/services/terminal-history.test.ts main/services/aiden-config-dir.test.ts main/services/portable-config-core.test.ts main/services/portable-config-core.roundtrip.test.ts main/services/portable-config-watch-core.test.ts main/services/secret-map-core.test.ts main/services/dev-log.test.ts main/services/process-diagnostics.test.ts main/services/dictation-coordinator.test.ts main/services/dictation-paste.test.ts main/services/foundation-models-connection.test.ts main/services/foundation-models-connection-core.test.ts main/services/gemini-context-cache.test.ts main/services/generation-bound-connection-cache.test.ts main/services/generation-context.test.ts main/services/generation-messages.test.ts main/services/generation-runtime.test.ts main/services/external-editors.test.ts main/services/git.test.ts main/services/model-runtime-core.test.ts main/services/models.test.ts main/services/mcp-oauth-operation.test.ts main/services/mcp-oauth-session.test.ts main/services/mcp-presets.test.ts main/services/pi-credential-store-core.test.ts main/services/pi-provider-contract.test.ts main/services/profile-share-core.test.ts main/services/profile-share-files.test.ts main/services/profile.test.ts main/services/provider-auth-flow-core.test.ts main/services/provider-auth-owner.test.ts main/services/provider-key-policy.test.ts main/services/provider-list-core.test.ts main/services/provider-artwork-core.test.ts main/services/quit-barrier.test.ts main/services/scratch-workspace.test.ts main/services/skills-discovery.test.ts main/services/tool-approval.test.ts main/services/local-runtime-status.test.ts main/services/usage-store-core.test.ts main/services/workspace-files.test.ts main/windows/pill-window-security.test.ts renderer/components/assistant/use-assistant-chat.test.ts renderer/components/assistant/assistant-ui.test.tsx renderer/components/activity-feed.test.tsx renderer/components/environment-subagents-contract.test.ts renderer/components/subagents-panel.test.tsx renderer/components/chat-sidebar.test.tsx renderer/components/composer.test.tsx renderer/main/chat-transition.test.tsx renderer/components/usage/profile-share-card.test.tsx renderer/lib/accessibility-refresh.test.ts renderer/lib/agent-activity.test.ts renderer/lib/chat-activity.test.ts renderer/lib/assistant-dock.test.ts renderer/lib/assistant-motion-contract.test.ts renderer/lib/dialog-motion-contract.test.ts renderer/lib/chat-deletion-cache.test.ts renderer/lib/chat-terminal-sync.test.ts renderer/lib/ipc-stream.test.ts renderer/lib/chat-title-reveal.test.ts renderer/lib/codex-auth-session.test.ts renderer/lib/codex-provider-cache.test.ts renderer/lib/composer-placeholder.test.ts renderer/lib/computer-use-notice.test.ts renderer/lib/dictation-operation-gate.test.ts renderer/lib/editor-preference.test.ts renderer/lib/environment-panel-layout.test.ts renderer/lib/subagent-view-state.test.ts renderer/lib/truncate-path.test.ts renderer/lib/mcp-preset-state.test.ts renderer/lib/model-display.test.ts renderer/lib/model-picker-data.test.ts renderer/lib/profile-share-data.test.ts renderer/lib/sidebar-chat-shortcuts.test.ts renderer/lib/usage-profile-data.test.ts renderer/shared/appearance.test.ts renderer/shared/provider-deployment.test.ts main/handlers/ipc-contract.test.ts main/handlers/chat.parse.test.ts main/handlers/voice-codec.test.ts main/handlers/phase2-parse.test.ts scripts/apple-developer-tools.test.mjs scripts/check-macos-release.test.mjs scripts/computer-use-packaged-acceptance.test.mjs scripts/configure-electron-fuses.test.mjs scripts/model-snapshot-core.test.mjs scripts/prepare-macos-dev-runtime.test.mjs scripts/prepare-macos-package-output.test.mjs scripts/run-macos-distribution.test.mjs scripts/sign-macos.test.mjs scripts/hide-dmg-support-files.test.mjs scripts/update-model-capabilities.test.mjs scripts/vendor-cua-driver.test.mjs scripts/verify-macos-package.test.mjs && npm run test:telegram && npm run test:worktree-remover:native && npm run test:computer-use:native", + "test:coverage": "tsx --test --experimental-test-coverage main/handlers/assistant-parse.test.ts main/services/assistant/system-prompt.test.ts main/services/chat-activity-core.test.ts main/services/chat-generation-start.test.ts main/services/chat-title-policy.test.ts main/services/chat-title-routing.test.ts main/services/chat-store-core.test.ts main/services/codex-provider.test.ts main/services/coding-tools.test.ts main/services/config-store-core.test.ts main/services/computer-use/computer-use-foundation.test.ts main/services/computer-use/computer-use-tool.test.ts main/services/computer-use/generation-gate.test.ts main/services/computer-use/safety.test.ts main/services/computer-use/settings-core.test.ts main/services/computer-use/status-core.test.ts main/services/data-store.test.ts main/services/data-store.resilience.test.ts main/services/terminal.test.ts main/services/terminal-history.test.ts main/services/aiden-config-dir.test.ts main/services/portable-config-core.test.ts main/services/portable-config-core.roundtrip.test.ts main/services/portable-config-watch-core.test.ts main/services/dev-log.test.ts main/services/process-diagnostics.test.ts main/services/dictation-coordinator.test.ts main/services/dictation-paste.test.ts main/services/foundation-models-connection.test.ts main/services/foundation-models-connection-core.test.ts main/services/generation-bound-connection-cache.test.ts main/services/generation-context.test.ts main/services/generation-messages.test.ts main/services/generation-runtime.test.ts main/services/external-editors.test.ts main/services/git.test.ts main/services/model-runtime-core.test.ts main/services/pi-compaction-core.test.ts main/services/models.test.ts main/services/mcp-oauth-operation.test.ts main/services/mcp-oauth-session.test.ts main/services/mcp-presets.test.ts main/services/pi-credential-store-core.test.ts main/services/pi-provider-contract.test.ts main/services/profile-share-core.test.ts main/services/profile-share-files.test.ts main/services/profile.test.ts main/services/provider-auth-flow-core.test.ts main/services/provider-auth-owner.test.ts main/services/provider-key-policy.test.ts main/services/provider-list-core.test.ts main/services/provider-artwork-core.test.ts main/services/quit-barrier.test.ts main/services/scratch-workspace.test.ts main/services/skills-discovery.test.ts main/services/tool-approval.test.ts main/services/local-runtime-status.test.ts main/services/usage-store-core.test.ts main/services/workspace-files.test.ts main/windows/pill-window-security.test.ts renderer/components/assistant/use-assistant-chat.test.ts renderer/components/assistant/assistant-ui.test.tsx renderer/components/environment-subagents-contract.test.ts renderer/components/subagents-panel.test.tsx renderer/components/chat-sidebar.test.tsx renderer/components/composer.test.tsx renderer/main/chat-transition.test.tsx renderer/components/usage/profile-share-card.test.tsx renderer/lib/accessibility-refresh.test.ts renderer/lib/agent-activity.test.ts renderer/lib/chat-activity.test.ts renderer/lib/assistant-dock.test.ts renderer/lib/assistant-motion-contract.test.ts renderer/lib/dialog-motion-contract.test.ts renderer/lib/chat-deletion-cache.test.ts renderer/lib/chat-terminal-sync.test.ts renderer/lib/ipc-stream.test.ts renderer/lib/chat-title-reveal.test.ts renderer/lib/codex-auth-session.test.ts renderer/lib/codex-auth-view-state.test.ts renderer/lib/codex-provider-cache.test.ts renderer/lib/composer-placeholder.test.ts renderer/lib/computer-use-notice.test.ts renderer/lib/dictation-operation-gate.test.ts renderer/lib/editor-preference.test.ts renderer/lib/environment-panel-layout.test.ts renderer/lib/subagent-view-state.test.ts renderer/lib/truncate-path.test.ts renderer/lib/mcp-preset-state.test.ts renderer/lib/model-display.test.ts renderer/lib/profile-share-data.test.ts renderer/lib/sidebar-chat-shortcuts.test.ts renderer/lib/usage-profile-data.test.ts renderer/shared/appearance.test.ts renderer/shared/provider-deployment.test.ts main/handlers/ipc-contract.test.ts main/handlers/chat.parse.test.ts main/handlers/voice-codec.test.ts main/handlers/phase2-parse.test.ts scripts/apple-developer-tools.test.mjs scripts/check-macos-release.test.mjs scripts/computer-use-packaged-acceptance.test.mjs scripts/configure-electron-fuses.test.mjs scripts/model-snapshot-core.test.mjs scripts/prepare-macos-dev-runtime.test.mjs scripts/prepare-macos-package-output.test.mjs scripts/run-macos-distribution.test.mjs scripts/sign-macos.test.mjs scripts/update-model-capabilities.test.mjs scripts/vendor-cua-driver.test.mjs scripts/verify-macos-package.test.mjs", "test:computer-use": "tsx --test main/services/computer-use/computer-use-foundation.test.ts main/services/computer-use/computer-use-tool.test.ts main/services/computer-use/generation-gate.test.ts main/services/computer-use/safety.test.ts main/services/computer-use/settings-core.test.ts main/services/computer-use/status-core.test.ts main/services/data-store.test.ts main/services/generation-messages.test.ts main/services/generation-runtime.test.ts main/services/quit-barrier.test.ts main/services/tool-approval.test.ts scripts/check-macos-release.test.mjs scripts/computer-use-packaged-acceptance.test.mjs scripts/configure-electron-fuses.test.mjs scripts/prepare-macos-package-output.test.mjs scripts/run-macos-distribution.test.mjs scripts/sign-macos.test.mjs scripts/vendor-cua-driver.test.mjs scripts/verify-macos-package.test.mjs && npm run test:computer-use:native", "test:computer-use:packaged": "node scripts/computer-use-packaged-acceptance.mjs", "test:computer-use:native": "cd native/computer-use-broker && CARGO_TARGET_DIR=../../build/computer-use-broker-test cargo fmt -- --check && CARGO_TARGET_DIR=../../build/computer-use-broker-test cargo test --locked && CARGO_TARGET_DIR=../../build/computer-use-broker-test cargo clippy --locked --all-targets -- -D warnings", diff --git a/protocol/aiden-remote/v1/openapi.json b/protocol/aiden-remote/v1/openapi.json index ff19f8c7..d9d13609 100644 --- a/protocol/aiden-remote/v1/openapi.json +++ b/protocol/aiden-remote/v1/openapi.json @@ -268,7 +268,8 @@ ], "additionalProperties": true }, - "Provider": { "type": "object", "required": ["id", "label", "models"], "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "models": { "type": "array", "items": { "type": "object", "required": ["id", "label"], "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "thinkingLevels": { "type": "array", "items": { "type": "string" } } }, "additionalProperties": false } } }, "additionalProperties": false }, + "ProviderArtwork": { "type": "object", "required": ["mimeType", "dataBase64"], "properties": { "mimeType": { "const": "image/png" }, "dataBase64": { "type": "string", "maxLength": 43696, "pattern": "^iVBORw0KGgo[A-Za-z0-9+/]*={0,2}$" } }, "additionalProperties": false }, + "Provider": { "type": "object", "required": ["id", "label", "models"], "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "artwork": { "$ref": "#/components/schemas/ProviderArtwork" }, "models": { "type": "array", "items": { "type": "object", "required": ["id", "label"], "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "thinkingLevels": { "type": "array", "items": { "type": "string" } }, "hidden": { "type": "boolean" } }, "additionalProperties": false } } }, "additionalProperties": false }, "FileEntry": { "type": "object", "required": ["id", "displayPath", "name", "kind"], "properties": { "id": { "type": "string", "pattern": "^file_[A-Za-z0-9_-]{43}$" }, "displayPath": { "type": "string", "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$", "maxLength": 4096 }, "name": { "type": "string", "maxLength": 255 }, "kind": { "enum": ["file", "directory", "symlink"] }, "size": { "type": "integer", "minimum": 0, "maximum": 5242880 }, "language": { "type": "string", "maxLength": 80 } }, "additionalProperties": false }, "FileIndex": { "type": "object", "required": ["snapshotId", "entries", "truncated", "maxEntries", "maxDepth"], "properties": { "snapshotId": { "type": "string" }, "entries": { "type": "array", "maxItems": 4000, "items": { "$ref": "#/components/schemas/FileEntry" } }, "truncated": { "type": "boolean" }, "maxEntries": { "const": 4000 }, "maxDepth": { "const": 20 } }, "additionalProperties": false }, "FileDocument": { "type": "object", "required": ["id", "displayPath", "content", "version", "truncated"], "properties": { "id": { "type": "string", "pattern": "^file_[A-Za-z0-9_-]{43}$" }, "displayPath": { "type": "string", "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$", "maxLength": 4096 }, "content": { "type": "string", "maxLength": 5242880 }, "version": { "type": "string", "maxLength": 128 }, "truncated": { "type": "boolean" }, "warning": { "type": "string", "maxLength": 500 } }, "additionalProperties": false }, diff --git a/renderer/components/assistant/use-assistant-chat.ts b/renderer/components/assistant/use-assistant-chat.ts index 53edee8c..d77980b9 100644 --- a/renderer/components/assistant/use-assistant-chat.ts +++ b/renderer/components/assistant/use-assistant-chat.ts @@ -14,10 +14,11 @@ import { type StreamCallbacks, } from "../../lib/ipc"; import type { Chat, ChatMeta } from "../../lib/types"; -import { useProviders } from "../../lib/queries"; +import { useProviders, useSettings } from "../../lib/queries"; import { isModelSelectionAvailable, readModelSelection, + resolveVisibleModelSelection, subscribeModelSelection, } from "../../lib/use-model-selection"; import { STREAMING_REVEAL_FALLBACK_MS } from "../../lib/streaming-reveal"; @@ -240,7 +241,9 @@ export function useAssistantChat(): AssistantChat { // seeded once at mount, so the dock would never see the composer switch // models. Read storage at the point of use instead. const providers = useProviders(); + const settings = useSettings(); const [selection, setSelection] = React.useState(readModelSelection); + const [activeChatId, setActiveChatId] = React.useState(null); const [conversationLoading, setConversationLoading] = React.useState(false); const conversationLoadingRef = React.useRef(false); const [turnSaving, setTurnSaving] = React.useState(false); @@ -250,7 +253,16 @@ export function useAssistantChat(): AssistantChat { useAppendReconciliationRequired(); const reloadRequired = appendReconciliationRequired || documentAppendReconciliationRequired; - const modelReady = isModelSelectionAvailable(selection, providers.data); + const newWorkSelection = resolveVisibleModelSelection( + selection, + providers.data, + settings.data?.hiddenModelsByProvider, + ); + const effectiveSelection = activeChatId ? selection : newWorkSelection; + const modelReady = Boolean( + effectiveSelection && + isModelSelectionAvailable(effectiveSelection, providers.data), + ); const ready = modelReady && !conversationLoading && @@ -283,7 +295,6 @@ export function useAssistantChat(): AssistantChat { turnSaving, }) && !reloadRequired; const [threads, setThreads] = React.useState([]); - const [activeChatId, setActiveChatId] = React.useState(null); const handleRef = React.useRef(null); // Every async continuation below checks this before touching state. Without // it, a cancelled turn's late chat:done clears `streaming` for the turn that @@ -440,8 +451,16 @@ export function useAssistantChat(): AssistantChat { (text: string, restoreDraft?: (text: string) => void) => { // Read the selection fresh: the composer may have switched models since // the last focus sync. - const current = readModelSelection(); + const stored = readModelSelection(); + const current = activeChatId + ? stored + : resolveVisibleModelSelection( + stored, + providers.data, + settings.data?.hiddenModelsByProvider, + ); if ( + !current || conversationLoadingRef.current || stoppedPersistingTurnRef.current === turnRef.current || !canSendAssistantMessage(text, { @@ -727,6 +746,7 @@ export function useAssistantChat(): AssistantChat { reloadRequired, fail, providers.data, + settings.data?.hiddenModelsByProvider, refreshThreads, streamComplete, streaming, diff --git a/renderer/components/command-palette.tsx b/renderer/components/command-palette.tsx index 115b11e9..72992165 100644 --- a/renderer/components/command-palette.tsx +++ b/renderer/components/command-palette.tsx @@ -49,7 +49,7 @@ import { readModelSelectionRevision, useModelSelection, } from "../lib/use-model-selection"; -import { createModelEntries, isUsable } from "../lib/model-picker-data"; +import { createModelEntries, isUsable, visibleModelEntries } from "../lib/model-picker-data"; import { SETTINGS_DESTINATIONS } from "../lib/settings-section"; import { createDefaultAppearanceConfig, @@ -112,7 +112,11 @@ export function AppCommandPalette({ const chats = useChats(activeId); const providers = useProviders(); const settings = useSettings(); - const selection = useModelSelection(providers.data); + const selection = useModelSelection( + providers.data, + settings.data?.hiddenModelsByProvider, + settings.data !== undefined, + ); const { binding, canExecute, execute, palette } = useCommandSystem(); const [query, setQuery] = React.useState(""); const [busy, setBusy] = React.useState(false); @@ -127,8 +131,12 @@ export function AppCommandPalette({ } }); const models = React.useMemo( - () => createModelEntries(providers.data ?? []), - [providers.data], + () => + settings.data ? visibleModelEntries( + createModelEntries(providers.data ?? []), + settings.data?.hiddenModelsByProvider, + ) : [], + [providers.data, settings.data?.hiddenModelsByProvider], ); const unavailableModelProviders = React.useMemo( () => (providers.data ?? []).filter((provider) => !isUsable(provider)), diff --git a/renderer/components/model-picker.tsx b/renderer/components/model-picker.tsx index d981e630..c7963c43 100644 --- a/renderer/components/model-picker.tsx +++ b/renderer/components/model-picker.tsx @@ -25,6 +25,7 @@ import { PINNED_MODELS_KEY, positionSavedModels, positionModels, + visibleModelEntries, type ModelEntry, type PositionedModel, } from "../lib/model-picker-data"; @@ -33,6 +34,7 @@ import { useModelPadLayout } from "../lib/model-pad-layout"; import type { ModelInfo, Provider } from "../lib/types"; import { Check, ChevronsUpDown, Pin, SlidersHorizontal } from "lucide-react"; import { ProviderIcon } from "./provider-icon"; +import type { HiddenModelsByProvider } from "../shared/model-visibility"; interface ModelPickerProps { providers: Provider[]; @@ -41,6 +43,7 @@ interface ModelPickerProps { onChange: (providerId: string, model: string) => void; disabled?: boolean; settingsBlockedReason?: string; + hiddenModelsByProvider?: HiddenModelsByProvider; } function formatTokens(value: number | undefined): string | null { @@ -244,6 +247,7 @@ function ModelHoverDetails({ providerId={model.providerId} providerLabel={model.providerLabel} modelId={model.model} + artwork={model.providerArtwork} className="size-4" /> @@ -317,6 +321,7 @@ export function ModelPicker({ onChange, disabled, settingsBlockedReason, + hiddenModelsByProvider, }: ModelPickerProps) { const navigate = useNavigate(); const [open, setOpen] = React.useState(false); @@ -343,7 +348,8 @@ export function ModelPicker({ } }); - const entries = createModelEntries(providers, infoByValue); + const allEntries = createModelEntries(providers, infoByValue); + const entries = visibleModelEntries(allEntries, hiddenModelsByProvider); const orderedEntries = orderModelEntries(entries, pinned); const detailPositions = positionModels(entries); const positioned = positionSavedModels(entries, modelPadLayout.placements); @@ -352,7 +358,7 @@ export function ModelPicker({ entries.some((entry) => entry.info?.metadataSource === "artificial-analysis") || positioned.some((entry) => entry.confidence === "suggested"); const selectedValue = providerId && model ? encodeSelection(providerId, model) : ""; - const selected = entries.find((entry) => entry.value === selectedValue); + const selected = allEntries.find((entry) => entry.value === selectedValue); const selectedPosition = positioned.find((entry) => entry.value === selectedValue); const detailPosition = detailPositions.find((entry) => entry.value === previewValue); const activePosition = @@ -474,6 +480,7 @@ export function ModelPicker({ providerId={selected.providerId} providerLabel={selected.providerLabel} modelId={selected.model} + artwork={selected.providerArtwork} className="size-4 text-tertiary" /> ) : null} @@ -624,6 +631,7 @@ export function ModelPicker({ providerId={entry.providerId} providerLabel={entry.providerLabel} modelId={entry.model} + artwork={entry.providerArtwork} className="size-4 text-tertiary" /> diff --git a/renderer/components/provider-icon.tsx b/renderer/components/provider-icon.tsx index e1cc0d94..f1ef58c6 100644 --- a/renderer/components/provider-icon.tsx +++ b/renderer/components/provider-icon.tsx @@ -1,5 +1,9 @@ import { resolveProviderIconSlug, type ProviderIconSlug } from "../lib/pi-provider-display"; import { cn } from "../lib/ui-utils"; +import { + providerArtworkDataUrl, + type ProviderArtwork, +} from "../shared/provider-artwork"; const PROVIDER_ICON_URLS: Readonly> = { "amazon-bedrock": new URL("../assets/provider-logos/amazon-bedrock.svg", import.meta.url).href, @@ -80,13 +84,27 @@ export function ProviderIcon({ providerId, providerLabel, modelId, + artwork, className, }: { providerId: string; providerLabel: string; modelId?: string; + artwork?: ProviderArtwork; className?: string; }) { + if (artwork) { + return ( + + ); + } const slug = resolveProviderIconSlug(providerId, modelId); const iconUrl = slug ? PROVIDER_ICON_URLS[slug] : undefined; diff --git a/renderer/components/settings/builtin-provider-editor.tsx b/renderer/components/settings/builtin-provider-editor.tsx index 99a349c2..f9606493 100644 --- a/renderer/components/settings/builtin-provider-editor.tsx +++ b/renderer/components/settings/builtin-provider-editor.tsx @@ -8,6 +8,7 @@ import { type ProviderAuthSession, } from "../../lib/provider-auth-session"; import type { Provider, ProviderAuthEvent, ProviderAuthPrompt } from "../../lib/types"; +import { ProviderModelVisibility } from "./provider-model-visibility"; interface BuiltinProviderEditorProps { provider: Provider; @@ -232,6 +233,7 @@ export function BuiltinProviderEditor({ {provider.models.length} Pi model{provider.models.length === 1 ? "" : "s"} are currently available. +

        b-x>{ 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Π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�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�|)~$b#C;j{^39T$uEEF@BI1y{jdLe{rV^QZA@HL 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

      b-x>{ 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Π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�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�|)~$b#C;j{^39T$uEEF@BI1y{jdLe{rV^QZA@HL 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�e#T6EFkRU3bI?)vhVh?M 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ƍwkn-(mTPPD{-e8Za8jqyqujnEm0D#-e`iF=}h)gqhGMP=XY_bo~zLYYr zTEBl|`L`&5WOMwly*h6yhz3Jz{^vDR{qq<)5#fLS)NPHq;^jPQVPExW7i29yodRgh zrllS^67u2C^SHG)p#E%Kf$IbkcK1ay+1!G=+`iz%^K+qCeFtkC&G(UDhFFZs`M8g| zoLh0;4|RvdCC$S!Jo`AZ2DhTkKF2zDArDDCg2OeE^(#3*`y{e`$|e(?3kih)_&;kh zROULKmd%X>*bf|P?K!f=;7eaFsG(C-*vOz z$Yl0f?GKaodmb1QH}r0R^!k%f0K0tsHu4d9ft7OD{7@4QAi+dA5e^};OC%;!{elo= zHR245*hfbf1p!FR79x>l`Xe*vL}C}99Vt1hTi?Hw3@MqUl#nbvn3Oi^w&B8Gzk#&X z^61dUBiLC9(`r!o_a>(>`bE3KNqcr;jX9?&YKtq1E@U5e5Bt!6>?^WX-bC%XDjl() zhO)EEF5k>%ZF=23ayWr#a6~HfY-xzsslS${Jv{KOIPNfvA2DIpsivJ1s=>pOaK~%s z3PY}mrgouu4OxSV0p<*#9YE}|Z8VeUy4UK_T;M&rNC-ew#U&GkDWt5YAZ&xNRo9$V zRlDs^Hi3Y$mf+gmXS&H5K=v4-E{)TExHd54`z$xn*yBu#SAm_sD0tr2P;2pVCc3u} zdfBb*WArF=HM(^O^z&Xl-CKZ@&2PUD_SZ2UY=8(RkcQ^FR}y~ zWXyu+OHDOz&2iqPAPnc6J%%G=(QbZ9LF{&k2kDsGk}M^|$AH!m=Ip)4J?Z_0sPkY? zFwov&ch-5AWJ+*Ea9m?_il@#T2NvCqU=2sb5d?n?^4AT$0Z`}108<8tEs)rTq$%22 zZ^@qF%j_dY7XbmVS1w~`5`m>-`DIxqW<<0D@QkITx~?Im1T#~E0?2lo2U?lDAuJyW zUU{hM-Z>AYhhNuz&V~xc`L}2Ty=ibU=bpD>MWE0`zwY?rkxUYM=VQ>GQ(Fp>$J$^H4%%dVKcqo>E|luA4uvcB%J2}BbF5=JnHv(r zjV5NcWDp25KrN}2L_Y|E-g9tF5a2!NtC_)j!ez0}wFiRt#%mJqZDU{}f- zbzMtYOCcre0Ki&G=xZJ}=^kzPgc^7YNi>iBXzUGRLpb=$_2ua$&a>#Kr?{@B4`Yj$ z%C~qezSsxy_@4IIeQfKI;z!!_aeefiY)LmW(D6-qQS4;hf;7Jz=R!PTG|0(=lG(Ee zeT-9;!>u;Usq!M+h;b!@Y&bZY@fxh3~{~UZpId*Ltd_`J+_%o%-RKl? z#LM)HwN;D|(=R;la(n(lFN^(1mQ*qy=`z70PW8RE_;*Yo8S{~N?CC&m?+Cc(e)P<2 z<%zMz^V;ixKWLoKKI9icz|>f2HTve{K!Vv3z>>j&fn6fnqkOW3Mgd@bZ7>OdWea~? z49j29ML+Kq9{Qr3{QRNW`?Y%*og7Bi+&GGtvdw*p@>H-cZf2aEKU+(9%r?T4rc zo^kk{t){LzZ~URL8y7dbgVEH|{~21h^P|oE*TX1wy1_^A=7I;tH)6?#KiBM6SR^+}~>VQ_PpvRfV4BQJ?1TeeU&5 zwy9NgzNKWl`RRv4RtZ4OJyK9@k3=-F5ad-Pk$s8}UQmUKmN!1Bc<2K~gn08k2k*ND zq}xPR{{A=NNqTm!evCo{u`nfclnzf)ho|+W*r^4*a>Mob+&LA@VpW};QEAW*my!N@ z?6+IhJ9S&2;vgG$y3;UwXF{|kPA{a8Jthv)52C0A+@C%;z|TIB(EhD|xdnu0N=Dee zPW{2k!|s)Mk-0s;_G z$%%-Fs;Wd;7Aj4i=aUIDCd}MM{!5nn@7DScAix9-)mn8G5w0ZxlKVEGx*OmkKa#10TZ`%2enbToa+x!Xs+rSQ;(sN2raPOyE(G+<$l zSs1n?r~da^i_<}2JU*m>a(3#jl>Wf3v<%kJ7aLZ~l;SnXD|kIGdV2Q7A!ZNfI&CaY%Tf|Cv+Pp$9U4B0-}(eKni*|O#_ zM>!vo53mL&BXU>rUec|PUcSO!>=aoS0XKmF+@6}*oDN7ci0M|odoV3ek5*h45?li` zAu}0Up}QS zO6ikhuK3$T_9oW7^=Xrr2Uj@M&^h`22AT-_f^Aq6&uX-?lS^ur+hF*IMX6TBkd~<0 za&Bc0;jg;DnYGev?8N!eN@svfjEe`Ka0SK;(=4BEw-_Jug zvCe}o0s;^+kO)Eu0>F#qoB`0)D>r6>vTZQU)Co=+{%3B^|7W%AB!&(ritU2+{X^n? zcaZ*`T%&c3Xf95E*pQp-eheDWF}a2pZ@hM_r5DkLp7kghnxcdEr!PJ)d=6(_k7FP z?LP_T7J^qN`LT3z^AQ6ZX%PBJ%01$@IN}WO26n&?U2(U+h*Rmhdr2n?|8RRgp?Iki z|JYyGVjU3c;J$@5q4zt*_zNBdDG;2**u#-9vcpCM*kwR#BsMolwrC777(9cZZLxE8 z27tI)HC-$OaUHG6BaZ|;$_RiBrFf#5KtccjmsQCi(DrOAC&GjoIher`U~AvMf_7Br z29)Ziffl`mx%JXIBO-+UIgv6h%nKclDD^WF`G>?BFfMNp0`#%8=sC=064MOQO7_70%5PewX4|ZD~{S1 zb6v0P$?_Ot`LTT!)9i;&nH+1g+SItyq>DXtS8{W~7FpUIx2$*Z##Ah}vKIYY1_Kvg z5F=riVBK&R^Ap&=?UVEo#1C%b=`dt-#1Hd#wG2lJD_NbOm7)tMfF<98by z-nwtAf_;ZWAF|a8JvsULqu=JIBbk(rq!BxBJo}cqx(if zhLd^0MT*=zDQj9Qk_~H(dKAv|KjYSMO9KZliC; zkT?gLA!jBGK^oX?#)xA4y??@x>Cux}3q!FokMbm@WI9S4F+|2R6A+WWiuq8}BT0`E z0zf^_^ss11=6r$1S8G zRE>NbFO}bb`si0l^OV8XVUPa!P=Bc18E)CBSx6U{i`b&2WW=3zYb*f4fL_tj zmy;+lmeVk6wzn2N>q>tcQ#Sc}bA_Ogb=o+OWc4`pu)lTD8gzBRpzet!7&@BLE}%9C z0U;)oZ%xE>&j@ykMy;F!gC9OUN(jJUA|j}5fB^W?!DX?#y)(;1HUmf~7l2X%z-);e zW1kR{xy{R?{zFkLO_RdO=}+<4p(l}#X)&f03~jMFCQNv;T-1kdX(_&C4c7`Zz(&NX zA&Wm8eyxAC>z{2=DGP%w%b`)yyG^{Pt5KsDwE=D^wYZ;kK3e>>tVu0i9E-!W#YR^g zRz-cG5#TvuW!oj2{5uo|E4?}B&9;!k^8J4IV>C>9%hFeMX$>*oOWV)ih+o-PxPoEl zsoi>9YoA==`=(ADX+Al*o9Ov|HH!`Kh~l@>KGk?OsI2i65ky7+| z-MogEm(*ut6O8+Eh%r_D)3Ly;W1h8&ms(;3jIY)Fkp5Gu$ewX96qn~&0h$19% z@M@%#*ukwl15);((k3|0npyW1BLD+G%5;AATy>Opow?Rjh#-h)i?VzsraM|wAQD(- zTlb?xk0JuluV_QN02qMW-rW<~WHKY7=Bmav01&Yq0D8hP`j@7D3SoaP_WyNSt5#_sx@^i|v0ceyP*fzd)Kfw(ZvSQv{pJYm5N5wVmM|0F=qa`P=j^F?A7 zdDeSy)_jB`P7tGl4!T%lmlHQc^WPZB##n^T`{L{y~00eb*Fp)FMON$J69`qbd@C!XoSo$6`eBg26sGlBcT-v_B1F zG7!xm@)`1N0kjA}KT|p|t&f&2Bm#go4zT^e)7ch@ET4dA0zK1O!7t$mf50q{Yn*=G zBgfk@5FO;)k>Liuya83TTe_Yk->?Fm@MTyBie%*?v75~iJI0`!SEgRUIyiu$HXEg~ z=xU44iJtz7uYqv++}G#4*5fe$kYSw2YWN7ZgyX;+A$`_-+LrGlH&B@1_fnsI66cXU z-kP6Q;0|Kad0GDM8rZc)3|+tNzxQri^8+L z*A@PuMPN_o80u`a+cet2k-r>9t8e?FxqZ#tcaD1tr}E*u28goEd(@W>^jSxATf&m< zN!(-c7}G~B*5-b78%T7ZK>D=c6N)XLwhWRI)PyMfCT*U=CNH1S|2IX{b9erP6M19@BG{H zqK&k(C-=AM;DDFujO_kw`_YR53X&$YvIcpR;cI%_bgxVP34@_BtoYcBMYa(p%~tk` z3GWEexjFO6d(UBknxIOt@+WqxgS>Cst-@MeBh&i;3y{c(vI%9mWjtS4`Txix04b@Y z>@5bsf&>vDn2~8cli7*kDDnfF3{-uDBh^GPZ0x)c?Q5?I#W zd;W|aiZrlFK45~8Z2)}A?l}-O7iPw_RoXzymhp(w31CEVFd%SWm?@|Ut`>s~B>;Zt} zaAVC2Grk>6Xt0=4r$M&1V;?eTi%7p&(Zp^diUpA`*W@sqjXt%N+?4t+y@6GtXDsNn^EeZF4&Dj8fmbX=4~A zd3`brA=zl8b+lthTO+j^oK?Jk**icm&SG1EXaHxxIk0%BjCv&LA|U`4xl1642@&NP zL1a+`z?sDMfbUwsht5uG3I&QfcfRMo)Z=^eec0L+=?fzl$)JJxyX}sh*`0PSy3KYM zH|e(73^?oy>Xh(_X&tHYX7{fvQj7}*!L7YkPkQ6b zJHeBEew%uvS8D(60$9Sp>ahNc8~XWf_hkpSy8F|5(T6vyGtn)wYCvP>kcYu-65JIUoa&kqb=>)mtAqdXy1> zo}p~M79@md0su2G$U*?f-0PeIuxIpu%4Jt zG$5D$w#3i?^DMl2HlnPsT^_mFgV*OnYh;mN_-$hNKuC;AgtHT^R z=1BwPx%Erd;;gtuB-d2G40*ScLjGsS$Pmkf<06PPkUmksWtIWqhM=|Wd*syJ@PTbctMS4d&nCqp z3!z~z{}fM~$BC7|Zhw1Sa{T(~b=Agud2|Jp>@`+rqH1XD@0U4(kx;Owll{IA$piUwY1BajctusAWhU z@3KO{TZZctVZ>@oWAXFe zWURD*S=kG2w)FUm6T#diIg}rsqapD^7Yg8^Jx&uhnN{I1`slxI^CG=)SH2-sdT zs}BK6!gy@?!qzC?`y7d7Z=REFBT#bUP>_$Op_09KH)3_9G#SsemvZ&>`W~(9$oV5x zJ}0tiUbGJej%E|vvf<}To&`sM*{$#5fYdIMuCG-2QcW~Fdlq`&4cBY8Z%MZ~2Vv$u zCei-u8&dv&aj5&sUd2Oc*C(l$IzCNfd-}JwHE@gD-$=H1j-T}$4|E28`*{RR*q zni7dAi3yn;F0KY}5fFf|C&=Zw$P@yAoSBK$;Xm#7#)UIv(5+c3io286&unOYI-M75 z(B?TfC?cm)a8C{~&^l#R8nq`mKQ((40>J)T?I%}(Fn^qrHKy|5uJ5hC?J*fe@l%#} zm}e-K&e9gUZf%(zx|kNC3B@PqvWOB9tjCGl^fsHhiN57HFJoL!`ki*s^JZ&*1S-t`rBE&PRX?f6Am>jDL%mU1GIldxO>jXNx< zH9X4nC?f!jgo4t@e|_fMR zg~v42^jo$;=_L0WW}(P6ZclTfIA%T_+WE^#|E`ig}i*J<$*bv&~q6$ClOjTvycp9&Dl5x8}?a) z*j2*Z3bVbS>h}69a=Fv2t!!11wGQE`E8)Ni^q_Nqo6WFyJAl>Ajt*33r6IKp+2h~% z@sp>(`r|g@k^uTZ1;36qn!3MEH_*Nei=pF7j#;8nEQ}N%JQ}mGxtdCz)KRV zzn926z%tYpd-&tN=glT*zI3u7a^*sz3}rkrksx3Kh^Z9>$USzn2In3{x(EmW5Rn=d zB+4KXAj*OEE&v1jlScp9UslXk!LHdgRF`)FXRoqRvMq>@LQxDGzD_N)?AWO4Lte5o zH;93*b(W$LTjrmePThD7`j ztd6&1pjhNnvS{m%wOa05-%&-+8a!)}uX%4w_EJHOjG?^Muq)0uYkb;g^N^hsxdaEVwd z16_(Sur%D4?;#lendp7KJ$6fM`ZV0GWVY)ni>Gaf<&#v#odNc24Unud+PSWn)_%>c zRc^Z?*eahb|FQqQe#Tb*Epn*)#kR<8_%ha?zJ=*T(OC*IyDUuwKliS$z9s7+6Y4B^ zg>30q0B@03D23s80&yBReS$qOzVBa+6=%E){eY~v126?KAfZrl$19VIunY!)Qxjex zJ2Wi0&yC{jv_0KhgyZ10Pz@_ zE&yTh$_q3DCkHTb1yHakUl_WG3P6m@0__0}+E!$L?s76O1IKUDf9(Rm;n?dRQXYIZ zuskJZ*Hvy~9*TfNLo0I*mVeU*ZvizmacbnI@8 zhkD&2OW=`PBQnZ>N=szMl35^QW6@MHI8KDU-|LW)ZBEP2uqF*Q=PXpsE;|96_B)!AEHrptrisdtE1fvxDln)nP*>(%u&I^$;C6zY79sC z<5Y--zq&C+Igl|Z!^J_*JLw`R0RD2M0oX?QWYPHlLCqe{Ruxb&08sX$n}I{tz)7Ek z=cRA|aHcDHnRPZQzC8KhW0;etse6WY%4g?Sp1rg_e-eO$S@wcxcrSNOW?((FB^P@+ z7)cqM|Kak>j)*b!Xq7D{*bYas-zIJZy#wvHc!3u7Wcrv?`S!ABMX77PwzXh5<(g9* z?OyQ|k~X#8tq>76nqSU(851rP{R@$l?qh;`Ae!9{Z;|xiXA@%*wl!9MNXonnDiF_! zlHWLJOu8BoO|3)QP$&G6hI|~T!%7$%|1sRmMHs?c#?^TWcANY~%kO)Cyr=nY6x`TZ zCNZA+2%qZzB=$$wru{XVh4T9w|NUFzD3_+fYOZELqyJ#T-$U# zHDcXS9Lv8CmQmojk5{eJKKqkDX4kEMstx`82ve=MhMo0&mm49!!vn0>p&@<39nVc@ zZu|nOi${-z0syGkqHve869N!_)vTQ=2k(3Rr7FpO?vrhj3zd!$*Ur(ouv1s|XeU8z z|914{?m_AsKtybYeB||>gS65V^hF%9@P)NreuLc*?UKuu_1t3kf?CUwsP+1imV+7* zl1)1$bMqr5YK93HaUhn)0ElZ#$|h)~plAerMgl?!d_fSR*})pdFfX#?Aq{PbCSwOGdz@F5D{GPYB;*0??8 zTenXm)EnWhZ-#8Ig;qxdH?aVXU{L}(BLXOcXpfxEM05Q&2?3%^V}T~7l%dX^P>Mt? zYe?BB7*KbbW_9&j~*=_EqQM?Je zGX{CbdpsnCg7_9;qK<7QqHU;$xDRNLpIMAyT{e1G+HNZ|#)sHU8j^>4n;e1L%?QVH zMSh;c3?O6ga8y?I_jliSNvLamvYY1bM~ot)I=u1Xn*J?aNCkkIDW)>&vD$gKNBo=q zp5%cETlv~d~|_q0~K3W2bheH zIf}rfdtI%QoGwZCg;OnER%^NYh9@P9bPDj#P4Io^KnuXBNkVEfHd$|XHWA9z6Y?_> z|Hb}Hu%Y;vqQLQwzOv*mJKD{XLc$ovUben&cSEqZ&R8j6uZF434*}W6vDY3QZlmY1 z+gx(q^P@>j!z0*EOc(5~r+Ux7W5PzV$gFvdt#2PDALQhJypV{%MN|NijJ7+=EG)xx z$n#VvRVEa;4bGnd4^sslWH6l>N%4K&p|4e0$aWkUGa#izS=Xq!MlB_zG_bi48L}+L zG@oF1Yl6u%$28BeHJM^M$uOHXf`5B9!}it`yE|Lh*`8s0Ylhiuf~{$*08A#B<~gR5 z9FshQ5W)lswhN_{jH;HXYKgL}uqsN-mn$q*tLFD|h110n^JRhgs=%tKuq;cg${LGR zg;iOjC~8!7jjF0p6f1CDL%ESih{ly51VknpBmzP!0Nja222LYeof;wvJF~XWi3xiS zYYot0C;(YXK)R)Fo*d5g4R`z`S~;*)_UCwQO?_ixo}9lnpZi^lok2U&^M9aKFpyLJ z80^DGw3^~g7XRxpaW5mkw}ge;1eb_praah5g+v zY;Vo5yS;_2tr@nqW|*}~LY`&FTR|&Cey$JR@J6|;7-Z5$kaZiybVgavNu=hncUYCP7#b9TA}>=5b(%3xPQIiAYAjiD z;X!|wF02CJQs~_pD9^%d`qkC`LEvHNvVGm1(Hd<)J8kO{)x_BOVrIcC!dcDJ^$x3hz*mkx3D@*!?KaT(VxAK=>M103${ z;?ltmc6Vmj+1|p|bb{G*ihMFbKFN>?0pTs7>M$vTBu6-{`cW@*Vh?|yKEciy{)_wL=tdk>CqJfCB}EU+p|tf~r?lvpeZfGU6l zh?@9>ObCdq@rUR;Pt@Y&pIfg5wkuHFG2|_FfiEcDp~-PLp#ORBJ8VlI*sbfzB2-TY zTJhACv)}rGjZMC*KE>2I#%8ERcZu=XfJBpncmO{^!mVL*#d$Tp_QiF@j#Vy+_8K(U zaX0ujk2XF`^VD@^&b4I<_0wzB0KPrq^mr%$_F4y|FaJ9erPSA0W!iCMoB5tI-h4~} znLU%8IzNWbQ$9UPFXrY&3ZDBGBZ1xP=f`j`;#bKw(Wu9B0%xnLi8v; ze%aCmx=3Z?wA(+M!JD5taSFk=2Z8lvJm!JCdCuW9*A07foe#CpgkW_KsA&$MVRr?+Ks3;Vix~$z;ImpE zi5dvKu27d{>(d8_1Sa_e2ix1&-I-x;XA4&k_i*jqUG6)jQBFKcItg%=YcyOW=fd_c^gZp^v{rB@HCCav^ZbhXlJ_9%SydlkJ5t7!y4+E*KwaD_nzP3I47V za}|JYy670p9(yx6$=lzlLGD|v>a+hWTEkBMoP}a`x4W-CVQqWLldJS{az5BR4?HT? zI>|v{DWhb4QfjVN4}7~Uh$k?P4`)tzu8)gOkA(uzNoQ5ox}eAyG-_E0sXMowyrY`_ zfI6p~`{%CpvrK<$$vPM-FsEY~^0=wJWxOwFO zPu{$aXP&%)r*1!ir*2)t6IU+b>cIiFXIl-Fp9$nyhAb1vG65-T6h#HIvw~Zs$Ow92 zLE-FJg)K>I@>~5C`i8BK){i;Kjr7h9i6(}DWMg$$#|8laX1T!CgI!$O-NutoTt-n> zSQIN9FP8Y>{RjBL`w#HO58lTcZ@q`_zWFZRe(yfsKRN-b8d3n5PBF>yX8R%x0Um{+Z~`1&gYB+1p))TT3y4v2Q$ zP_^3ZX!b{e9t#D)R>YkF!F_MVR)ug-camdLq`tF0a`^m_CWlRlRg}A4_I|f3oIYEh zC91kYRadBG6ZxJIurr%t=U^X~FCF04wX1mk?j1b);KHC=0z0--bP{Gx(gvsy$57#0^=7jROSuFA1UMHFDo+5Kt%=PV z#zq-|dz&A%u8&M)m`*e7?rdW+ZFV0>FisXr+&ezSoA2Jk_kZv{zV-bd;*Gc7!y7+% z5AWVP#)FenEQ$iF$UyM`h(rqq}D%o(bZF=58jDL?M^UBxf&Al z&z@^Uwpa+V?wVc%ORsaRklJjYrzANKh>j$+?VUN%Br9sCrzj_l#q>mkwOp#ZRo!e&4n`l7x= zT1L5p)V}XMyq`T#@a+;Jqurdu7OSw8a}-wFgb6+0Z#+K9)Yr1OVn-{xf7sv|TMGj> zTj@jtd&`<3t2UCqERat!Tsl0!(|2y+xx07p!qZRUnI~`I>D$+F_0m4}cDFE@G~4pP zz^Ys|d-3bW_pNMPfo2;fH=+6MAc67eTUukI0FcuSrdzBf%I{BGcsQ$Sn@}j%hWX9* zbAbl=P`ewfbO0b(qLdP{to8Q8S)Sp>r9E8Q-NFlZuH&<>K7)5ZIKj8S|1Q4u-FNWK zH{Qgz-h2n&|KWRB&gTG2)YGX(MH=B^j7K9=I5f@?5kj{0WH&k1{kFXo8|P6ZpwULS zO9`;D3BYsj`!u-EUcF+efY|5S2|KFl8dXuCswD`Fj0lIj zJJ{d3f*V&a(}tyt?PLD);0Xz8*kzFzWWy5{Na0e_rX!K zlVDY#C@Mf?$nqR{o;L~s8$}>?RPYeXvQKr@?e`7k$V(2OmYgpXO|Z^<*SZ_I3;ueO z)E?upcRi^>UHP~QgA0!vq3*b|5EGS&{5{Q;z3&y?W1nu>!@b_Nxo(I}R^yIL&$LK) zluU-(syM%AyPY@}i!QDLz=>xY_Y5We_EPzZMyHF7(g^}cfjaV8qxwUEgdiO!GPJ(8 z3}>>{VaF#I)>yQ{t%*_xDp{f|E6A$EY&OLcmk;p5-8*>cnY(!TnWu2~_6^*8;tKZn zx3RrFL!JwiRpTaL0J4@q#h{vy)dEriT%)z{8;f6khVu8b<*Cz#kY&|-Qr#?B{(bTZ z-doV2k1BH{J-0{!wLcJ|MFaq31j?J=LZb++xg>y^8C=$=>!wg^yx7x1YF- zPrvvKzW4Tf_=7j!#y7tGU3~M6H}S>~-owd!1puhC2_{){o&Z>)u2~z0j5gv~O9FF@ ztbKVhc?ghuz@-IEdonNp0(5e6-X~9HXqT*WEwd2Y{m7!7ejst+t=N z${V-QSBw2*`pkSrRh6i!)>_$ zrKCy(5I46MjFKQA1E>Zl2RI{*7C^M84x-j~4>SslK%V8;*_vX1XB#)KUcsH~SMkj4 z>$rR48lJv&9pCuQ_wn5yet@^{pWfOtlde$|IohbkuG4b3>Jp|4FY8r;)^>+}#Nw}S zeRH(!bX$j00(GF|#|uptSOKsmLhJX}F#rRn^I2e=ehb~xd9COx4l=SP;F>`$>h<%7 zq(@N4CYM&c*jSs?E(^8g)=s zC=1i$?QFLM!wc5Zt&5G6Ln)=qy4&Sx8FJs;UZ-nq383(+p4D zxPmK(dwBKvr|{-`@8S2q{{#HS?|l!y^X<3r?YG{?a#5fbge;rt?TIxLYH7vMc>CPV z!FS$;tlr@~p>Cv9F9VI(3U|z6>;#T|OO)bYr)JZpv1Imf)ZP8hrX1DpY^jw|f0emPRWSBFi)qZ*JweEGqYP9ZPOM@bV+$7xye!D4_z`p9G9VK0X*>;{BTVK1` z+}Q(K0cPXIByYT3U~S+6D*$nF7Tn&H6r>KehHO{?Qh9-Umc9!{Hls-S-Oa4Cqx)B@ z0(IS-(?6SJxPJ8zx36Eri_biX&%F9PKKaVCc=4I1aQR>#IWnx41y-vS7OMiXZZK)8 zq5b((6HvF7zCoz9FgF#n+!Oltq(^{f_TSM3w0)+6#p?#b<<>Pof*U0u11LiiJSdth zDw1GX>%I^fVK$xOiOc)Ab^S7)x^)H5+_{D)uV2QK*DvF@{-99;-nn;#MNyzwtsrs% zku@hGl8!_VM7^UZg%nz2wlEKb`r1I4#?%s#0PA;-LaF+FJ;~~%vUYy!lVzjD48t_s zd;NIeTTL&h3!!br*I)n!8FTYf&Xyh%gRF+7tde9zUe80VICr?;;!z+M&O- zI*gLpY&2{h6?*Ix0NrV<0$SMIK-bero)>FSX~7i%E&Oa;gu=>|oypyylt;1DW2D86oQf){U8I7HOPR-8+1Sj(I^IBkN_mtsOl1+8q*wj z>iT6|KG?>~&)&x8zW*+M^ug_qM1T2*aN<%UO|r^Jyr^UiyB#Ql{Y5aPyHFKAtY_2&x}*! zEH`UEo4+~CpDOS)i`a2)Xt*x7I#twA-Fw784_VoVqZX1;El$B9-U_}&>iqYT!AE|^MRGuY5#AZ8~ zHAmP=*JoL#YpOJT>nXV+u(R5}q8{aLnBHcTitsOGg{mr176o>;Cb)a+20s6(SMa${ zzJgD@@CxPrsO9S{M_u@HIZ;`teMzo#rM)(M13#r>} zOfrI!PkcqF88;I4VDnW0lhlVx%(?fB29fdQc=RGH8v$kW+bx*Ghk+5HhCRiycaaU| z$Q9V+)sYiZ%O9s4Nn@vO?+mg2Tjv<+N1YxE1;9t8l<&P;_zloZ*HvBvgrOPL_E`0m z;!vKRgT`#FV?(Puq*V5#zgo1)vO-lg!hbr+aP`_{Jp0rweCE{`@x|9af!AJq7Pqfo zMV1lDqQLRV98y{Q5+F(7vVr^U!_v%FL(5v>zE*i%p#PBl4B3>?(a$kBcNn)jt0}B8 zq>d(j^o0Ne)JYLq2{I9*Vk;IU8u9J`{0Dw#dWy8+o_tzS5%p0m4(S;x(WT5`Nq_NO!q zyUwf#KiMAjp#Hj;M!+S8IbY&QfpI732@OMDO?f6ME#)I>X)hP3CmT5tYl;)aQFIU>~Ft@ zyEm@krMq|V&wl;u`0YP<1MhxtA5uzSi!hnaz)bC_n@%%&5v24~v>DQYi#X#V#@RK= zWn(xi^!RHN``iA!4mbPMZJ6CIPVvFh{1g02SJ-?e#n|??Jq?{%ykx(^x*2YLI8u%| z6M%sTBH-rae>vPe1T>aE1^ECfCP9~#8c3-l0Ud<1e^xtND}CJ_Z2+ZxaKZ(r3#$M` z^=l7tJ4+XJ*>CIRK=H>(`Ex#w$RXB$pGVi(iqrlrS{W^+#HuJz*Ubj}YnKo4^sVc7 z{k50yh1WlU*I#=H*RNfH0I*mraCEwCw)9DYV9}n|2Y;7YgH=Z(vZE_?ueBK@d}QWr zocnGQ2UJ>^w2hw0`dkEAHwPkd1#k_m3^eBn0E@DOtP4ysfoY!O_O(km+~32M!+q@U zY~j-0E`Ia(-@u#i-N$lOU|!dtJjW!T=u2}3>)7ty$%v0{9`IdA#-cU;wIS}WRBhsS zy;H{peVwqxbFRM0yhP-+h z&ljI{d46Q*!YTkZ>EqsQ?UnUE>d9Jp>LG9YxVq9S1-ly`(+aW^1Q=+Vwo5f(`)kcM zWpTKF+ryZx+zjC5aZ2;gFjU<+&)it=Wl;hwar5eBeEyTK;0vGmBtG@(3wY}GbsX;P zfU6Q^Eg=gD6abZ?Il#xMAsc?^!o%ZW!sXZZtNIC2zW9M1x-RuyAQf1!trxsqRk%Im zh}dk%a$C>(Ew|Zj|65lbHT~DdRn#RQbAp&PN&w|R2J3Xjx(1;{PK2j#K7sx1Z9IMJ z2A;lk6TkAC-^6eH{&%pL&%w+P(@DEckXn~)d_@j?)TvdP(&AMO7fK2nUxQ>vFd4nT zwdyPb))9~XB|O%qllHv?lEn7+Ap#ZNZQ%04=OOitC#~#QR=PJ1;5`Yso2ZfILCgUY zcLI>Lt3sV}8Sk+)G7w)nJ>?~`F6t`BhF8+nnj-T8!JO1dYAez@F31Z&kA(tY$k*I$ zk>`IuY$({mbC`EN5T)*0wkwOg3^gp=F5cu%zV*r1{Uv-Zn4@dk+jzC`Cw0*W0Yn0l z8AVy4EJ_d&_O@rZbK@#L^UCx1@z1@E&%F8)?%cYLJQG+gS6H4d+DLu^YJ!mFAejN{ zQG_<+LdrGz5zl2?wewkWraWzUnj04j*J#`V?IA;A0#Me(2h?cpXDG@F1j6=g8&6)p zf=l~**x%W~{_YNTw`chJcfN-oJ~+m5u>wSf$z;+fh5GD=7`xg1Gi5G1Q^X`R^RLtU zjD%lpP#?!^}3IMoQ&%bE&m?!|o|D|>Tn4#-x$V2ID3vz6*vaXG@+IUBC89ko#@$c5x zLB64$?OBL^bT*q5t}oB^J*$I58p}UJmWf9AR~4$d#Npl!UVGs#e(H;##gBgaH9T|Y zHuiURkpn0eE7YqRR0?p#%|Sk`6~@$|9JPFM_IW$i=ZrpqxQd0!^BOgD3-t1?s8-%NpC03@_fj zflCKFc=Fa&{K{|qE`IrIU&pt9@Gh#d0%sXW5HdliH4@PDnR_c=+b(k38f!%OebzTKd$W_;Cl}i5g^vc9b&8Ea0ka;hrwmZXGi&{@0iTpkH=ATiRga8Q5 z7O=9cuq+DX8R6y=m+^@gpT?JeIn__;t!pZR|%4G$r1xU7#KchM0 zegHGIpwg?&89j^_8nNqg>~z0n_JubP4z)p)Xs=F2h4yG=G|0@fK?6WQS(m5_iOD3# z)@+Jr?>vFSgB=|1Z)1CFf?xT~-^1^}`9mBpR;bDfli379WNlEW-jrY|->}R~B{1Aw z>0PJVyDkyeo#0<5>tY8UQ#xG8u`s?Y|2wW?KnxC$qW^RFfcNS%z{| zU{w^zz_@+=3V!6%ui$6C{CRx#6EES$wJXRWP!=VsRRy_{Kt)ZEpDKf5zDc+I;4E`T z35c=Xwdh@OVkb1{gTvpxQ(uN^fGHoN??fI*6!fF5Lx$ol824Jcvtaqazuab31H1z2 znjvdKHf?@HCfd6On)?NWARO+_@Yz?M!L`f#xO4L={_#Ki4gC5y-@wuF0%ciaIu*$C z43%VnWgE+2je4z-kgkIn&)oiRwT*mn)b_HPOYPV&qm09$V?z#>53jpN+;QlgHKj8m zjp*5?BlpGt1?M6-1IDv%ZN9nLB5T5R#AkCUInITBzNF#V-6BLD)a-BK5o9t#B^POM>20}=VCK>t!e zgHjHyk}2EXYN7`jStC_Jf$Ozc=N?*ArtP8suK7+cPVJGtbuCd8C5ocN)^vg=Z(YNW ze&%KTQ#l~2dAi(5_Q3if0>2Rmqp+y9MXWZ`e0l-ANb@WBhLei z!Hh|PCuAuB65s-0$*9|O*`x^$lq^wHB`U6wXBiImw(;8j9qjB(K}6V^nRr6onJs`x6P9Rbm=cZH22E2rMm3OBM!?Uz$diA@GroEu@@=y+y- zms;CG)o8ssMv#xznz9u*QkQTA{BS?VhvgZOz7!40*5a!&7FfA)2yTSVc`i&JJv~MW zKr6)5jerUjFn@Sy*YCvWH1||d-vW%BThNAQskn?@a}4 z&xc`*4NKZ(!};Ca8&*C(gFFR@Q}6d=zYhPc=A>f_2Ci?Ee4tF4$US}j}LQ(=93 zTMfu%dw#%#kj+GMl3<>->ms8lOJupg-J4hNN5A}OTsz#w?$!jq@|(Ypw?BA*s#syN zHA9}~sH^5K0n?#oyz6U>sdY~{>``81ciGh=*d0vXRHk59L^pjzCr|tldl^zRnm>|f z!m{mGb7HGZqRb?5$F_Kd2q;lTA%B80tS>2?Z`~+@mxGU3=`Y!P*nV-IMgTgH!YG6n zn`3OQ=Z>)FqS3`w0N5Ak@S>3WCrLveX772hrnPtlz??qZV?Z5)acT$p`N~g<7YqRc z1W?x%%A!D76xiFE;`zIG@Rcuo20!(s&*Iasy@37QZ7fe$IKDq`;Qki!7ww5zLl=c) zbIzelF7TY}Y?G_9`HhYHC_}BKy%&zieZ6kxP+d3Hdp;wGnE)~g$r6jz5>x;OyE}OL z=2gt{46?3~3xQwx?LWX<@83tcm;?2+xe8iPyG57X2DD?_9QWW^moUMQLk&1vF9_%V z=A)rcZ|JtVsVB#-QmNxrFK_*Es1@x60%HRVBck6J<)T&BD8&OS3$~O1eT*e}Wt?#~ z4{luR-qFA9(%ZV7qfYWD(1lb0V%nR1MXd8Z4oqGf%>zF^r zXXU)1Ut$LiO*k_c*8XD#3DHFR7X^yt5_{V-eBy;?@Rc8Z9bft4=kU^VPhlsYVsW%Y zF|UAvn(ck|r3Shyg@h`u+x2c3QlGme)#yu9@kQ5_CS zcB?Hub3E_Ra5nq+)mPLm!m0?ePkowC$8Nhz8qDQfaJcZ7oBBIT7+i zMf*#NeQQ;b{h7=D6te0aJ0(7@1i1Iz+Yq!}dD|_gD`dS!;Q;UPS;mn8g9!BP(g~(t zQ1#~aRbp%FoI=dxlq@ zyNjRv!e{XlU-&Fue(q^(Pp6n4FL8RjfT%^|>$j|aon^FM38=JZiVx>!SnxS}6CWXR zg-y6z`uvEgR&i5?faZk8y0P5r_WgXT*)B)~tcntISzvEx8`rNK;zvI50?Ml0hWj;% z@4oXMs^t<4KnT&?A%Ip%CvT!At-P?#y@nk7Z^W&0<=GvsbXhsBrLV2)Y7Ep5guZ?C z6@{-2=ahcY)No)uQ%&MVN!ALHeP;x*6%-t4N~-p$EtFbo-OQES+d!6Rf|t~G=%hp7 z!qelS0KkHiI3Z&c6hP{1={35tYnC{RA#aLI}K z+9GS227A}V{R4f@<$HRac9EC9g)+Q*zQ;eiA;Pdec{TGH-JxhXrK*4@?RiJ55E3La zxZvg%K!pTM2_ysrG0Lh2%O&=Aw(x~dy@bhR0)X+$U;8?~^Y#a*mn&d4!!*xPNf(h) z`{dnY5t8_gBLiY$ovmPp*vyDex5&iy_42?%WUqW>-TFurI)$|F6KC?j@M_vthyrxY zUp!oQXwrTUJO~OI@wga^4X|7;j;ls5f&T1|H7+%e;?Mm}qhU~io44Ek(0J-TNi?E( z5v-jn>s)8`Xwze%064L~KHMLLMAy_D&!YAPMOl8%UZK}x6{%TA;T!JK8CThxYhqi= zzbH`@E9`E~@ba^F@s%%p7C-)l*YVubPhyr$usm5{alC}!3>h-(at$R=J0?W@h`2P1 zM(U(n}fpqAZ#dh~?Mu z{kPvoydRqVLnTYWof9N0%qxVcEB zbvhH?z__HN=~YguP8CKu za;#34kV}b-GY|_vkf&fo7;QUjxko`5LeZ7DKY3qw^;aw2zTQjS9ulSM@1vw~3$VYH zZ2PCFU9uVAl29{4))Hc?K?6vL205x~FfVawcN<^$c&$B=UAS7{!B9^V0h}j*uDuz1nTU6nTU7aux+H7-aG>7=yLya*{=Q`SqOiYUqTQ zh_PR|)1KBx;ndB^Ks8XbpGk%KQ~Ncw2AcqpzYXBx(uGw3e6k@Zv+K_f5-|f{J@?UO z7DRu3W}>D~gSTKk*n-{U;~Y-mjPqC{Tc5uWsB1=96)1}uJ6l_L@!2Qwl`nlBKk}`DIi?8FSzVst_@tLPEr3|a%CF+$#CNl^K&+JVx)iz0zvv+;sp3fVQZLRs1 z^}191yj8?_U9GaZ*8c(Z2wv~%9x{u0b8rx&0wb&2^8y3`S%#{T0IzU)ZwEj2>6ak{ zuw1S1&wuNic>nkmWl27YOrc}r) zq9SQa9Pfpx(=#-0`;NFQi)}(SGb0;^22z-uZsGgugboUyiFs?QjOM)oLPpLL2T||_ z-Wow%wPAO-iHQkh0{Ul3iY(nuo!$x2BXX@G*FlRZ+czn;D)n#YMl?T=GM@rKqHy}9 zCz2S~rS=DAto!1pn=M#o18o-q;HYp@FeT0W5%-tDg?58V?c7zD6~^NdMV8_2ja~dN zzyAXN`Hvd*?`0hf4^J>S7$QTCOy;JEsZghF8qkNmLFQS8zqs{y3bDoVFM>WHFoND+ zf?W9Es;)5{jj`14!g*#pmNx` zwb!D4OaN-QUXH|Po{cn&LLd@%OdS2hpoRV9L+6)L(|9SO76~Z#t1u#|1O-BX5Fa*u zo3+fX01%@@`rrnGuebn9A*gja;@^bp&r%x1g~H!rM7^2!9j;#%?GTIrco(iv0}^g@ zw9QsX9k;(MYjlbnclNgN_rH0H|MK@=;^Rm6(3J%S2Lsdtg&aAg4iChn<5|Zl(fQz( zGpgF>ETZMYS(|Y=!eO&q^gH}|jC8RUgey>tN$8`3V?u2hch=1yO$5nG)j&DI_Sy>m z^KU+dlsQg@CI0t6e~pt;p;Ee_EtToemhURYtI_&KE38yQf6V zCMHu258EfS^k8vNbCU<}4>ex*)}VVnt72QkfoA*DLV0|aA+(KiTdD|Z;r$J3Y@ecN zf`Zn#7y%-+lM-z=f}0@4+U?-qrguY_Ai*!1xII6cn~+eU;Ly!KDmYW~SF*tS=Q$=^MJ;IAMj$P1}|Rkgxs zQ~{*0v%P^oeEAgr`H!FD@%>v^>~wLwKfuYm0di!>q)88Vo^H?U&NyWk=UX3`#cu2g zu-;!nJM@i73*SPg7M7Vnr7DaEB^DPJaARi^zyIVB-n=`&xUTVkef<+o24jdKL!M`b z@v3P2LSmn62l#Js`=>9uW-PoAOCYpVohXheg7f%`G72Ts{uJ}4)0-!oV|n=qUJ7Li zVgFgi?Go&PZUo2$i4r8dvSt1xX4@_EQs!3x2n0F;l!cBsj1}JQgak2NmyeRiF%9LM zQy;8vbE1@C`riAM1Nap7g(IBy{BdNv)KDk6bHh>yl(j;wfJ^}ED@*w7$zA-ffBF)? z`Q!n&Wipe!u6f(?(Aso2Hm@`>fTz?=toMQyMxe?CsVg zKfh1HB@?QkkdcJ!3J576rGlz!2vOnswKe>=KYRk20fSMAfBNbt<4g#UJr>Y@z|$M>+dw2Z;KA&&M>kde{+8>-M%&~$$- z=PAMb2S|Vk7-V2IS!i?9xGtf}5=#p`Ji4=sQ8mU-FZXdg7~z{2Z!s7QkmVV=MUL7C z1R(h1)(w;_l;|69FJe|1tyVG`+zjw_{9J&6r;uvH`MliF<{NXC!s!FQ<}YiWXnKAo zA$yWa zs5U#snAjlkT1_2uvy4=KnKJFI=B&hX+`G9I;%nB|+%^K9ih2o|OlnqDSLkE{_it_E zzx?Sp`1{{}hV`Wt3=c;r2Q{S1^cmL)YT?WpXWPJDc8T9Nqe zyk*wv&K|EIJ#jqxBaAbYAUJbC1%*rrWL*J~0YWGc)#&CO+`qn!|N8r97*`ce{=fex zp1*j58Xd@Pp%DS5iII_9i3zen%dA&;j9d%8SJ=L4Fpg+MgygkPumMaki4i->2MUFN z1d>4fJ^Tn8LpDM@fhi>%(Qwfm;r73VcD@qT{Eo=ULVyHk=>xMxMeH^kM#Drg7*^UU>Gxe@|^r3H4K=sh4D)Nf(8*mK@8 z+cOdWi&y|A3R((|vwo)mGY{@awcC5C5)p*dj#i`L2xVC!&obP-xre{|{A2v?)2Fz# zy@PsK;c)*1veuE>#8hPD*U~IkVX)d3^`yRexLlBcssW4$H33oz4YCP=vZ|rf2#dWQ zK6`uzhbIGk`_oJ8A0A;?))z~%-Fk#b2e8WS` z9Is3GBDJ-T*V*{pNIAAmF_WjskIz;v-x$_LW~lYX*)Ri|R4=bfME6^v(**s|Y{Yyv zf(}_nfL{fG52sM{=a?hg2zvfRw27It?Cwm}%&&;q{NkqATwMe-Fa(id797C{s7~Wi zKXpFNTv{NHUr0r0*kotCugRKPt;7HX)6~PKQ4^>n)FZvJv#tabP^uc8qQJGaWqk7B z7XI}4V;m2L_&@*l1BSyPWYIyVm!nonYyF0~Ic1o|mGFixzjM8Lj_n!7VLa?_e-FZE zaYJEyaRK?K-Nyzsa3*4FV``{iyRV~}ax~3j05T`qe|Bk{jXh$1OX-ex(X1fITi-y zRscwmT|ZfD2?bDIWJ6;jVeDp;X8yQD#@4iV`e4O;StB}GdII^|NCLFwiED)2uPGggqSk((3{?0;9QKuD-A)dc5e3NiI$T21Q^o~KFE z1l;Dk*c+MOgbTIg=qLyK*&oCINf54z7vS6q0K9}50GoJ#wFqbdl^G6W57mSgkak!R zjg2)eQr3jlCtk>Kj0E%*5Xd0no{g*~H1uDW7>|ZnTIl21;|KWT7oXvydv~$6u#9Rj zLOH4-Ne6M6d*(ld(9yXZq*`D}fT@-d?s}<&w^^p4-c$QC<(Iblg-Giogo>qf%C?0E&_q_KE z&qc%I4Hr=#90E5ZE26{*@WJrMBpNh<4`S1*-ZK74gn*mp$OspqEj~F}7iF?c6oHWb z1cC||Mp|t3OJbl>L@V<~FKYOVfAc{P^7#9{umTz)Y!wOyA~5$cgbT^b(&MHr69 zsOlQai#$msF#Gm_(!tZTG#$(6c z%2~f8;Q|+ldB*)&nNtKHwn&0SkR%-xqw$Q9--P`PZv{X@u>fDxUX1h_xv%RiHeZfS zQgq4MSIM6_1t(Xw9ZeS|DRlNN?R2G($qbvD%Xs$q0sijuPx1K9U38FPa5w{#i;jo?nOq zg3LGre*F;hL}$MT^@?EshZQj*z*t$bTQSV-oY(wK&EA|BJF@R#yf70}xU>lAFe6@il|rH_D-2GCSm^ce$&*L;{TH9%!L3_Z==4yJ zDvX9>kW$DpnU28uYx%XDr4XaLLPrNgO;Dvq0#r$mLZGf|91n)bGl6?Ickr91_wn@p zO{}dg0jkDuG=frym@A@_T{*y;|dvJ*V{;z+<@zDvW+eOhS0<)g?GG}dJ@c^t&ALwTG zw}8=$#>9k5sA9Hf5ds0TjZO$Ha&qBx2d>rghBj(qUs;@l1V|nnGmNtQ`8(r<8w2ZL z?U*1iG&`EU3{Sg)A6zVcP6uP*#(QX-dLIeQNZ%UR47en#4~}VplA9fVjRWvYm!u#} zEe37SZX}nDw*V6sSguuA0Fse%kA+U386VIMJz!x8X zgxh<2z^KOY;R!Nih6sxbM=*XZzZQTAs{n-5wpdjGYD7>ux#e5c6=+0QU0T2wA3eaU z{R4dW`~|)`H~>|dL%*fl9SNproO(TziBkP=j^vJ_rKGo*LB+2-!ya#OF$yzfQ*@7* zSly(n)VPOVNuV3d6yl44x7{rlM( z;Kd1TR{IDprn3T{a@PDY5<^7zdDUsFvC-H>{Wf_~Buo&qWeI?JJ^RfhppWoXb&b)u z#OC@czWn4P{PBxVac6f2U72H4)u0NXj3BO((_a9mp5{5Oi8QMFwM;3EQ!m^L9!~ey zKS2utR0~uig-i&@j$W^2U8CFS;o+@4yxQN#KmFw^ynOo(N8=i*)*0WjOhOT_-fuV? zKf^aHXeFFISy+9usT^>IWNC;!svn-iZ=A}XFhHq`PaN)hT>K?;Groh|(C^Jn<{ z=@Tq>`xqV$QI0COKuGf}vi@2=+|n?Icsq*_QX~H*L5&GefT|V7;|in{mik@XyS{@j zp4`X7+t<-8Iv5SdsFgw{C8R)jyl-lW7#vx%87K6U4R=jV^*p=2$%>l*fq576s}ree zror>};j^}pYfSIqygRjgp8nKy6yda^zy*rna|8hYY6AFxiUPSmubF%M(nP3;L2=%} zga4Z_CnSD3>v|9k!32p|0$i)|4Y#3It6HHVLY`;XSY5-%5AWjBj~?On-X6q=aC~wC zK%e(Nebkc+E~j-mzN~7+lzQ4SKZ(iDW452?zK}~`752928X%l000f{0sML;uf&?I> z&I3>@Y_F~0_n$n*+oJ(qz8m0&pI)I-86&m|R%a2>e|@AsZ#(wPRIp{wq6-G?HFYuG zM~*{V_`#da+;dYn0Y%>EB7U*I(bQ*J!z>sS-$&-8P{VB~^ot5h6uWh<2@qb*ITLhr#+F~I* zoAf7IWHV6Y)0o7={da+@!f7hD-q`7h`$Y*C@oz93qTlP{qlb6##mA3vb9);dQJ@;v zD9Z|p6r_|_o-zGe-e;N3_T@z>6M(lCP$TUZppOOC7>tJKb#gqpdjnrSxrf`kn~>cc zAceBl$694%myKQFd}%`6)L>%M+bt;I2e{CE&RQWR{E&VlcT}VB+DwI}{6>IKGu%6& z+6x^jJJ)emK5F@kpFSO~;E(i%|E2`pc`sK20nmlK^cXRJ?;6xB?E?IiMPuT7UOW^; zffVcsPVY-XcaPBh*!`8Gnpny)PJ`)m!|M>Tix_ zIF2p9`wKMr4p-raP6DR&{2~jpvNrtcEU7lkngmb-^;n|_B&TWVH9i8&jj%C{BKV-U;;h5k zrjb(PLjXHdbgxGoV1S6w5!m?vh_?R5>o_vH$1n6A&Qqs6S?{?^Y%&oO?qaBnV*sHW zL$FSSNXv6+v6?2NqFlW|JpB$TGJGKu`h!FgOtCiikGEd6%hm$P=lJe1_&v z`tJKk0O|AAK>i5_3ftCrH zCnJnRBH*P|8-IX`plY1}s8$L>2yCyf;`2v$@$uaoSm+c`!x74=c0#OR+Ajrd@Yyh% zwtH$4=^2z}9?8$f=(y;@Ee)yW2eRf&jAp`3VkT-cd&A$WJ&~gSrGA~%E3#<8~wu5lYyNY<=c0V%hT4Bs1t+Es}>zWlTKDiN7&8&TFJw zA@tFxaap0PDl9B?@$lX)eEReeZeQQSQg;F4@d$NYBa<**|Cg-!M~Z})yb~|&{eEE0 z<*5_W_G>s^O_L?J+4}ls4jgyE#f6Ta6`{DG_BAx;hFi9`J@b5cUB7N5uP+`O~KWfnlfLOG~VRrMwI`ae?nb?l~> zX=*eArUs$I{0aron4rd*5mvRrU^qf9C2nqS;>n%sxV^K9ZodltERbxxIpgwdTJV6y6q0gUW<;$|rTTk@2EUgw{XH$lUiV+odN=c{NFYq1 z_;)qv!u%otfsNld{}GaZetmN2=%OZYMq6trh_ma|@;H-Rd5&`u4lV@<{ay#R_IB|3 zM^Eth&K>l#E=I>gkRnK#y*FERtZ}uN*DkTls(Mc`1tn~vf#w=L*RZjYm~!7G^?p*f z-ve!qkpP#eA|Ryt4vk2tacqwfzC`PIK&uir~L&!{EZ)X#a@7=-u z8#l4JxC&7bM#B+mrCgfdU(4Cc+HJ-m&l z_itjU-v!1cs@giWdS+_F`!x1Ee_enTQ@TMDnjWKd!7mI=xD21*xKkqfZl1XKrOVVg5ZqfNwQ49`;d}RaxcoA|2tYK=+-4h|i8Z?*wN^#= zRe#`sB6J++OK7!Qa&Z>9pg~@JaCR#xP}L>IqcOHN*6{Sv1AO-6F?LorkSU2uncA4Y zK+wURLgO_67L(;Vu01<{kd^n;-FZ|IqkwX{PH;ZzA-= z;ife26QS?7IX(OqPeWD>D|0;04aCQ{Lz<3;qQ*I9mLlFG7+s1y+Q~3~K-hyrZjeJ0 zV+!d)f&<;A3?sCBMX&L3F0G6;Z` z#2tZwC*o~3A)2}FS=%%FbJH#J+>T-UL?BYARRuzg&9xPL`uGu^-oKB2(Zk?mh^nd~ zGU-vQU$|T*{_p{G?}KW`2URG2&Bg)%U>KS41tux*36X4uHZ3V7e;6>RciKDXR{LayZ+MTmaCv=tc4^rnoAq1pgE3|EIUT|riE^2cRo^=c+ zmnNX`o_A!M#@ONv(J+0+8kQT7d8I6|7EXTcvg?6YyW5Kfl3vJc5)0S&>JYovm90@7CkZC&C8{^ZQ&d%}%fge-!rslY#)y z6vli}rD>`UJ8XKP5xB@ik(SL`2t(*Pz-CzlZ%VXGe~bFxaCzd0%vp$2bSmKNJ_hN> ztsuaK07&z91OL8XWXSznc_Y&FJ&$ffJR|VzZn8B)J$w~ z@+-^xtiL09=QPD^R>jTPLfZ@zG4nS~5E7{C66JV=jnyrD^xy%WJ$i_Zcd_5nCDQ7q^sOc%EY(U^UEGfsUQk^o{0j zp@jfa`c!}@fh-eHwL(>vSX$`f@!jkA=BF3l0Dc!e`}|A_<6;{#ZK`3v{*Ff@WeAkHz8AtCmaievDqc$IwGgCkukmCju$}N}#F~#-kFQEXR#&o49*@2OBF(fRuVakd7ee0<{FA zNI_d75n*CUdxoEJW4(__ZJ6XKMfw_rBLQtVN6VA`PY55(Y^q;Xi(|<996i@}@+_FB6oKoJ5VVu8d94SsD2 zfONYh0HqWvMaZPY(#i_%+_-_qckbcF)(#2aeMRCuuT_dXdPbakhH;{`Rq z=V_1UB@`Md3h6i1nDa{!%o$MMq!3^RRvJ zPM%|PWd(Qlc5waLCWgS7x$q=vJ|uA3oHvi7SJ}h*tR)& zyk;9$JD?Z5%rC(tdZzc!N^CC3);>KoX|#annYv8sU&=@3(eJr z&(W;&JjK)L37C^S352=SXKED{~*$GA_nZEiK-MJ^}EJiTo1+Jth9# zhEY`tAj0~}DjwgxkNY=oqn8&L4hE=H4Vh)yBl_3!Ynj`^kTd{bz5sQtFfJ=BbUV0z za|aJ^?O|ncA;{|1$PY*14r{(;nm-?BNN16zt@$x^{o-JN)9?Dz`6s22fKa)LV+^#5 zQj`4yX6;nl_}TGR(Qh=p%DAP>WWtdS>kQa91u(~Dei49RuUie;x}xxjV;XUS>9?T( zZ5E}p1vdfg8)YD6^|E9xBu(ZzDKF(Jg&NwqZ)JH2H+OgO@YWsNxVDY1$T1uYATnW? z|76YI3A(3UxRer+FQEM58z>|UQ_13cMkVK-z(h87YWW`8#Dkl=xVEv1Z+?Dd9QKvN+?-F2;EbzzuHTF428D}G1G#QEIUo|~BUgH%v5Uk2 zP*{&?m_i`#G+NfEXM%6QL?pA}6`=nchx%_sa5Tl{YybcyLa|7{SGaCECpk`zEOKJ! z!44Z)AFQa?!TXz|V?W0k`zVTg5~_R_jTR;ebO4S&R$H+2??X4}`xp4z~n_F1w z_8>s0Rjsq5O?MUhwftH>utF_Y*-BOdsDNs$P?i;PDX_k>h`noT*x6jg@L2$VRte?Pyj!mqSCVXp=1Wwr* zns9x(Bp>hxqxL%*D{Ow1t3h$+7Xe^}EEgrX>|;){Ys|T~&!6rcRzsNPl^rgy*1L(d zlAff&l+NF z+H{?+c^B9J_hsX68@7vne;bM$5p-%hV#A?1^4c8L1XU^s5Tp?3bqicuU%~C2O>D0( zV>lXOJRTzx1@cY~Wp}sWyPV@}akoj$!oJ=%TD-I zOt?$5t+CQF+hZh_yFSHZ0HwvRzvThdQ~|4z-{&dY?_sbSPMB1lEX_8;5- zV>)*_&70VVHbcm?GTXRJZdYg|3hA~0L5TMrc>L1Jl|TRj0lT58k{3kj92?~n8gmNb zYLgd5v|*&s&MaiyZM&TU*ETlr@b(?t-rYm5(?wa8+H4>vfd7%CacXbl#Mn;0FM6S~ zwZ+EX==NzZrrCaXn#YG?JQwM&vm%_+6uhagpEk}HJxr|~r0`!csRDnGfI$m%3@i`< zb*)g>H5U6l+`GPwd)K$|Z(n_jnu2+=q-1eCaU(}XWR*uuw?Ac7zpL&lnKmHDhNJ7N5=%auR?BG4Z$ zqzdR8@&g2&}HG;Kt52ZtiSjV|f`;2#m`T ziqvdoj1Pn#{#xE+nFHFCcJAU_#Ow@9Zxv8g3ZqelPM%|LYZJG2uIbYNmg`5%jjTzK zCTZ=dEP|mMF(y2By1u6CIn{(s12s=s-E1#PY03U&aYR8F!9cXuca|br`WE^gQ2NOk z$sntM!x0S=P|D9>KFi!901Y<};@})(ulB1r-_8&z5w)#hg8keS?86TcJRyWYtrV)V zLbubw&D|Y5xP1#d8*Auy3yg;&?f54`fu_wwcIivA(V_5bEaAuF)@a{(V{A`_I^NS} zO#>h2uV}{G=@gG#<^Xs=AnlCM=v{!{3M5gGK_lme(9jW~+BLUvK%J6GMs|Y1^0(O=BHCpC_T(*J2O9< zPmS+~)$}=HVW9|O@|TFaHu3#2U2Ov>g0bMQj-VDkdMTRdeGDi`H2HMJpiH=Cu7+lH zB@h4z0twIjgX8qUeTheLvD0ZiW>fV}6wt}omV`iEYw!PVr-R#jd$@b!I+hm}pla>) zf2KzLTK;2|UvSP(>0XNoyTbFA8mLNzs?w4F7W+MHuB~8meFe)4eMl)yx@={HV^(h~ zLGV_6&pkpr_Vm8Q?LR9u3H=K$OpymJCf>=EI#P5`u|cKi3I0bvpB2>*oLzm7N81XYrw zy`7@K+R7q!x7M(+wuF=MKB}sM%raz|WT$}yoG&*eg3$!cwL(JVr<3vzXipzHaUQHh z)2z=50I?n&P4qN(_{O@Mb>`y*m3R-O?z7*Tc!glM^D!hh&b~j5w@qy}O}LMzol1{e z>?`Ku6SNJVUq=&>p*bsai~xkhKba6{(jsDn^`cRLNiF7t&o5JQWI-vN;jGAWY^<)~ z_U?7;Zfqf!1xDi%N)e=dAFcf`OBgIsKnMvbC8W$uHbmw$bVX8jHV5ICZop-UXiXwTA7DRTQ~IU6rU+?H&9~y^~_laUUETxv#;VXJSUD(b*>{ z4;QWf|FX*^Mgj!P=>gZcSfQBK_eox@7fBaGQH%?4aSG~rCdG4#0HDPQfJFqH0kR4n zZ_!lG)4r6zEMh6eewRAypHhS@lUQC}#Lb-@+}zp4#_AfN6h?!gW^$125z8)ZF-9ZO zP=G}1t*PP_s2I&jz12HB@PJPO0#ZOnLKMbUOfYF;jl3=q#csItftsyM@Wu{rOHVY9 zz!p>!^RDQ65=gm-!jU)Lj+S%s9{Gwiz@*Sd5DK7*K(&CX2$T^P`(51H-NMc7P5kBC z=Qut*hRP&=ticnS`fX~(F_ED~(e!=-<|WpoZ1L{a{-=D2IC^FJ>&Sy(*BL(2X5MVP zF1!%?Ce6HUIaC?k?2%39!C)Aq<&##qu~8mHKr^_W&OWnq`krE@*N`yn3Pgt>X@f{0 zJA44h&4Wf)$@!r2akn`DRKoviUS)v!6#(MGXP;6(##b(P%92fBiz(Qz0+2$ObN>~J zJjeFNCT{NTVqiuChEgin%vY&20qd(&1YnL{5dc8~q=34rQRe!H?C#bE_O>>#(Cq-V0%~P= zuw4(6$#at>oQ0gz3}R(GQ{3S%q9@k+2mwTnjH8T!nHq1b|@vLR&kx3rzB=ZE;d= z-w^wzKM9`VIC32ZTW;tr5P*xSj&G|iaIN@KVFSqTFu)(3PhL0I9_&o$b z5$d{zs0p2Jj%%B1*xTB~%0eGb({tyyjqP7>fxitR0Ro3FtGPC8?rj6vax5^Ika$!) zw+?d!#IFi98;@AB(RKC5ut$2mr(9{`nFxj_hD3x&)fC;PEI7ihrF@7yiV{{vp`R1Q z@(oLY-s(%HjexR+scifI>o%_M-tA?cYLd5d)6jcrhbLGdPH~2@+!R&z$)u zZ)9}Fp$Dhy)5Y$H)A~_NuSx`_UGh{y_%R7F2hm$rRKE^gnrg{8$s2qLJm^1cL9=kJ^)8rP{gB&G~I&#Rsu zx9~&L7CP+>rz}$jP&95#yjS?<#qhhZ9G|Ixf(WDpR0$wc5G1hB?P7I#5vwZ;==VA} z8EZ#CKpKJB?mfdIg4Z_%f#4iCm`_5hq8Y7wiUhKx&5oMug9|2nN zbH57zZ%7qI&~En}wICa_uB&MTsxf>FVWcKYM-+xPi1F|CS|lP_lSE>zdW0hsqqY1b z+>+FJ0GLm}+)3Q&GPeQ%FE$xa$Xh66KtM=)E`0*^f{Gz418};lLI9)`l%WZ|P6z8N ztJvARhK-d~WKv=@8oNXkb8`UYEWQ9>5S!A;U-BYHp5-X360hFA!C$`m3;yk^f5%tf ze~njfUt=&FK(UF>^9&2!K7M-n9Pf?}prCN)=1nXwYBZp(6)KZC?y79!t5djCVKMk< zAt};F0&Bg$^DM(+zl)XS1uQP~Fsw|h3;|?9Fl8W=OhfslVK8y6xfrbIG~(tYBYtdr zCeOHHth{9d3fB7;D}Cgtz@K=}r4c=?r6dF59N{O}Q73^(Y z!}i7omKGME>JoKb2S?myYd}jUE?xC)Ye?9Jb^t=}PNfJ%Cqt3vDC-hGy?Ty+{`0@! z|M;i>fp32J7RSS5KnU#{V6`9tC=lKqAL3*T^DF~AM zUq%E;fV9bvK)5<#faX~Mh>PR>O`E$&^Ps5{Hw_)1tx-I*XvzTwsFgxpmFVULHrLj% zyS0Utg+=62VqBep3B;@OeTna4$~Btyu(9_FflLYw`wPPBcW>~ofBiT7-`D5=MR+W7C`K5UBRqfg91w(Fw}Z{~bu9E3Oxyr*YDoUqa*iUV{1zj3 z_6PDeI8D`@^&|yQO+aB$vt_Ibgc*m00fgvA1;% z*RSnhsow+K5w}wf#pG+Ly>t=#tHq}&S+_vamTHr?gB?LVwX2Yzc&#Y7=BcYet>zDY;*MGy__8u1dOITZ8Ls4`< zq)=7bC*WLrdCJ2xnm{jpPNsH>*;+5ML>hC`gDSzo1Q4I&4%UZt2rzR@3P43BE`UP6 zQ{dXhDz?^FP<)>OC&uB^oURF2h_m4uMsH6TM4B_lAILVydx*8K_vpKGRpYV<8~a(O z*}qnW3^)K3612ws&in=%J*A+($^MXEDHaxQw6W11OpiO!G62(qctySrQFXH(^Ny{= zc8a~<7&H0{;lj0F5&nNw1OQ$1WO}H55)~!PF2zmVq!F!am-G47)zF|60?II>PN#$I z%`NO~ZlRMG&iU_Bm)kF5+-q+PDTS=)AkT9g4i544_ut{4|MF*i^TT&I9t_a!cF^zl zQPnkSs##zl{k#wYMP4A!_0k>=hIsk<6~6lR8*Hp@px<4<(!vs|x&lzLdHJ>c(q*D% z8EhQ^C_q&@)pbmz1q9lKk7wI?r6^YLu=_d4~-=TxlC=5%=%@o`gY0fP#_WRh|+QIh5HFWY0s;Zt@0BD1>rao{{ zg{EDmiTB0il8Es7?Hl~-U;m7M|LU(e9-N>k3J9C@g}W_9R7WYz7>Ec`N(_br{P6q- z>~8Jg`u26KEU!S4@V3AU+|7;f5Yfc6X(mm!bIu8x_3?}9d!+fdMk<`rwh);UiARjR zUIBHjP*)W?MUJiY6NhW&*#R0M#gcMeD@BNQqmlin;E*IJGF{^yy^f6X_wd#OYSMi57W+N(>n zBXmnit80RPX!Eh0<8mbg06QZ=k_o;Ojq`>O{t%57=!hoOcwVa=TQL@EVh9C+Y|Ikm zd5-19C0yIs#QMrAvMfVYX$^I*y#LQwh;$fEDfDr_e^F>t!rOQIc=h%b2BU$QnX5gXA8a{`Aw9Zkw~(q7>Ke$4 z467@PSYKPhLa*yKHo2fcvdN5u7t&Lpcw3uX^tx2LruBr80e(??q^BbYoW|bnZTW3f z%bHSR)Z;ANjQbTto&y5Wam)8rX75X2#z=rVPOMB$wAnD1>Cu5Jx`DQ6z8Ev$iTC%13KmP z#9+;GKU=5NxV`CLU|f#0Y#GzzITi@!HXC4UYqGZBzy-kF7`p-bV>FW{?$mSYRuZuV z&jb-ztrhP4e^qHFzq-7Ft&L5rEw7-{=|BK3GvJlbRwwR9;o$l_&r#JCUcG&d7q4Gp zFdXRdlFiD(jTsVkrMzZG0IIsixE!M_OJhR7>`ovT(#K!R=}HTnEBsk)9S=Z32!Vd5 zgQbNY7JD5OnS?z`s+=;w>r5H;Y_giO7+;!BbSdPSrqprDvj(#bA-8~954YsNp7%vj zRUi0XNQ*0Rx4e*G50toQ;=c&Oaq(p?5rBA~C=)AQ6Mb$S_O^c!NCnO68k&FWec_js zSXo>`E;Ec#`gRF{)*Ur%qy5qwaq9bh z--MLNGKsP-@$T>)_7C4-T$O;-CvSZ!V8MJVjDTcdUF{13Zx7JC0CiQPsw&h~rSk@u zWJ3Cx8GMGPQ4E^x`-PwDZ&TQ29RKr~xTd$`QYy44o+8vWAV{E-=UD7@vDoXPSLE7% zrdA-4K|nBMjzQPX;4VCk2;Rf@U1&paS^C_>dRu&>vFwuz!s8H{gi}JwERmR?rk8|Z zB!<<%++;|EH9IHC2>3`2`owv$?~gS9ODZXE4gPi7x+VtI@`GIZkN+2DZHVCde6xLD z<`w}63hBTg9{!J{Y4?$Nck+cY0m4a3^)%sLO2B2d0nDMu1hJ{=3Pr93fNSd;=ykf< zi@9MlxXMMiF_BWBt}C1jPcR$}K>)H$6LF;!NTswSjtF={rI8^|+(>EY7&fnx`_G(} zIc)OTN@A{=R~8vl01+XR65XP}La&2vkwXdrsx@5@ag~-MI!K%w@~o+_ZJo6XCNo&- z8dt;GPXE66{;7TltY+a_Koi?OTk&n>jPn;)KxSxl2=#|v{(}5>K}L`y5s~=$-H`K% z07QkX*9pPpinoi}-D5Ypak%j;nfU-X49gNvnTW{SJhuNOI+T3hR7BvS$G7c!YJeyHZrAg41T1;Od+|L9D{%%j zf=T|N{YQG;0!xd1EcCm0cRYY11px`45s(}j*YMe44qpOca2)-JM-x+ezF)i?(@G6V zq(+dZL!SJ2lc5L}G#U#o?acs&Uc^vk^ZfzNVQ+6lENt;X2&!cnSGg6Gfi0=2btuH7 znZH<99kzc%dHwz74q;yKDs>oBRAQmWZ zr;a$)?{;x*Z37#tYbde|Rb4_+4Joh6GMiSoe}q03No2KsAGJ?UF)=}pJTd{`dElX%$z8*vms%`1lBqqo2_JTlI zVT?dng2JPMMR4BH#wf4P_w>43AC`ptcs+}i!dZ)l=uf=)-uIPh_qI4EBo0!{@% za7IuNAe0_k36P|@v>c|&)j$ADah89BIT?Rp^Q#Dz{~1t##=R{!1&D()A5x=!a{hce zQkfJ$ujpWPX$hNaYgk!Ygv?B!8@dR)+`JSDh?0~NMV=$ia|4TN>LH{Tu)ITHb8@sV zfdmMs>IzawEH5o%ePs>(ZXZHO|LljtP=7766^?|Hma&z|0bb-fGvGq63n?=#AOwoM z|KjZ5RxqbP1O+>2I-#6@@ih1NEZTkxiox$`C^c==yW-or$j4-y@C>zT_a4m}I zY~0}a0hPElV$uyCG$D%?s*wPQ3<^MmqL5fz>|$}Di!2kcxiq{}n$__lOo;?Ym~-;% zylF@&+I0JH@ObJk@aN-o5wF{zdr5<${@Iy2_4@T&fY|TROSlJ;Llh!YM+04@x$Xi(#sqpyB|WsVVm$W{X%2OvzrYwrndYN-T4 zahW7Hr|wNjyIwdU*9=nn_+Or7SX)`a*7^pP7Z*{O`1M{f{h%rG#T82<6+tP5JS(ua zvWm6k6=YckRo4(Ofr4Z}x}7F(pp%t?1=EZucbhGA=OHus%!MReQd68V0&u|ON$H0q(BvS?y`k|ORqk* z-6q~kLdQi=gsQ4g&%=E&b-X!B=I$iF5k!1GJzCHrd{268CyzhhbArbch%=tjwEDSSCRVX# zTX}8!by{hA&Z7EpYue8KUlIO4&j^4oJPIHYu^uRiBrl_l~l!^YY=u5a&RYhw$&ZWm=$ zVK^G0R!R#3Hk~WHqaYDbD}~W;h?A2O6f(!H-RpRK?*Xpw>|$}T4=JIu2!iGi0hr`B z&4^QgiU>#{-Ji~Ar^iYqT_EO#v@!FIi31{qM8DfbzthzMKqF=ZC7GQyJ^8D%j=^Lo zyD1#v%o0_~=aiz)jaCydwLjEW>$H3A?bI0J*~&CDfLaZoXWg};rJbRvKN8s3%K}q? zXo?B*SmqZ2;DrQ837rf8){m1r1nNZ^`)z1#DDBQIqTTDkg2-iu;`^CoPhf(FaKZ$U zf~sqDivpW#tJqvyMW-lGt6C?rISXTI^<9X{Wv|o#qCEvE6(;ntQYbnFmKT?BeRmJ{ zZ{5MWgMIw?;yFg+5sIQfx9Fk_cnlK6>IH)6Ie zFU*ZTu0J$-7StFqWQTC^jp?6nVo0`@i2YKd%Ak)fenFfQAOcR?>4LrABBvJN5{;D@ zqYic)lI{<7C%Ta8QJ+Lxo8nITH7CIjRPx{FuUriQ0EkQi;k6X_CAg>Fl*wzAi)qS3 zEx@hKRMzR_SYKVi>hdyjDN)z8@pzxl#dNMB%(w^*=gLfCV|5Kr?mxtEI7C@h`1$3} z7!HQ0N-ZcEHzeoqX<^`Qr;Dw%Ej)en5uQE%2sd_jQRF$sf90XF2LxdB%3$EOo!o`fRvL*y}EDH%)w23VjSxm4sfmhX=RsLyAmC zM^uELU%kL+Gy*{&&vFoy`%Ms`-&w+~y<7P7qi1;b_$lt(yop|~rya%W+GtD?E*fNn zSWH!2r`UoUnogsdOYjjOI3Wl^$4@Bo96|_GWoZ&1DF`XiDRLBf4k@HYUCIhdDW_dd z>;7dgt2UFno-G~RN!lC9CIu2uwKnN=@*KUQKrSV;rt2p-gm^vt+_3jEEt-sW*OUv| zke$Q^m@sJ8+kor46ITXBfOC|U)PffAs-)m~|Mr;7I3$Gm7Q{YGd=%rxQZz22_4kAk z-Vb=92cfSHBC>49u`%pcIEf&S)@{jeg7*Qi!lU(}ndOk^Jd`UT0C>>KX9MIB-fVt= zv@AY(r>35AG65y%CFd%Uag2zdl>WWQBo_NUtSv8NX<-qWlo*f3APa5&(u4X-6az&o zQcz_LDY{r*SjOG!w^5Eubc-&&`tBRNefJilvIK!pllJuQWH}bPeO%kz!h<^x@a*wZ z+}yj4UayC`s!+T93e30nOqt&ZI%{K{9VwD=pPdTV&I4_z31<( z%&7oCF>8ggT6s*=8~K5m>?XJ@Y_x#}0kp?I;`6<>ekKjDmIbynBZ?AB^3&^fvAVp3 zwUret_WKafrvrqGG=0V;c44;HGrrdk+r;AS_NfZAW>Tvfq5>BB3wU(zAvV`H@!tSzsi$U3OY+6f7%Guo4Zi*1JG^-H0!Jsus8kIIpj&jX zzPg56*KgqNtvk52cMA*s1rYQJ3~9AOAKLOP(;Ii-3=i7ZbB#J8|ICL&kwPXi!_Uq%5MV^5`I5d~Voo=}-acl1;mKT@M>2yF! zp|0u=HdCSqK9NJAZTME13FNudJ^+>{x$vAYCLs13GXbQ>dPXhTX1oa)Kd&RS-?v#D zXDQR}jq%6HNB|#~DG#)@;IzwxH!Stps@Rcv^-Oy;`Ou`1G4xs(rq5J=!SVoH6^wR% z1%S9H0FvO31X9TGnbj{8nnlE{Q-uPdFes7W>i}l!X>Qk(AShKq)e1$PV|8U2n`>)W z>MtUfIYv~Ywk>kB?X(cx>2JTV(qj9Me~+D*CZUPe7lM>RRhN)bqTlPG(v`5@Tptge=KwzK{~$AA1Zhvq1I^Vv zs+o3R_J}k}A9*iqKzzzWIIs1|GdsImTl2n2wewshT>psa7Ei3jO{XsVaqvLX>>^s< zYwmB6Y~pr!7Xvc$m%{sF|Bb}5n&SIpr?x@1-9j+yClH4?qi4vl(8z>7I74l(oU3vr z1c2b!e_(^Rm!sw5Rv-KdZk688+$!+Zuhah$s=7kAQ($>%39BnB=yp2#^#o>S@LPAD z7uUIq@!hFajcTNsQkG@N^IY4mrG|KIT!pHxFdU7IQ=3n_3;_I?P%_POU?cueM!m&+ zVAfJ5GYBd0?(iM{{j0y?AOHD(;Gh2dFL?R(6#!^qscVD;5)K9*k4G4dhxqa5pD-Se zk!2F=tLx}@`zW$P$I%x6L0z1tb74aoGA8fmBN!@T3AAT({CK7nYywg2JiZSzlML^J zLeaHZo}A@1vx^gP(=`&}1s((rd$ZMh3Hzr_fIOqT-aZg|R-6YGF`*+ijWqm4=C{nT z01z%+*l2*n9RbOHd&37)4t4!&Un2GuqI5Og3IXAd{F-+*ix z0SnR}a%!|7K!P0Z7xjBq0QNi}?}XY(XE(IM&u}=zw?BM`|LuSNcl^_z{}~5Ihsd*B zYrzHvSHuFw5~0Wo6nT!}XoT1AUgK}yeueF=Ygp*@vAea4qEkSr8f95K;nmH{X}f*S zb=W$ZT;aAtKT``Ed2*OatibNKwq~1f%D#=vPHqSWQA=VQw4W&UL}XWH^>~`mZt4l% z%rt1O*0c`u1n5$j|7ubd7){~O%Y?WBglnQ_Rl z{2XkTDEX?$U&4`CE>un$XJ zO$@xIsE6@G9m6JBx6r>80II6Qi`Osk?_d8F|Niw~@bl}J=yp0->@R8)lrYag`87il zMJ9Ei9H9k>*ZXhqw{O43#_A^4me;V}Uq@8}V;X;e(God<7vRU}{2eX~n7Pwy-3?a8 zAbu~d6X2Y2&(l&j=&r^5zvo$bDW-o`k#lle*fGh}&#lt5ZNWvYAJ)oA9is_9q~d%B z;o$vRv2ToX1GEb+^I7In0B9s#Q*>QjyWa7RZ6|GP+qT=-HX1ax)!25@#;m8Tq`)PmnsIf zv|P$wpbppDS5J3)aQK?QC9#O^j+OJLIc6(BC83%yAf?rk=oas~_E^?i7g?`IU*Jm7 z+b?5y#*BDx=FWp-f;Jdf_MEaaZ1OrF-#F0OXcG^3?@=GFRvbUVdVFqhft^akVhz@z zL2=EQ0{p8BEOIn(6958oAe{vI$(%Uk|yW75a|{;*EDXd#z}7= zb_vY;-|f0)D_9-tlqK3(KZ)Hk-5w;K&W$zudgo;!qZ`C~Z#9pXe-m!%R1djjafhiZ z%P0?heOw7e9Y)s--6={w^oj=Bn(Q$m)j`Gs8@iJsylf&Ary;Qf6H;3g4a?Lj!?%y( zOXL`0z=el>}a%W$k zftkUEx}`E~igSvPc(uyzg#e=syhbVzeUxc`tamy1lA%A@^&V83`~_G3R?*BTtRLRz zlEUP&HJ%lhq(|V_6(7bV0dckf8gk>j$cLB=8oCma+sB_f`c8fT)@6e)5fe{|m%;xQ zNgQ64(r?5ucs0ff8w@etE=C0%!Z8+nh=D<^h#$T%86=E2^MeTHm@QPwiW^60s0ums zwBZ&%XA>4HT3?iaQPHDzs{Z0q&{#8E!`{qpUGJ+*{NyR@6kdhZ(QtJ^z3zy)%en+n z#*AVJah?+wfioUK6E{rNC!Yix=EMnjanUtT*1$`j^ujCa2_FQIsc`xf#Yk0`{rh6*Av+Dv(2Hdla|`L`Z%S; zU=fulBn|mESs>lnL5y~RuDt-vjOU<*%4 zZHZ{pQ0Ki|JyU>Ku2b-n9#A(s%=Im=!Jcn+Mz4Ff)B@`4e_I%--{lXniF2Y2o#r5DiBzbzw&@(`QYVhazI|myxc;{H!WT z2I3XX*;TX27tZ36^4L;FV>d|D1FT!8J$(}^znt8BLUfi!L!gU6$$_+OqoYO)^tC!? zatJw_3R%@Q$(q1eFjqh!F)$qk*;tZeo0ISMvCb)^U;I>Q;{qSoD&4A>I7vwu)hY*@ z)apBvg5XC`2vgyFr&8&#<)GDrsbK|~6>qIgxPRb}=pk3l+cG8pU5Kjhsp1Jih8E<1 zs)n}{eCBw0VSh((HTFz0xc)g4b_1>9sdO%x~HO zU9KWUp1Z<7Ksjivfn#{XRS_y#X7o|uO{Kut6zo$wyWQz6MnG>(+L6@(jFzIB&;`{Q z{Z`gBN-P@~DSs#x5$z*z^vK1Ryo>^%k@C#(*)kx+L}&$3GbYJO7HK1`b(ESP3;|jl zY{n+8uj%RG8Q}qU!SYk?;0o1KJ=vG)knbh}J9QVFBdMJD-jXtFSghi9URj-dVrO~iaQ2Z>FH`3`;M=3qT5n;+j@ok>M1O6t1k{W-b{3c60Rzlri6P-WTlykC zdVKK{Wmw^qe+Hl^3>ZuInh6E%gGDe@Y$}=p@mSK)Ahd8Jj)p8p__ddRzv0CsZ!!pMA#W_ zsjf+TBF~<=sSxrAL{L4FV?oL! zh8@!*f78WmVb^8NU`EkCVWS|RDg~pd6^@T~XnEa2&NUdUET!A56w zX=rf*-C_Fl!X$(0=LF!iRwLv~WKMX!6-+XW98C2*B$TNZSN2s#ln|F0w<`ZA2TqZX zihpS$$ZKH~OAWvCa!uT0kQgeQttKlU(WDOkjQWUbG77M=5rU_{Y){E2b_j z0Fy0aJH+UR8j3yK8lG~5MNZEKRFAa4y*#e;yb``&0aJYU+mZKTQ~!CB{_Fm4%f~5d zL!*baIKNT`<-W)MCG5-utL5G7*Rb*vEMz5crUY6o8kJ6=eV$V(3N0JMElzD`W5`gb z5TB$&5aV1Mbbc}IxMEuRB@$Xk86eq*=wE$8J}yY^_tXQ#k3~P}0+N?kCtYwRHx59Y zUpi8=LwJj}SEVPbf7p1n>b5<6cT*;4md~VBw+-8Zx%4+fkrDYHbh?Q_Pi7 zVs0!gW5~XPv%Uy^5dO7tHyP{C!Ntgin~^0U;VGcMW4fzKm-Z3<8i&*OVMTYBUB=b} zDFOv)CALm7F2|sb0d^*$g03}zftC!ZoNxA1L)a_~wyh#;?QV!3@gUv#JwV;s))gIU zfNHL3!a`uoK@u!;B;zju6rupp51R&o3~3C@&3kvw(-fPdjcKl{8Jvv(JyrBqf32CnNq$i+Q zHX)cw-b!?QyIGr*<;fFug<-9BhatpcSX}n=qFA(72Fme-m9-yg;Dxr_dd`LXg&-*mo2zlghXjEv`i^b#SZ{T&N#iWX8n2nC3ekva?&7X)_5{E= zW^m4Y*g+#F;eMfC2`y@{jw=DRQhe;4qMf}1KqL;J&nz&c$XD6mZ7 z#fh^>I1JyyP%1m1Do&M4lm0D6DT$-A#`kq`VrcT|R_BkF`k#fPt&t%??kUmq?fs4} zF=d0==tdtBD8^&ci!;N_TT0U*bbiuzIIO1*izaTA}`O4g7TWKTXyKp@EmFDmz}R7JBI5^9F*Z(A>*gn(5lDQlvCjH$WOe-p9EI?$;fhW2&{`+M> zAutl&b?zS8+p-)xx*WR>Y&8~J#$Sh={oCv`M42s8GRQ9-ZBl$u(Q4Z?TZrQ+M3K-O z;!pY;oA7h=pz}Rt_7rXPc&-CS{ue4vYcxYzV$f{+{7d>F4NAI zLYQMnU%%szS0iJV`2<*W^R%I`Ps=Tlm9VpJ_4uL!Li^#9mS0XPadQ{UA{LMraa+45 zq}{_kzD?l(W06y1{~z_hg<=uKdX*73#44%*K2ne)aL{5Rs%@`8_rHa1uuRC@axnxAq%vPuH$xaU(Ow+n4PyW^Vbv*MVBm^jwcs1}$^8oO*@ ze$OERY#^}!Hg$D|y8UP8aF*zE8juqLC{PDKu9QEZK$q(?-Gf{AOvUr>#KmAaT1;$( zcCJr~i&*_pYTyV3K1RLYobr0<5oqWu=)p9!T^7$>B<{eo*f|&M6kHwI?tUW}m_yrC zg3Bp9!FUI0na(A!qXRsFrlzs2FJhj6U(}4yGxWNy?pjEGGQS!R>zcMHapTm$k&y${ zJ@l8y7T6ay*Dti8dvjsoHgr z|IQ9W!nt%WSht9`d}%5`%T)Xy80#R*)(*;;nWjxxwR}{`lIVw1gb*oa`AN6fM3p@e z4eTiH$jTRjgJtnT{d?=EbTf$r7KT zmM-0WR6Lc(^SDq#9+7siN^q6|@4&*zG#&Z%)a8j;`*we6vZj<^(*c*z_CK2r*ZFpL zd{H0#ef(AdgZy9Zu=>i&vv^^VaU$SXfJHO~zAOg$3u7uPQm)){q&XQuPtM>1al!Ro zaWR+o@P{>n830GVMY0jZiI zTULH2vJ9^qk?1(#yRNwdS!M7&Q0nc{Paa}9Rp%J^#io^LyBKH>&4~y z@08Gxbr{2s{vk=mtkT&Z&r6iQXv-shr8uEjDIHO;tN^wiv(9K_@=!KNQG1~kGF5TB z3dICL8|t8P>$_GlP1S^WsiXWHhDf%h_;yn zoUcRuJjNcWq?*4#t8gp4Y2zS!+c{wO^tC7|-NOjO>|uz7vgPK)wJr?llOh>~XG?r^ zuL~;3E_ky3QkO&gVJWHjwlpfjg)r8^8lfeJZvIA80$3jJb^gcKJ!REp?DvD7H^Wr` zG8dRg{Kp%%hu;1CPMEA2`NeT-XHS}R_x?^7Fu90o{aqt5Tnd01;elYrUN6xDZiUMheT}Ge&1G7@`UJ_rB-doQ@GIOpyACP_0Fm($0&zR&ev zT8E3xeiuhs>E>$s{lnGfKT47Rl+|NrSbQtw?_D%C@sLLF;^=5#M9i2bECS}gl6QWq zrRLTNfa={Uwg~0!zzH$t2ZB@O!V$m7^_9?JH6tTIC19{6jk29FQ`prrXU-R9^dgoD zIH^NhcyU#mmX2lcMMbVjy24J2qeDNkcDD+l8-j6@FTZ`Y4k+TaJbMb)*w+Aoqb(X| z=bOx5lWTwCSLh%FO}ZJ}=^Q=odTH%EPcyxrH_Tl98zJ4>ip>J#-XS5qRy;wK^kx~0 zPs1r}T-uJ4uB@;~A!eM+nwZdTmCFJv12s0xI(*O5r|-+oG1+ry8(O=$3)|wh+n>&A z@KGtD;kr_LBYq$&XBUfDcYHgJvleb9LCxEDWhtT66UCUlcPhcsP&~Z*zN)w0ThxL2 zACY(5-oLW#9}RF)h3aI?E*hGlc?LowS7X4Ncy6n={d-`lE338511jqT06g@>sF@Qi zVX9X+A%?A;Vm!jbAD@*G4O&vy=1m}~tTt#P4=e_ zj;6M*m~Rd z)0~5ZelUbib3hPVSy+sdXyI=eCFav#iP0UOwf#0Ce?RI{71TXsX(MJu$l1+CEQ`zC zi@Bz7cF#n-mQW|{W$#A&zKGRKG^_tWDXyT|X{rbSlLs@?-u~7%pQ}0b*WGC5%fownhWlr{cqu$2SiE>SMYmX7ZWx6xnAjMn zR?(v1WX=}ql>Mk^cdzhcqm1=n3mimMmNszA#l z0my#wV&W$tctZ8>s?K_EgS2vv(dDMHu+dkPf*8fJpY*S<&m~HIFZY~&es37UJ$^uZ zjV*9V7wuV>E<2=10^;(>V^JL zE_{Qp*aIzU{>vtibZR_uDc!XbF7;~>68K(6M-elQ0L&|}Md%S&Y#D^Zs0R~&^g&qA z8Rh8|eh>@Oku^rwFNCEA-ij>S@D!nyR8zOen@2dj&(VUn9Wc*1L^%TDOrJx@7NKKo z`}3<=@KXMJ{nN0Ol!gP~Uz05})I}H+WgL!-Ig{CRuSr^P_eD9T6w6x2sF!N z!$E+TN+j&Zn5Z@kGh#do-dTH`GSnf+nEG>CJU;rfps=*#y~U_LkJrz`izoMw{~-`+ z|3LG9y>5BD9mCY?X%2PBEuxc8HiuDzre)uowcN6>^T@=|^@ma>O1|pV3Zuc&j&o4W zPqIu1xK}~R7k1GJU6IU`hYWJ-T3v* zv{BHUJbhy39{D?cZV;m1U>I&+4!?Ozfo6qBqHJQuSz!mrm6Vj6*m(M!ctkW;4&C|j z;t(~cKLZpR5iu!{;v=RSE4DveF^fP~y^cIJV%3X5rSJ`z_7l!`UpH@JKsCi;Ts1}p zi_tcjDC=q>NlU#0_7(#zW~X@$6V@9=f8)X+(S@0{QsfRSZS|XeiXn{y(P^5pknBC0 z1@t+AOR%o>c=8CDpsm7cLP7fV`??gc6H`$n=*`%KK-cNfy__gjSR_e;Q$Op;F!tNIT=Dp$hd zns-rxjBt^5zV_`i`m3`zr9TD2EbbYz#kOO$uz${~Yj-!e;_p<{Fkc5zHJPCYL0~Rh^lmJYW3uFA*m|--qzwLnDp3wt%KV{8ZiF%8+Od|!4MvoBj&H!}ybcZ!^ zN6PY_A87_g7_*M@eU@WSVCcX_#Y6ElKc7c{Pa@TNJC5Q4kOj(E`wBE#7-)SyR_!JTB&KO zSZh8a%Uno~mAR{Mrj}Zj!{wFVkOBn6Rn11De zr+gmY1&KlOpHuGciuO~FDe>Q`4 ztif_HG~v)4;kKPPM>~$55LCU=6^)bj^;Pjmt5K!=fptsK$gy-~gv3yN;>i5D0Sh7? zTH_mxBrl&N0NvUH3<4nLix~=)toCgQOLH{%&)nZiM93a5gi0>(wG1WPor_P0|XYYB~_E|Ni`9Vsr8 zR@~r|uTJUpbgEi>l8^Hys4ebXz@&+KH8PX?$0JXmKj&oqtIql10;q`%qQqwdBPUOc zVuBqIh^}hmuC8zNo}DS4I{Y*-H3tG90{vf};W`C*`mT&Z7?@SCyxjCya)(!;S73GX zzU55?z$*E=6NH!UOV7QYQOrurJsNeZl@B!XvP_$1$U~5Qj{Ok{u@eVDV5O9K=rFJg z%Aptu)_u72$n%R7_IcLb=s9pI%;HLRLzQsJS}M}_jwsw8s@ z)=%=+PoUQ#YN#9KD88Qfb#QUx&*YrtKzH4up@FJIm2ocBi;xhXyrEfzWEnd*OgKGo zMOl21e@mr*Uo)KX#ceH5>0~5_`~5yGwOE~0Du}JN#OVS;d=om~Y7bsY;S}h60SR}% zq6_!D;{P`by#@OM@*5K#l0v2?kcwuLRF(o^G5TYaLqWq?jl<5`GLFOP>F)3}2>=WO zAaj)8FT@gsYvM8S{?*^{FX-+YZ2r+g@LBt7Q!N?deXgUF5vb@^Lw%iBEGkF%uC@h= zB=(U`t6J(Y;6abeCdSZ!NzNo;0wiD}2U8EA=i7in#U;dC5woVNp1RM=HBh@ly?&=lk=csl*_L68BdAYg-= z)If4znFL6#G~EAQ$W{v(^cp2&gFo-cps``f!@PB-xvKCnU-t0^XiMPrc)>7*tR>2^e0vMe1&~jkN(O|FG>G?TsD1;kbeP7=W4? zI;V)pS65iMPafl-V}nYmbH4~kM8p)4{uKU}wsNPe8ZxV(fgXYdc*jkXdH8sTJBmzW zhD$IxLK~74zlQf^?(Y|i-c0UQ+&Byl+hlmHlDeTPCuF0QB}4ew`Wu+>*0(5r&K-L3L)swe75ic|}wJGSvPaGpnNmCM! z_~iPUS*Po0XCc862pdbh4Tj?~<*s+zn(0=y+k)E--F0z#rX6XDbdeO5kxkIYg9z2c`b6+;%5zh)Q);7q zqmb5v&e`Aml3|rpi<+T1vDmLi(eamteh>J5FIT1;8(U((Mp+1uRaX$i;3L*npI}6O zWTPhDZ_b`2s+DnuEvhKg(8Uag`1M5Vc4R$y+?~ILm&u2{^)WZb zl{p^R>-!Rdgd&s6cM`c9f8s*-=>CnT?vlH@bkhq+Px}61Mc@_HNZkCWM{`Ge(!krb z*h#|jeK)hBhpXVHS1Jr3lmR1#mrT_6k3}kh>)Vj518(+&oN|F9c($By4 z(3EdE|A=+bYfY3Ziw#4-j+J8F#($^rN_1Li^L`L~nYma%eXVI-3-D5z zqXo?W%^{7IhUF}8^boWTU+ARyFRkOm4X{fzvXhNwOVHloI};XuQDFJj84vM&2J7@x zaOqS;yL_DoBK2D&;D7;C+JF-%V6rilxQ8*okurcAz9KX~FM$u)PJ3DE<`8ZQi4a3&_ZW_i3$8xDiZq$?9-n@28{VEer8kas^G;^%Jo z1=OT`lL5z!@Tth;%Hz^4F)|cs+l-*bUwISvzQzlh{!?aa_9qWi(gKjP!i6)ki4tmx zeuQHzw(%Vi`+;j{N4Oh|z|zl?j+ugTN+nuzL9zJ1miCJ{^YFHJ2!}Ym1_xiDFAvJE zgg!X87Uy0(f!Hj@pzn~O(28m5%cf{BqGCs?!npGcIh(?ME!!3v z`nb91=_M8N4%)m!E>|;l5|H4n6g4%j5Bwo|(<#9eRL2cAgdL3&S;idY0J~uOjh?d{Lg5C!vriAOZ!ub+5H|bHW3hjAP+_ zP>~kh{FRed36{Y){m5a${;DhQQdVYinrHo5I*Hl@>Lqszegc}S$c2DrkKwRRs?Jm~ zctTA*NP^x_JPNTAI*tAGJd4x$Ro9Y02j64uETZzCw&mkyNF4d4O1$>*iBU#fQ$T{N zq$d-+Slg*sHqFU5A)?r;`yI3klz2cg$+mECLfdCn1B-i}&eaeR6b+&)j^k80uHB)R z7xZ~Pq*6WbJ zz#P`JB~0|sS8=;p7Ujb`>tF?h80pWI&Y>wKLfx;2DDUeR!UGq%LjC8*e8rguEI-heIoP-EQ7#Ef z<{D8VPOwpj($gajop*K;2LB^g`L8b7240kr?9h-B&H7ZN5&+H(g;BenRzC@NRzs)ji@jclKOy} z%b549RQBG5>i()Bq25?Szv9ECuzrT&57(_Yb(UOn%{?oY!S_DaI3*xYU8(FcI2kSB zRSBHoZ-<^ZykC8+GjdNFPX_Je?Lqu;#7(-VoaZ0Yb%&fxjUBgz?Bv1{BvI~Jl(%QJ_Lup?K_FTV5s|8-uvahw$-mTH-wN#uyk>1T>+>I|K|yXDI% z>mNiJ-pu4%geH<}3jxrQiq~W?nUOg^u-PDbZP7$`QX3V!Ai{rl?)8~RfdicOp_n8` zmg9_Ul{n^pe4&#FnjOYkrog<+K?w;3`G6B6@De+%lCp2aa*ZTfbP`U;5y`eK-mN}; z7G&gXqdBHjqTf;cbzKPWvh9D574NViqJ@sKm77n1$(G396HhfS$*S!B z=7Kcl@C|ddkp$DJP+ayv5dH_kw;JHguWN1$$%)mpt6|DtNx{?PBY_dk%J^|`6N;;) zyT>Jgq%VP8YVRXVcE515Np6vCX!76szr+`{!$< z!;e}vLvtn=5bfvhn?GN=>zc!|I=q4M8sFFbr1;CjW&PVIrr{!jfnBt{dgw$pW5Yek zu||*_BLOoy-(S)xw*f7@gbazfvALtaz!;C_yC-n6_>7{O-4}FybySZ|>ShRk**;&J&%q48Ps>SnwZnNu8^EQ8t6j zNG^|b;^FDb^0;tvasz6&gGLp^sJ7J6S^cMXp+1ob3hmX05X9GZ*M%|XZ4CUknh);o zsw!bc!KT12ooE@##prUe+Tf-pf`|s4Ghkevqh!vM|EZu7o(}GsVZ_D%|I?YY4|k__ zfxsUKF|rx_TNZh>NQpe<_5CqlMOItZ-R=dJm4>s1Sq^+U;<~mu`It3Qi4HhJhyBS1 zO~C5{@Uauo(T`(3A5+wMyYW45P$XMzvC=ILM)noayP#()oFftZN-aBem#Nr`WtBa+ zzRi0^*tziygkC(IG3L`{F?J+$nqV zpmX^BR?4sc5a;kSf#J8Dm#lzahcRPJeh~laJQp;3Xs$OZk1uPtI;88J9>0$XAH{{OheGGHG??x+MWl6`^k#+EV=NI9vPK=Dn zX`+_0%p}gy6v7e%A*f&jjfi(LMe*bLDPF0j6+HnpOPoNI;NIpHSX0yb1I{6>We=wf zvQaT6{=xV#a$R^Z_u!fH=^l1d&fdYD@oP8|u4|i!+I<&SxiG6A>nA*qjh+N#P0S3Z zeJa!Oh2BcHHlqIV{a=$McuBIv>YJ%$4;OpUWu&{iX8_l201X02mtjq7aF|fEJktD? zKkLl{U(icVVXXZLYZ}%O-nJMRkR#uI*Z=D^?M0NCu)`quuib0)xE1=#Z~L<{p8@lk zA;gb!6?w~@7lqHsHI(eIs)f`ICP<{cVMX}A-gKBs@nnCba_YPIvVVm zdG;3zM7>Xl=DBRLnfU6f^WZRb9L#jRrP=lGlov|ktK}3^HV(hV)1DN@zxu- z66#~>b`Nc!`81i)E$T|t9%{2F(-&~-LE4q8!Mb3imFx-Fg12xn6ww8-LI*xsW|{90II!T*u{gkZhKWEb((6RQ+hDdP;gI-WOr#29?Yt z5;BBHxcLb7hO)kRUOjV$?Ww_mT(&+YW%gArYe{@nLMe@NDZoivxJBe#9?h2k0=G*l z^g*<;(i~8yM2UR)X8z)-gqbiEXSqfXkpQ{))!Jg|%DZfZ5x^fp?owbBQBMexu?-S- zgY#SjcabfRyEnZRNNv*Ld4E`5DBo{v;yJRcqg`*t*}p9|TfKG;-_jE(2yHrl4#+R} zf_B_Hm{0hyJGvf%JAXDG*Fyk_v~m@<<=cch%a+r#9h|rAPoD-TcpuONEZlotlC~h8 z3JP5W0M*GLW;HoLBL;ku$Rr)mO`eUS7k_Mr<>_zqw2o=`qvry3llTi@c5I+UgcmG5WaVFPF&dz8zpw&R%`7f z8z8apro|J8MG{Ft*h(v|8PCc@UtTHUSQkz0;rJ&$PiUaQ`(bv5kB7H@I&5C@x+5Lq z486!5!Idq5P2?|MVL3&pzziVY4)|9B6U(EMlx)PXk!|b7zk(p>@D;fR(S^$CiogCd zbVH(@*X80Mq3Na!%Q%LGk+_w(9Xb^OW4cI1s}UwV0c{=)cM#;4&#-6#yf`3FRg|@d z$!S>Ax;0^K_%x76s8f!q%s9J$G#(8zJpI?RypO7rM6ttD|4kG=$)6ADHijXJ?%DH` zUAMijQzEbgxA8QjNTcI$4k=;HjXb0+O}Kn)vIfIVGtM1WoriV$s&j-LpKWvsx62{^Ry%lG%5CuBpSGuVRbG$=$j^>@|ZI#zTB{%VSmn zUtV53U7aG-XwAH)6S(pAm8bm>-(jbrWy>&e#mbyWV&VT8#Qe9vo z*&7SxSbDTYUFbErYH%>MM1=63yh-d6Pc}vIruRq|vKXyf}e%B=U z27*SweCbmEn?S{%rp9O&vW9zJjDa{~km#`1pBZ!yJ$w>Vc;tK^7sOuemU$~qh_LHO zjxyEiz3&+g>}L6R8cHJJS~wwcOFRI8y1z+|5~rMxWew3w)VC-7{Vh?SWN*M#RDLEB zY_3_vKre#kaB8;;^p|7#KmNsc*IHJIw@KccKRY4wJ`9y}w}awY2)#szRJDcmB#eX9J+N0 zdl-(oz<)X8xC6sCBYlyBhXJH$y;;Ff{}mkXUcqC*|J5FjI|w^jXn0fMaKA%<2h^>{ z1YT6m_O_UYey@1`&#&x0ZZ9P5bOSq9U<$ER?J^VfXrrP{fx|o|j(=hZyewoTO-ORI z3IFCLgc`L&U!nTpMFmVkFQE`>k#Q(K1;!6cXfPCG4Caf5b8-CjL0_y|KRddSILvKH zefn1X2Th^6sLsLE1`e()?4xMsDs?P7i!BW-QUtBy_nO2a>L(WV3)}G5)kqnXm>yZ> z0uupDxy}gg;Y0i3LkHFEulP~yK`fJt3_XS*<25DnxoeGp7Omq(VL8te(MyG|9sssp z6&lpBQJVt;lP-1G%7vjfk(Xx&P&RUXxzx&s&42%M)eqkCW9$xDjc<-i~|^ zC>~;mL9{aPNl*a~d?=^b=PtHtKe3H@=v3pRR=;ZZUgQc6`#=9t7if1zjP^oZDj&Au z2vZyX>l>gARDoi}h1eE_@KDi%h$A*01$grE(=&bxD#jkeTNS_Fv4wy?Fx)8LN!2*D zf0P)e&Q_jvp%cu)ex8&FvoLzEP_>Yl=^B0=;VdDqnqo!<+JnjVSuO1AqLCmaOgaE}KqQE6vsj75X8)ANQvxZ5Z2 zt%y=Y6BNbb4^`MSgU-B6MXun)B8evP_0Z$;?>X}8%C>Kyu+J;jNe&EBd{{Yso++6` zeJOJ9(=#!0kTf~+&97sG5?Qr>*tYVuqZgX1r*3FDUN_%CNlK8NgVs<&i(R%B=JKwrXD=?0M*i}gNP-UQPC&Bab)m2VDsEwQ}^ z={-?R5HA7y8Plq>276l{G5ECVEpac*v$QU59WV%KPs;d*^cH{YH3E1SSRe-d?QQUl zr-+buWQ~!o-CfnD#dusxv;vVum zh{<6|#yRWnK4q;u9s$BPDZa<}DNI*Wj~F+RmzaxEMmQVxB14`a_cQfJ93YJHdbqf0h+ zevR?-yOM5Zxi|uL3^ICTqSfY=H3EyNuBq{Wlo$pQt8q8Q{dN@vXp#{U4Y#VGo&9;b zV4DI0hfW>op1au>vJADa&EIySB_be>T%|D-Qa3?qMB`pm_q!YH4&jb0TLd9 z2}uzn=#O)Nt?4V)%+DQ|v|1{3L}6KEiW+&2(^PxQAM5R|+Cd{&85Ld*FSs-udrj(E zbR{TFzH9`4UebPD4oq<4#o3ZwP$WtAmib&yMEp*a$|{~-v`uXm%Y}GwwA<@>yJ~st z@x8)#|H7r3N3&>ZetL2xGg5<@3)UZe9NGA7z+T@?2BArd6mE(XF248;UY@&iz*OLT zuvf~WsBFPQyjqD%aadX>MOR zUD%`g8X<+A5(GlIbAj*-a($g9>YcvikJr%^#XAgNvbp|#G9!01NXSPm3ioD!>y;~s zxZZDL9MeUGc1G;VAWCn+{nmzr#z8K6QiUnTy%5_m>#NWu;Imln?l5>+N zPHMq<=1SDa-AGidNOrI}_(k%yC&mT-cJ7J{J|f16jb!&=#qsL;3}PIJFlMS(_=K|J zbh@@&(z8-I47B!9<*(2>Jq)9~-wusDzE}ck!+VlJJRUv*8`q@dn)2Jh_dMGiiI?PW zlKcYl8Y$B#o&tCUl^l>p#2@`q#f^TBXc)9V5;6l^)8O%hIz}xg5vg;}!HvqHgLVsl z83`Dvn}`s0)~O_Hh;;ecan0jkKthM(#*YOxc|j*2w7lI1XUNfk_sDYpZaU--ddMgS zasPto_n>d@H#391-%aP*y+57ATJ#aw%!>uEpSQVQkS|KLRlWx~># zc)(wngvIKfLhkQ(bOyo7%k^)~)AfxE<-np9fO|u|El+I`DEKVII^AB8DN0Y_djh%4 z8GOkdKI}FDD51f2Mr}L86j%gz1S_GK_(D$e&(2>ofbI=mc?mDaDp1EF zXo9?PcKS%*)s{*S6On6pND8{g{heHAbSg7(F5qkQ{+1FkBk#GAtdyngo%N5>~R*%u4`qYLw=A(#wq`bw=7hNBTw zU87eNxO;sM|M1x}{4bw>nKC z@bE;3EtUdO7_(dyyCc~1B9u!=Tu5Ull*kt&&=jVPr{HRg8wvsf_olOedJVt`A(i7) zQIj0Ha{GTi1%QOvr|ISI!FRWRA_2jh3Vbv{6=?qqEwJ|}y%_U6L#I=q+v%XlatP=M zUHZ+s2-gnM5_nSN3^f-kiR(oj`n$W{+IqdAlMx&WgaFZi0};G0*pTiXf8N!3N_qT| zO@V*L#TOudyL}svmdb@OUElNUg)?24%`oV|w9=-j zH#``k--W+Z{}YaQQ`PqZ>76d+=jdIR1 zp8^1)edHKAHSS?n0R1n6{Ntm3Hq@_m^mBjE1W70TA(Qp5K)=^Pk>}b;C=O!@&MeUQ z4X{LxG1tH+pvjh#d&7#t);k95f#99?f{d>7e`RzZg^fU5XUyK20iJxb&c9jsIJPjn z2BsK~bIY%nt^8a{iTyXu2f2Kxkfz<6Qb^>T0!QTuetPv9-#veUgQH`;q1_n7R9R1~ z6atEXab03G8iP<{vDe4_8+-WEXP@Igef}k$+_;Ud$T2(~LXpM+GuXsJ=~2Jb0dARI ziwY8es!})}4Ds&p2q!1UP^vuc9M>L?*SS^7eo2=%*0c{`Eat7*MDcA&exObVe1(g^`NcE5?uZKYq1Ev6!fk+*SKNj{Wvg@>lhv+kU;)xv{=;W1_L zPJvc~PrBK#euat;+X^Y{-^l_oh)+%1cId9x@JtekZE9fwVK6{)0*LUf`g+&42CBlsv%{e zXOx)38u_e+j>Nd_%?0<_yL7$@Z|`m>i>DVF&sh*62B*U^JjQA81Y%+&X{15_tNl;( zpY@%XUxpu-Dv)Gi4uu z#IwAM;Ut0)eFqQ5@a$3*5P*<6%=oY@ada}k$>0RzatwktSvXUOh<2zo8%F*x_NeSt%5c{Lg$CDR(&w4K z3Hi&Ieegem`@0I|$_M~f2yi^zM9Z~=!EOQ@5Qrc7bAm#``yYTi0;o5cM5j}r*YBX{ z=;&ig)v!9Mwc6W_Wqo*jPCcfc2MDGn1TOdx(_VE4LafW3vlZ-jM9%<-p$X!`0J~L7 zz@a`$0JD8Y!)*qXdjW*?3-~Bs+~5|y)LHb>VnJI#j}X~l2uS00D-1;oMx^fZKiixF z?c_i9&6rYRQ$swiGscjQ-*;+lB$9=i?5Cx^izaw&8Zax;WL_f>M&%f<_xJJrPe0+s zt5+zi5}l%B0`u5?BOoCmWd@N+RO1mw;|in**VfnZ*@H*;&!7JmUp{<{>+72kr9x$V za3D-NV83A}4ukijKjq*=`@q|?8BQibrVV476hc7c5;Z9to(%Bz;0TAuC#cF2BJUs* z)@*0~BuD5dcz2uTed&B{ihw=q`)A&FZHh}o0$g@c7UazSFK%{h0+?r)fU6+@z$^%h zbGdW=a2bAK;d}@2Gh4L{_GA45?EhHUUsdZ3S{U1Zr^t~>9raJSkeeZZm1N0e;im{< zZ`>IF!0qu7O5i#&c~_BTxT3ja!#DYaDl~<~a1Y(aO9hf`BKI?0?#_uq2{(H(Me_>A z_afSJ!S!8Gxd=V>qKmctXPJZ|!qH%Wm-}z=;`M7B93Fs>A@63)q@)K{k-~VabcFo6 z!a}!)-L*A5yLS)2fBFf2_v9J&);Azjjlu9lpYfLhQe@_|z$_yXp?jtcr|TfXy^Cmj zv%0MbKuCc+&rm|)@c0P(2k&q)80j>@xnWmKsJ7mV))APPsf2=m|KpYhIBKto7Pc;! zzIFx5l@I`|^PU$1JhNA%f`8$(e=jI2(((IiD;yw10hCfuwaLHT?_y!0r^Eij?6HU! zhqfgy6qsLppn!JmE7n+pkJc{$aNh33-(YLA@egFQ(i6nWyjn$()BH(>@IKd9q9$`? zYPbN0XW0k|@@-G;rZyY7XuP6r1o?1*Q(O@VYT<)1=cn`wX0`pd^lSQ!@nhk7YRh*% zt@+c-IVGRz#@DI$*}W%C?EP`2@Z#-0zJKvEUcP;UdN=?&edpk(Ed&A}i5k%M{#s#q zaREyW|9owd#-ZE zb$)3jMw&#+P1Xs(&?pi*uZmzt052`_^Dj{5Rsi7MxjwLu`4XfAw&!9wb7#!oF#DRk z_dnRCAy{-kAQUDnw)ssE9`C%kxh?7+TBL5D%kI~hhNC4PSW8qZ(9 z!Ejswc@8P1cV@P*C487}Vdx5v^J%jPhRgf|g{dxKOl@yNGC;u}r-JEJ!1G<^RsaYJ z0i*;A2pC5M1UmuXzw@Jh-Z>CK)IV#J&oYTlR|o#-bP7lzjV}SS8v=~il3QHF8DSd= zEvyj0tWK>=Q9_W-0>~f;UVkLe2R}Pm!btxKTQ}DnoRvPQb!z7Cw}>V@f@~CqHYXzy z?m4i`WsUwu!yr5|z#{_!o;FkSE$jytT%`Fx@NSDkL1aF z3T0J8)r8f>KJM=A;maqF@%f{Nczp8~wpP~AD{_p>F_h7yMa1Q`hQ}ShCikax0%Dmm z2Czi>>4qzhxyeT(1ccNZ_3-2b-~Id&KfZj8aix$IU1U-+f+w7(nv0+5!rGe1qr#f_ zLZMuwwhM3PDG`vi{=WAz4cKtM8~SA_U%XB*rJgw}b1DF&Rw)HY0Jq~iTRu2iz|Z-U zHzSazNu-p{vL&?b-!XqFov^_*hEx=NVk|UmhxWz&+Thx4j298(vKRO=H-PfT-xCEM3RaZDVIf8ZATI}|*zPyTiyVvpQgZuc+lP7p^^9DASR-lwV zHdl@-WHLi0rOqn=hCUfC+rB?TQ`^LoC1-J}h!MdUFAEHtUosK>J z=lMTit!!-=p6xra6FO~;`HxlFWYspefO#%+DgcBU0E4st zq)gHug*?m9?{~4V&_|Ky%*VjOMBcVcHaLmfgbs3mmMcC%NWqfe}gib5aEL6+v9`2|d)IH{@*5ogivQed|J-K9E{U-2G~wa9LImxi7K-K= zp|H%KzuWGNW-rp5FvLnzS;5oEfPEDjxtO4hwLHj~ydNLm#~95CMU;VXQTp<8i zCXgWm1mS3OjGy=4;Kx_5@b=AH06^YfK(FXvq1(f?)eYR*zK)OY-p9xH@8i+!TUc9K zgaBbU8bhfO2vVC2j8h?hR~T|RWYVoNz@&L^Ri2&gD*Bz-m4dh(7O^C43Mupnhj)iZ zc=>uC`v*rDj!MW}BO2C$*$OnA-D|*taV-Ip9n>o>7iR>TuM*Qno<=F4dF+m+ z>M$?S`^+!+khHKUqm%Yg%X)1wI0SOg#gWtfx5)!x|lJ*a1ug`z>Y%jITh{5dB>Wr9mU@oxwi-*M1&@A~4d0Ruc zbTm8^R%S()nO~9|fBxJRq9kW7%u&<`;xfz7>Ew8GbbxPu`T^hm^b<%4^fq^~v$>83 z*KgwH)*kNe-opJGx3ImofyI6oK#kF8424>2{CVa*{eeJ32H?~DGS1fs0nN~^nP*v98_--6nno!|a~k69;UT{H;W@tk{zn|141i1;?|-;{L>4!5$^pSr6^+djwjk(A zn~C6*Yd3h~j6x~SdjAEKb_3b8AmQ^>=28G?I1U31NFNN^w+Vfe(fA2k-c|xGM}R#Z z2m}4swL($k==ZzmFZ7XTnLA!+W6vjA)*^_8uw(Fn*?dFTSpT^nhh472oJ ztHBva#4i@n5hnM>s6P=j56zyoLL_H?gs}hNa#D78e#Er9@d( z5JH<}q|7{;M$x*3MT_9~ko$$wEROhZB4|8?-E18_il24Ia8>ve8z^_H)NOqG?hya} z)pz*Yx8LJrFhK5(|Jl2}wyqs@WlCNKV7+=22w7kXi7`$8HDr9Dfs8#Vn!hyH1A#=e z|5^r6v>dvRWb^hd)G{HBmd8pQ#hPnN|4az znMx^CRSl^mc2+iUd*eE8UfadZ?LAyu-9T4%wV+NlhT|cm1oB)W&oku4(RT6*2xGiY zFKnevTU-KY1l{M-Fwa%o7$N^r2rUc&z-V0I<=X>%^TUt$>E%n5RfSHc4?t*VXET15 zSvg}MNAOwf58-~8Ai4;Kl)1oN@l2j({-d;DsVsz?4#doVmQ8)m5;A&5f!u$bD0d~w z+zJ4}UeQthh!hb?B98_rcDA2bnqT-VfXbW#u)YIIGm#?C(Cv26>vm8SI_jTN-q&5U z8P%0VuzA#f(d5%n#61_;E!Iq1R$6@i-QU~BV!3vUX!HAz?NP!J5HzpAa><{Pb@~vb z6Uc@;T_6Ifz{%eNOz7b6Fyt=MV62%ZFncDZtGzwE1<)ExFh&$qbY_$?`CtOeJ#{hT zDOwj$Q2&_;TFW+w0j@@en_!ta>n>5%HOgTP35gq=Gc^$k7aLT(@mLA5qd1PDR|X=3KR%&!2zrp##cx)`lr z8OH7${MecQKtNz9L9G<(NPw+wjz^YLK435_QL3q?!S0K()*?jr>?rwuQ$ctOF_pegU2QYa?S zQ7g_*46cek(O#yq2O^h=udWnEWeHUYtS_!%DPKaj$dStoRaIj+9_YCJQs)ZDLRTBYrd(XhpdAlnN2>)^(55u!#vlG_qW)bxg(Y@Cc9Yj zet%st$=mfVfG`*nZyebR-|G-kO8`Vuz~C_kiP_?vLgm!UkP*0^iiRH1S_09-Iv_{du47UpP*n;i$0bgV#wf=%>RO?a zwL#ViSuULbAf#l>oWr-~FefozfQb2r2y`TO7%HcpA)b4Lj1jdi22 zha412I?5)cct57{W!tW>G@evt;(_{Ne#~DFC=k zefGPS0BTaIR?_FPb4-~KhWmBNf#Bdan@3wpfljBuLcfb{uVd2vDlZLlgfCJp(+kzz z&OXgXZ2hte-!n-F@X#a+cYEp*W2G_d?k`({koE@<>E~O=W~x3Gkmgy`nmu_CIBh$_UI6Q z`Sx3U_5BYRj!I;mF0w2`t#m>kw;CgRizij7#oiPRLWJM%L_vv1qiZ}G`Ug=cq`7IV z)_h7xsZPOZ{#fh3@P`ko@24npDF9fe?Y^p-NYxc7RT%()#q?*Ie*^aS;ewUYM4?mU zSm<@p>lP?72_l8s1@{Ys*%C2;I*=ZdP4-f$|CY4T20%J_(v*zrkN6rfZJO^?1KTB6 zYD_gf!3bDwO4|@2ISXuzLEZXl;9QrKF^tykRP3&i@f_^jtK*M{-=^GE+&)IiedezO7#{T>zqF*l_+uv(UIE0Pk6{*z%qOz1W0HUz-ITe=ijAp3)dTjxH#m` z0eDn+!m3E33#TdRNf820G08hQilT!`Dg5;675?_!_jvy5HOkQl!uSQ$#&0R~sUy>0 z)7yCeC&xb_oQBfB|JiOBrtieUIVE1>Oei$Va(utgSCIR~(F`7ZjEMP;5P%A#Dj)OR zJaQ2@}#`;|A!RaLF(x-L~sr9Kegz5V&IKd$k+nEy(Fly==O@(ha$ee@T4 zkeM{cYeb7S<*j5ch5#6#=Qf|0{Ec9#`{1P%xEt8>)+;$Q9k5TvFvk<~Gz14~vrQ&2 zJrV$9!-mc*3MnDqG)TA(>UiW7-Q$@*Mb`wUChWH`5ht_5x&{z~ED6lJi;rUP$vzh} zy@l8{7hU?YFag;05Cnz+Kk2D@&LS5C_RP2kk>SI3H;%pE8oBL)h;D<4!~#*vyM;c_ zJ|0&X4aXRlr4Gdn$yhXeX1wSR)&bDYgJJv<2(QOIb(+6mJ|xWQ#(TpzaBKh5LTS9q zeZBleTCvHp44qB~`|tMguU~zQfBNfR@%rEZkP<>>z7L$UawMw71QH1OfigfoYM^Z& zB`aa?_OohNo)t*=64N*f7zB#O(O`XJi%@c5!a$6`Bw$6`A95?guRLM#0ab!jNu+E5 z2jPj({FS*B000CCKvh+$uGLs6DoJ@0fbj}6L{A6EAxDsm6JV`<0g60Fzt=^t-$7C2 z#_mW>0?Pi|FbOnT8QRg>mvU3;ZSV)T(?YR3wd%yAD~-JsZ#>abQ>ZC8+QXi6AxL2b z2F(ydQ$Q24_pfSuf5_a@y-#Tl7q5Lc3u{&Iky? z?fu!$B}tYihW(D2yWj1L$UUpF_Uf*#US_6eyh|Jjc<=gJ+?soLxZf3{K&F{Tm zWK>l|<~^BTeBbqJZo8f3XUC4kGWpjb_fJ4%l|-j#Tf@d-Lx}i+EuwSb!AF6ys|z3?#4fN9I(-@{b_*qwm)N_l*VX0#AGr;k!P(IE05Dr zu+X@zLvWpFr;cdf?J5W%+~`VJ-)5P-UF6PpSjHT1n_AHVcK3fbDmry?Y?YdL6^hj< ztrdUT!8!@a{k2qU!acVb5wMZo^!_JR-7XcVUW;;zUm8Zd|cu zU9BiWQaozcoNj76hHM?eK54LbMm&M-$S1Di*f2WiY(+>NDVSMe9yvv_3A4IlHf3E^ z0s-iWzWNlg$nP?@)lSLKS!x-4rpm1#l&1jqiGGlHS;_fCO?Jj*d24>1}Kpp-&w ztgs@nFd!_4(BGc|@AGth(e7B-jf)Tpq|_Up6$NY6PSbXY2?-YS_x}3$3wJyW2m}|B z1ULH$FKHISpKOb4jnd~17#y|NWgGRYXoP?^C63_hAGfh1-Gs0w$0i*-fq1w6F_P4U zqJEbc(#gsdOmJIX!|Og%f4av=3Ct&%h6t!@gZXlWVYWA(38hff68F zNgn3+VqP5I?Qvx3D(b(U9DFBYcxBK_*^Qu~zRaKT{U6*y^_ZOZ0 zw~9dXifM2B0~4}LV>ldOJQ|?LGl1G-f7COp;QwYh#o9mnBHpo8eeEy1AL$rc42|2# z-=idW?&XB9$Q<#?NY_g&D2r}bW+-khYf{Egz33F#7aU~m>m`_qQ(%asU!UwKgv`c! zb-P@J-a^EYHQ*Va?V~`{yQ+#U9Q!LWfj9E~UUzXRZ0u)o*{F0s77A7`AK@_1Zaq@v z+x90CbE4jqp6X3CBLrt#iRg4@$vbAVy7hG)6)#8ic&a8@jMWyLpPrU=0#zeW_erRCAkJ%?LAj@ zY*h+=@?~|VU^qG<7fg)^iE>(g1V5F$^>6kQlRhzmpt$Uzh1JlF>64wBGum8Ggq5t6klkpHmk%1Ju69Qc=u{*!TF}GZFsLA0b7R05xcS_kWf%USk^6$ICi;HSQ2JryAV5$j%!EFC+k|? zvBDC-1MQQrE-m)fTJ;fpDOG)8*+^!wogW(Vg1Y>Hjy=JhLP|X&ZSc9VOX*=61)ZO*> zsDqjMF7Z9}`g|POa{=J8n0s~cN$I| zTZB^6b#g;kS&eZ|2zUs33`H4RpCOo$f5AcgY%T3@*&-^({$V$$lzW{J$V!)7NIL}5B^ANe zQfKe-3u~oEuUj-7aNirTSo#2Mia!OCE+vS<+K^;0wqC+VVtspk{rcIbwef8lBW^H* zD=&ohCBz}PBgO6TO(Td~Gg1ov7YxP+HJNphe3D|X_?h)ty z9~>Xx+@()(=A-ktb?EGZF`x{A3Z(k18#Shlh+t+59dB1^du_)F)r+=ne5*FZh`<> zY7b@F>www7UeRX^P*)746h`9##*-oPA_tLbV*%J8Bl3)YTc#C0Bp`KHIk`KHVCZ9m z5Ygu_kWjm&HDGa;*R1r|Hp0%Lck)~A={cf*w0 zg(>vC2yoDLL9>6mlLY0!nF1NuWOE?{}Q7=?vxS zB9&C?vTWz|?k2cscdG4?iX9^$2|y%#om&A9+%EV^+uLfg&H2Z@l`J|~B=_$P5c$MaMwl|oVLEOJ6> z;k(la(XEVD-SktAt+``b8;fNgYvetP!k;*Lk`(|;F*~ycz9p*a5~i+=;a2{0E3n(i zY1-QX>-NllmT8Q~LyX5mXr*9kd!)}=7_C-K>6&=3+*(N?_wTp65#%2=$>+f9xJDly z5ei5dVLYV>NW+YRMH^}#P4mLJ8!t*ZyF0SSg&(HQ+i_Xp2OH4{_o8 z6?|~<0&>d&>ABp^RGN%Z31Ep<4;#k6;LF*wj7~KW%tJp-&t=4GKV#Sh&P4mkX}yvZ`xS z)uuMYZ4^KDR|D5o4K~e9VuNAPT=tXaAdtW9Pb?-D^U#CQaKhRC-4BBKf2{x{X;!X@ zkdboUp-#Td%ded&mh8ehKSkNwov2kEf5L!+faMUTDV#pGA^yuF~D-{Q!Cjz9J!(B^!H{Gr!F8p z1iV5P?^Szcg?q}0EU>-`bvCD|C*79JH!3ea)9rT^yjL|dqpA&-s})wO5_MfeYu(=R zC%mtfaN+};P=|{W*z#y@pR4ekyNX6ps>Z%QeHz)*p|uTk9KJf2Nvs)vF&2gmvaCR! z)#s-F+&*axem@0y< zHqBq&^qd+*)jbxA%L#arAHvrIBSfUxwtl^on(r-S;sm`F{w)e>pEcfOmEpRotCdt! zo_ImuM^C2$z}&_GYKCjMnsZf^D`V=FF{S}?T)cmvt{GL;9PJwp3rr?M6h#h2!888c(s57BOKCuwBL2f8|j&3@1(plS{gN2kr0aD*dp0Ku5U^9AZ={kW=i-Q7zl+i zMvER7BG6ay$H>n({AnQ9=6D|)#&TI=u~?z1Di60xfp5FO$#l6T(6^i4v`M00@p6+} zIF^&7b)Cm({kId+sxDcUL1h|u5BKr*`SUpQ(MQ-no&v=HS(Y_zl}dhe)G@51{~ZTP z>ZXdkZ#rvr+xABDCwa04>HGqo`CBBmh7`v~{sIJmY{!9D%=N1B3IMf!XHTUP@Kh=Q z42BU9%xr#aR^@7Gj9D6E3<2(}e{GsGwg4f|3+zoMn2blzS~VvG&}1XUqTJ(8%~NuL zAL0>YRQBXfW^X%Sne6?xMycDpPWe4FIUrxSb zt!ihpy1g?ISVVY6lG`Hb(cGm}PRS!zCnL&`P4iZNfE7m;c^nC_3z|xWjfJrynW}%$ zBSK-FMVzwUeos`ur*}FH_3+IT3jei91-I^nE>5Q11ZuRL|A3oq%>_P$)vAsBH(Q`? z6?7%y_uF)&yjIZc_cuSPGuN9{;IEO2uoHLw+>>d)@>>hC-Ez^VJC>t;!k%QlcFfQ% z!couUx^REHNC8nq|}~Dj_f@Au1XDPjUx}*-Cf5MYk}z_uDNt ze?^HMwQ${c`)HkIE*?gPcYnwAeDT=CEO-9&^F^#Z$5R;81e95tx>~MQa~BDacaZ>p zv&0<40idJlfmtkOtE#S6rZ$F%tmV&D)$aS}MYHce8X(Uz5C9iVxHHE*Gw2+<3ns3x z)CChI0Ets;{uS|F7{1dlthnrO?=mC`-t;TKpJn^7Iw5}wrKbO0+74k&<+lLB8;2#X z9w+Rnm)~NM$Fan3uc>rLOnC6cTQC&m?-{79Sa(Y$g+7p`5$biP1Vddl(+rD@}bs+`h5u737=|5toqs&lMOc+FD$ z*jepeqUfZ6ELCC@wo{yu9w|BfstnL^?~f4{Vpq{{&r=F`JBR{;0l9bxE`U%FT}d)S zsrtx6Vc~Nbn3+WtH(G{kIR=Ynw4q`NqO7iJ)=w!AnA2Y;-#Q_4?=&m45bdYhFjlJ) z^VtH+#S+{`)TWtQ!kf`>2h|w?-c;U%xl;4 zlS@ys0#{V4nc1MOOyk{83WK7J^*?OR)Yi3uyKkQQ3gNIK z?d3o&9HmI(WG1&zq6=POiIVxdx8p^MgpupYOWilm@p|vrda3ETSt!Mfb6ro#`h@R# ze!?3TemLZvm~(sS4bub7Q#Pe>8E-!#8(pN5b#)8f!vzvZWcXiNuTH4M=h=u z5UM#$Rn;|?s|w3yiK?pGqknC%AIE|HeH4MLmsty60K}=tf8VnNJ+n{E*N$X;v;l4q z%Nj+dQ4~W!5pF+xfcGz6z+2}&#HZJ;p)xhb!x0SH(@H`4amyJF0&&{`F+9Is!Uky_ zs^;4$O_D8J8RhOlc?+5tAY6nlU0uYlo zvf5kz(2mc+@dG}Z9adFcm9;U3pqf2JCJYA!Mxy~nqXJo`n(Kc}>wO&`pbtt*OkxfN zhIAxSVn5n0NEVF9R#rA9U^No%B>~X+&A8y5P^gJdkO>bth6iIyKHceE*DSF&x%N_{sGRHAl;XZnp{EEW~!vlYs+ZkLs} z@ZCN?`LULWfQ0k|0KnBZcC?Lr1t?MFwquRR&!^=zEL<>zz8+ylUDr@WfnqSg{lf#i zbN)lT`tIAfc=ZZqi#f6)htkc76&K<}Y+E?-?mz)~L07CO*a~rk9QcY_VB3vO$D;4L z1IPR!REZ@A@%0J1F~J8nO|R6t&D61NEy{hZl)OBq@Y``@m%Q8&HLyX1+@>~PoGhb&>AA&1Va_S5yK|9_O&Coh|A{hC2G$B zZvXW?*+@OstDQ0u|9KK^FR%O%6e(0T^51NUsx~6xUm)AtrngRj(iK2XMo<-y4!o?v>?6@Wf+I+s=%)u!gUD(kv5RaF_I3D7!2o-0fyBMb%^ zm}^v(-y#dwiGbFVUgBbq*)GuLW3zqK#u<~$hC{$!ZV8645{D(&y?MdlrKTNwS+8>r z^rRX=Yy|{!U_p8!f1cmght#9}V89|dgLct+lPZHUI;~@4#j6^jSq^auFsxC`rkS1`={yM>@ zkJRVS;@3(&s3wD(kiS&q1p(m2VqTSHUDc(jn)Chx?Cp&)9OlrvnGl9O#O@ahN6dNM zILZJlts|g#B|Lm2=9N$a>?>=V81Umw?A*im>F%TC-yz^{zjN1KM3$6qJA<*E%Zu+D z?>9kFJ!E6!zKGHXuS$f1*5n=b4)^p^ukeqb2G*ok0%hL83}PRJt~0RQWw4vZcs+Ub zgA@I*&Aa_p;^!pcM{NvawQ3N6<#Gjpp|t|3aKGP&VHJ*j4$0}xY8e|q9^{ULJVhgl z{wJTK?F_JKf>>#-F(?MmI>()d5Agc?XYk82Z{f<#8!)CqmTTmhZiGIEPPML9kprNN za@ei2oGS$1c>C45b%LPmZV!dlzbs$o>TYNbzS}}R%1X46T$#7wG%Wb4SOL6_GuZjX z&e$L{S4aB@lpSQMVm8%sF{^5TRhBo!aF?q`#XPa}Bq9KP5<#S3)Xm|3j7JliFOST% zTh~^r)oRY*Wszr?Ohyfga%qJBVRRm#)?4G1MwWZ?%9!(=;^YnL@Glfb9F*WGH`8^w)9G@=%= z*SLL#m`TMvKo_W($xKum2pV}#1x8RPJ1Z0c-)nCbF(uC#=c{gwDkaL1H0oq?2sVc_Ax z0p7pxF@E;eYdG`K2bj+1C0JGU-5^bZw|5oTpM*x!4I04oGfO?!13IG6r z07*naRLOWk0L=Rzzq>MAEe3;pH5v~v9uJTg83E0aqh`<>4(aqYp&?pJRrss*lGHAp zHP>wTHNT>?ULzK^BFn|MUMpwbN`D5>l--KfHL0Xni1n>5Yg}hnukR|$fK-+~a7ygK&e%6w=$NjATh7#Yw>Ce=ui9EIy zdjB^IJAu)O_ITmnlzDQJTT_Fgz;HOk(R_;cJ~@wHy!$3T{PbfS9vwhu8l&L|TIq(D zF?Uv-zqv3R%h>k!>%E&c8V>P-K=j~mw+RJV7pr_Ub2fU5PMTAw2p~|nV^kKc<8%V) z3zTB}cen=Sp1++&t?&oHtEw&+H*Z~90Wf(nAOMX+`V%SMH`9}d0Bj@%qc%12ET_?M zOaOSge6Z3v7L!w>)p#;&&inft1G%|RlOkpt3Bs0HIqgv*X$ey?0HURZ8==eFs=i=5`M=s)s)YZ0jo_M+$DkOVlnz6A><^ZesbEq8H%G z0}=>9&{97ZWGx<+t$^q=@mY9^cji#%+UCf@O&Gi#Xo`Ho*s;m9rw%&ml*puWqhQcK zeUL|rIQlcT#$;Ym!+P%o=&4tX7zDRIcHeX8iBhOnrHlMm)pdJ=pK?k-pc{&PeH91( z5kKrs+qhuSPIw1G(pHti6=aRT5F-dh={6%^4-^?TZ7?GONMXJz@yXRI_|Y4`z^iB8 z#J$6PsKEd#%i3UO?TZ2GC~6T8m;IBXS<9j#@w}kefMADI+E1JBd`RIXEY??AVU+#a zt)Ez&Jy3tA{}Z?HhHBuZ6M5lVO>7nRd>K}G1UCUE$*og1W>uBt;_%`96#&ljf`}9~ zSI|A38o<+{0ED80o3oc%XOuVlFaS_zIhEt_pd1uA6dI=*c6(k{%cH=-4hVTJrAn6H z_l~n<13&Y-2)5u-sX17Dnm}E`ZLAS;t(2 z<==-Q^l5b1Jl2o&MvoC$RyC%xIp&K6m<@D$>z}Qiije?Wk~hUfoB(0vlc<|hAiQEm zHvG0w_d+4RevWL@D-CoH3D1KBPJ*uz#T*$HN~Sj+xuX`y_E!KF%5 zl_h{00F=r|DUIe_d8blOg+}lsDFCS%9M4>9JTrq?H5Pv;9r2l;}B^jQY0;tPJ#>ZKC@KY?~EqP#D)^jxmG* zP?`Ww>@yZ|8IY()>||u~4ew&=DrsJBH*j^+CEjVtAr%(U5h5)(MM6Q2u3$UWdWXS9 zi;G~|NTKK&_OTjD!T9%5JkK5KnzsCW+C~D`mBDhk!gM~zV%Z#_%AE5boNy*#`NwEc z7^TfJYzOoN?ejhAUFHJC(fzU$-Z#+LvFe4lWXHBW$$l4JYYn4-D|c?;hp+zmSv_J{4CRr-w1Lo6+G{HerYm2El_|f64A^ zQ2`Enzf*%~9(B+#$aQ3&*fgp$@B77VtPrIT1i#;J{ZSMVv?3^_sJWV&3>YO$C`PhbaEi2@$PB=g)v7ecEZFc8pixJR*~LVDsmKCS zpF<^JeTAh3?q0FpY$O+5s0gG>?)Cyl$!yl)jPsu?e!w=Scm%7bl~!_jr<4FId;V=K+NUmpn&3&Is zq2!NZ%3+R~n!TkAk4R!XUA?-(qo+l!u~odkKkec*e117 z-#zkwONGJt2y~dfxBl0mJ+jZNBD&f8#`^Ox27>~5mZ9Prmv7w0FW)+@(bFr1~^AOYKFY!yo7 zeyk#(NRa{&mH-x}E=#lyKST|cvu!0l0jB$Mq7 z{MvSfN{RHFPchV9bG|qKPCb~NV6G~I`Fw@hY>9GJfsLPS&e5&)549G(+YV^80ssv` z^1f;52XO)7PHlfFLO7p+AbOrX`enCh!nhJb8C}b^z3^vdD6N4^;o8kx`0*RR!jE2m z9T%=$#nM#Bw1(2k`D%J(CIt5F>|^oo`tA;uIMMx4pP?y%W( z?b;f;TOp{sEF6&+RVgrD{w=LzcJG~Z4uxVd1b%&Z9{V{IrC}SSe^O9aj`}F6=zAgP z?PJfHLzO^`M{D_p_Pc4b^9X>0b_I0GabC%R>|d9Gg~qhe0(WSDZG}KN-SN+0zuz-U zwiYX+GfqM4CFCOK?l#2j402jatmWEda^8O)o+Z>F)!e-?n=de(El?Y70{_HtKCEzW z`@7xsu>o1!h`2pjQRg5Upg^8n0lxbfUO8w!_n~fU+HZM9+mFpTe~p?M*KXd#ug;#q zPv3k4?_c}`)728g;SlY1rBfE9@)P4&c3wv=uD(xu@r!6qp9}~{bwX!6gYU%=PjB)7 zavLjkqB^B!t`+V9iS5m;s7FzWOCdS`2t>rB6e*=hkzxP~Fwc!KOEe0A1Fw37zb)vA zzAN}iRsbyZPGfH30oacQOe<5HnK8Tos3uaGTPwy|`H|%;ID^x=$pqG8C`j99*6Qzx zG)3_rZcn-`jws9z^7~Lx2(E*T?k)Fw!L~SWCZ!m;k6|Buj-ze`mM8)*Z?gRsA5jR( zixCQP?QWXEu5BFV?8T7Wmn1Ph%x;g}jZO$fKwh~V3rxvAozFp3eOI!uoNQOg@%BNH zgtMkYZz8Ggt{?WyvOy2X#lx3rBu3!Mc3idXjkGM6Bi@T^>UT7$B;y5G1xQV>ThdX<1Slu#@TnC;HJ2giNj=w_t>Q zDk{VMC9N^KQ-FtLSZg}3^QQy|T-{Rb3+jAw6R!=m)SFL;1)Y$GwOOfFsMob=&;Bp4 zELQ+2s5bJQ6>QG>hqZ5GyIA^5#M*#;u~lN*$L`*!2+`|fCP9KVZEKG1p*`n604asC zHn@5BI$nGKEPndN>p1htM>v>Ikq>fYI)irCle6E_u>8jLuv7{NU8gwTJK?nHU$>iw zD#X_w5B02W-tE2W{cyptd{%C^#Op2fI;&nF|)LBNNCWG<2vX$=Jw&fccd7& zInGJr$irg6FJCtMTvMLM@~Y~NiEgb%M9xZ)9wX282MWKapKpf0Bq=oj@nVx~ER0KU z!*P;BUi^s#$&ll1kyfNL#lsLuTR)sm`GU0$rKKF3?y`3F1wV#iErjLoz9z_FiY*Z$ zi3-3~#`dY3b!~aE*kw@;wOc?bbxbm@5DDnxH*UZK1<=*9a~dMH?zSU_-@j*OESDwb zvqcm6udb2hs`0dzy=R%P*yNWAftUHR*M*uO0D!<^CXcoB$#yGrJ3)UsI%ON%-?X7( znW2<|fx?yB*YVT0Uc=Acc^w~qb`gj3DM%~iSq4J$9>c~4u>EQ^6o!e^pKkg7d#S#L zidsBQ_V4(5u+SSsPK)5(UXwcRzZ8PfBM;@g3w(p(dWn+VI~0894H{u6t*s~!cC9Vh z`>H4_^qW9bY*7GGQ~~C|aJ65RtI}1iIsY%K6v(Hifu05hpijMH26JsHhXv0T$K`4@ zKdMcAL_p;nY^3Vn<{0YUwgdqi2k8(nZ})Xt3T)k=xOuT17J!72qR-L7=;q9DXV3*q zE#+O;?_2oS_wJpCWI-@aao7N9;K2DQ#$aM4pwqD)?+xnOL10qk#x?98hVolLuDG-n z!clgzdIYOdSSvJjGa{!m3)*Xy01}fc#zn#{#S__XftG|co9bkEQY3&usyRYcmNn+H zIi|BYs;UmWtmRf!$H}iP*B;xoMh39270d$rl(;CLGaN_K$BGToQdZT1|q^diw)(&{o zvRKHjGhE{`)}e8+GQvo%;hviJwPFiErnf%pUSJAUDLX!m<6rY*odBC7fT#d-WK4Nf zRtu{DkZCRg>J#`+g(C1YC;+|0hEY`|&<@Dq(Sve2KR&3c@{pNJrIe4s>#T}alV)#^ z2B9fs40bCJ7zmuO$jhT{rNk^lY^kM{2yYi#tKOIG9Op<{hcS6!-34raOr`N#(;PxBX7OyhP8-EM9M{GQ=+N7UH$d;Qi6tB}@_Vvt_{*uV$FL|PzH3DHp zPxjFaqxA{Hr`BP(ug)F)m0Q~=^q*?(Cy)Vg{K2rnYPG_2Hp61E1T)&R|Nc4|D-;zGVwoO30HnZlh=4-~ zETUlNbX0F=Imh3{Xisz~l8tlDEOL!OUceZ_;dF|N*RSChXWqn5-ufjzx_l9EBMnCKwgH!VbvppTolv7@~u6=6y`!5F&DN!YG;sfY(2}rdm#~@vTJq7Jlb}zK@5DI zd_}CqEiVKKwihDQI~=c8ZL(IY-KKD6WVa@(pEc>mK+@Wa=B zfnUAy>+R24YzL1CCJ_787$BQf4r^z$3n$xDkVeB z+vdiC#paR*5yP-E)Tz1rvvznrJGk^z!;4tP&$KB3e0um9Ugf5dxa#tkBmAi!aEAaL#U*_ zm>!#WsDeu?9+e)4)y``^KTcG@&+|=~g_H=?TK!sSXw^8WYGn}s0L-hZDi8J_+&#K| z^HL2!17Krn_8Wqo#yu4Z!P9gdaHlhwnJohF;- zYQ7r)+!k|MMLcB{mumliktHJgFJqoB<0hJ=JZ!}>S(sdLD{+&#^fZK~#6 zyRkn6aMy{-9s`qC1GwmIV*j`~i%+8K#F%ob=8-x72FTGlMyyH1$@&) zZ*X$#A=(k{gOF=yFb=ZaNtW2hr@C`R&R=8af=$a7MpdY4gQMvJN5>14l>w1z9IN0R z`AB(`3s&d}XF%HkJ49||$s5+vanO!|YmMA8a5|RgQQJ|rA00BpXbg)QN)ZOb0?-O~ z4iE79hacci|N4jc!RxQ$>YbZlMbLTf?lWMPL26{JefLS85-6pj4&70*989@2C8kr* z?q&|#2aNXoVTaGf!=9Sck3?~WI#v&N{tV~h=OfQm!dp@rq)C+R5>tx+w5L-vL2CuA zHMAy>VldUSvRdq4z4FoFe7UTLcQ#+C^{uEW=TPiH4RybH%d1`P>P0hF2O|#+h;_+BCEY zTw%?zq{te#a>$jx<$Dl9W$pM0CFYUXQaDNNScWwb0*@N+|KsC1rn3bM13J^K=YMSF z%Txc}X*5(1{h4^FTn3tgG>zrLb}+kqH_>T!KAYxT_n^qJw>QCPG{&;3@Y#*)c=hZV z{NVLp;g|2diz~NppkjmZc!JSz)W$rpYl1+e5!`Levj1$$B4dxmXtO#ACnp zJfKu=kC9@O%dXEy`H{+c$a_-7ajVQq=Rc(gS^-LfbVj5!HOeN;bhVrve)Pew<^arS zw5I@|uB%u`#HBs)^fYV%q=*5qVZ&uzn(<(wN5hGA+nYZ;zJE}2y$?c3q;h4HYR=j+ zlT%Nd_|1OL$m`>-001tq4#iQc1rrRAXbu_DmaVa`I0vpsLvf#+?|`p|v~qk@W1p#1 zcsrd4ZwM(-lE2N5q6%0A$wsdeA$zHtyF&n+^5vbk;?_C&ZQJ4&bR7INhQ{qjk*V{R zXHhrN6D~f1{oJ=+<>?Cv-i8hK<0opxetdk87YjSWwawk^C@gnHuvkL6t)e$=`BM4o z;0NOLfgGo86u!RF?R@pI7YJp^n9oa0rwc3=D*y^v-a7vYhd&j=_`;fR3^NtVc;!($ zDNMb?V5~OSI(FAJfW{rjPVH9sHFZim3GP(N>i z{_LNP@!Z%ZA;nvHk4zz7l-$t*5z)eI5IQt-Qfq?F2ucxatw?7|X|14u62|PW=EnyB z<^VV^hAPVkWa`EX5T8T6{%zCKq5yQ{GSDpCx~`xH6DXxEOD$)M!&9HN$Mnx9G`u<4uUdUjb641i{ zgS?5|d)9H2-M``pXs27E`5Q@xKxqi>4p=+cH;$d0d%^M0)?=C(SS(6RXG<&=E7X-i zmN)yU9`ctS`w9Ukf^yhchEj-`_))@SV5}{d7d@L_ZYZ! z{{cR|eig5sJ%eAKc^B_p_yk9@W9UJF$!OG`5HNs4!t8BE8u21_ib@CXuq&WPx;r5q zkHaGpM@uUxhl2~qK392<1Ql!}L;JWQg}-IwvraQuj9JrMrO+q> zO?^Mh^HZ9$yjCSzJDD8Q;l|36#c;A3&QyepyY2StA`LmvwA-r6_1z~JVoy7X;YaUz z+cwOO#P_mAer8(xTZ)yq-oC)lDxu(B9pdmsjw%yIH4Ig!K<3IccI|Sv4A))}2u7Cg zJ~_#&JoIXBfulICfEe3gmZem|R_w&gM4~LBO%f4kXR7;7s*+4X*o#Y!r0}pMbn>*% zVuW9y3$9-%_@bMC-)f5{2$Am#+pGq$Agrni)9D;X$8#)KwVOkVT0qSI8mV5O)i1`nCc?)^{aFs8X4 zSJ_w`Qiy}1EdcR-B-m_gyg;R6t)Cxn2?VCA6WO+lmQAfP!};#Wc@=QZ3Rf5gE zx+?FM)oN}l(m4Mmpu=u=J<-A+crq1$jkZWk#uzS3rvRwC_ioIOkM?gqH-7FG2K@U( zYV0@!tCpeK$a@j6DiBgBW~{v(GIMs{xbw-<^6K%vlh(5H=apxdc)0`=qsVh8BGgswgg8@p;zS}5?y2&h+%12u-`eNV{0J@lt~QhOK?1^% z*5QPTXI8qqI#m(;b&O{2pKi! z|8uR8X^mVd^W%+4(nBWy>O!)vYx+=;sHm`?!?+Gzc9bejK2zvi z)$PWK1;>lAj~H_8!$?DOCSH;VhD2MwyVlGLD&>RjfKjeW%x6vD zpR%mlu)o%ey6NxO@7>)Cpd>_2Lcsg5!hy2d?iDp(QFp~fkh?vxq6jFm40_N6>@kLL zcmE---?@de7tiCZ56|G8^Y7v2y*o_+pwSrP!2p@gd`y3CH51>8VBCDU(u}Ywub&On zKO>NyufJjH1GCqlE8XuukIa*ERq|=61jznnY&XAKHJQn4T5|z*1~)G61Sg&CAu?p!LLg{p^odfBXCY z@Yc7!_Pv|WoO)rNQ5y@76oX95U|M)ixScl^nz$q)Fmpmt)Hw>pQ92>vD$)O|UXBj`9`?wiF`K9Vn0U+sr+>kYa(Rhcz}z zLQ^)0eBC5)-^su$3H63;A6Z7y9wUm*U^fT`yLZ?{krXIMr(eTym-{{atek_6w-E`! z#*L**DaEr!5$gTeq8Ot zO-eu-ArRMp$qCt+dVSya*y&vi$kscuR66r0n4M82L;=*>-dIt$$cn5@*Ca?82G-e; zRO9)dU?ePYpdy3PA5_ zFl?}Dw*bEK!Z-Bo``4IRp42MTkRFx4?)a3C#a#npjKHfRdB@WW~k74w` zRud7*W!pC_{PLATJ{UAsp3+#ACGH&_;QH-bc>lu3_|@6BapvO>aryRjpst_>Lrliw zcI(2%2M_}(&i6~~!3Eq-oa0Z4!cY;5zVG;MJZ5^_B5JGrXmcKvdS@F>#_KYPifOqt zHT(ZMQ|>3rG_tHw5}VKt=4i2;-aU8jmk*8(530%J*=#!9uj>W@_)YfkuZ^B21t2D; zaT#NHRW8h+7^!DZzo2j5zqb0|({~T=?%%%st*?LoPQk-kkoVRXmHkIm(oIL4_64Vtw_l``08yPlIr8U{`)31_jZokrR!ZdSKXd=OD(f9hc2|0Sp3-)xyoB81mW+_6J zsrH@+jXcwwXPSyUQ_ZDSRI@R6X4C!KS1+791YpK{&*;TsW~#C@%x(+dsk8beJ#7j= zQpIC7T-D5zVhq*9Tb zrO4qgzy%<=wA;bzK!75{Xho$E2NDe1T0v=n-zD26oap6Ez}yzB%GX*-x2-8{4Fq`$ zB`F5J`)dv~4tGY>Da0Opr;65g`ltsX66v<-t9Yo>mjp?GlcTrNtq#>0k9fp$D88qm zQ$M!WQ?vJ7u4>GtD;!T3SS(l2S|iWx4S1;_KVG|ZMzur;oNSlYehQLqpK^1MmrU!E zpn*Z2H#Jz)uxN+w$-oJ1GZ+-Y7-aYpruHU(V)oKo1>G#$V&x zNDBA>Kbyuct|>+T()u|mk39Z55rE$ixfQ#uc-&@zBwyN8fDr!e?AMB#xvDc{?f!q3 zY2-zrvn&G<-8Wp{S}u+s09XLfN@E zO%L}M%lYNW=+w0$FHR}aqe@kcVh@5+N9=j5`Mq6yQjE*KRsuATb8ZF9+cMUldUpBMe zH6L^li3o`iknQ@5gN~2}Pj7$9-*#`ey${;%{S#QofVQ^{1T1X3*hGWZTEfD@mr!v^ zPzNHv?XU3MyAa_W?NszjXrKm&LH{mJc)X9LM%g}Z0;DL^hB2KkaC9`oeBK29(OTOr zduROn6`EW8nl4ZX;eJ_(u*2AD?QElkMrkPQS3Ala zBW7K!Dw-8>hTNYdcpj~n;MmTupC}BovK^hIYVq+|x!}8dkj+OTC~Ad&mLbnHXIgWf zXH?_`vMieun`@>jFPDqOKAQ9YnoZrZ-{AcJZ?;1pGd*nzK-%1CKi8(_#d2n!>-z_H zriau0%cmx%uZ{+z-%+GSt(9Q_XmG=W4B+Fc3+n`q*-ASptV~!7LYIY0iY&{xVF}|n z5Y}RHOShw72V5Y6Nh8~%l08;cTow|EV~8A()~S5mJDHJ4Bi|%+SL${Yzm)<_Ha5k5 zQc>qCk(p^_e5@r=a}kkqb?>NQTT|+-ZoShrU)MSAm}4^ERP;9lf~u1zw+vKitX3-= z9?fxhI73y|&1EW))gGbuP}Y$y2<_|BTH)>;11Ssx%#9Lah#RFsDdZ|^?i)}F4AWdj z^xz>5rU$ru^9DY=coFZMKZlP#yM&wf?!lNESy5m#8bLJyU0_Uei=T(~8>c^6d%5c$ z^h_I6jAR^Z(Xvog^+*6mPs3i1ZJehc5%~2`V&(KLBp23QA+yK+vgWr|+GILYd7eQl zS{hSdT`gvpXGe!qw6UGbYRM+;{r}W%2Rz9NKuYb6*GceWOwEhcyhg*W^Vi?|(d_rW z^EWO%d+NE*CZkjTUZ%2VL4YD+=Ls4d1aFE+iF@xo?=4%ou+wucG~=78YNNhIdw8PA z5M1gqi=cLFJHYON^+gUtC?Q<}iwJlHf)Xz~<;bOzwUV*n+B$P<;tHHtpa-zON8el; zWfhJV#x;z_C$9#prZaM#BhPXW5sv3m+`4}UpIrS6=PzBv`AeVS>aAP2`|u&=%O#+I zJkO!DYM_2)BStEBVNn=S$gfo?sNL@?s!cua7A9ImiusJONXa z44anUqm>^(rjI%QIMa^!73;WNJ4z{JS%#v>k>?q*JVTymoaY%btw7-EYPI<6{=FNQ zE`9pm3_uP*RpqKq?ES|=KV*zgxI*wGD*%#Go!FU5bzSpfIWqw0*}dnBv*&+RzVh<7 zZhZG^-@o$Q=@$=GuD;$v|1hiou3`5c``ps4$d{tOV^29o+rKTu$y`NaqB-0PyNgY= zls1&gyKk}EBQ!QadY7UDw1}gH8{kUzH6`Hl~X?0K#xMz+^ar)(Xb3yVBUt&j63`M}=K2!|$b{Kr)b5 zfO&N1QM|q9m!X`4TgZ(kTC_wB{B74&!2eo%=f5n|$ny+&ks;4AXhi_b;bJkpa{l}q zH$J`aepw90#cDaL%W`Rqsd-HaPaP8Qv?u^6>U4Os1u!>7K2R^e@bzr}_`&Qi|N6hV z`=9^M{)?;M_{w)~7FqFqMWnUT3Jj{b@f>Q50Drz2^0zIme}A;XOF|d zuNe`oE!Y;I#E)~hZ%gK8t=TWNR>%vDT;~7?)7cz1@7=*Cmp{YDmoMVu%NKC{&Mn;A z-^YBlLRp(;k6$%$`fVgY2k8@mqm|qgtvn(j=ZV*b&}_!u$Bb^?zW!_O_JLUU|$1AbRae!W!toONXcdJ6SU8=Q0 zo@*3Ejv~)d(XCt;UjuSovjI2pRf)KXN@logtJ}=2 zCD|zIlA&>23tvUCh{HYmP0dM^-^#__g&SAH){Ue56B{eseJiR}$W;1;Xk3V3RzY8E zGsN4F5M|tDa&aoBhDBP=rW(194@~A5KGGXrmaP?$q z>9^xRzV=cD@C}Hz2!dX-{T4qzIoWk-ahMnb;@)-V%Y0HLQwked-^)x&Buxr76pDtX2!d%)F)wPn|OGv?%~U zWB|i=xB&oWCT8a4YGKpYckbW3e|UUwel(c;PM!@<>kOx;CZLTF&)yLLK+I-a5>kVi zdxQ;l(#mQ(i(RA+<%t&W~XH$KE=BnR3%TN^U zPp&!7H5Y>%gW&*Ko*e__g=#fBKRddAA3zO2*QRQQgxeVMK8AS2w4P|4D@jkI0uXm} z+~HhSB~NFEji*0Qe0=G><3IeJzxVNEbn1Jf!Q>CL&Q6g%Xdm&wwWB8;))2GWNV2&o zLO^l@HfT@)1_u-&9(pi{zU=;OB?YZ%D`IO;+Qrn9w|~$74o|O`XqLsfby0u_U1;~X ztGdQ~w!rjwhQ)jZB5ELZgb=<3z#C^j1#cO*xwBm&tWwagl)EUh#^KwZ~3m>y#~ zpW)%*5w6_4iHldS;QXf-@#(e8xOw*`7V{|pAR7!Z91M}?xjUxl_W4N$nT~!$M9A>+ zp}v_ADFFehk`h~3J1;j;3h9Y2uj-?ep0H79{st)3vyWARwiz|IWU`C)7hin-s~?@-d+x!2=qm(*R+`xu7&6U0v}<>CA|Wk>dkgn_ zYo49Z4fgnwdO&1pvEQq0{*6p=4lO!D8gQ@{d$5)0U~nMmjck(&<;8i0`(^?F6o)bM zd^uhSg#f8E=|d*u^GI1*ZM_5KOLbp%DMR5J-EW8#gSO(oV|1a9EO&{c!7+jho`P{r z?mWDYherokE*B_k16>TAA_9Ux&+pnU6;4b3tUo*F#`ZX$RGrAK?Qm&|}{29OR|8z<5~Ds2#XDP<#AvSPjkthXm$$MgF3 z3p(ql=_IWPd8RSQGYpEN@c|eV7z}bQa*a#_;QGOIcJR@AXaD-*yKnz&u5~e}>y=rp z7Pa9zRn5)t*%MArlL7#&-xhLdYGZh@nwvZ;)K^~kM*ivS-28v|`9Ht&pZ(+i;_}yD z{?7GW=ik>V)1;wFUJ;{ixjD!3WMgG0(5T6;EeYdt;`=qZvW4pt6|sCgSSUSP0o2!; zjzkg+QoQ0Uwbrp)7DG|7p&H}GzORSNF{oi$==NA%3~>S>z;7RU$%X0yZ%7qCliHn( zCb$6)aUIhtirezyE$(0lTjSNLphwpr|DDIoP^6l3|5b^j=^RJL&AES4Hr_wA78an~ zb-vVYKQNdLmrHaIvw!t!B>{o^U_K74?yHyfe<+0|>f ze&;ss?ms|TE&)X-@*Jb#7@5{BI&R!vzuZFcPKEYI0pgi;1eB5e`-k2h9qtN$2hls_ z`tu3d^*B*t;1XL5evNiF$bMURHE;NQ()UwD1Q<63ny7z8?x26O1E#FU`z5>Ui&dtmSi7F0NuU1_Q+#2D%=JSA`_x7ot|Z9%~6XaI487N z$cy$n_U+G0B5*}$LKDi-anMsIx@BB{3Ppn#4z0ihuw0cmJeuNgI!DFL-oMh+h6c7< z0R9T!Mu9Y~bB=0@Xq@@9ZeUkZ4CV^c*&GMc1KfUa7uWCJ#^sy0aO3VB+PoH?}-<_q1O;KUN+mTLB#BC{B z02B&tyEZ%M0Ob0!f3c6C*qP^@39()EoUs#kCU4BAJL3}1yTC@eKYNR5q5l?HDh5S+ z{y*p8prG++sEeXlDZ-^yS$=qQaQ_;BDS%vSO&WgK>I_d8yS}4^6ju=nFJJ=~1 zzB@KEAGU?lcIkr``?fy%OX_SS^yXmE0J6L8@s+y?o)K7!X}hfNkQ5*cP6oU$f>jgS zx4R~xn`fO-ZO2(zOBe0A|HTr=vnJ+0qd8JViovMCP*4PDrJ$8+&r~-*iu~<>AVOW& zSd_;o>k{+D6vy);+&{RFJNx%>{qAjCzk3JQ@7%$I!vic9bAT9GrZFfAOa>z;(oVTx z_@93758r7n7Q&i|uuh*oDaE6nnk7>0i)L20P62O9&#LW_ z0ghhqifNeUWF7H1kO}GepJl4C{0BLTB12weoM+k;d9DV799gDjb-nyxdi3zUySJ`9 zKm94%-DSY~ElmP2p>a4Fdfu`)e4lW8F61T;5+^XYEi%2NNmMGa64StZA&_R*h zcraS)o?z-4v-uK-$IaeE}DPg2JKP-ntRlfXasNSnxr{S$FXqR)>K z{w@LVBqv6y3E|xH{Ke1la4ma}pg!Kwcbng~hFIK@4M^sN|6q_~ILt8|6c`i*27`i& zqPa8*Fb`(4!?PDZdH4OdUi-@<00RJ6F6Xrg&;5U4y#Fmd%?d!=@g0b!s!MY`JFGLE zsc*mXd-=s{=NA93fAN35`uG0eAAR)Fv#(rLq`#|GM#!M5s-(K+CN6+dAQ+HoEz~t= zRygV@7%Mt(7t}aH0&D^SN2kh(L|I1^SUSl)91_KlN7v#_h!irBs+g>QN>C@^n}FG! z#ICpHL`uu1#yU>g!D_<(w8~ec&GnZ#ONfBYSs&IHT!_mPK#x|+U*bc+vRYxfSYWv-F`dnD zG(E0o}2M-P&;&66^x~c)Cq4NTHKEQZ5a4E)1}>?%IZg z%A(lUcd{@F?Q3KGcNB)8zvaxQ@UiVt?mWh(6F-kaebclT6KV&wpYMd3hT8<`nHS$f zDZle|VDxi~)LW0A8pEw^zoHg7$}lW)>-o=vK~AILPz?qJlmeT&yn1|i|DzxN`G0j4 zK>5N;UmrfWf3sXJXSJ!TSm>wFfTvRU15c*{(1$6>*=qn0Gjmz3%y_hyHSzvX130{T z^Rv&s^U5EbE%M_Tv*-#-)h?a zQkKw)4VPO`7E3pQHLxX%Wxh^!SUo2aj?$YyEI$;A-967WqZf2syYDXz9`3#%1y+B4 za_dIkddMAa=@Ao&Pj~_AVEfkcws!-%cYAQ&KxDL5W(NgvDJa?&YWmeG0TOxWMO}o_ z6o;Mn3Bpm~R(eTU6P#Yh@QpM!KMVbA@~ooMb+ z5Tc0^J@rvaTOcYx7&;3l!D`4&s__iQKcp`cJg42 zxa~X%7J8reHhb1aHXVhH=|c|Vv-w-~*cM872O``eeZV-V|J~3KX&E+o>-2tY^Rr{S#5#*JE5QnPw%@~!`;A9-IAht)2T?jtq~$r8e5}GHI>{uZ3*?I?!%PMDEAkwl4S9bn7-q~+F0iVwm6?wcNKc$D0?t$|lylp+*) z3;hoY42v9tJm*24nc<*N!(o9e)5rB{`tISwJ8xdUbnYG+JzuX@b54Z(l}$nPU7Y5r zp{Gd!ILZ172BxYjKAs&qUw}XT$^Z8VOlSVL|MdUx@?>=CZw`vl^NKXI&P{D<&87x1 zIVY&*x?r!m2O~}0>IJ-lL&od5344?P?c%UzDYvo2+t-xj(juGYZR7U48r{KLl7P@T zn0)@C8k=XWU)D-Kmidb8I~I%tLgb*Za0zq_ijdg+7R~YScq-<3*R~PQI|qkDuE=6L z#xs0EyqD|jWLvJ-t_T>M>)@tMuC48F6(o#3AnZJlJtj3h>eR=lPj`IidTIN|Pi7WH zHrM(B#sKqqh2!HDrn40m%Mzp+E5lf<7MQOVn9Zk{&SyA0KE&bl5c@|5IG#^2UCc0F zt*|H;D9aVfYK7X=Agz$=ymh8iP+9}6*S_f}fHp#;aM0^$nXO=VS2B}WW8%jN`EcHb z{p8CJ?{<(VgMiGtI*BXeM2@MlNqwdbrIW|%hg1X|_!TPwy#=LI-{_j}#8Fg|<%PZ6 zsc?L_1#OSU0S>o8Rl4(_=>SE|zW;ETV>E2{{)Yud!vdq>&91>@KU3(USn(1S*9JgyFi;U-+SqJsJW{q&*o6?Y z$A`iNEEteD$JEDAVn}}L;2v8k;$6%0L`F$Z;*OEi6WWy4WE#ry_`qG#SWD+e={LH* z1d*_Cz2QeJEGMCC2qi7uJ<>djOf^|{VB)32A8#gmRZW;ZGs74l*9uvtaXg*l%FUa& zbnOOi-MNeDVuqSa7^*Q_%rITda5Ou{@ob8t=@E{n$2guJV^J<^7lH=KdoX^q(6F zvt_2{Tr4_M_3N9fn`T=|T88UGuCQ=p$2WJ~%ueM_F5;ig2u0zY{)!^wJl8xJ7HT{m z6@x(mf;p(m<@x=Gx6Zxs`VVdZ;Fn+d&iLWIo8@9LtC@|3{5SakJXHcerl(y2kd#sY z7+?c{3>zp?v{=pUcdPd=zH#|mU;n*VGhIA;dh+c5NN3rkt}E0i4M3U+?$}TF4v5gw z23p61zAXY0f}O3UF3*1(#}eHFiS`RAA>heb;1T5 z(%a6kAU%mLkNU$+n@~v08~p&kos^i+aDD+ov9@CqSh=tYO4LUz);EiXt|oFWa8R_f z%#}X^_^ZyGW^d)@}6o9^&*%`D|wKNAu_v^OZ`bV$->A_cD`sSPCzcYDZJly+skqv$~ z(;1A(QJV_Pb;ElUwE`g>A0S-BEF6j&3c+TV450_J0=K)csm0-}BkH07b|IU1ai=(j zg}T?8#_?+${1vx3@N+2=zo`QhlW`%Zjm}-479(xHL_~LDW z{%|kUVXf&>nzU*|TKh@}TW=rRJ)9q29cV%=Yn4G|&5^zR!vkEn@+sar_cq@9_yf$A z^ClW#S;BDZjc@nZl|2QZk>zrk$HcfpvSNewn$G!MRw3lo-@w&8v zK7QJ*LD+x&9j*|s@#xKU!@JJR{+wt)U$3tFK2?oA+L%uCL#ID`^>^;eCj7Rs@t_nT z&ozdF9HT*Qo&GQw6c`Q%7>))gny7!bX7l6Mu3bL==K1%3b$|x?r}_M-t~Y!Af1wls z{oAg#U{?=da|D(V2pXleG`RX_Gtav5U*+419 z#;|IFKv>mU*u~T*3WV@(t(bN8qx&r~v5^SZu5k(@?T6WPcYDLXh2sg^s2DTzp>b&mt|iu(hhCpubT~9P6@>#hI7do;hsB(f+Dz~PPUg)u1nqCAlVU} zkyf2|D9B5E3N}eeKr|c=Hyr0A(O&UOj-!ks;gB2h$doaRJS&iAIi~YDKE8Yrubq7Z zZ+`GLZr;6tk}G6+*7Ow-NUJuYU*jy;_zjR#2A#rcFXM6DNp1_kT^8iF1;o4);dX=X z$iIUYp>qwg7Y|7kAt*1L@BTQTpSKoKi|gan1dm{?C6_FWf|tAykyIFU)wn;)B{!H(gIH+y_~I;%>RYwvT+)*R5iU$6`L<#&4y_GmOUrOvXb@ z#zTxp1xBL*PbMSU+nexsJW@rbj!eCJ>)!3l|MtKAum3M+rpE`1Q>R}Tu9kB!8)J;A zTaCh6CVk%j-1GaXqAvm*U1dX*O&4CeOF%jVMLMOsk?!v9l#T`I7U^#32I-a%q&t`H z?pSu;ecmtk516@U&YA060dLCiiRX+m0Um}w=w(`U^9Iqak>yXWHaD4czYePOz6`Vi zD@+!r&g1i+I|asNSLe$WjSyrI3h4Hn{Aq%H2lvv`{DGLQ)-~=btT!y#!_EIvw?h9t zJywTjKb)lg)ayF7i01j4v!!Q~5VqlLw5%>C`?*Ea>#X-<#!oK%Pm&5`Vh$90ylSm9 z!ZoLm?!Unb+q}&W6Q0e#c#{p6cgat$urO@=L9#MwR-=vZvQ2Az&|}%BtpQj_sw)+M z6ebEm;5>FLGnG7T+!(WKZL@sBG-=Wduc4c*C3rt25GZ5prV%w8mX#>QS90ZkYp~ay z${@S;5j`2<*f+cRLStjVL8MHUaQjRF)y+{9BlH{jL$UvFR<4;^u_rF`{5|6XC5Ia? z4*jSfcHuC(&==dBw+JlwzCoHQ*+Fq0N-WQ$)AkCT%ZUU^@XbwlQ$BnK+^FpC*WdaR zr~BsCg+PNY0!W2&(8;l#3_$(X4=eK8E}DRYYvTO~X7FUt&C5>eVUO?K4F+g*c(?nK zK2;b6V9NT}m?$D5-a8O3%dtlK(@rlnZb{N(byYR@jvTAbYaWg^&F=At{%@NCv!s4Q z&&5a5PGfcx9X8Y&gGHhb$xW{Ht)7BBL=voYPEX4;C1IubdSlmHpIM5XpEoOGd*kOT zd~yu^up6=6-{JDC1nws+Y>v$O=~Dj%sdbL_9!`DGRcLdv+JfX_dn;7>rXBK+bqs&j z5gAh1d~)b|ynfAKIiQb$YhI)-&8ngE8t%T@{AJp~UzPm_wnF#LHn9}mWtz<~(iRhu zm&nuMKJQJQcE{6owUdESsG}>Z$~2#nke-`{UsPQ4tbM+(oG+@>D-%<{FFV+fNElPy za34sd`5j7}Zkp%v6Oxlb6(~ynCIzBZX0jI0;Ju|c#@D^lcocpON*(}yfkj|YnsbuC z=n|%!q~>2pVP~M4E?Et>F9if9xk^-!m7&DVr)`y>tEsvc57<ph=!ruR)*M&^% z?u;n(KLfnzLeJMP&SKH1FskS}sMjby0{>nX^!jQ6Sb*-DfT`Gw+ivDK1^8{yMz0Pw~Y<^!_*9{b3Djz3~bc#nuWcAGg5 zoZH9un*s*oI$s~JSMC+s0(Kc65^)MOBd%?~3o)4{mcj20C%jQPePvLaLBAFIULmIT zA$>T;K3fFx0mrDGnM2dL!()I6OWfEk|D7LQN{Ynij~Cg+Ggtad{7x1h9a1LCA5%wU zyPh+%i=rRqB9?Z0japT?Tr$ZdjU2)Qwi2IP;DBxPjedOO<$o=|5M!g$??igG`y4_x z9O~YN@*(L&4xuMiCa<|}X*l!OGeKHuOteKJH5%|es;GT)uq;LtfX9>#8AW}2P8=58 zwCu<$&jfg(3Fy1MpKkoT-Ma7?F{PLX9cf?0=Rqe_t4ZznBuG8GcWQz>TmqoZ+mIf-dt$ad4jRHowKvd_=|~YLD^q zGN9qXfgjb!^Z+qE=t?+H4Ar1sM48HX2*}V*p)89E{DDoww?bp7wkHQs*tsPSAF@P0 zxC)Nd5tZCnfAzgqCjXnXir3(Ozu^+d2Xe0aLr`Tn>oU06K>bgi7x<1&&=Y3sQrLDA zD4AAE^7v8ZPiBFacvys_519LtDy<^ti->U)p8xvCS@RL{=gg(<#laCo^FF7biRN>g zpBxTa`h~=n#PP_)@!9#;w!z!=TdU`_#E4N0;hL(Usz?;?pr@5^Z#F;%!f4vK z9+_Xs#;r@x$biZF{8_a;dl=nq6TY5c%C`EM{N(AM=0d4{s=`>4_``Jz*Z5qr4R$YN z*4x%4;3ZY@u&x>Rm$fxKZ`tEl_r5BAjAwn{qE}bXEJ76Ag2Yei1r3L1Ip6*B zQ)=H(j%!aOC_4qEMg4*vv*i~Reg8Oyktj9sj_~pPg8Sn8Z?!TQ{d_fWgR4;$bn2g& ziyPy8`uY(q`rAOYJ)ziR4h_!04Ga3iL|g;7t(dRT9tF6|N%E1JL!v6xpWMxZ zws>XCuz2dAmM@!{**%y3IjUK|kMk$};@F8-Q$oHV63d?+w1I4c|F(rdWe2}U7Q^q( zsGgt4-&=AHxatPC1p)m6pi`6G&=a;zpL@281#TMvQ*cG4Hn)F2w6qZt!j=Y4Evm7@ zRVuRd3mNY}0&NBb1r_rq|Ks>zFD!!!cn{_>At4&N`j&)~x5TLNk2|iyfks^&&XsFG zZxQ#O$}jsEFVHsvcln-e13WIl*Nnx2pC5J7ZWZKKN^)-(u;rpURTf82*r{jMP1ca==rdUe=_g|E$C*r>S=4|vu?FEF{W&Mq-cx(y}LRp2**W+>L>B{8d5@!UQ%yG!Ld+CZN1zs$q*rc3Zf7|}lxyEUK zu5+g|El8uxhb<3~DuycsWnXK2_dI<1w+KnR+MfEvcE*T|c@I<%PaT^Yh5}nsvlMNGNIuMcvuY87 zZ0C{@0iT+v-n^+OD9+gc1ZB#i1{ocdr$Y>zHv|(*m}p{bpp2#GGobv0 zP<#I;z!C@N;vW#an7;vi1>T7o33omY-0G$ErW^-I$g52tM=;G1)5X9mAiB@ebU%Xd z3?X4|T_ZNn+4n3*tIO_e$=_)>lrrH-U*D_%41M8ro0EQMZ!uh%rmS0H$llzWSM zXN}1_$gh~Q5no?4^ij()fos8(2%|Lby4fTWM8fq|YzvWHq(#t_3&I-^1PcA7YkrjL zm}140X>)K6o2nAP8WBqJi`{eBQnxg@><%m_=vJpqc=qbSVI)M%(KSLg>QefgDPBf5 zhd-;J_$W~AD8a94eCna<`q%Wh{>szoR#*=wRqm2wWe9ht--6E!PdeIonlCK35& zn2MrA@dU>~WHBU0e4e5ZWiYH?Gdhn^|bIebT9nUh85{h|0GK%VlW~&vJ&kIf;{IPzPh5>2FF3W!B`7S#F zj)i3978p0Y^pl&aqHV=+FS{RG?ggvAswqZ0s^2F|cv+yq+%=BczfnG{xUvJHT#pa9 z!Dlr~R~PM%Zmt1gLQDPU0dC#Z@Y6NP1bylpNuy$MfT+6~T$>O7B=0o^)MOa7rnFOc zR5^}WrBG3nIjFi8aqo^?^3UMqnb>phH+RFS_S2rO)*Os1*+Kx8=jTDiGq}5I4$q0m z#8@aojcL$LN+mjrz03{+tyq%_(YEK%rJ(CF7sF1!XOZBYRF?o3O&zQ53>5`yi>M<7 zC)jVRh-5nV7}(nns2TfoFwOw5)NwJ+5MJ)}f9wQ3g$`BxJtpGJ?aeu^RxRms93{4h zijrfvQJl2?J)EiFh$W{0(jL)+?7a3AW99kQ9^<0R$IQx2NP@pn8{})*rbifqYaz6))UJ6# z?l`@H_w;Z*HR`%8#xn#WiO^F&C4;g)+kau})B2KC*2kiJt0OBaB7def{ji8?1CR${ z5AMRygKo#>XI~(|gsQ=YN_S<^BbOY0>rBwQQH&lwXBPH+#Fx#0~A%hu9w4+XUHaL(1ZiM`1+7Pn!XWx#$Jz+e#&Sd)DLVE_nuTU zB<2Pg90B==&3g>DipPnrUSM*}Zk5U1?QOUl<`GkNN7`q-C&Blr@_Q~Qy3dzkjpj&Y zF)VjJ6hDaO)q_V~?5do<%`ZQ>fHd&lHEgpNN)Zp_Fk}E#7{kx8^#C6X72w3uCD3^o zW_o`ikNVjy7@i9{hrTKf7eD;WFKPgf&LLDxJK3AFk3 z&F#^F4d@_3sboB_2Wec?L#83-DUats{9!Lq$uA@B7JUu+@!exkNa5@nSv9jwZNksA zOiIT#TRz?&-=TPt&CUr|!{=bbEyaoYp3=qV+WcrCPdD{~Kz!M_ioi#q*VlQP98uoD zAJSMrL>Zv@1W4L1?~i%+qywK}FsHsK zykx_~j;nI`%nn%BS*w&A1#!o*GPt zOpPg<|9)oj&30fhcx!+R=iwnw6tb$*lZzFz%{O&plRAkkMa|CCT!uhu*(UphfV$wA z)Cff~Xt1P}s-;nJY>*2N-_7{==@wf)t}^>Kb{Bsk&jPJFfV}B*_Bg4jEOAd#of-66 ztkD3yM)&tplUEobZhVQqXsXA6@wXP0j_=c1UZ4JY%+@;Dh`idwTxU1C#yryN=QNWCp{4uMPJygn66iIwY;gB z9#6D@=5KgwKv*uS<3Flw}h{qS;Z-?dq1zd+gZbErE z9k;U{b^l~T|K7Gwnv8wR!^4Z?aBSmY6oda`Z(l}$kA!3(vWyq}Iou}?zZa^5$}@u?ievR z=V+Nvpg`Dj#~kO5_0|>QcyT-_76EFG<;iFt_`acQJatU`IQIMdg5L3rwvw!9b5-a@ zN3+VGr4Q=3tw=$XomAfMJ_N8v>O}hury=N-7g9L-m%8Au0fYZq&g(@`u38^%WqPGLAAX7DN6w|p8k-w z`N+~$zXCO^)NC^~uIh+HO`$8LXC9G>lx>{{ zD~F_>Eq|bWIu5RMm7{|BYt=Wcy$KVL|s_-CO}~*aaR&vxM?*KXbSE zRrKj1UscMavfBWu%vMz2YXMw z?bvb(dA<%E_P7g?#^wKQ1cx(^qnsZDoarbS?2`bkE{rkF7(8KZdVko^NaS(3C(Sn% z4X#NpQTK&A=N1KGaX}P{{R<<6AO>377~lmlcE1VzT+nHpgoCsf1>slvcNE;eicNGe zvJD|2rcugFVQCsg-AVq9y-XFIPTTKB%ibaVJbrn)kI*}{NpN4FO{2Ac)q9}m`!^a3 zA3>aN)Ek%IbB)n;yRCOdML{#}egv}>KmwvjInK1=z2uC|FcOsjW@i}C5#8YBJn`VY z$|S3&iOLY)9niPd~V~DZ_zk!(~42x-}#yqPQsfikD>O zcy2&*#tt=yPx=`l4p>j1XLTD!+O0SpFS~!3GY=g{Pjor-l19;Lct4 z-;+D=G8;Hg4R>e~sA~D#ztnp}ff?(TWHZE2)AThIRw=P-`{g_svHf<^qpJ}yB<;F& zQ6wgUgl%ytk5?_bf3vkW8!1o+F*)k|2(Jh4jh&1RD=kDTby*evVLceL7`;gD6pB z6kLn|e@s4b@GzBrfsv^FvUvP@V8-6N-GTqJg{VJz8;EWhW)8OR1zj_>V==*zJRWZ@IQXFnT6w&W=tyE3Ev z;Zg9)I3?W<{#v2-kjzbM|~gOBYu<& z%u8v@9vXI(>ykeO3XC=GYQTTeO_wbSG5}M!h5)1UluW1DurP5~$Lv^3HehWTQ-Mil062EJmdXaZb`p@A&FQY`NLdXILmnT_e;Z?{*&A0 zcfT=pJwpUxx;K&X?sK*|5_qeK4%A=@6|^~p-e4ciMT6eAv5=)e4K5A)rZ8+Uo9yS+ z#)dt~f6hz2r+z`V7C_Qrj0FNAH}}=yDb=*W?01hMIEVMNUnONiWm*grN>KIE3hCYB zWTa28mSi5tu-M75NXLOfv;N5k+P&AVst!szmF}?B#BqMRTA+e@ivkbN^NmpodLAY~ zWMmq10e-q<7A03Ehi9$vSI~jEbaA-)ly_&@DWM-pG%N%J_xA1K04HNL7)^pGye_?W z>q{32k;yR6@owgE<5>X&@eniZMYbPeQs?p31YF|R-h-%X%V6aMFUcmq-y89SD&ifu z!ck=8vMslDzg&(ka)Qc^5JmXcbmnDrW7lr_<&cvdb(-JA|7^O$a^MKSnO7;s5-dW; zIYhhHU$X^1EK=v`fv|iHNT&_pPX2`8GN%Jhjw=!fomf~74u1DO9J+eNJ+kEt!iFdO zlfW0WvC3gvzVF(rXaScMD#1TJ9g#!=>hp2olFp;(=Jk`g%+yenBOC4B+a0fP{^1pl8qfK@-LR(5gxLf7UY2*4>E@{JY$3Q$o7a;Gitb1$#J2*G|cny~bgm#!% zl~SXe8|V-?nO)=)D%gEd3^!a1hZ89+Oo>y~-Tl?eo^#k%R}6->>61gZ9ww$B=WIbQ zE}MMN?bFAZQ8pNo&pN(OHx9&~hhd#ymcSLl;N+59IWZ%%cKt0_am#wUXoc9qMgjLP zs}r%WqJD26p(lghM;sgfVhzu{!*KPNX6;HEUX1m>l}2g6`@Mt<+%~$<{S`k-OFc=H6E+rP{klz`uj=x1O4jZ6=fvd}&*`h3 z6Nm6^g7VKb3u-&bHQK4)jx@G4KWs>lL*L&};gBr@=lx(%g;F`_`o{yEMGG&9&F(fJ zR6?cy{*SjUkXJrbp#_v*#Fh+CecVuu zVqvd%DBEQ?0VlTP*#av*uQ>U__X9O%9DW(cNrrbldxfJLkX zQ)z`|;{I2FbbaH;V|85gF*($-4%*c@qot4#4P(prPE=os=^_3xKhHB0gT&FFu7s_a z!)Pv0W42YY;oaFbO1wG9^1z&W+Q zzJ;}B(1(*!9N^uO=u2`Ub|R(X%XM!fLkgH7y%c^J>@9T;IFbK%Wcfa*)W0HrBOgr{XWLkjAz7#{fBuWmh zvj3cHM)EyPvLY{q6D-(7_Vcxn{aOXC|VnWM1Q; zSE5Oa27Mk8xQ^(jk#jSr&#EbB3?%BS#_*Qe1SUGnP*gdbh4H6snw51Em2RV!v-h$FMl`miq;0?-yaT-( zAQzUP*TG#_X63cqxzg~-{H9d=2rW1Ho`+IE3oB8tfI!=8I#myJ+yp$7{|01LE{ntf z4C?ei1SPEpCvP6{Aj znR5*O#U32Jy?y5Ea<|1unB12@Kvqwu&Ha#c^W_TDSo9UUcB2QTHzT&PBdCe- z0`>vRfx`H*^IRA2Zdg2Z|1KtQCoTtb2TDk(#uRYH(<~_;@%n>nB7vPCQODY={TWwN zx?;8NR^CN_e~Y-!l9(}P!R>zDUf`{%%k`%_wn^YsmAA3~Iosuer%`bOSZ%3P@7V*= zPz;*95ru=@w}7pgq~HStyvmN9ef!De8HTsnTN9=m<|*s(-Iu{w7brW z97i7=*M`1r4)fuQ>yqDDM~YkPv~JRo7N9(mNc|+8Xq2qgs-m~oVvu)~>E^o}an6UI z4Vx=0G6H8y|B#FOabejEuxRV6hxR7eLs{^@0LJ%KpKgRksM+8#l#$QThGGJKeZO{+ z;atFW_w5isdh7l*M;S88fYUH&x9+t5rW`ai)0sj%H>EY#^Wx&ecu#e0!DK7xbenei zTJpzBl;d5x*SHj(IK#r(^&tszc*|4w0_e|`%gz&yEYk4q2N*`2r8sQQNpd`68{AKw zZ!fJAuh;DxaJN-PG}(Kd41E3!ox-U#?yfgkYN1q8#!=4o$XU?IpapJ9xDE4rb z4kL&QB8km<(Yecotn{0v?8hV?Mh+8O>oovfN%O1zdc*z@$Kd6%MH025om|7#=L$Jp4Aw3@cB}IU9=kBB zQG|VQ@(9!iD;W;@zL@Ldc&4XgSQg0|RZk);kA3gla85q9!R0Wu(ep;N_YtWc4`9H= z^+8I~vadB!T0}`u!ff_DB4w4%y1BD`xFe@2`_Fn%8WBj>A)h`l>yuq(Bz{?P#Jfu` zy3I6piE_Tud96BmCPAJZDXF$B7KxT_POov>;#ui@k-y|7{vhA!+> zu?2AfU0rQ_FPd5@!3$N;iF25yqWeu_*XEc0e(GWtS52yV3S0`FFASH<%jHnA?Lwg! zL;6^9#FTx9eDuYp>9HMAk)hF&1?~MuKL&03ftwxYR{zUWBiv3QPkfAXf^|Eyr>a+$ zze^RL6OY<2TaJheMztP>1Xv0B?$Hh=o%-_slj0&3pu!{%yBmojf2v%1!^fBELg3;e z231AI!9{O~r%y!WZwQ}~J_1X$wrEq%_PL!Tbu`X=xyWr|WxmZlnQhZ|aw~Kf`>S%M zb;l%+098#QPGe6Mwh=g<>G-1Qa^*|mXOsPDm-u~(qo-|ponCIn8*yZI4l<2Yt071Av|hr8zKe)n{2669xoomi+hj{51jFnYohl?M za#40qBRH(7^1Agd*JfQ7XH6|%v_DHt%m%o}-{nN}k8>KCBh|5J`u=$(R?R%6+N;44 z(!)W}z04-`D^kBW*v2nx0Z0C=7-XRt=n+>uASK{aHff1F_F5^e0?z`&m58z)mQ6zX zU4uhc@L4EFjVE_Ah?eZdaWbPktY)mm9l7?6F(02=&V*MhWZ_FvyY0sZIT?4bN ziRN#;!S$gx(sJ(!I(HGfZkt^9S2(0{~xiJCBkE1uHXs%(ZO|K0=#0YS)DZXp3a zU+UjO`{HB}xH98l&|D&&KW*Fy?6&`BnZ-K4f1?u_=?#rE;1zw_4o?IH9sV;AZRgLW zteMWcG+{X~OpEQP9LwS-+4?L4@ahjzmRqxsHZjyM159U8r9K%zv;hQ3vs1Z29R3IM zw=X_ks-Qqu4F|AasKXdM647fv;h(hak|~NPl z{_VFZQi(>y0jen0{p~Bk6;hZ@nt*}dHLu6eVp%R%tqX(P;dUUy#A?1*(c?t!WTSPy z{^%>^wdvZ2;Z`k2Vma~=rGLx~$m^AEbD7C?)m5EZZRd+W=R7isZ|K3yo@l6_)*ixX zdh0+J=MJS=<3-IfdEBUCdAz+%%)5%M`L~#%Ld8N;9K?GtvydTb*}Oygn1EC(KN(BP z`ifeZ|!kMDrsODLPlTdwGnEF0v47jm^`61bfW0zxv= zA&a(r&yqE_myD$trKqMdMq1^tq;@4IOfvQ+`o#YAdiW~96X+al!yxxzOQc<-L=2t( z9m-)^w|9W8;)?&%Ue-JGU{!X_?7SL}g6<9;QUF`F`J(f-=M(6A(*3}f#ql-tt%6ir zmqmpxl54fk0RGXAxAXD(?tNljNmML!gp24HSWH0}1w+lUHSP*A%se?wGjm{YSF-D2 zbM1*&^aTS(`SYFs$GW9L+om`NO?o~F7ZY^S%Xv+#nVtk+-P3%CbK)c`>o3R`YGz^e zI5B=Z469|CXzgdzhipuj(PS;4l?&20sjwox;kvMt+}QPS5gd#ck=vEmX#x0Hzvrl1 zrk0!TET6oCDh$O@VYU}K`4}q_`i$hK9Xwhs zC!POn*64w!%uu$Lt=47@fRHfbyXi>OW>i2D;i9kh(Y9$$F3h?-HXwMa7T7FR{7ecc zgFZsfpm*^=s0(xic$Z4lbIm(4*eyzoDPuBz+mE4N!9kXn(V;y&9|FvgL|Ojg10Z7n zN1F*?dj=;X@E9$-x~;Qcpte?44}GdZDFbBGQaH>dg^{hGNigwA{9*xYCcsZa#_33- zC(r>~m8h)Rm|J9;H9MGp12R zqYc1-U~kc9T+zT=E$~k6lItnN2oqT{ZUle61ttIh<8g^q}Tg+&a_dUG?tq}ep zt#Kv{GwC^PuS!NQO=3*+FM`>vuM}C}nZa1oOpomo91JA#CFCjQ#nG z`OS#{K+OX7h8GnVL^2f~k`N8rECW<)Ax<+Q?9Nm;3gy5~UN!8kL*~uO0#`lFoX`!t zp#s+#zop6$8g1N2u*s&t({HUaxf-kZN3@B0B=dMxonw>7qZ?8u{< z_!Dg7oaBm$SF0#4+aU1M)b(aKj;Sd-X?(w;YDux$cV5ceViVhEUP0bHmRkZby5L+~ z_vjjXXtfGl{TgDoq;&LI=}){Zel(A|n!w%g1qCfMr|0=rnYZAn zGi|ZO7x+CA8@=DtG1{hF7W0w+4k9=f_T9OmPg~nHD*v$>ZCtt@b~^zHGh*N&uBo0c z(*nhn_7cym>9O+U=W8FIU}tQP{vr%Y3M*{c4B?2QhuJ|NZLnZ&xrh1=HBkgL+87zu zczjX-u?NxTPN?ObAn0lETE_%DjKL-fZAtHW*lF#3KI38wvTy^ho6#UI|klAHDdV^*FXw0#nnf1q&m)Xs^JyqU)b7Xqw@P)Is zUHF0MV(>NRtyHp5nTBN__aGcdXYA4NpuuH?N;gtm`lFuZJu19LIYC7^>$_PiJKR;j zDEWwb=MsNUy24A}!MF|{;`g+BdI^Kacp^_{LP3y8w(D5$py=#JmttbfdR~$J9PIAF zd>1xTE%*&ac&{v;X_+GsTj#%Gl#jS+7hm74EDgBFliCN;EFR-tD8(q($PI2RGLUB%cJg8KC>gwGFsEYs zbAI1xBhR2culp2(D(~R@03oz1O|r@yyHVWtO<oF;2O2lPcfYvM+|3OL-b?Zw{{QH|EpGxn$58PIxv?%?HW|6X3~ioh}V`r|yS5_3PWJ+*{2`c?KLp+rWFY7DW( zEPFG*uGxVX{$5(R%>^b%XEr8T9g7})@Y+D$&mPuq-`Pp?FE{@D$ZRmtPP~@zH@n zXw1Tnm7q!t(5ejFMoYvkjf$p!3ElZ~jfRsz&?l9GrYsWw#XMN=dB?JQjaypcE3SU| zTw+OjSg)^|b}!S`M!-i76L_S)9-HuPRBD;cDiHKbNPy;HWI?Kzc+hITx}Da~A|$-J zs8fE^gv<$}!TXv@Lt{PWgu2aM;O^`QRj=>unL%$Gj%k97>tQgfvgm6--Z7?D%Z__X zwN90^(UO+E3D6(|_!&%ve@+m59{nzVU*%-wcT6x1O{wRv=GhXtnM(b_TBZu5HE`2^ zBo$W3@FTSUsolnyRMg|7-6mh~ZO6mJcbkk&xc6~Kp-grvz*9HQcrvzMhbvdK&J!Y( zv`TAgnwdiLMOmEs5&-~kBpqp#(X;i6Zx`lwCPZlPr#`1YopQP4b&p$G^%hPeST5cm zc>GvitQm$50OPrsl}7qT*Pxy3r4Z|R4{V|_9v>ZjfT(8Z9Xeo?`+q@DrW8PNBKwOa z!GZPz(7&{LSjm!;S-2_}q<_d~{O@}5nd=GWvV#O0FT%-dSn!dzP;QVgH`lF-aVD|b z69DioK^WJTWzsPr_0@l0Y~_{*4SKwo`VB7OgVM7KBsJlM+139zQo7!s>y#AjT~OR0 zSd7S{u3J*p?5sBD3(xL`^-`SG!f)LYrP_u01b2QdQ-JRxba|b9X;k`bT5YCxOd?=d>UAaFcvescU(JgFvI2T(fo=&myb+{(u`NeZC0?e-m$y^m5lKk-& zTor>AIsc+Oo6l;17uDd&CdI=_dSrXuIM~6P(M!`XS_*(}d2yR4o=Y?tAcbBAJ>sZ9 zWr3HXz>CM;=MfX%=Vu*bk-(SdI_$_~B9!N&A;ZI|0|B5m1NVp0VfhLPrd)?o?kG+G zNd{LiH+P|Q%KD)TVs9#VQ@sgusz&67r^MoLpaSR5#S>x6bJG?uxV1d@3T;!Cx(jzK zu7165Cjlw?Niv9TYF_ectr%_~0OPlqBSqKF^u#8Uv|1O!V^P2qBj60lne5u-2Tnor z@Qhm^8L#_z4Qu)I(%RBljms0L8%1p=LaSkr|{xN1y)@0+~? zJ$<7*cnCx-(jMIH(~lRFdq3g~w#u>vjy;HCEUz7>M4KfD2;R{*Oa{0SpvzoF$$0*( zp=lkQU&5qh7y_fuvoy(>>Z}h(R zuntsNIZzHz1j<(+v2FI2mbWYQltC(Q>f*H?pDW(}_*#4k+1NQXd3L_O!7w4i zLfM`1@v*=8_q5VYlo$AXXX*ZOXUZP*dlb1$_W5X~i1=*(c`HTzgaA*tST4SK5us)t zSIPn&YxG=MUYt6kw1xD%-MoLVd+2l;;X}bWPbeNcV-B4el6rlaimRE5qzsD|UN-P` zozpn_+j1SvhY=Q56{kNRk16Gfuzw0MvnGZ987R6K5qlrAB1ZEl=IZ8u=8_Tm1r}x$ z`E>07jE5y7P{51@W$ohe0l=;(FOYQ!Mz-%l=S&1JS4QRMpTz$1zc7+LWxmu;hTfT%&_5ZJXU&Ld81hA4Cee?( zD1`ezFOEHq!54-)|HwJq@kf9C%`%8P^R6#07-^XqE-&;cxL@{HZ2~N4TF0s#wsmG& zERV2?w;kZ+(`Klcm_9!Atwnz_u?*+n_0?OOx6a5=sf??$jOs!iPM$$Y-v(ex8X3gn zbk#WW;m)EQX!8tIhIK8U%RJ@8!0nePQ8Z~qCM34LbPP4kj5i^SH)gk;#FdKs?7t3* zkAvQ>j*O8mT`gL%IKM2}Vnh3>pu5+wH^fL;&%;2ga`(;pQoirOyhqssU75=M(3NIw ztWC?}F7wO&_SX=aJ}z``&J%Bw(e52Zz--WlPP zQKu%_{hD0=epk<8zfr?ofB!qlG&+Fn$Z*%k&h?bR>l1#)J1Vou{2cg{gZ7X1_M%v^pc34*~u_J z4~lb;zZalllx|l~!DC3R5Sr^4y=A7d%_YoS43)7f$k}k>{C}3T1FR&cj5Wjd+7f^s z%oWT$=_^d0vXg*Y}&?EoKConTNOwasVksn!kGpz#UiJQ+ z^Nq$%j9myku22~B4ECBk)1Fz)(r;;rT{Of>)!@Ca@W)M`S&VdkYLc&BC$K=o+|aMS znT3p-+#tPM^u}cSkpbTzz($DlsOU`MBhBKYWxZ&dhaCK2Ckr*cakAOm73nG_lgU~m zB)eE(o+c2JlbSWN-I7Ov=YA~1&n}K(WuA~1U3|z=-r_{sC^dG$z8wZqaT=y-X{Fsh zJF2Dc%j4CE8P<#q+3T@Z?gzl!QDqk(gPjZggPqpx`laR7z1;a+=A09^p z;Cd!l0@uXe1jnHq)BoR67Ll^BK(O!H){u z{@rJAn@Aqjs_GxF!jaf<`xjCOYO#ptOZbj~$!qsc)l|27916ZLF=itT)BNU9rX#07 zZZ4@P7c8rAsGgX}H;qXD(04aA7AE4v*f~v< zMi|SXc4Ky1DxNVMyD-IabBr*T@;|>Aqy{GGixrG*^Zk4*H`yMPOn|mA+>gh14L1Y% zTlZu%{rzDY+Q~I=*`%T3AwqM@q9T9Vr$qWY4@LWA^8>%{9&;dv9!R_&M2;X?Yy!yy zN%~_O_A?<&UbB?~qB>B8;Kz)59l|?on}b_QgE5sg+m^b`XYT61(G$F#68!uVAB`wh zVLK|3F~k5k4@z+tP~nr5i_`MHiE?vms$mG$C7`)=#ZPkVBw5$JiHGka44x%s#q#+3 zi#5JttfQ8R$8+OPpN%zwg=yS&$CqEmT2}3qWBc|kSuE+}zAaf(upmYyy_(9#EEu*q zxG?z2tAq2H=qQB8^lv{aF+JMg)>lL`v|sh`IXFwtGi%NN*>OahYoid>K_^Ca!L4$?eBX#rZ6?95g}$vi6`| zh=zm5oqEUb32XMm0Wf$f&5nF3!EJxh)3C=43#PS^HCiIZMkXc**8q@S4WYkUKXrw( zRKHBqcCR4~e8)1PlL#A(d9Cli`68WB8oB8Ir}e0J((8vJIW0mG{IsuhbYrn=It5a} zZR7Nfqv&wMqo9#2k7iMKU)6JLoSE}?@2pix(PY+a;}%z;w)9EI({5~G z7G|{DWH{;5V(8j#=&Gev9`{vmwQ+3+B(AkfRntVbO0VYstlOCjg3LH7Tz^IMN8eE? zy{x)b-hz_amH~uB^dnKLH2~`Q`&{62!vzo^=Z}pNFvk2_e@gO>-t+>!@sUTkf6MzNIWzw9*g^~0Lz8KQ6=H2<%GqlO@^*QhLv;MHU*`DjUg${5?kC->JJIJWyC z7PHgpJA&Eznn%c2W=NZ;*B$YH>k+gnlbkqLD+v(6@vB$f*1heFr#nbNL|Ox#eTc6*2tDl@U<9knwbK$Ru zVXt-YOZ4xjE;JI|$BqBZ?ESZ=Tc25SRzAGLIcLYW&bwl^k!u)Z&nI?VKXSi}J^tFW zlXE8RzZjJpIK%Mmw;scDfrYpCJdXXWJ}KxQ$7>Qc-{_w zp1ZI9%J;G@i}i(bg^K#Q+Sd#J`E=7Px>DVh({Ivsv(kGP*Eg2&{4n=dFBFUU{&z9Q zzuf)yolL-?2VfLFUmo8sG(l+D>?uvT`|lsS^!|7C^E=;L0=@wc3F%|{&>@;^5R0^LY7-$A8cRQ{a?J~_LC{w`DAaTynNsH z)wXd<@-Bb5xknZ)<9$Ep$jbMDz*TT{^_NP39-H1+7t7^%|MTPpEVDn^oV5Gr_rTt9 z%V)-lT^>6yYXK&pstotrmv-Hd z|9HRle3?^~dv<2})Y{4yh5cXi-o43i_vf?COWYqUw~u9sUA-7E6#9TZ0~(#Vc&_O7 z#gi}PodYS-d#m$ZHrE$;sf{~aNyaD>98{Er!xxS(|JhBNkJs*$bgg0l0#8>zmvv4F FO#s}2$Ws6S literal 0 HcmV?d00001 diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/AidenSidebarLogo.imageset/Contents.json b/ios/AidenOnTheGo/Resources/Assets.xcassets/AidenSidebarLogo.imageset/Contents.json new file mode 100644 index 00000000..677c9407 --- /dev/null +++ b/ios/AidenOnTheGo/Resources/Assets.xcassets/AidenSidebarLogo.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "filename" : "aiden-sidebar-logo.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/ios/AidenOnTheGo/Resources/Assets.xcassets/AidenSidebarLogo.imageset/aiden-sidebar-logo.png b/ios/AidenOnTheGo/Resources/Assets.xcassets/AidenSidebarLogo.imageset/aiden-sidebar-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6e69b89f00fcd5d701d7a9ffc3607973b81ff12c GIT binary patch literal 141352 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

    ); diff --git a/renderer/components/settings/model-pad-settings.tsx b/renderer/components/settings/model-pad-settings.tsx index 9ebcb3ed..33b84895 100644 --- a/renderer/components/settings/model-pad-settings.tsx +++ b/renderer/components/settings/model-pad-settings.tsx @@ -2,7 +2,12 @@ import * as React from "react"; import { Check, Loader2, Plus, Save, Search, Sparkles, Undo2, X } from "lucide-react"; import { Badge, Button, Field, FieldSet, Input, Text, toast } from "../ui"; import { ProviderIcon } from "../provider-icon"; -import { createModelEntries, encodeSelection, type ModelEntry } from "../../lib/model-picker-data"; +import { + createModelEntries, + encodeSelection, + visibleModelEntries, + type ModelEntry, +} from "../../lib/model-picker-data"; import { emptyModelPadLayout, modelPadLayoutsEqual, @@ -14,6 +19,7 @@ import { useArtificialAnalysisStatus, useProviders, useProvidersModelInfo, + useSettings, } from "../../lib/queries"; import type { ModelInfo } from "../../lib/types"; import { cn } from "../../lib/ui-utils"; @@ -49,6 +55,7 @@ export function ModelPadSettings() { const providers = providersQuery.data ?? []; const catalog = useProvidersModelInfo(providers); const artificialAnalysis = useArtificialAnalysisStatus(); + const settings = useSettings(); const [saved, setSaved] = React.useState(readModelPadLayout); const [draft, setDraft] = React.useState(readModelPadLayout); const [filter, setFilter] = React.useState(""); @@ -63,7 +70,10 @@ export function ModelPadSettings() { infoByValue[encodeSelection(provider.id, modelId)] = data?.[modelId]; } }); - const entries = createModelEntries(providers, infoByValue); + const entries = visibleModelEntries( + createModelEntries(providers, infoByValue), + settings.data?.hiddenModelsByProvider, + ); const entriesByValue = new Map(entries.map((entry) => [entry.value, entry])); const placed = entries.filter((entry) => draft.placements[entry.value]); const draftPlacementCount = Object.keys(draft.placements).length; @@ -321,6 +331,7 @@ export function ModelPadSettings() { providerId={entry.providerId} providerLabel={entry.providerLabel} modelId={entry.model} + artwork={entry.providerArtwork} className="size-3.5 text-tertiary" /> diff --git a/renderer/components/settings/provider-editor.tsx b/renderer/components/settings/provider-editor.tsx index e7fc0cce..eef10ebf 100644 --- a/renderer/components/settings/provider-editor.tsx +++ b/renderer/components/settings/provider-editor.tsx @@ -18,7 +18,7 @@ import { toast, } from "../ui"; import { providersApi } from "../../lib/ipc"; -import { useModelInfo } from "../../lib/queries"; +import { useModelInfo, useSettings } from "../../lib/queries"; import { resolveModelDisplay } from "../../lib/model-display"; import type { Provider, @@ -27,6 +27,9 @@ import type { ProviderModelMetadata, } from "../../lib/types"; import { resolveProviderDeployment } from "../../shared/provider-deployment"; +import { ProviderModelVisibility } from "./provider-model-visibility"; +import { ProviderIcon } from "../provider-icon"; +import { isModelHidden } from "../../shared/model-visibility"; /** Compact k-token label, e.g. 128000 → "128K". */ function formatContext(n: number | undefined): string | null { @@ -59,6 +62,8 @@ export function ProviderEditor({ onSaved, returnFocus, }: ProviderEditorProps) { + const artworkInputRef = React.useRef(null); + const artworkBusyRef = React.useRef(false); const [label, setLabel] = React.useState(provider.label); const [baseUrl, setBaseUrl] = React.useState(provider.baseUrl); const [kind, setKind] = React.useState(provider.kind); @@ -74,6 +79,8 @@ export function ProviderEditor({ const [defaultModel, setDefaultModel] = React.useState( provider.defaultModel ?? provider.models[0] ?? "", ); + const [artwork, setArtwork] = React.useState(provider.artwork); + const [artworkBusy, setArtworkBusy] = React.useState(false); const [testing, setTesting] = React.useState(false); const [saving, setSaving] = React.useState(false); const [modelsStale, setModelsStale] = React.useState(false); @@ -82,6 +89,18 @@ export function ProviderEditor({ error: boolean; } | null>(null); const modelInfo = useModelInfo(provider.id, models, provider); + const settings = useSettings(); + const visibleDefaultModels = settings.data + ? models.filter( + (modelId) => + !isModelHidden(settings.data?.hiddenModelsByProvider, provider.id, modelId), + ) + : []; + const defaultModelIsHidden = Boolean( + settings.data && + defaultModel && + isModelHidden(settings.data.hiddenModelsByProvider, provider.id, defaultModel), + ); const usesArtificialAnalysis = models.some( (modelId) => modelInfo.data?.[modelId]?.metadataSource === "artificial-analysis", ); @@ -98,6 +117,7 @@ export function ProviderEditor({ setModels(provider.models); setModelMetadata(provider.modelMetadata ?? {}); setDefaultModel(provider.defaultModel ?? provider.models[0] ?? ""); + setArtwork(provider.artwork); setModelsStale(false); setConnectionNotice(null); } @@ -107,6 +127,7 @@ export function ProviderEditor({ id: provider.id, kind, label: label.trim() || provider.label, + artwork, baseUrl: baseUrl.trim() || provider.baseUrl, models, modelMetadata, @@ -117,6 +138,35 @@ export function ProviderEditor({ isBuiltin: false, }); + const chooseArtwork = async (file: File | undefined) => { + if (!file || artworkBusyRef.current) return; + if (file.size > 512 * 1024) { + toast.error("Provider artwork must be 512 KB or smaller."); + return; + } + artworkBusyRef.current = true; + setArtworkBusy(true); + try { + const dataUrl = await new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => + typeof reader.result === "string" + ? resolve(reader.result) + : reject(new Error("Aiden could not read that image.")); + reader.onerror = () => reject(reader.error ?? new Error("Aiden could not read that image.")); + reader.readAsDataURL(file); + }); + const dataBase64 = dataUrl.slice(dataUrl.indexOf(",") + 1); + setArtwork(await providersApi.normalizeArtwork({ name: file.name, dataBase64 })); + } catch (error) { + toast.error(error instanceof Error ? error.message : "Aiden could not use that provider icon."); + } finally { + artworkBusyRef.current = false; + setArtworkBusy(false); + if (artworkInputRef.current) artworkInputRef.current.value = ""; + } + }; + const applyDiscoveredModels = ( list: string[], metadata: Record, @@ -211,6 +261,51 @@ export function ProviderEditor({ setLabel(e.target.value)} /> + +
    + + + + void chooseArtwork(event.target.files?.[0])} + /> + + {artwork ? ( + + ) : null} +
    +
    + 0 ? (
    - - {models.map((m) => ( + {defaultModelIsHidden ? ( + + {resolveModelDisplay( + defaultModel, + modelMetadata[defaultModel]?.name + ? { name: modelMetadata[defaultModel].name } + : modelInfo.data?.[defaultModel], + ).label} · Hidden + + ) : null} + {visibleDefaultModels.map((m) => ( { resolveModelDisplay( @@ -428,6 +537,7 @@ export function ProviderEditor({
    ) : null} + ); } diff --git a/renderer/components/settings/provider-model-visibility.tsx b/renderer/components/settings/provider-model-visibility.tsx new file mode 100644 index 00000000..1602d55f --- /dev/null +++ b/renderer/components/settings/provider-model-visibility.tsx @@ -0,0 +1,134 @@ +import * as React from "react"; +import { useQueryClient } from "@tanstack/react-query"; + +import { Button, Input, Switch, Text, toast } from "../ui"; +import { settingsApi } from "../../lib/ipc"; +import { createModelEntries } from "../../lib/model-picker-data"; +import { queryKeys, useSettings } from "../../lib/queries"; +import type { AppSettings, Provider } from "../../lib/types"; +import { isModelHidden } from "../../shared/model-visibility"; + +export function ProviderModelVisibility({ provider }: { provider: Provider }) { + const queryClient = useQueryClient(); + const settings = useSettings(); + const [query, setQuery] = React.useState(""); + const [pendingModel, setPendingModel] = React.useState(); + const [showingAll, setShowingAll] = React.useState(false); + const entries = React.useMemo(() => createModelEntries([provider]), [provider]); + const hidden = settings.data?.hiddenModelsByProvider; + const hiddenCount = entries.filter((entry) => + isModelHidden(hidden, provider.id, entry.model), + ).length; + const normalizedQuery = query.trim().toLocaleLowerCase(); + const filtered = normalizedQuery + ? entries.filter((entry) => + `${entry.label} ${entry.model}`.toLocaleLowerCase().includes(normalizedQuery), + ) + : entries; + + const cache = (saved: AppSettings) => { + queryClient.setQueryData(queryKeys.settings, saved); + }; + + const setVisible = async (modelId: string, visible: boolean) => { + setPendingModel(modelId); + try { + cache(await settingsApi.setModelVisibility(provider.id, modelId, !visible)); + } catch (error) { + toast.error(error instanceof Error ? error.message : "Couldn't update model visibility."); + } finally { + setPendingModel(undefined); + } + }; + + const showAll = async () => { + setShowingAll(true); + try { + cache(await settingsApi.showAllProviderModels(provider.id)); + } catch (error) { + toast.error(error instanceof Error ? error.message : "Couldn't show all models."); + } finally { + setShowingAll(false); + } + }; + + if (entries.length === 0) return null; + + return ( +
    +
    +
    + + Model visibility + + + Hidden models stay configured and keep working in existing chats, but disappear from + model pickers on this Mac and paired Aiden clients. + +
    + {hiddenCount > 0 ? ( + + ) : null} +
    + +
    + setQuery(event.target.value)} + placeholder="Search models" + aria-label={`Search ${provider.label} models`} + /> + + {entries.length - hiddenCount} shown · {hiddenCount} hidden + +
    + +
    + {filtered.length > 0 ? ( + filtered.map((entry, index) => { + const visible = !isModelHidden(hidden, provider.id, entry.model); + return ( + + ); + }) + ) : ( + + No matching models. + + )} +
    +
    + ); +} diff --git a/renderer/components/settings/providers-settings.tsx b/renderer/components/settings/providers-settings.tsx index 910c89c5..104e676f 100644 --- a/renderer/components/settings/providers-settings.tsx +++ b/renderer/components/settings/providers-settings.tsx @@ -85,6 +85,7 @@ function BuiltinProviderRows({ @@ -438,7 +439,12 @@ export function ProvidersSettings() { {i > 0 ? : null}
    - +
    diff --git a/renderer/components/settings/telegram-settings.tsx b/renderer/components/settings/telegram-settings.tsx index 45c07c8b..8bde1938 100644 --- a/renderer/components/settings/telegram-settings.tsx +++ b/renderer/components/settings/telegram-settings.tsx @@ -22,6 +22,7 @@ import { telegramApi } from "../../lib/ipc"; import { queryKeys, useProviders, + useSettings, useTelegramSettings, useWorkspaces, } from "../../lib/queries"; @@ -33,11 +34,13 @@ import { GENERATION_THINKING_LEVELS, type GenerationThinkingLevel, } from "../../shared/generation-thinking"; +import { isModelHidden } from "../../shared/model-visibility"; export function TelegramSettings() { const qc = useQueryClient(); const telegram = useTelegramSettings(); const providers = useProviders(); + const settings = useSettings(); const workspaces = useWorkspaces(); const [keyDraft, setKeyDraft] = React.useState(""); const [profileDraft, setProfileDraft] = React.useState(""); @@ -61,13 +64,9 @@ export function TelegramSettings() { const threadedMode = telegram.data?.threadedMode ?? false; const activeProfile = telegram.data?.activeProfile ?? "default"; const profiles = telegram.data?.profiles ?? []; - const workspaceOptions = telegramWorkspaceOptions( - workspaces.data ?? [], - telegramWorkspaceId, - ); + const workspaceOptions = telegramWorkspaceOptions(workspaces.data ?? [], telegramWorkspaceId); const folderWorkspaceCount = workspaceOptions.filter( - (workspace) => - workspace.value !== TELEGRAM_ASSISTANT_ONLY_VALUE && !workspace.unavailable, + (workspace) => workspace.value !== TELEGRAM_ASSISTANT_ONLY_VALUE && !workspace.unavailable, ).length; const saveKey = async () => { @@ -75,7 +74,9 @@ export function TelegramSettings() { await telegramApi.setKey(value); setKeyDraft(""); await invalidate(); - toast.success(value ? "Telegram bot token saved." : "Telegram bot token removed and bridge disabled."); + toast.success( + value ? "Telegram bot token saved." : "Telegram bot token removed and bridge disabled.", + ); }; const toggle = async (value: boolean) => { @@ -183,8 +184,30 @@ export function TelegramSettings() { (p) => p.models.length > 0 && (p.hasKey || !p.needsKey), ); + const visibleModelsForProvider = (provider: (typeof usableProviders)[number]) => + provider.models.filter( + (modelId) => !isModelHidden(settings.data?.hiddenModelsByProvider, provider.id, modelId), + ); + + const selectableProviders = usableProviders.filter( + (provider) => + visibleModelsForProvider(provider).length > 0 || provider.id === telegramProviderId, + ); + const selectedProvider = usableProviders.find((p) => p.id === telegramProviderId); - const selectedModel = telegramModel || selectedProvider?.defaultModel || selectedProvider?.models[0] || ""; + const visibleModels = selectedProvider ? visibleModelsForProvider(selectedProvider) : []; + const currentHiddenModel = + selectedProvider && + telegramModel && + isModelHidden(settings.data?.hiddenModelsByProvider, selectedProvider.id, telegramModel) + ? telegramModel + : undefined; + const selectedModel = + telegramModel || + (selectedProvider?.defaultModel && visibleModels.includes(selectedProvider.defaultModel) + ? selectedProvider.defaultModel + : visibleModels[0]) || + ""; return (
    @@ -201,13 +224,16 @@ export function TelegramSettings() { {profiles.map((profile) => ( - {profile.name}{profile.status.status === "polling" ? " · connected" : ""} + {profile.name} + {profile.status.status === "polling" ? " · connected" : ""} ))} {activeProfile !== "default" && ( - + )}
    @@ -217,7 +243,12 @@ export function TelegramSettings() { placeholder="New profile name" aria-label="New Telegram profile name" /> -
    @@ -305,42 +336,58 @@ export function TelegramSettings() { value={telegramProviderId} onValueChange={(pid) => { const provider = usableProviders.find((p) => p.id === pid); - const model = provider?.defaultModel ?? provider?.models[0] ?? ""; - void saveProvider(pid, model); + if (!provider) return; + const models = visibleModelsForProvider(provider); + const model = + provider.defaultModel && models.includes(provider.defaultModel) + ? provider.defaultModel + : models[0]; + if (model) void saveProvider(pid, model); }} > - {usableProviders.map((p) => ( + {selectableProviders.map((p) => ( {p.label} ))} - {selectedProvider && selectedProvider.models.length > 1 && ( - - )} + {selectedProvider && + visibleModels.length > 0 && + (visibleModels.length > 1 || Boolean(currentHiddenModel)) && ( + + )} + {selectedProvider && visibleModels.length === 0 && currentHiddenModel ? ( +

    + {currentHiddenModel} is hidden. Show a model in Provider Settings before changing + this bot's model. +

    + ) : null}
    ) : (

    - Configure at least one provider in Settings → Providers, then return here to select it for Telegram. + Configure at least one provider in Settings → Providers, then return here to select it + for Telegram.

    )} @@ -378,9 +425,17 @@ export function TelegramSettings() { /> - - void saveExperience({ rendering: value as typeof rendering })} + > + + + Native Rich Markdown Legacy HTML @@ -388,9 +443,17 @@ export function TelegramSettings() { - - void saveExperience({ voiceMode: value as typeof voiceMode })} + > + + + Hidden / explicit only Mirror voice input @@ -399,8 +462,14 @@ export function TelegramSettings() { - - void saveExperience({ threadedMode: checked })} /> + + void saveExperience({ threadedMode: checked })} + /> setBaseUrl(event.currentTarget.value)} /> @@ -920,12 +1052,17 @@ export function OnboardingFlow() {
    ) : null} - {step === "tour" ? ( + {stateReady && step === "tour" ? (
    - + Everything Aiden brings together @@ -933,7 +1070,8 @@ export function OnboardingFlow() { focus a tile to learn more. - Phone and iPad access starts off. After setup, opt in from Settings → Remote Access; Aiden must stay running, and Tailscale is optional. + Phone and iPad access starts off. After setup, opt in from Settings → Remote + Access; Aiden must stay running, and Tailscale is optional.
    @@ -961,7 +1099,6 @@ export function OnboardingFlow() { return (
    @@ -1024,14 +1161,20 @@ export function OnboardingFlow() { > -
    + +
    + +
    +
    Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 090/266] feat(settings): update builtin-provider-editor update renderer/components/settings/builtin-provider-editor.tsx as part of the reviewed Aiden On The Go and Remote Access update. --- .../settings/builtin-provider-editor.tsx | 85 +++++++++++++++---- 1 file changed, 69 insertions(+), 16 deletions(-) diff --git a/renderer/components/settings/builtin-provider-editor.tsx b/renderer/components/settings/builtin-provider-editor.tsx index f9606493..f8a6c3f1 100644 --- a/renderer/components/settings/builtin-provider-editor.tsx +++ b/renderer/components/settings/builtin-provider-editor.tsx @@ -1,5 +1,7 @@ import * as React from "react"; +import { ExternalLink } from "lucide-react"; + import { Button, Dialog, Field, Input, Text, toast, type DialogLayer } from "../ui"; import { providersApi } from "../../lib/ipc"; import { @@ -22,6 +24,7 @@ function eventCopy(event: ProviderAuthEvent): string { if (event.type === "device_code") return `Use code ${event.userCode} in the browser that just opened.`; if (event.type === "auth_url") return event.instructions ?? "Continue setup in your browser."; + if (event.type === "browser_open_failed") return event.message; return event.message; } @@ -38,11 +41,14 @@ export function BuiltinProviderEditor({ layer, }: BuiltinProviderEditorProps) { const sessionRef = React.useRef(null); + const mountedRef = React.useRef(true); + const openRef = React.useRef(open); const [prompt, setPrompt] = React.useState(null); const [value, setValue] = React.useState(""); const [message, setMessage] = React.useState(null); const [starting, setStarting] = React.useState(false); const [responding, setResponding] = React.useState(false); + const [authLink, setAuthLink] = React.useState(null); const interactiveMethods = (provider.authMethods ?? []).filter( (method): method is { type: PiAuthMethod; label: string; canLogin: true } => method.canLogin, ); @@ -51,9 +57,25 @@ export function BuiltinProviderEditor({ const session = sessionRef.current; sessionRef.current = null; if (!session?.isActive()) return; - void session.cancel().catch(() => session.dispose()); + void session + .cancel() + .then((result) => { + if (result.cancelled) session.dispose(); + }) + .catch(() => session.dispose()); + }, []); + + React.useEffect(() => { + mountedRef.current = true; + return () => { + mountedRef.current = false; + }; }, []); + React.useLayoutEffect(() => { + openRef.current = open; + }, [open]); + React.useEffect(() => { if (!open) { releaseSession(); @@ -62,6 +84,7 @@ export function BuiltinProviderEditor({ setMessage(null); setStarting(false); setResponding(false); + setAuthLink(null); } }, [open, provider.id, releaseSession]); @@ -81,40 +104,63 @@ export function BuiltinProviderEditor({ try { const session = createProviderAuthSession(providersApi, provider.id, authType, { onPrompt: (nextPrompt) => { + setAuthLink(null); setPrompt(nextPrompt); setValue(""); setMessage(null); setStarting(false); }, onEvent: (event) => { + if (event.type === "auth_url" || event.type === "browser_open_failed") { + setAuthLink(event.url); + } else if (event.type === "device_code") { + setAuthLink(event.verificationUri); + } setMessage(eventCopy(event)); setStarting(false); }, onDone: async (event) => { sessionRef.current = null; - setPrompt(null); - setStarting(false); if (event.cancelled) return; + if (mountedRef.current && openRef.current) { + setPrompt(null); + setStarting(false); + } try { - const providers = await providersApi.refresh(); - onSaved(); + const providers = await providersApi.list(); const refreshed = providers.find((item) => item.id === provider.id); - toast.success( - refreshed?.hasKey - ? `${provider.label} is ready.` - : `${provider.label} setup completed.`, - ); - onOpenChange(false); + if (!refreshed?.hasKey || refreshed.models.length === 0) { + if (mountedRef.current && openRef.current) { + setMessage( + `${provider.label} is configured, but no usable chat model is available yet.`, + ); + } + return; + } + // A cancellation request can race the provider's irreversible + // credential commit. Reconcile the parent/cache even if this + // editor closed while the session reported `finishing`. + onSaved(); + if (mountedRef.current && openRef.current) { + toast.success(`${provider.label} is configured.`); + onOpenChange(false); + } + // Refresh dynamic catalogs independently; an unrelated provider + // failure must not turn this selected provider's success into failure. + void providersApi.refresh().catch(() => undefined); } catch (error) { - setMessage( - error instanceof Error - ? error.message - : "Setup completed, but the model catalog could not refresh.", - ); + if (mountedRef.current && openRef.current) { + setMessage( + error instanceof Error + ? error.message + : "Setup completed, but provider readiness could not be checked.", + ); + } } }, onError: (error) => { sessionRef.current = null; + if (!mountedRef.current || !openRef.current) return; setPrompt(null); setStarting(false); toast.error(error.message); @@ -229,6 +275,13 @@ export function BuiltinProviderEditor({ {message} ) : null} + {authLink ? ( +
    + ) : null} {provider.models.length} Pi model{provider.models.length === 1 ? "" : "s"} are currently available. From ff68f6d66ea2d6cc4559bc47bbe00305960a7856 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 091/266] feat(settings): update codex-provider-settings update renderer/components/settings/codex-provider-settings.tsx as part of the reviewed Aiden On The Go and Remote Access update. --- .../settings/codex-provider-settings.tsx | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/renderer/components/settings/codex-provider-settings.tsx b/renderer/components/settings/codex-provider-settings.tsx index dffd013d..c46ba531 100644 --- a/renderer/components/settings/codex-provider-settings.tsx +++ b/renderer/components/settings/codex-provider-settings.tsx @@ -2,7 +2,16 @@ import * as React from "react"; import { useQueryClient } from "@tanstack/react-query"; import { ExternalLink, LoaderCircle, LogIn, MessageSquareCode, RefreshCw } from "lucide-react"; -import { AlertDialog, Badge, Button, Input, Separator, Text, toast } from "../ui"; +import { + AlertDialog, + Badge, + Button, + Input, + Separator, + Text, + toast, + type DialogLayer, +} from "../ui"; import { CopyButton } from "../copy-button"; import { ProviderIcon } from "../provider-icon"; import { providersApi } from "../../lib/ipc"; @@ -39,7 +48,7 @@ function statusBadge( return Sign in needed; } -export function CodexProviderSettings() { +export function CodexProviderSettings({ layer = "default" }: { layer?: DialogLayer }) { const queryClient = useQueryClient(); const status = useCodexProviderStatus(); const sessionRef = React.useRef(null); @@ -106,13 +115,7 @@ export function CodexProviderSettings() { } }); return () => cancelAnimationFrame(frame); - }, [ - authView?.action?.type, - authView?.action?.type === "auth_url" ? authView.action.url : undefined, - authView?.action?.type === "device_code" ? authView.action.userCode : undefined, - authView?.phase, - authView?.prompt?.promptId, - ]); + }, [authView]); const restoreCardFocus = React.useCallback(() => { requestAnimationFrame(() => cardRef.current?.focus()); @@ -450,7 +453,7 @@ export function CodexProviderSettings() { ) : null} - {actionable?.type === "auth_url" ? ( + {actionable?.type === "auth_url" || actionable?.type === "browser_open_failed" ? ( ) : null} @@ -626,6 +631,7 @@ export function CodexProviderSettings() { ) : null} Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 092/266] test(renderer): update remote-access-settings.test update renderer/components/settings/remote-access-settings.test.tsx as part of the reviewed Aiden On The Go and Remote Access update. --- .../settings/remote-access-settings.test.tsx | 73 ++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/renderer/components/settings/remote-access-settings.test.tsx b/renderer/components/settings/remote-access-settings.test.tsx index 5b60c01e..6a11e00b 100644 --- a/renderer/components/settings/remote-access-settings.test.tsx +++ b/renderer/components/settings/remote-access-settings.test.tsx @@ -13,11 +13,30 @@ test("Remote Access settings use the existing semantic form and confirmation pri assert.match(source, /max-\[540px\]/u); }); -test("pairing renders only the versioned QR envelope and labels its short value as visual verification", () => { +test("pairing offers QR and a distinct IPC-only manual setup code", () => { assert.match(source, /QRCode\.toDataURL\(pairing\.qrPayload/u); assert.doesNotMatch(source, /\{pairing\.secret\}/u); - assert.match(source, /This is a visual check, not a manual pairing password\./u); + assert.match(source, /\{pairing\.manualCode\}/u); + assert.match(source, /Copy setup code/u); + assert.match(source, /Certificate check/u); + assert.doesNotMatch(source, /manual pairing password/u); assert.match(source, /expires after five minutes/u); + assert.match(source, /evaluateRemotePairingLifecycle/u); + assert.match(source, /pairingSessionId/u); + assert.match(source, /connected\.`\)/u); + assert.match(source, /Finishing connection/u); + assert.match(source, /data-remote-device-id/u); + assert.match(source, /aria-live="polite"/u); + assert.match(source, /pairingRequestGeneration/u); + assert.match(source, /remotePairingPresentation/u); + assert.match(source, /effectiveRemotePairingLifecycle/u); + assert.match(source, /Copy Mac address/u); + assert.match(source, /Private Tailscale address/u); + assert.match(source, /Code unavailable/u); + assert.match(source, /aria-disabled=\{pairingPresentation\.qrDisabled\}/u); + assert.match(source, /Create new code/u); + assert.match(source, /Consumed Aiden pairing QR code/u); + assert.match(source, /observedPairingSession\.current = nextPairing\.pairingSessionId/u); }); test("Tailscale and folder controls explain their bounded ownership", () => { @@ -25,3 +44,53 @@ test("Tailscale and folder controls explain their bounded ownership", () => { assert.match(source, /Paired devices can explore only these roots/u); assert.match(source, /Existing Aiden workspaces are unchanged/u); }); + +test("Tailscale takeover is disclosed only for a stale Aiden route with bounded safe copy", () => { + assert.match(source, /status\.tailscaleRouteState === "other_aiden_stale"/u); + assert.match(source, /aidenRemoteApi\.reviewTailscaleTakeover/u); + assert.match(source, /aidenRemoteApi\.takeOverTailscale\(takeoverReview\.token\)/u); + assert.match(source, /replace only \/api\/aiden\/v1/u); + assert.match(source, /preserve every other Serve handler/u); + assert.match(source, /Another running Aiden profile owns this Mac’s mobile route/u); + assert.doesNotMatch(source, /tailscale_route_conflict.*toast\.error/u); +}); + +test("Tailscale setup failures retain typed actionable remediation", () => { + assert.match(source, /status\.tailscaleErrorCode === "not_installed"/u); + assert.match(source, /status\.tailscaleErrorCode === "not_connected"/u); + assert.match(source, /status\.tailscaleErrorCode === "https_unavailable"/u); + assert.match(source, /Open Tailscale and sign in/u); + assert.match(source, /Enable HTTPS for this Tailscale device name/u); + assert.match(source, / { + assert.match(source, /case "reconciliation_required"/u); + assert.match(source, /Verification needed/u); + assert.match(source, /aidenRemoteApi\.reconcileTailscale/u); + assert.match(source, /Verify update/u); +}); + +test("primary connection tasks stay visible while advanced details use progressive disclosure", () => { + assert.match(source, /title="Mobile devices"/u); + assert.match(source, /Add device/u); + assert.match(source, / { + assert.match(source, /label="Mac name"/u); + assert.match(source, /aidenRemoteApi\.setDisplayName/u); + assert.match(source, /Identity remains/u); + assert.match(source, /maxLength=\{80\}/u); +}); + +test("paired endpoint collisions use typed remediation without exposing socket errors", () => { + assert.match(source, /status\.errorCode === "remote_port_in_use"/u); + assert.match(source, /Another local Aiden profile is using this saved endpoint/u); + assert.match(source, /Aiden will not silently move a saved mobile connection to a new port/u); + assert.doesNotMatch(source, /EADDRINUSE/u); +}); From d824a4dda290c81669b4fd118b0465f9d5c9c567 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 093/266] feat(settings): update remote-access-settings update renderer/components/settings/remote-access-settings.tsx as part of the reviewed Aiden On The Go and Remote Access update. --- .../settings/remote-access-settings.tsx | 754 +++++++++++++++--- 1 file changed, 651 insertions(+), 103 deletions(-) diff --git a/renderer/components/settings/remote-access-settings.tsx b/renderer/components/settings/remote-access-settings.tsx index 974dfd56..126db433 100644 --- a/renderer/components/settings/remote-access-settings.tsx +++ b/renderer/components/settings/remote-access-settings.tsx @@ -3,11 +3,13 @@ import { useQueryClient } from "@tanstack/react-query"; import QRCode from "qrcode"; import { CheckCircle2, + ChevronDown, Folder, + Info, Loader2, Network, Plus, - ShieldCheck, + RefreshCw, Smartphone, Trash2, TriangleAlert, @@ -18,8 +20,16 @@ import { Button, Callout, Dialog, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, Field, FieldSet, + HoverCard, + HoverCardContent, + HoverCardTrigger, + Input, Select, SelectContent, SelectItem, @@ -29,6 +39,7 @@ import { Text, toast, } from "../ui"; +import { CopyButton } from "../copy-button"; import { aidenRemoteApi } from "../../lib/ipc"; import { queryKeys, useAidenRemoteSettings } from "../../lib/queries"; import type { @@ -36,33 +47,217 @@ import type { AidenRemoteDeviceView, AidenRemotePairingBootstrapView, AidenRemoteSettingsSnapshot, + AidenRemoteTailscaleTakeoverReviewView, } from "../../shared/aiden-remote"; +import { + groupRemoteDevices, + remoteConnectionSummary, + type RemoteDeviceGroups, +} from "../../lib/remote-connection-status"; +import { + effectiveRemotePairingLifecycle, + evaluateRemotePairingLifecycle, + remotePairingPresentation, +} from "../../lib/remote-pairing-lifecycle"; + +const FRIENDLY_DATE_FORMATTER = new Intl.DateTimeFormat(undefined, { + dateStyle: "medium", + timeStyle: "short", +}); function friendlyDate(timestamp: number): string { - return new Intl.DateTimeFormat(undefined, { - dateStyle: "medium", - timeStyle: "short", - }).format(new Date(timestamp)); + return FRIENDLY_DATE_FORMATTER.format(new Date(timestamp)); } function pairingVerificationCode(pairing: AidenRemotePairingBootstrapView): string { return pairing.serverSpkiSha256.slice(-9, -1).toUpperCase(); } +function connectionModeLabel(mode: AidenRemoteConnectionMode): string { + switch (mode) { + case "lan": return "Local Network"; + case "tailscale": return "Tailscale"; + case "both": return "Local Network + Tailscale"; + } +} + +function tailscaleRouteCopy(status: AidenRemoteSettingsSnapshot["status"]): { + badge: string; + description: string; +} { + switch (status.tailscaleRouteState) { + case "owned": { + if (status.tailscaleErrorCode === "not_connected") { + return { badge: "Configured", description: "This profile owns the route, but Tailscale is signed out. Sign in to make it reachable." }; + } + if (status.tailscaleErrorCode === "https_unavailable") { + return { badge: "Configured", description: "This profile owns the route, but HTTPS is not available for this Tailscale name." }; + } + return { badge: status.enabled ? "Connected" : "Configured", description: "This Aiden profile owns the mobile route." }; + } + case "available": + return { badge: "Available", description: "The Aiden mobile route is available on this Mac." }; + case "other_aiden_live": + return { badge: "In use", description: "Another running Aiden profile owns this Mac’s mobile route. Stop or disconnect it before connecting here." }; + case "other_aiden_stale": + return { badge: "Previous route found", description: "A previous Aiden profile left this route behind. Review it before taking over." }; + case "unrelated_conflict": + return { badge: "Path unavailable", description: "The Aiden path has an unrecognized Serve configuration. Aiden will not replace it." }; + case "funnel_conflict": + return { badge: "Funnel conflict", description: "Tailscale Funnel is enabled on this HTTPS listener. Disable Funnel yourself before connecting Aiden." }; + case "reconciliation_required": + return { badge: "Verification needed", description: "Tailscale did not confirm the last route update. Verify its exact result before continuing." }; + case "unavailable": + return { + badge: status.tailscaleErrorCode === "not_installed" || !status.tailscaleInstalled + ? "Tailscale not found" + : status.tailscaleErrorCode === "not_connected" + ? "Sign in required" + : status.tailscaleErrorCode === "https_unavailable" + ? "HTTPS unavailable" + : "Unavailable", + description: status.tailscaleErrorCode === "not_installed" || !status.tailscaleInstalled + ? "Install Tailscale to use this connection method." + : status.tailscaleErrorCode === "not_connected" + ? "Open Tailscale and sign in before connecting Aiden’s mobile route." + : status.tailscaleErrorCode === "https_unavailable" + ? "Enable HTTPS for this Tailscale device name, then try again." + : "Aiden couldn’t safely inspect the current Tailscale Serve configuration.", + }; + } +} + +function friendlyTailscaleError(error: unknown): string { + const message = error instanceof Error ? error.message : ""; + if (message.includes("tailscale_route_live")) return "Another Aiden profile is active on this route. Nothing was changed."; + if (message.includes("tailscale_takeover_changed") || message.includes("tailscale_takeover_expired")) return "The route changed or this review expired. Review it again before taking over."; + if (message.includes("tailscale_funnel_conflict")) return "Tailscale Funnel is using this listener. Aiden did not change it."; + if (message.includes("tailscale_route_conflict")) return "This Serve path is already in use. Aiden did not change it."; + if (message.includes("tailscale_ownership_commit_failed")) return "Aiden restored the previous route because it couldn’t save ownership."; + if (message.includes("tailscale_route_recovery_failed")) return "Aiden couldn’t verify route recovery. Check Tailscale Serve before trying again."; + if (message.includes("tailscale_route_outcome_unknown")) return "Tailscale reported an uncertain route update. Aiden did not save ownership; inspect Serve before retrying."; + if (message.includes("tailscale_reconciliation_conflict")) return "The route changed after the uncertain update. Aiden left it untouched; inspect Tailscale Serve."; + if (message.includes("tailscale_reconciliation_unhealthy")) return "The route exists but this Aiden service did not answer its health check. Nothing was claimed."; + if (message.includes("tailscale_reconciliation_required")) return "Verify the previous Tailscale update before starting another route change."; + if (message.includes("tailscale_not_connected")) return "Open Tailscale and sign in before connecting Aiden."; + if (message.includes("tailscale_https_unavailable")) return "Enable HTTPS for this Tailscale device name before connecting Aiden."; + if (message.includes("tailscale_route_busy")) return "Another Aiden profile is updating this Mac’s mobile route. Wait a moment and try again."; + return "Aiden couldn’t safely update the Tailscale route."; +} + +function Disclosure({ + title, + summary, + children, +}: React.PropsWithChildren<{ title: string; summary: string }>) { + return ( +
    + + + {title} + {summary} + + + +
    {children}
    +
    + ); +} + +function RemoteAccessInfo() { + return ( + + + + + + Aiden stays in control + + Aiden On The Go can use only capabilities and folders approved on this Mac. Provider keys never leave Aiden, and the desktop app must be running. Tailscale is optional. + + + + ); +} + +function SettingsDeviceRow({ + device, + state, + onRevoke, + highlighted = false, +}: { + device: AidenRemoteDeviceView; + state: keyof RemoteDeviceGroups; + onRevoke?: () => void; + highlighted?: boolean; +}) { + const timestamp = state === "previous" ? (device.revokedAt ?? device.lastSeenAt) : device.lastSeenAt; + return ( +
    + +
    + {device.name} + + {device.type === "ipad" ? "iPad" : "iPhone"} · {state === "pending" + ? "Finishing connection" + : `${state === "previous" ? "Removed" : "Last seen"} ${friendlyDate(timestamp)}`} + +
    + {state === "active" ? Active : null} + {state === "pending" ? Finishing : null} + {state === "inactive" ? Inactive : null} + {state === "previous" ? Previous : null} + {onRevoke ? : null} +
    + ); +} + export function RemoteAccessSettings() { const queryClient = useQueryClient(); const settingsQuery = useAidenRemoteSettings(); const [busy, setBusy] = React.useState(null); const [pairing, setPairing] = React.useState(null); + const completedPairingDeviceId = React.useRef(null); + const pairingRef = React.useRef(null); + const mounted = React.useRef(false); + const pairingRequestGeneration = React.useRef(0); + const observedPairingSession = React.useRef(null); + const [highlightedDeviceId, setHighlightedDeviceId] = React.useState(null); const [pairingQr, setPairingQr] = React.useState(null); const [pairingSeconds, setPairingSeconds] = React.useState(0); const [revokeDevice, setRevokeDevice] = React.useState(null); const [removeRootId, setRemoveRootId] = React.useState(null); + const [takeoverReview, setTakeoverReview] = React.useState(null); + const [displayNameDraft, setDisplayNameDraft] = React.useState(""); React.useEffect(() => aidenRemoteApi.onChanged(() => { void queryClient.invalidateQueries({ queryKey: queryKeys.aidenRemote }); }), [queryClient]); + React.useEffect(() => { + if (settingsQuery.data?.displayName) { + setDisplayNameDraft(settingsQuery.data.displayName); + } + }, [settingsQuery.data?.displayName]); + + React.useEffect(() => { + if (settingsQuery.data?.status.tailscaleRouteState !== "other_aiden_stale") { + setTakeoverReview(null); + } + }, [settingsQuery.data?.status.tailscaleRouteState]); + React.useEffect(() => { if (!pairing) { setPairingQr(null); @@ -89,6 +284,74 @@ export function RemoteAccessSettings() { }; }, [pairing]); + React.useEffect(() => { + pairingRef.current = pairing; + }, [pairing]); + + React.useEffect(() => { + mounted.current = true; + return () => { + mounted.current = false; + pairingRequestGeneration.current += 1; + const current = pairingRef.current; + if (current) void aidenRemoteApi.closePairing(current.pairingSessionId).catch(() => undefined); + }; + }, []); + + const pairingLifecycle = React.useMemo(() => pairing && settingsQuery.data + ? evaluateRemotePairingLifecycle({ + pairingSessionId: pairing.pairingSessionId, + status: settingsQuery.data.pairing, + devices: settingsQuery.data.devices, + }) + : { state: "unrelated" as const }, [ + pairing, + settingsQuery.data, + ]); + + React.useEffect(() => { + if (!pairing || !settingsQuery.data) return; + if (settingsQuery.data.pairing?.sessionId === pairing.pairingSessionId) { + observedPairingSession.current = pairing.pairingSessionId; + } else if ( + observedPairingSession.current === pairing.pairingSessionId && + settingsQuery.data.pairing?.sessionId !== pairing.pairingSessionId && + busy !== "closingPairing" + ) { + setPairing(null); + toast.error(settingsQuery.data.pairing + ? "This pairing code was replaced by a newer pairing window." + : "This pairing window was closed on this Mac."); + return; + } + if (pairingLifecycle.state === "cancelled") { + setPairing(null); + void aidenRemoteApi.closePairing(pairing.pairingSessionId).catch(() => undefined); + toast.error("Pairing was cancelled because this device was revoked."); + return; + } + if (pairingLifecycle.state !== "connected") return; + const connected = pairingLifecycle.device; + if (completedPairingDeviceId.current === connected.id) return; + completedPairingDeviceId.current = connected.id; + setBusy("closingPairing"); + void aidenRemoteApi.closePairing(pairing.pairingSessionId).then(() => { + setPairing(null); + setHighlightedDeviceId(connected.id); + toast.success(`${connected.name} connected.`); + window.requestAnimationFrame(() => { + const row = document.querySelector( + `[data-remote-device-id="${connected.id}"]`, + ); + row?.scrollIntoView({ block: "nearest" }); + row?.focus({ preventScroll: true }); + }); + window.setTimeout(() => setHighlightedDeviceId(null), 3_000); + }).catch(() => { + toast.error("The device connected, but Aiden couldn't close the pairing window."); + }).finally(() => setBusy(null)); + }, [busy, pairing, pairingLifecycle, settingsQuery.data]); + const commit = React.useCallback((next: AidenRemoteSettingsSnapshot) => { queryClient.setQueryData(queryKeys.aidenRemote, next); }, [queryClient]); @@ -96,13 +359,16 @@ export function RemoteAccessSettings() { const mutate = async ( operation: string, action: () => Promise, + errorMessage?: (error: unknown) => string, ) => { if (busy) return; setBusy(operation); try { commit(await action()); } catch (error) { - toast.error(error instanceof Error ? error.message : "Remote Access couldn't be updated."); + toast.error(errorMessage + ? errorMessage(error) + : error instanceof Error ? error.message : "Remote Access couldn't be updated."); await queryClient.invalidateQueries({ queryKey: queryKeys.aidenRemote }); } finally { setBusy(null); @@ -112,19 +378,83 @@ export function RemoteAccessSettings() { const beginPairing = async (transport: "lan" | "tailscale") => { if (busy) return; setBusy("pairing"); + const requestGeneration = ++pairingRequestGeneration.current; + try { + completedPairingDeviceId.current = null; + observedPairingSession.current = null; + const nextPairing = await aidenRemoteApi.beginPairing(transport); + if (!mounted.current || pairingRequestGeneration.current !== requestGeneration) { + await aidenRemoteApi.closePairing(nextPairing.pairingSessionId).catch(() => undefined); + return; + } + observedPairingSession.current = nextPairing.pairingSessionId; + queryClient.setQueryData( + queryKeys.aidenRemote, + (current) => current + ? { + ...current, + pairing: { + sessionId: nextPairing.pairingSessionId, + state: "awaiting_scan", + }, + } + : current, + ); + setPairing(nextPairing); + await queryClient.invalidateQueries({ queryKey: queryKeys.aidenRemote }); + } catch (error) { + if (mounted.current && pairingRequestGeneration.current === requestGeneration) { + toast.error(error instanceof Error ? error.message : "Aiden couldn't open pairing."); + } + } finally { + if (mounted.current && pairingRequestGeneration.current === requestGeneration) { + setBusy(null); + } + } + }; + + const reviewTailscaleTakeover = async () => { + if (busy) return; + setBusy("tailscaleReview"); try { - setPairing(await aidenRemoteApi.beginPairing(transport)); + setTakeoverReview(await aidenRemoteApi.reviewTailscaleTakeover()); } catch (error) { - toast.error(error instanceof Error ? error.message : "Aiden couldn't open pairing."); + toast.error(friendlyTailscaleError(error)); + await queryClient.invalidateQueries({ queryKey: queryKeys.aidenRemote }); } finally { setBusy(null); } }; - const closePairing = (open: boolean) => { + const confirmTailscaleTakeover = async () => { + if (!takeoverReview || busy) return; + setBusy("tailscaleTakeover"); + try { + commit(await aidenRemoteApi.takeOverTailscale(takeoverReview.token)); + setTakeoverReview(null); + toast.success("This Aiden profile now owns the mobile route."); + } catch (error) { + toast.error(friendlyTailscaleError(error)); + setTakeoverReview(null); + await queryClient.invalidateQueries({ queryKey: queryKeys.aidenRemote }); + } finally { + setBusy(null); + } + }; + + const closePairing = async (open: boolean) => { if (open) return; - setPairing(null); - void aidenRemoteApi.closePairing().catch(() => undefined); + const current = pairing; + if (!current || busy) return; + setBusy("closingPairing"); + try { + await aidenRemoteApi.closePairing(current.pairingSessionId); + setPairing(null); + } catch { + toast.error("Aiden couldn't close this pairing window."); + } finally { + setBusy(null); + } }; if (settingsQuery.isLoading) { @@ -154,29 +484,39 @@ export function RemoteAccessSettings() { const { status } = snapshot; const transportAllowsLan = status.connectionMode === "lan" || status.connectionMode === "both"; const transportAllowsTailscale = status.connectionMode === "tailscale" || status.connectionMode === "both"; - const activeDevices = snapshot.devices.filter((device) => device.revokedAt === undefined); + const groups = groupRemoteDevices(snapshot.devices, { serviceRunning: status.running }); + const tailscalePresentation = tailscaleRouteCopy(status); + const effectivePairingLifecycle = effectiveRemotePairingLifecycle(pairingLifecycle, pairingSeconds); + const pairingPresentation = remotePairingPresentation(effectivePairingLifecycle, pairingSeconds); + const pairingTransport = pairing?.endpoint.includes(".ts.net/") ? "tailscale" : "lan"; + const canPairLan = transportAllowsLan && status.running; + const canPairTailscale = status.enabled && transportAllowsTailscale && status.tailscaleConnected; + const availablePairingTransports = [ + ...(canPairLan ? (["lan"] as const) : []), + ...(canPairTailscale ? (["tailscale"] as const) : []), + ]; + const summary = remoteConnectionSummary({ + enabled: status.enabled, + running: status.running, + error: status.error, + activeDeviceCount: groups.active.length, + }); return ( <> - -
    - -
    - Aiden stays in control - - Aiden On The Go can use only the capabilities and folders approved on this Mac. Provider keys never leave Aiden. The desktop app must be running; Tailscale is optional. - -
    -
    -
    -
    + This Mac + + + )} + description="Connect Aiden On The Go while Aiden is running." > -
    +
    {busy === "enabled" ? : null} + {summary} void mutate("enabled", () => aidenRemoteApi.setEnabled(enabled))} @@ -186,8 +526,140 @@ export function RemoteAccessSettings() {
    +
    { + event.preventDefault(); + void mutate("displayName", () => aidenRemoteApi.setDisplayName(displayNameDraft)); + }} + > + setDisplayNameDraft(event.target.value)} + maxLength={80} + aria-label="Mac display name" + disabled={busy !== null} + /> + +
    +
    + {status.error ? ( +
    + + {status.error} +
    + ) : null} +
    + +
    + +
    + {availablePairingTransports.length <= 1 ? ( + + ) : ( + + + + + + void beginPairing("lan")}> + Local Network + + void beginPairing("tailscale")}> + Tailscale + + + + )} +
    +
    + {groups.active.length === 0 && groups.pending.length === 0 && groups.inactive.length === 0 ? ( +
    No devices are paired with this Mac.
    + ) : ( + <> + {groups.active.map((device) => ( + setRevokeDevice(device)} + /> + ))} + {groups.pending.map((device) => ( + setRevokeDevice(device)} + /> + ))} + {groups.inactive.map((device) => ( + setRevokeDevice(device)} + /> + ))} + + )} + {groups.previous.length > 0 ? ( +
    + + Previous connections + + {groups.previous.length} + + + + {groups.previous.map((device) => ( + + ))} +
    + ) : null} +
    + + + - +
    {status.running ? ( @@ -215,10 +687,7 @@ export function RemoteAccessSettings() { ) : ( )} - - {status.running ? "Ready" : status.enabled ? "Needs attention" : "Off"} - - {status.running ? Running : null} + {status.running ? "Ready" : summary}
    {status.lanEndpoint ? ( @@ -230,15 +699,18 @@ export function RemoteAccessSettings() { Tailscale: {status.tailscaleEndpoint} ) : null} - {status.error ? {status.error} : null} + {status.errorCode === "remote_port_in_use" ? ( + + Another local Aiden profile is using this saved endpoint. Stop that profile and try again; Aiden will not silently move a saved mobile connection to a new port. + + ) : status.error ? ( + {status.error} + ) : null}
    - - - {transportAllowsTailscale ? ( -
    + {transportAllowsTailscale ? ( @@ -247,49 +719,69 @@ export function RemoteAccessSettings() { {status.tailscaleRoutePreview ?? "Preparing the loopback route…"}
    - - {status.tailscaleConnected ? "Connected" : status.tailscaleInstalled ? "Not connected" : "Tailscale not found"} - - +
    + + {tailscalePresentation.badge} + + + {tailscalePresentation.description} + +
    + {status.tailscaleRouteState === "owned" ? ( + + ) : status.tailscaleRouteState === "available" ? ( + + ) : status.tailscaleRouteState === "other_aiden_stale" ? ( + + ) : status.tailscaleRouteState === "reconciliation_required" ? ( + + ) : null}
    -
    - ) : null} - -
    - -
    - {transportAllowsLan ? ( - - ) : null} - {transportAllowsTailscale ? ( - - ) : null} -
    -
    -
    + ) : null} +
    -
    +
    @@ -312,49 +804,105 @@ export function RemoteAccessSettings() {
    ))} -
    - -
    - {activeDevices.length === 0 ? ( -
    No active devices are paired.
    - ) : activeDevices.map((device) => ( -
    - -
    - {device.name} - - {device.type === "ipad" ? "iPad" : "iPhone"} · Last seen {friendlyDate(device.lastSeenAt)} - -
    - -
    - ))} -
    + void closePairing(open)} title="Pair Aiden On The Go" - description="Scan this code from the iPhone or iPad app. Do not share it." + description={effectivePairingLifecycle.state === "finishing" + ? "The code was accepted. Keep Aiden On The Go open while it finishes connecting." + : effectivePairingLifecycle.state === "failed" + ? "This one-time code was consumed, but Aiden couldn't create the connection. Close and try again." + : effectivePairingLifecycle.state === "expired" + ? "This one-time code expired. Create a new code to continue." + : "Scan the QR or enter the one-time setup code in Aiden On The Go. Do not share either one."} confirmHidden + busy={busy === "closingPairing"} > {pairing ? (
    {pairingQr ? ( - One-time Aiden pairing QR code +
    + {pairingPresentation.qrDisabled + {effectivePairingLifecycle.state === "finishing" ? ( +
    + + Finishing connection +
    + ) : null} +
    ) : (
    )} - 0 ? "blue" : "red"}> - {pairingSeconds > 0 ? `Expires in ${Math.floor(pairingSeconds / 60)}:${String(pairingSeconds % 60).padStart(2, "0")}` : "Expired"} + + {pairingPresentation.badge} - - Verification {pairingVerificationCode(pairing)} · This is a visual check, not a manual pairing password. +
    + + {pairingTransport === "tailscale" ? "Private Tailscale address" : "Nearby Mac address"} + +
    + {pairing.endpoint} + +
    +
    +
    +
    +
    + Enter this code instead + + {pairingPresentation.qrDisabled ? "Code unavailable" : pairing.manualCode} + +
    + {!pairingPresentation.qrDisabled ? ( + + ) : null} +
    + + {pairingTransport === "tailscale" + ? "Enter this private address and the setup code on your iPhone or iPad." + : "Select this discovered Mac, then enter the setup code on your iPhone or iPad."} + +
    + + Certificate check {pairingVerificationCode(pairing)} · {pairingPresentation.qrDisabled + ? pairingPresentation.badge + : `Expires in ${pairingSeconds} seconds`}. + {(effectivePairingLifecycle.state === "expired" || effectivePairingLifecycle.state === "failed") ? ( + + ) : null}
    ) : null}
    + !open && setTakeoverReview(null)} + title="Take over Aiden’s mobile route?" + description="The previous Aiden target did not answer two bounded health checks. Aiden will replace only /api/aiden/v1, preserve every other Serve handler, and leave Funnel unchanged. Local Network access is unaffected." + confirmLabel="Take Over" + busy={busy === "tailscaleTakeover"} + keepOpenOnConfirm + onConfirm={confirmTailscaleTakeover} + /> + !open && setRevokeDevice(null)} From e4474b89afb26c0680a2d286d147ae3205b67fe1 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 094/266] feat(renderer): update ui update renderer/components/ui.tsx as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/components/ui.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/renderer/components/ui.tsx b/renderer/components/ui.tsx index cd4e49da..b7772019 100644 --- a/renderer/components/ui.tsx +++ b/renderer/components/ui.tsx @@ -1064,6 +1064,7 @@ export function AlertDialog({ keepOpenOnConfirm = false, returnFocus, onConfirm, + layer = "default", }: Omit< DialogProps, | "children" @@ -1100,7 +1101,10 @@ export function AlertDialog({ busy && event.preventDefault()} - className="fixed left-1/2 top-1/2 z-50 w-[min(92vw,420px)] -translate-x-1/2 -translate-y-1/2 rounded-dialog bg-popover px-6 py-5 shadow-modal outline-none" + className={cn( + "fixed left-1/2 top-1/2 w-[min(92vw,420px)] -translate-x-1/2 -translate-y-1/2 rounded-dialog bg-popover px-6 py-5 shadow-modal outline-none", + layer === "onboarding" ? "z-[70]" : "z-50", + )} > {title} From 2f8f9bd8815dcf0ee378e0b2a813d2f47fcfd703 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 095/266] feat(renderer): update codex-auth-view-state update renderer/lib/codex-auth-view-state.ts as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/lib/codex-auth-view-state.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/renderer/lib/codex-auth-view-state.ts b/renderer/lib/codex-auth-view-state.ts index 83f8575a..17a235f8 100644 --- a/renderer/lib/codex-auth-view-state.ts +++ b/renderer/lib/codex-auth-view-state.ts @@ -2,7 +2,7 @@ import type { ProviderAuthEvent, ProviderAuthPrompt } from "./types"; export type CodexAuthActionableEvent = Extract< ProviderAuthEvent, - { type: "auth_url" | "device_code" } + { type: "auth_url" | "browser_open_failed" | "device_code" } >; export type CodexAuthPhase = @@ -83,6 +83,7 @@ function eventMessage(event: ProviderAuthEvent): string { if (event.type === "device_code") { return "Enter this temporary code on OpenAI's verification page."; } + if (event.type === "browser_open_failed") return event.message; return event.message; } @@ -143,7 +144,9 @@ export function reduceCodexAuthView( if (!hasFlow(state, action.event.flowId)) return state; if (state.view.phase === "cancelling" || state.view.phase === "finishing") return state; const actionable = - action.event.type === "auth_url" || action.event.type === "device_code" + action.event.type === "auth_url" || + action.event.type === "browser_open_failed" || + action.event.type === "device_code" ? action.event : undefined; return { From f499c0709d780a5678640bc3cc512c061a7a8e20 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 096/266] feat(renderer): update ipc update renderer/lib/ipc.ts as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/lib/ipc.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/renderer/lib/ipc.ts b/renderer/lib/ipc.ts index 2aa9c6cb..7eed0f52 100644 --- a/renderer/lib/ipc.ts +++ b/renderer/lib/ipc.ts @@ -58,6 +58,7 @@ import type { WorkspaceFileWriteResult, WorkspacePermission, } from "./types"; +import type { OnboardingOutcome, OnboardingSnapshot } from "../shared/onboarding"; import type { SkillInvocationV1 } from "../shared/slash-commands"; import type { AnthropicThinkingLevel } from "../shared/anthropic-thinking"; import type { GoogleThinkingLevel } from "../shared/google-thinking"; @@ -135,6 +136,14 @@ export function onNotification( export const appApi = { getInfo: () => invoke("app:getInfo"), resetOnboarding: () => invoke("app:resetOnboarding"), + getOnboardingState: (legacyComplete: boolean) => + invoke("app:getOnboardingState", legacyComplete), + setOnboardingOutcome: ( + outcome: Exclude, + selectedProviderId?: string, + ) => invoke("app:setOnboardingOutcome", outcome, selectedProviderId), + setOnboardingProgress: (step: "profile" | "provider", selectedProviderId?: string) => + invoke("app:setOnboardingProgress", step, selectedProviderId), rendererReady: () => invoke("app:renderer-ready"), setCloseGuard: (guard: { dirty: boolean; @@ -172,6 +181,8 @@ export const providersApi = { key, ), refresh: () => invoke("providers:refresh"), + validateOnboardingApiKey: (providerId: "openai" | "anthropic", key: string) => + invoke("providers:validateOnboardingApiKey", providerId, key), test: (provider: Omit, keyOverride?: string) => invoke<{ ok: true; @@ -402,13 +413,22 @@ export const aidenRemoteApi = { invoke("remote:setEnabled", enabled), setConnectionMode: (mode: AidenRemoteConnectionMode) => invoke("remote:setConnectionMode", mode), + setDisplayName: (displayName: string) => + invoke("remote:setDisplayName", displayName), connectTailscale: () => invoke("remote:tailscaleConnect"), disconnectTailscale: () => invoke("remote:tailscaleDisconnect"), + reconcileTailscale: () => + invoke("remote:tailscaleReconcile"), + reviewTailscaleTakeover: () => + invoke("remote:tailscaleReviewTakeover"), + takeOverTailscale: (token: string) => + invoke("remote:tailscaleTakeOver", token), beginPairing: (transport: "lan" | "tailscale") => invoke("remote:beginPairing", transport), - closePairing: () => invoke<{ closed: true }>("remote:closePairing"), + closePairing: (pairingSessionId: string) => + invoke<{ closed: boolean }>("remote:closePairing", pairingSessionId), revokeDevice: (deviceId: string) => invoke("remote:revokeDevice", deviceId), addApprovedRoot: () => From f46e05c6091c0c1be3510b21743aefc19c3e3b8b Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 097/266] test(renderer): update onboarding-state.test update renderer/lib/onboarding-state.test.ts as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/lib/onboarding-state.test.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/renderer/lib/onboarding-state.test.ts b/renderer/lib/onboarding-state.test.ts index 62d3e111..5f5def36 100644 --- a/renderer/lib/onboarding-state.test.ts +++ b/renderer/lib/onboarding-state.test.ts @@ -1,6 +1,7 @@ import assert from "node:assert/strict"; import test from "node:test"; import { + clearLegacyOnboardingCompletion, markOnboardingComplete, ONBOARDING_COMPLETE_STORAGE_KEY, shouldShowOnboarding, @@ -16,6 +17,9 @@ function storage(initial?: string) { setItem(key: string, value: string) { values.set(key, value); }, + removeItem(key: string) { + values.delete(key); + }, }; } @@ -27,6 +31,12 @@ test("onboarding is shown until its completion marker is set", () => { assert.equal(shouldShowOnboarding(values), false); }); +test("the compatibility marker can be removed for non-destructive re-entry", () => { + const values = storage("true"); + clearLegacyOnboardingCompletion(values); + assert.equal(shouldShowOnboarding(values), true); +}); + test("only the exact current completion marker skips onboarding", () => { assert.equal(shouldShowOnboarding(storage("false")), true); assert.equal(shouldShowOnboarding(storage("1")), true); From 0298533e5934e2077aa1be76b692854bbfa8ae4e Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 098/266] feat(onboarding): update onboarding-state update renderer/lib/onboarding-state.ts as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/lib/onboarding-state.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/renderer/lib/onboarding-state.ts b/renderer/lib/onboarding-state.ts index 63c1d4da..2a467a39 100644 --- a/renderer/lib/onboarding-state.ts +++ b/renderer/lib/onboarding-state.ts @@ -2,12 +2,21 @@ import { ONBOARDING_COMPLETE_STORAGE_KEY } from "../shared/onboarding.js"; export { ONBOARDING_COMPLETE_STORAGE_KEY } from "../shared/onboarding.js"; -type OnboardingStorage = Pick; +type OnboardingReadStorage = Pick; +type OnboardingWriteStorage = Pick; +type OnboardingRemoveStorage = Pick; -export function shouldShowOnboarding(storage: OnboardingStorage = localStorage): boolean { +export function shouldShowOnboarding(storage: OnboardingReadStorage = localStorage): boolean { return storage.getItem(ONBOARDING_COMPLETE_STORAGE_KEY) !== "true"; } -export function markOnboardingComplete(storage: OnboardingStorage = localStorage): void { +export function markOnboardingComplete(storage: OnboardingWriteStorage = localStorage): void { storage.setItem(ONBOARDING_COMPLETE_STORAGE_KEY, "true"); } + +/** Compatibility cleanup for the retired renderer-owned completion marker. */ +export function clearLegacyOnboardingCompletion( + storage: OnboardingRemoveStorage = localStorage, +): void { + storage.removeItem(ONBOARDING_COMPLETE_STORAGE_KEY); +} From d1ddfc42e4e27d8db261b1bfdfb5ea10f2f1a163 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 099/266] feat(renderer): update types update renderer/lib/types.ts as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/lib/types.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/renderer/lib/types.ts b/renderer/lib/types.ts index f1283af3..621a88a6 100644 --- a/renderer/lib/types.ts +++ b/renderer/lib/types.ts @@ -128,6 +128,13 @@ export type ProviderAuthEvent = intervalSeconds?: number; expiresInSeconds?: number; } + | { + flowId: string; + providerId: string; + type: "browser_open_failed"; + url: string; + message: string; + } | { flowId: string; providerId: string; @@ -725,6 +732,7 @@ export interface AppSettings { scheduledDefaultTimezone?: string; assistant?: AssistantConfig; profileName?: string; + onboarding?: import("../shared/onboarding.js").OnboardingState; telegramEnabled?: boolean; telegramAllowedUserId?: number; telegramProviderId?: string; From 8f8cc556a8335f6c60879f788998fb91956ff2e0 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 100/266] feat(renderer): update root-view update renderer/main/root-view.tsx as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/main/root-view.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/renderer/main/root-view.tsx b/renderer/main/root-view.tsx index e0659d26..6121e1d2 100644 --- a/renderer/main/root-view.tsx +++ b/renderer/main/root-view.tsx @@ -252,6 +252,10 @@ function RootContent() { React.useEffect(() => { return onNotification<{ path: string }>("app:navigate", (payload) => { if (!payload?.path) return; + if (document.querySelector("[data-onboarding-active='true']")) { + toast.info("Finish onboarding before opening another part of Aiden."); + return; + } if (navigationBlockedReason) { toast.info(navigationBlockedReason); return; From 5c5c53d66b91f5f6b43b081f9fd3d1b495563208 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 101/266] feat(renderer): update aiden-remote update renderer/shared/aiden-remote.ts as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/shared/aiden-remote.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/renderer/shared/aiden-remote.ts b/renderer/shared/aiden-remote.ts index fad8a04f..907d3966 100644 --- a/renderer/shared/aiden-remote.ts +++ b/renderer/shared/aiden-remote.ts @@ -10,11 +10,27 @@ export interface AidenRemoteStatusView { tailscaleRoutePreview?: string; tailscaleConnected: boolean; tailscaleInstalled: boolean; + tailscaleRouteState: + | "available" + | "owned" + | "other_aiden_live" + | "other_aiden_stale" + | "unrelated_conflict" + | "funnel_conflict" + | "reconciliation_required" + | "unavailable"; + tailscaleErrorCode?: "not_installed" | "not_connected" | "https_unavailable" | "status_unavailable"; pairedDeviceCount: number; approvedRootCount: number; + errorCode?: "remote_port_in_use"; error?: string; } +export interface AidenRemoteTailscaleTakeoverReviewView { + token: string; + expiresAt: number; +} + export interface AidenRemoteDeviceView { id: string; name: string; @@ -34,12 +50,22 @@ export interface AidenRemoteApprovedRootView { } export interface AidenRemoteSettingsSnapshot { + instanceId: string; + displayName: string; status: AidenRemoteStatusView; devices: AidenRemoteDeviceView[]; approvedRoots: AidenRemoteApprovedRootView[]; + pairing?: AidenRemotePairingStatusView; +} + +export interface AidenRemotePairingStatusView { + sessionId: string; + state: "awaiting_scan" | "finishing" | "failed" | "expired"; + deviceId?: string; } export interface AidenRemotePairingBootstrapView { + pairingSessionId: string; protocolVersion: 1; instanceId: string; endpoint: string; @@ -48,4 +74,6 @@ export interface AidenRemotePairingBootstrapView { expiresAt: string; /** Versioned scanner envelope; includes the LAN trust anchor when required. */ qrPayload: string; + /** IPC-only 100-bit setup code. It is never exposed through remote status. */ + manualCode: string; } From 963fca0cb34872336aab7a83f0e36093b21e0489 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 102/266] feat(onboarding): update onboarding update renderer/shared/onboarding.ts as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/shared/onboarding.ts | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/renderer/shared/onboarding.ts b/renderer/shared/onboarding.ts index 3b230242..4f7d5f99 100644 --- a/renderer/shared/onboarding.ts +++ b/renderer/shared/onboarding.ts @@ -1 +1,53 @@ export const ONBOARDING_COMPLETE_STORAGE_KEY = "aiden:onboarding:v1:complete"; + +export const ONBOARDING_STATE_VERSION = 2 as const; + +export type OnboardingOutcome = "incomplete" | "deferred" | "completed"; +export type OnboardingSatisfiedStep = "none" | "profile" | "provider" | "tour"; + +export interface OnboardingState { + version: typeof ONBOARDING_STATE_VERSION; + outcome: OnboardingOutcome; + lastSatisfiedStep: OnboardingSatisfiedStep; + selectedProviderId?: string; +} + +export interface OnboardingSnapshot extends OnboardingState { + profileReady: boolean; + providerReady: boolean; +} + +const outcomes = new Set(["incomplete", "deferred", "completed"]); +const steps = new Set(["none", "profile", "provider", "tour"]); + +export function parseOnboardingState(value: unknown): OnboardingState | null { + if (!value || typeof value !== "object" || Array.isArray(value)) return null; + const candidate = value as Record; + if ( + candidate.version !== ONBOARDING_STATE_VERSION || + typeof candidate.outcome !== "string" || + !outcomes.has(candidate.outcome as OnboardingOutcome) || + typeof candidate.lastSatisfiedStep !== "string" || + !steps.has(candidate.lastSatisfiedStep as OnboardingSatisfiedStep) || + (candidate.selectedProviderId !== undefined && + (typeof candidate.selectedProviderId !== "string" || + candidate.selectedProviderId.length === 0 || + candidate.selectedProviderId.length > 128)) + ) { + return null; + } + return { + version: ONBOARDING_STATE_VERSION, + outcome: candidate.outcome as OnboardingOutcome, + lastSatisfiedStep: candidate.lastSatisfiedStep as OnboardingSatisfiedStep, + ...(typeof candidate.selectedProviderId === "string" + ? { selectedProviderId: candidate.selectedProviderId } + : {}), + }; +} + +export function onboardingStepIndex(snapshot: OnboardingSnapshot): number { + if (!snapshot.profileReady) return 0; + if (!snapshot.providerReady) return 1; + return 2; +} From 8a2ed941cee0f6ff7dd1398ab9a5a9cdd1d3ac66 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 103/266] docs(plans): add aiden-manual-pairing-plan add docs/plans/aiden-manual-pairing-plan.md as part of the reviewed Aiden On The Go and Remote Access update. --- docs/plans/aiden-manual-pairing-plan.md | 51 +++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 docs/plans/aiden-manual-pairing-plan.md diff --git a/docs/plans/aiden-manual-pairing-plan.md b/docs/plans/aiden-manual-pairing-plan.md new file mode 100644 index 00000000..d445b5b5 --- /dev/null +++ b/docs/plans/aiden-manual-pairing-plan.md @@ -0,0 +1,51 @@ +# Aiden Manual Pairing + +Status: Implemented; manual LAN/Tailscale UI acceptance remains open + +## Objective + +Add a human-readable setup-code path beside the existing QR flow without weakening Aiden Remote's certificate-pinned trust bootstrap, one-use credential issuance, or multi-Mac isolation. + +## Protocol decision + +Aiden will not send a low-entropy numeric bearer password and will not implement a new elliptic-curve PAKE in application code. There is no maintained Swift-native PAKE dependency that interoperates cleanly with Electron without adding a Rust/XCFramework/native-addon supply chain. + +The manual path therefore uses a uniformly random 100-bit Crockford Base32 setup code. The code is never sent to the Mac. It locally derives an AES-256-GCM key with HKDF-SHA256 and decrypts the existing canonical QR trust envelope fetched from the selected Mac. An offline attacker receives only an authenticated ciphertext and must search the full 100-bit code space. After decryption, iOS applies the existing strict endpoint, expiry, private-CA/system-trust, SPKI-pin, and 256-bit one-use-secret validation before using the existing pinned pairing exchange. + +The QR and setup-code paths share one pairing window and one exchange secret, so exactly one device can win. A code cannot locate a Mac: LAN users select a Bonjour-discovered exact instance, while Tailscale users provide the canonical endpoint. + +## Invariants + +- Pairing remains off by default and locally initiated on the Mac. +- Setup codes expire after five minutes, are replaced atomically, and never enter logs, persistence, public snapshots, Bonjour TXT records, URLs, query strings, or App Group state. +- The manual bootstrap endpoint returns only a bounded, static encrypted envelope while a pairing window is open. +- The setup code is normalized with an exact ASCII allowlist; Unicode lookalikes are rejected. +- The decrypted envelope must name the exact selected endpoint before any credential exchange. +- QR and manual flows consume the same 256-bit secret synchronously before durable device issuance. +- A device is presented as connected only after its first authenticated callback. +- Failed pairing never replaces or damages an existing installation or credential. + +## Phases + +1. Align the documented QR envelope, manual-bootstrap schema, errors, threat model, and shared fixtures. +2. Add the in-memory setup-code lifecycle, sealed-envelope creation, bounded unauthenticated route, IPC-only code delivery, and adversarial TypeScript tests. +3. Add Mac scan/copy/manual-code presentation with expiry, regeneration, accessibility, and certificate-check separation. +4. Add iOS Scan, Paste Setup Payload, and Enter Code paths; nearby exact-instance selection; canonical Tailscale endpoint entry; sealed-envelope decryption; and existing pinned exchange reuse. +5. Add Swift contract, cryptographic-vector, endpoint-binding, lifecycle, Keychain rollback, permission, and compatibility coverage. +6. Run focused TypeScript and Swift gates, three fresh-memory reviews, fix every actionable finding, and update project memory and plan status. + +## Acceptance + +- Existing QR-only clients remain compatible. +- The same pairing window cannot issue two credentials through any QR/manual race. +- Captured manual-bootstrap traffic contains no reusable bearer secret and cannot be modified without authenticated-decryption failure. +- Wrong, malformed, expired, replaced, cancelled, replayed, oversized, and cross-Mac inputs fail with typed behavior and no state corruption. +- LAN private-CA and Tailscale system-trust pairing pass on physical devices. + +## Implementation and review result + +Implemented 2026-08-21 across Electron main, the authenticated Remote API contract, Remote Access settings, and Aiden On The Go. The Mac creates one five-minute QR/setup-code window; the setup code is displayed only through IPC, while the unauthenticated endpoint returns a bounded AES-256-GCM envelope. iOS can select a discovered Mac or enter an exact Tailscale endpoint, decrypts locally, then reuses the existing pinned one-use exchange. + +Three fresh-memory reviews covered server races/security, iOS transport and credential persistence, and cross-platform UX/contracts. Their accepted findings are closed: credential issuance has an in-mutation session fence; iOS stages and authenticates the new installation before promotion; re-pair credentials use versioned Keychain scopes with an atomic registry pointer; bootstrap reads are stream-bounded; ASCII/base64url inputs are canonical; stale discovery selection and pairing-task cancellation fail closed; Tailscale endpoints, expiry, regeneration, and selected-state accessibility are explicit; and both runtimes execute the shared crypto vector under contract revision 3. + +Automated verification passes: 216 Aiden Remote tests plus seven transport proofs, TypeScript type-check, lint, iOS release policy, diff check, and the complete physical iPhone 13 Pro XCTest target with 101 passes, five expected environment-gated skips, and zero failures. No simulator was used. A real manual setup-code entry through the shipping UI over LAN and Tailscale, plus physical-iPad acceptance, remains an explicit hands-on acceptance step; no claim is made that those UI checks occurred in this implementation run. From 3b2ee2d2015ddddde900bd0826cc34e2f6cc8b1c Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 104/266] docs(plans): add aiden-remote-multi-instance-hardening-plan add docs/plans/completed/aiden-remote-multi-instance-hardening-plan.md as part of the reviewed Aiden On The Go and Remote Access update. --- ...en-remote-multi-instance-hardening-plan.md | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 docs/plans/completed/aiden-remote-multi-instance-hardening-plan.md diff --git a/docs/plans/completed/aiden-remote-multi-instance-hardening-plan.md b/docs/plans/completed/aiden-remote-multi-instance-hardening-plan.md new file mode 100644 index 00000000..734dddd7 --- /dev/null +++ b/docs/plans/completed/aiden-remote-multi-instance-hardening-plan.md @@ -0,0 +1,175 @@ +# Aiden Remote Multi-Instance Hardening + +Status: Complete + +This plan is the authoritative delivery contract for pairing-completion UX, multiple mobile devices, multiple Aiden installations on iOS, multiple Macs, and multiple Aiden runtime profiles on one Mac. It extends the active [Aiden On The Go plan](../aiden-on-the-go-plan.md) without weakening its protocol, credential, TLS, approved-root, or Tailscale ownership guarantees. + +## Product decisions + +1. Pairing is complete only after the newly issued device successfully authenticates back to the Mac. Issuing a credential alone is not shown as “connected.” +2. A successful pairing dismisses the one-time QR, closes the pairing window, announces the connected device, and immediately renders the device as active. +3. Multiple phones and iPads may connect to one Aiden installation with independent credentials, activity, streams, approvals, and revocation. +4. Aiden On The Go treats multiple Macs as first-class installations keyed by the server’s stable `instanceId`; display names are labels, never identity. +5. Fresh unpaired Aiden profiles on one Mac may choose a different stable LAN/loopback port pair. A paired profile never silently changes its endpoint. +6. One physical Mac may publish only one canonical `/api/aiden/v1` Tailscale Serve route at a time. Other profiles remain available over LAN. Aiden never silently steals or resets a route. +7. A stale Aiden-owned route may be taken over only through an explicit, reviewed action that removes exactly the canonical Aiden handler and preserves every unrelated Serve/Funnel configuration. + +## Security and correctness invariants + +- Pairing remains five-minute, one-use, 256-bit, pinned-identity bootstrap authentication. +- Device bearer credentials remain digest-only on the Mac, instance-scoped in the iOS Keychain, absent from renderer state, logs, discovery, and notifications. +- The renderer consumes allowlisted projections and typed IPC only; UI state is never treated as authorization. +- Device-connected UI is driven by a successful authenticated request observed by Electron main, not by optimistic renderer state. +- `instanceId` scopes credentials, caches, App Intents, workspaces, streams, and installation switching. +- Port selection is bounded, deterministic after persistence, and fails closed on exhaustion or paired-endpoint collision. +- Tailscale ownership is exact-path and exact-target. Funnel and unrelated routes are never modified. +- An old profile cannot disconnect or overwrite a route that has since been claimed by another profile. +- Bonjour exposes only the protocol version, public instance identifier, service port, and a bounded display label. +- Existing v1 clients remain compatible; any additive projection field is optional until both TypeScript and Swift fixtures prove parity. + +## Phase 1 — Authoritative pairing completion + +### Implementation + +- Separate credential issuance from first authenticated contact in the device state model. +- Persist a new device as pending without falsely advancing `lastSeenAt`. +- On the first successful bearer-authenticated request, atomically mark the device connected, persist its real `lastSeenAt`, and broadcast the existing bounded remote-state invalidation. +- In Remote Access settings, correlate the open one-time pairing window with the newly issued device while ignoring unrelated device activity and settings changes. +- After first authentication, close the pairing window, dismiss the QR, show “ connected,” focus/highlight its row, and expose it as Active. +- Keep the QR visible for incomplete exchange, Keychain persistence failure, offline first load, cancellation, and expiry. Expose “Finishing connection” without calling it Active. + +### Tests and gate + +- Pairing issue → pending → first authentication → active transition. +- First authenticated request emits one durable transition even when concurrent requests arrive. +- Unrelated device activity, route changes, and approved-root changes never dismiss the QR. +- Expired, cancelled, failed, duplicate, and already-used pairing windows remain honest. +- Focused main/renderer tests, type-check, lint, and React Doctor pass before review. +- Two fresh-memory GPT-5.6 Sol medium reviews must be resolved before Phase 2. + +### Accepted result — 2026-08-21 + +- Credential issuance now persists a pending device (`lastSeenAt = 0`); the first successful authenticated request atomically records the real contact and concurrent requests produce one durable transition. +- A main-owned, non-secret pairing session identifier correlates the exact pairing window and issued device. Conditional close prevents one Settings window from closing another, including unmount and in-flight begin races. +- The desktop keeps the QR visibly disabled during finishing/failure/expiry, dismisses it only after authenticated contact, announces and focuses the Active row, and renders pending devices honestly in Settings and the global connection popover. +- Save-failure retry, authentication/revocation interleaving, post-deadline first contact, replacement/closure, revoked pending, and lifecycle presentation tests are registered in `test:aiden-remote`. +- Gate accepted after three independent two-review passes and targeted re-checks. `test:aiden-remote` passes 138 tests plus seven transport proofs; type-check and lint pass; React Doctor has no new pairing-specific correctness diagnostic. + +## Phase 2 — Stable Mac identity and multi-installation UX + +### Implementation + +- Add a bounded persisted server display name, defaulting to the macOS computer name with a safe fallback. +- Project that name through `/server`, pairing/install metadata, Bonjour presentation, Remote Access settings, and the iOS installation switcher without changing identity semantics. +- Disambiguate identical Bonjour labels with a stable short public instance suffix. +- Show each iOS installation’s display name, reachability state, endpoint type, and last successful connection. +- Audit all iOS caches, navigation requests, streams, Live Activities, and App Intent catalog entries for exact `instanceId` scoping and stale-generation cancellation during installation switches. + +### Tests and gate + +- Two installations with the same display name remain distinct. +- Renaming a display label does not rotate credentials or instance identity. +- Switching while requests/streams are in flight cannot leak results into the newly active installation. +- TypeScript/Swift fixture parity and focused iOS tests pass. +- Two fresh-memory GPT-5.6 Sol medium reviews must be resolved before Phase 3. + +### Accepted result — 2026-08-21 + +- A bounded persisted Mac label now defaults from macOS Computer Name and projects through `/server`, Settings, Bonjour, and the iOS installation registry without replacing stable `instanceId` identity. Same-name Bonjour and switcher rows use a stable public suffix. +- Pairing display metadata is opt-in so strict legacy v1 clients remain compatible; current iOS retries the frozen four-field request only after an early-v1 exact-shape rejection. Production-shaped state initialization durably seeds missing files and migrates legacy labels without rewriting current profiles. +- iOS activation-generation leases now scope workspace browsing, chats, Git, files, schedules, navigation, App Intents, cache fallbacks, and Live Activity reconciliation. A→B and A→B→A requests cannot apply stale state or send opaque handles to another Mac; accepted background turns retain their instance-scoped resumable stream handle. +- Scheduled Tasks binds its entire retained modal/model lifecycle to one activation lease, dismisses on installation changes, persists deletion/settings changes, and prevents delayed history writes from restoring removed run summaries. +- Gate accepted after repeated independent two-review passes. `test:aiden-remote` passes 146 tests plus seven transport proofs, type-check and diff checks pass, and a generic physical-iOS compile-only test build succeeds without using a simulator. + +## Phase 3 — Same-Mac listener and port hardening + +### Implementation + +- Introduce a bounded Aiden port-pair allocator for a fresh profile and persist the selected LAN port only after both required listeners bind successfully. +- Retain the persisted port across restarts. +- Auto-select another pair only when the profile has no active or previous device credentials and no externally established endpoint ownership. +- Fail a paired profile closed with a typed `remote_port_in_use` error and remediation instead of moving its endpoint. +- Make listener startup transactional so partial LAN/loopback/Bonjour state is always rolled back. +- Present the selected port and collision remediation through progressive disclosure rather than raw `EADDRINUSE` text. + +### Tests and gate + +- Two fresh profiles race to enable and receive distinct stable pairs. +- Restart retains the pair; exhaustion fails closed; partial bind rolls back. +- Paired profiles never silently move. +- Bonjour advertises the actual committed port only after successful startup. +- Two fresh-memory GPT-5.6 Sol medium reviews must be resolved before Phase 4. + +### Accepted result — 2026-08-21 + +- Fresh profiles now choose a bounded even LAN/loopback port pair, commit it only after both listeners and Bonjour are healthy, and retain that pair across restarts and connection-mode changes. Inactive transports remain leased but reject new work, and existing keep-alive/SSE sockets are destroyed when their transport becomes inactive. +- Paired or externally owned profiles never move silently. Port exhaustion and committed-endpoint collisions fail closed with typed, bounded remediation; legacy committed odd ports and `65535` remain restart-compatible. +- Candidate allocation excludes every exact canonical or legacy Aiden Tailscale handler target, including ambiguous multi-authority Serve status. Startup and persistence failures roll back listeners, sockets, Bonjour, and state without leaking a half-bound pair. +- The final gate passes 170 TypeScript remote tests plus seven transport proofs, type-check, lint, and diff checks. Two fresh-memory GPT-5.6 Sol medium reviewers found no remaining Phase 3 correctness, security, lifecycle, concurrency, or coverage issue. + +## Phase 4 — Tailscale ownership and explicit takeover + +### Implementation + +- Classify Serve state as owned by this profile, owned by another live Aiden target, stale Aiden target, unrelated conflict, Funnel conflict, or available. +- Keep the canonical public path `/api/aiden/v1` and one-owner-per-Mac policy. +- Replace raw `tailscale_route_conflict` renderer errors with typed, bounded, actionable status. +- Block takeover while the incumbent loopback target is healthy. +- Offer an explicit Take Over action only after a bounded loopback health check proves the incumbent target stale; re-check immediately before mutation. +- Remove and replace only the exact Aiden handler. Persist new ownership only after post-command verification. +- Preserve old-owner safety: disconnect remains exact target + exact ownership and cannot clear a successor’s route. + +### Tests and gate + +- Live owner blocks takeover; stale owner permits confirmed takeover. +- TOCTOU changes between review and mutation fail closed. +- Unrelated handlers and Funnel state are byte-for-byte preserved. +- Old owners cannot disconnect new owners; failed commands never persist false ownership. +- Two fresh-memory GPT-5.6 Sol medium reviews must be resolved before Phase 5. + +### Accepted result — 2026-08-21 + +- Tailscale Serve state is classified by exact canonical path, exact loopback target, explicit TCP 443 HTTPS state, Funnel state, persisted profile ownership, and bounded health checks. Live owners cannot be taken over; stale owners require a one-use reviewed token and an immediate pre-mutation re-check. +- Every mutation is serialized across local Aiden processes with a kernel-owned exclusive UDP loopback mutex, preserving compatibility with every retained TCP listener port. Aiden fingerprints and verifies all non-Aiden Serve state, permits listener scaffolding changes only for the first/final Aiden handler, and never resets Funnel or unrelated routes. +- Route ownership is persisted only after verified mutation. Persistence failures conditionally restore the exact predecessor without overwriting a successor. Ambiguous CLI outcomes use bounded status retries and one durable pending-outcome record written before mutation; pairing and all ordinary route actions remain blocked until the explicit Verify update action proves an unchanged not-applied state or an exact applied state and atomically commits the result. +- Malformed HTTPS listeners, delayed daemon visibility, external interleavings between verification reads, process crashes, concurrent takeovers, retained legacy ports, legacy origin-only migration, and old-owner disconnects have focused regressions. +- The accepted gate passes 199 TypeScript remote tests plus seven transport proofs, type-check, and lint. Two fresh-memory GPT-5.6 Sol medium reviewers independently report no remaining Phase 4 correctness, security, lifecycle, concurrency, or coverage finding. + +## Phase 5 — Multiple-device and multi-Mac acceptance + +### Implementation + +- Verify independent lifecycle ownership for simultaneous devices, streams, approvals, attachment uploads, scheduled tasks, and revocation. +- Improve desktop/mobile connection summaries for multiple active and inactive devices without exposing secrets or private paths. +- Exercise multiple saved Mac installations, failover, removal, re-pair, App Intents, and cache isolation. +- Update Remote Access operator documentation, project memory, onboarding/feature-tour copy only where first-run behavior materially changed. + +### Tests and completion gate + +- Two phones concurrently use one Mac; revoking one leaves the other unaffected. +- One phone pairs with two Macs and switches repeatedly over LAN and Tailscale. +- Same-name and offline-Mac cases remain understandable. +- Full `test:aiden-remote`, service-boundary, onboarding, iOS, type-check, lint, build, and relevant physical-device gates pass. +- React Doctor reports no new correctness findings in changed React code. +- Two final fresh-memory GPT-5.6 Sol medium reviews are resolved. +- The plan moves to `docs/plans/completed/` only after every phase and gate above is complete. + +### Accepted result — 2026-08-21 + +- Device authorization now has a synchronous admission fence and mutation drain. Revocation is durably persisted before admitted mutations drain, then retries device-owned chat attachments, workspace operations, streams, and approvals without affecting another device. +- Stream revocation is durably journaled, surfaces persistence failures, retries safely, and filters revoked-device records during startup. A production-shaped restart test proves one device stays revoked while another device's streams and credential remain usable. +- iOS retains every request, cache, active stream, Live Activity, App Intent projection, and removal/re-pair transition under exact `instanceId` plus `deviceId` identity. An installation data gate serializes accepted writes against removal so a late response cannot recreate purged data. +- Installation-scoped chat, scheduled-task, archive, and workspace-environment caches purge only the removed Mac. Legacy flat workspace cache migration deletes only records attributable to that installation and preserves another Mac's data. +- The final gate passes 209 TypeScript remote tests plus seven transport proofs, 18 service-boundary tests, 30 onboarding tests, 20 Ruby release-policy tests with 42 assertions plus 24 Node release tests, type-check, lint, build, and diff checks. The connected physical iPhone 13 Pro passes 100 XCTest cases with five configuration-only skips and zero failures; no simulator was used. +- Two final fresh-memory GPT-5.6 Sol medium reviewers independently found no remaining server or iOS correctness, security, privacy, lifecycle, concurrency, or coverage issue after the repair rounds. React Doctor completed with no new hardening-specific correctness finding. + +## Phase review protocol + +Every implementation phase uses the same mandatory gate: + +1. Implement only the current phase and its tests. +2. Run focused tests plus type-check/lint appropriate to the diff. +3. Give two fresh-memory GPT-5.6 Sol medium reviewers the authoritative plan, current phase scope, and exact diff. +4. Resolve every correctness, security, privacy, lifecycle, concurrency, accessibility, and test-coverage finding. +5. Re-run the phase gates and record the accepted result in this plan and `.memory/aiden-on-the-go.md`. +6. Advance only after both reviews are clean or all actionable findings are fixed and re-reviewed. From d12cdc98a4c516c5efd8ee21fd240847a6bf50e5 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 105/266] docs(plans): add onboarding-auth-and-provider-validation-plan add docs/plans/onboarding-auth-and-provider-validation-plan.md as part of the reviewed Aiden On The Go and Remote Access update. --- ...rding-auth-and-provider-validation-plan.md | 393 ++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 docs/plans/onboarding-auth-and-provider-validation-plan.md diff --git a/docs/plans/onboarding-auth-and-provider-validation-plan.md b/docs/plans/onboarding-auth-and-provider-validation-plan.md new file mode 100644 index 00000000..e2a27839 --- /dev/null +++ b/docs/plans/onboarding-auth-and-provider-validation-plan.md @@ -0,0 +1,393 @@ +# Onboarding Authentication and Provider Validation + +Status: Active + +## Goal + +Make first-run onboarding truthfully establish a usable Aiden setup: ChatGPT/Codex +sign-in must be reachable and recoverable, API-key and endpoint setup must report +what was actually verified, and dismissing onboarding must never be recorded as a +completed configuration. + +The delivery should reuse Aiden's existing provider, credential, model-selection, +and visual primitives. It must not create a second provider catalog, leak secrets +to the renderer, perform billable validation requests, or make network calls on +ordinary startup. + +## Audit result + +### P0 — ChatGPT sign-in is deterministically unreachable + +- Onboarding looks for `openai-codex` with `isBuiltin === true` in + `renderer/components/onboarding-flow.tsx`. +- `main/services/provider-list-core.ts` intentionally omits signed-out Codex and + synthesizes a configured Codex item without `isBuiltin` or `authMethods`. +- The resulting branch can only show “ChatGPT sign-in is unavailable.” Even a + configured entry cannot render actions in the generic `BuiltinProviderEditor`, + because that editor requires `authMethods`. +- The dedicated Settings implementation already has the correct Codex-owned + session and view state in `renderer/components/settings/codex-provider-settings.tsx`. +- Current onboarding tests only inspect source text. They never render the real + signed-out provider-list shape, so they assert that the dead branch exists + rather than proving it works. + +### P1 — onboarding can complete without a usable provider + +- The global Skip button writes a renderer `localStorage` completion bit from any + step. There is no readiness check, confirmation, deferred state, or + non-destructive way to reopen onboarding. +- OpenAI and Anthropic accept any nonempty string and save hard-coded custom + provider records without contacting the provider. +- Tailscale setup can save a provider with zero models and still advance. +- Generic built-in authentication advances even when the refreshed provider is + missing a key or models. +- Completion is not derived from the main process's authoritative profile, + credential, provider, and model state. + +### P1 — onboarding is not an application modal + +- The routed shell, assistant dock, native commands, and command palette remain + active behind onboarding. +- Command-K, Command-N, Settings, and menu navigation can create hidden stacked + UI and focus conflicts during setup. + +### P2 — auth, recovery, and accessibility gaps + +- Partially completed setup is durable, but step progress is component-local. + Reloading after a profile or provider save restarts at step one. +- Generic auth cancellation loses the Codex-specific `finishing` state around + the credential commit boundary. +- Successful selected-provider auth can appear to fail when an unrelated global + catalog refresh fails. +- Step transitions do not move or announce focus, progress lacks + `aria-current`, and the feature gallery creates an unnecessarily long keyboard + path. +- Browser-open failure is not surfaced clearly. Pi uses a fixed localhost port + (`1455`) and swallows callback bind errors into a manual-code fallback, so + Aiden's existing `port_busy` diagnosis cannot fire. +- Retry status currently rereads local credential state; it does not prove that + the remote token remains accepted. + +## Product decisions + +1. **Setup completion is authoritative.** `completed` means the main process can + identify at least one selectable model on a configured provider whose setup + state satisfies that provider class's readiness contract. +2. **Do not keep the current global Skip.** Required profile/provider steps + cannot be dismissed as complete or deferred. The `deferred` state exists only + to migrate old renderer completion markers that never established a usable + provider; Settings offers a non-destructive **Show onboarding** recovery path. +3. **The tour is optional.** Once required setup is ready, the user may choose + **Start using Aiden** without traversing the entire feature gallery. +4. **Use native provider identities.** OpenAI and Anthropic onboarding configure + Pi's native `openai` and `anthropic` providers, not + `custom:onboarding-openai` or `custom:onboarding-anthropic` clones. +5. **Validation is explicit and non-billable.** Never send a chat, completion, + malformed generation, or zero-token generation as a credential probe. +6. **Validation claims match evidence.** OAuth exchange, authoritative account or + catalog checks, custom endpoint discovery, local endpoint reachability, and + ambient credential resolution are different assurance levels in both data and + copy. +7. **No background credential checks.** Validation runs only after a user chooses + **Validate & continue**, **Check connection**, or performs a real request. + Startup only loads stored evidence and computes whether it is stale. +8. **One Codex credential owner.** The P0 repair reuses Aiden's dedicated Pi Codex + session. A later architecture gate must choose between the Pi-owned auth and + inference path or an official packaged Codex CLI/app-server path. Do not run + official `codex login` while continuing Pi inference through a separate hidden + credential store. + +## Target onboarding state + +Persist a versioned, main-owned, non-secret record: + +```ts +type OnboardingState = { + version: number; + outcome: "incomplete" | "deferred" | "completed"; + lastSatisfiedStep: "none" | "profile" | "provider" | "tour"; + selectedProviderId?: string; +}; +``` + +At launch, derive the actual next step from authoritative profile and provider +readiness. The profile checkpoint is a resume hint; the provider checkpoint is +recorded by main only after one of onboarding's guarded validation, OAuth, or +catalog-discovery paths succeeds for that exact provider ID. Ambient credentials +and static Pi catalogs are not evidence. Never persist API-key drafts, OAuth +prompts, manual authorization codes, or provider response bodies. + +```text +boot.checking + -> profile.editing/saving/error + -> provider.catalog_loading/choosing + -> codex.starting/waiting/prompt/responding/cancelling/finishing + -> api_key.validating/saving/reconciling + -> endpoint.validating/saving/reconciling + -> tour + -> completion.persisting/error/completed + +any idle setup state + -> close only after authoritative completion +``` + +Normal completion is allowed only after a fresh selected-provider reconciliation +passes the shared usable-provider predicate. Migrated deferred records reopen +required setup and cannot be written through renderer IPC. + +## Implementation checkpoint + +The immediate onboarding repair now ships in the working tree: + +- onboarding renders the dedicated Codex sign-in surface and advances only from + its configured, healthy model-bearing status; +- profile/provider progress and completion are versioned in main-owned settings, + required setup has no Skip/defer action, migrated false-completion records + reopen, and Settings can reopen onboarding without deleting configuration; +- OpenAI and Anthropic use their native Pi identities and validate an + authenticated bounded model catalog before replacing the stored key; +- LM Studio, Ollama, and Tailnet routes must discover a usable model before they + can advance, and every successful path persists a selected model; +- secret-bearing discovery rejects redirects, bounds response/model data, emits + closed errors, and distinguishes cancellation from timeout; +- malformed model IDs and credential control characters fail closed, transport + errors cannot echo key material, and first-run Tailnet discovery is restricted + to `.ts.net` names and Tailnet address classes; +- generic Pi credential entry remains available in Settings but cannot satisfy + required onboarding until that provider has an authoritative non-generation + validator; +- onboarding blocks the workbench while main-owned state loads, fences state + writes to the active renderer document, blocks deep-link navigation, and + reconciles setup that finishes after a close/cancel race; +- the onboarding shell is an application modal, nested confirmations render in + the onboarding layer, browser-launch failures retain a manual link, step + headings receive focus, progress exposes `aria-current`, and the feature + gallery no longer adds 24 noninteractive tab stops. + +The broader cross-provider evidence registry, Settings evidence UI, and the +Phase 6 credential-owner decision remain future architecture work; they are not +required for the repaired first-run completion contract. + +## Validation contract + +Configuration and evidence are separate: + +```ts +type ConfigurationState = "missing" | "configured" | "needs_attention"; +type ValidationState = + | "unverified" + | "validating" + | "validated" + | "stale" + | "rejected" + | "unreachable"; +type Assurance = "authoritative" | "capability_probe" | "configuration_only"; +type Evidence = + | "oauth_exchange" + | "oauth_refresh" + | "identity" + | "catalog" + | "custom_catalog" + | "local_catalog" + | "request_success"; +``` + +Evidence stores only `checkedAt`, `freshUntil`, `strategyVersion`, an opaque +credential revision, a canonical connection fingerprint, optional bounded model +count, and a closed sanitized error. It contains no key, key hash, complete URL, +account identifier, raw upstream response, or prompt content. + +### Assurance matrix + +| Provider class | Safe check | Honest result | +| --------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| ChatGPT/Codex OAuth | Successful OAuth exchange; later refresh or real request | Signed in at that time | +| OpenAI API key | Authenticated `GET /v1/models` | Credentials accepted and catalog accessible | +| Anthropic API key | Authenticated Models list endpoint | Credentials accepted and catalog accessible | +| Other built-in static-key providers | Provider-specific allowlisted identity/catalog endpoint, when documented | Authoritative only if the endpoint enforces auth; otherwise unverified | +| Authenticated dynamic catalogs such as Concentrate/Radius | Intended-origin authenticated catalog | Credentials and catalog capability accepted | +| AWS/Vertex/Azure/Cloudflare ambient or multi-field auth | Local resolution plus documented identity/control-plane check when available | Configuration-only or provider-specific capability | +| Custom OpenAI/Anthropic-compatible server | Exact-origin bounded model discovery | Endpoint reached and models listed; key enforcement not guaranteed | +| LM Studio/Ollama/Tailnet keyless server | Native or generic bounded model discovery | Local/private endpoint reached; not credential validation | + +`401` is rejected credentials. `403` is a permission/capability failure, not +automatically a bad key. `429`, timeout, DNS/TLS failure, and `5xx` are +inconclusive and must not erase earlier trustworthy evidence. A `2xx` response +with no usable models may validate authentication while still failing onboarding +readiness. + +## Architecture + +Add a main-process `ProviderValidationService` with an immutable strategy registry +for every Pi built-in plus Aiden's first-class providers and custom/local classes. +Candidate secrets remain main-process memory until validation completes. + +The service must: + +- bind attempts to the initiating renderer document and cancel on navigation, + destruction, shutdown, or explicit cancellation; +- fence results by provider draft fingerprint and opaque credential revision so a + late result cannot overwrite newer input; +- use per-attempt and total deadlines, bounded concurrency, and closed result + codes; +- commit provider configuration, credential, catalog, default model, and evidence + atomically, extending the existing credential-rotation journal for custom + connections; +- preserve the previously working provider when a candidate fails or becomes + inconclusive; +- reject cross-origin redirects for secret-bearing requests, validate the exact + intended origin, retain TLS verification, and apply the existing private-host + policy deliberately for local/Tailnet providers; +- bound response bytes, pages, model count, model ID length, and displayed error + data; +- log only provider ID, validation strategy, closed result code, and coarse + duration. + +Expose owner-bound start/cancel/status IPC rather than returning raw upstream +errors. Settings and onboarding consume one shared renderer state model and one +accessible status component. + +## Delivery phases + +### Phase 0 — executable regression harness + +- Replace source-regex onboarding assertions with rendered interaction tests. +- Add a fixture using the actual signed-out `providers:list` projection. +- Add auth/session fakes for success, prompt, browser failure, device flow, + cancellation, finishing, expiry, and terminal failure. +- Register all new files in a CI-invoked test script; keep the focused onboarding + script for local iteration. + +**Exit gate:** a failing test reproduces the current ChatGPT dead branch and a +second test proves the existing global Skip marks an unusable setup complete. + +### Phase 1 — P0 Codex onboarding repair + +- Extract the dedicated Codex status/session UI from Settings into a shared + component/state machine and use it directly in onboarding. +- Start `createCodexAuthSession` by the immutable `openai-codex` identity; never + discover signed-out Codex through the configured-provider list. +- Offer Browser and Device Code as clear actions, with manual-code fallback, + retry, cancel, `finishing`, and actionable browser-open/callback failure states. +- Restrict automatically opened Codex auth URLs to the expected OpenAI origins in + addition to HTTPS validation. +- Reconcile only Codex after terminal success. Treat other catalog refreshes as + best-effort warnings. +- Advance only when a fresh Codex status is configured, not attention-needed, and + exposes at least one usable model. +- Add a packaged lazy-module/auth canary so the exact Pi OAuth path is verified + inside ASAR. + +**Exit gate:** a fresh install can sign in by browser or device code, cancel and +retry safely, relaunch, and select a Codex model in a packaged build. + +### Phase 2 — authoritative onboarding lifecycle + +- Move the versioned onboarding outcome/checkpoint to the main process. +- Derive resume position from profile and provider readiness after reload/crash. +- Remove the global Skip and do not expose a defer action during required setup. +- Preserve migrated `deferred` records without treating them as completed, and + expose a non-destructive **Show onboarding** recovery path in Settings. +- Make onboarding an application modal: inhibit command palette, navigation, + native menu mutations, assistant dock actions, and hidden stacked dialogs. +- Separate required setup from the optional feature tour. + +**Exit gate:** no path records `completed` without a usable provider; reload and +defer/re-entry behavior remain consistent. + +### Phase 3 — validation service and secure network boundary + +- Add validation DTOs, evidence storage, strategy registry, owner-bound IPC, + deadlines, cancellation, revision fencing, and sanitized closed errors. +- Harden shared discovery with exact-origin redirect rejection, SSRF review, + bounded bodies/catalogs/pages/IDs, and distinct cancel/timeout outcomes. +- Migrate existing configured providers to `unverified`; cached catalogs never + become validation evidence. +- Clear validation evidence during onboarding reset/provider removal and recover + atomic rotations after interrupted writes. + +**Exit gate:** security and race tests prove no secret or raw provider response +can reach URL, argv, environment, renderer notifications, logs, cache, or crash +text. + +### Phase 4 — onboarding provider integrations + +- Replace onboarding's OpenAI/Anthropic custom clones with native providers. +- Implement documented non-billable catalog validation for OpenAI and Anthropic, + then atomically save credential, discovered models, and default selection. +- Route LM Studio, Ollama, and Tailnet/custom setup through the same service while + labeling results as endpoint capability rather than credential acceptance. +- Persist a selected default model for every successful route, not only local + runtimes. +- Classify every installed Pi provider and Concentrate. Add authoritative probes + only where current primary documentation supports them; show **Not checked** or + configuration-only elsewhere rather than inventing a generation probe. + +**Exit gate:** invalid hosted keys cannot advance, unreachable or zero-model +endpoints cannot advance, and capability-only endpoints never claim that a key +was accepted. + +### Phase 5 — shared Settings UI and runtime evidence + +- Use one inline status component and copy across onboarding and Settings: + **Checking**, **Credentials accepted**, **Endpoint reached**, **Not checked**, + **Checked previously**, **Credentials rejected**, and **Could not reach + provider**. +- Offer Retry and an explicit **Save without checking** in Settings. In required + onboarding, unverified save may persist a draft but does not count as ready. +- Preserve input and focus after failures; use `role="status"`/`role="alert"`, + focus the new step heading, set `aria-current="step"`, and respect reduced + motion. +- Let successful real requests and normalized auth failures update evidence + without issuing additional provider calls. Do not auto-switch models, unhide + models, or change existing chat provenance. + +**Exit gate:** onboarding and Settings show the same trustworthy provider state, +and keyboard/VoiceOver navigation passes focused acceptance. + +### Phase 6 — Codex credential-owner decision + +The official Codex contract currently exposes `codex login`, +`codex login --device-auth`, `codex login --with-api-key`, `codex login status`, +and `codex logout`. Aiden currently uses Pi's OAuth implementation and encrypted +`pi-provider-credentials.json`; the two sessions are independent. + +- Decide whether Pi remains Aiden's declared Codex credential/inference owner or + whether Codex moves as a unit to an officially packaged CLI/app-server path. +- Do not scrape or copy `~/.codex/auth.json`; official storage may be keyring + backed. +- Do not use a user-installed `codex` from `PATH`. +- If migrating, pin and sign per-architecture executables outside ASAR, verify + hashes/provenance/mode/signature, feed API keys only through stdin, and move + inference to the same official owner rather than bridging credentials into Pi. +- Add bounded child lifecycle, output, timeout, cancellation, shutdown, and + package tests before changing the production owner. + +**Exit gate:** authentication status, logout, refresh, and inference all observe +one credential owner, with no hidden divergence from the user's Codex CLI. + +## Required test matrix + +| Area | Required cases | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Codex | Fresh signed-out start; configured; attention-needed; browser success/failure; device success/expiry; manual fallback; callback collision; wrong state; cancel; finishing; retry; relaunch | +| API keys | Valid; empty; `401`; `403`; `429` with bounded Retry-After; `5xx`; DNS/TLS; timeout; cancel; stale/late result; old-key preservation | +| Endpoint discovery | `2xx` models; `2xx` empty; malformed/oversized body; too many pages/models; redirect; origin change; private/metadata targets; protocol/auth mutation | +| Lifecycle | Reload after profile save; reload after provider save; document navigation; window destruction; shutdown; safe-storage unavailable; reset success/failure/cancel | +| Completion | No-provider block; zero-model block; confirmed defer; non-destructive re-entry; completion-write failure; back after save; duplicate click prevention | +| Commands and accessibility | Command-K/menu blocked; focus on step changes/errors; `aria-current`; live status; full keyboard path; reduced motion | +| Cross-provider isolation | Unrelated catalog failure does not block selected provider; one validation cannot overwrite another; visibility/default/existing chats stay stable | +| Security | No secret in URL/argv/env/log/IPC/cache; fixed renderer copy; bounded diagnostics; no generation endpoint invoked by validation | +| Packaging | Exact lazy auth module loads from ASAR; packaged sign-in works with empty `PATH`; arm64/x64 artifact verification if official CLI is adopted | + +## Rollout and observability + +- Ship Phase 1 behind focused automated and packaged acceptance before widening + provider validation. +- Record only aggregate local outcomes: provider ID, strategy, closed status, + coarse latency, and onboarding step. No tokens, URLs, account IDs, prompt data, + or upstream bodies. +- Track completion, defer, validation rejection, unreachable endpoint, auth cancel, + and recovery rates locally so UI dead ends can be diagnosed. +- Update onboarding copy and the final feature-tour gallery only for capabilities + that have shipped and passed their exit gates. From 31daaf2b0648e1ec9f17be82971e775c33765a29 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:29 -0400 Subject: [PATCH 106/266] test(main): add aiden-remote-revocation.test add main/services/aiden-remote-revocation.test.ts as part of the reviewed Aiden On The Go and Remote Access update. --- main/services/aiden-remote-revocation.test.ts | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 main/services/aiden-remote-revocation.test.ts diff --git a/main/services/aiden-remote-revocation.test.ts b/main/services/aiden-remote-revocation.test.ts new file mode 100644 index 00000000..f9de5df9 --- /dev/null +++ b/main/services/aiden-remote-revocation.test.ts @@ -0,0 +1,87 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { revokeAidenRemoteRuntimeDevice } from "./aiden-remote-revocation.js"; +import { + AidenRemoteStateRegistry, + createDefaultAidenRemoteState, + defaultAidenRemoteStateDependencies, +} from "./aiden-remote-state.js"; +import { AidenRemoteStreamService, type AidenRemoteStreamSnapshot } from "./aiden-remote-streams.js"; + +test("runtime revocation is durable, cleans device resources, retries persistence, and preserves device B", async () => { + let stateDocument = createDefaultAidenRemoteState(() => Buffer.alloc(24, 4)); + let now = 1_000; + let randomCounter = 0; + const state = new AidenRemoteStateRegistry({ + load: async () => structuredClone(stateDocument), + save: async (document) => { stateDocument = structuredClone(document); }, + }, { + ...defaultAidenRemoteStateDependencies(), + now: () => now, + randomBytes: (size) => Buffer.alloc(size, ++randomCounter), + }); + await state.initialize(); + const deviceA = await state.issueDevice({ + name: "Personal iPhone", type: "iphone", clientVersion: "1", + }); + const deviceB = await state.issueDevice({ + name: "Travel iPhone", type: "iphone", clientVersion: "1", + }); + + let streamSnapshot: AidenRemoteStreamSnapshot = { version: 1, streams: [] }; + let failStreamSave = false; + const deniedApprovals: string[] = []; + const streams = new AidenRemoteStreamService({ + now: () => now, + cancel: () => true, + approve: (approvalId, decision) => { + deniedApprovals.push(`${approvalId}:${decision}`); + return true; + }, + persist: async (snapshot) => { + if (failStreamSave) throw new Error("stream disk unavailable"); + streamSnapshot = structuredClone(snapshot); + }, + }); + const ownerA = streams.create(deviceA.device.id, "stream-a", "chat-a", "turn-a"); + ownerA.owner.send("chat:approval", { approvalId: "approval-a", summary: "Write a file" }); + streams.create(deviceB.device.id, "stream-b", "chat-b", "turn-b"); + await streams.settlePersistence(); + + const chatRevocations: string[] = []; + const ownerRevocations: string[] = []; + const resources = { + state, + streams, + chats: { revokeDevice: (deviceId: string) => { chatRevocations.push(deviceId); } }, + workspaceOwners: { revokeDevice: (deviceId: string) => { ownerRevocations.push(deviceId); } }, + }; + + failStreamSave = true; + await assert.rejects( + revokeAidenRemoteRuntimeDevice(resources, deviceA.device.id), + /stream disk unavailable/u, + ); + assert.equal((await state.authenticate(deviceA.credential))?.revoked, true); + assert.equal((await state.authenticate(deviceB.credential))?.revoked, false); + assert.throws(() => streams.status(deviceA.device.id, "stream-a")); + assert.equal(streams.status(deviceB.device.id, "stream-b").state, "queued"); + assert.deepEqual(chatRevocations, [deviceA.device.id]); + assert.deepEqual(ownerRevocations, [deviceA.device.id]); + assert.deepEqual(deniedApprovals, ["approval-a:deny"]); + + failStreamSave = false; + now += 1; + assert.equal(await revokeAidenRemoteRuntimeDevice(resources, deviceA.device.id), false); + assert.equal(streamSnapshot.streams.some(({ deviceId }) => deviceId === deviceA.device.id), false); + assert.equal(streamSnapshot.streams.some(({ deviceId }) => deviceId === deviceB.device.id), true); + + const restartedStreams = new AidenRemoteStreamService({ + now: () => now, + cancel: () => false, + approve: () => false, + snapshot: streamSnapshot, + }); + assert.throws(() => restartedStreams.status(deviceA.device.id, "stream-a")); + assert.equal(restartedStreams.status(deviceB.device.id, "stream-b").state, "interrupted"); +}); From ea2a8d503ab9695830053b5d7d9668d84ddb27ff Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 107/266] feat(remote): add aiden-remote-revocation add main/services/aiden-remote-revocation.ts as part of the reviewed Aiden On The Go and Remote Access update. --- main/services/aiden-remote-revocation.ts | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 main/services/aiden-remote-revocation.ts diff --git a/main/services/aiden-remote-revocation.ts b/main/services/aiden-remote-revocation.ts new file mode 100644 index 00000000..ff6bbd57 --- /dev/null +++ b/main/services/aiden-remote-revocation.ts @@ -0,0 +1,30 @@ +export interface AidenRemoteRevocationState { + revokeDevice(deviceId: string): Promise; + snapshot(): Promise<{ devices: Array<{ id: string; revokedAt?: number }> }>; +} + +export interface AidenRemoteRevocationResources { + state: AidenRemoteRevocationState; + streams?: { revokeDevice(deviceId: string): Promise }; + chats?: { revokeDevice(deviceId: string): void }; + workspaceOwners: { revokeDevice(deviceId: string): void }; +} + +/** + * Main-process revocation transaction. Device state is committed first so a + * crash can never revive the credential. Cleanup is then retried even for an + * already-revoked device, and stream-journal durability is part of success. + */ +export async function revokeAidenRemoteRuntimeDevice( + resources: AidenRemoteRevocationResources, + deviceId: string, +): Promise { + const newlyRevoked = await resources.state.revokeDevice(deviceId); + const device = (await resources.state.snapshot()).devices.find(({ id }) => id === deviceId); + if (device?.revokedAt === undefined) return false; + + resources.chats?.revokeDevice(deviceId); + resources.workspaceOwners.revokeDevice(deviceId); + await resources.streams?.revokeDevice(deviceId); + return newlyRevoked; +} From b2b433606f19a845bf6f339c620a55dbe8834460 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 108/266] test(main): add onboarding-provider-validation.test add main/services/onboarding-provider-validation.test.ts as part of the reviewed Aiden On The Go and Remote Access update. --- .../onboarding-provider-validation.test.ts | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 main/services/onboarding-provider-validation.test.ts diff --git a/main/services/onboarding-provider-validation.test.ts b/main/services/onboarding-provider-validation.test.ts new file mode 100644 index 00000000..fec05f52 --- /dev/null +++ b/main/services/onboarding-provider-validation.test.ts @@ -0,0 +1,94 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { + normalizeOnboardingValidationProvider, + validateOnboardingProviderCredential, +} from "./onboarding-provider-validation.js"; +import type { StoredProvider } from "./types.js"; + +const provider: StoredProvider = { + id: "openai", + kind: "openai", + label: "OpenAI", + baseUrl: "https://api.openai.com/v1", + models: ["model-a", "model-b"], + needsKey: true, +}; + +test("Anthropic onboarding validation uses the versioned models endpoint", () => { + assert.equal( + normalizeOnboardingValidationProvider({ + ...provider, + id: "anthropic", + kind: "anthropic", + baseUrl: "https://api.anthropic.com", + }).baseUrl, + "https://api.anthropic.com/v1", + ); + assert.equal( + normalizeOnboardingValidationProvider({ + ...provider, + id: "anthropic", + kind: "anthropic", + baseUrl: "https://gateway.example/anthropic", + }).baseUrl, + "https://gateway.example/anthropic", + ); +}); + +test("onboarding validation commits only after the catalog proves a supported model", async (t) => { + const originalFetch = globalThis.fetch; + t.after(() => { + globalThis.fetch = originalFetch; + }); + globalThis.fetch = (async () => + new Response(JSON.stringify({ data: [{ id: "model-b" }, { id: "unknown-model" }] }), { + status: 200, + })) as typeof fetch; + const committed: string[] = []; + + const usable = await validateOnboardingProviderCredential({ + provider, + apiKey: "validated-key", + installedModelIds: provider.models, + isCurrent: () => true, + commit: async (apiKey) => { + committed.push(apiKey); + }, + }); + + assert.deepEqual(usable, ["model-b"]); + assert.deepEqual(committed, ["validated-key"]); +}); + +test("failed, unsupported, or stale validation never reaches the credential commit", async (t) => { + const originalFetch = globalThis.fetch; + t.after(() => { + globalThis.fetch = originalFetch; + }); + let writes = 0; + const validate = (isCurrent = () => true) => + validateOnboardingProviderCredential({ + provider, + apiKey: "candidate-key", + installedModelIds: provider.models, + isCurrent, + commit: async () => { + writes += 1; + }, + }); + + globalThis.fetch = (async () => new Response("unauthorized", { status: 401 })) as typeof fetch; + await assert.rejects(validate(), /rejected those credentials/u); + + globalThis.fetch = (async () => + new Response(JSON.stringify({ data: [{ id: "unsupported-model" }] }), { + status: 200, + })) as typeof fetch; + await assert.rejects(validate(), /no supported chat models/u); + + globalThis.fetch = (async () => + new Response(JSON.stringify({ data: [{ id: "model-a" }] }), { status: 200 })) as typeof fetch; + await assert.rejects(validate(() => false), /no longer active/u); + assert.equal(writes, 0); +}); From 794ffde01288aaba86d7ee279dc0cd36a4cd575e Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 109/266] feat(onboarding): add onboarding-provider-validation add main/services/onboarding-provider-validation.ts as part of the reviewed Aiden On The Go and Remote Access update. --- .../onboarding-provider-validation.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 main/services/onboarding-provider-validation.ts diff --git a/main/services/onboarding-provider-validation.ts b/main/services/onboarding-provider-validation.ts new file mode 100644 index 00000000..a230de24 --- /dev/null +++ b/main/services/onboarding-provider-validation.ts @@ -0,0 +1,42 @@ +import { testConnection } from "./models.js"; +import type { StoredProvider } from "./types.js"; + +interface OnboardingProviderValidationInput { + provider: StoredProvider; + apiKey: string; + installedModelIds: readonly string[]; + isCurrent: () => boolean; + commit: (apiKey: string) => Promise; +} + +/** Pi currently reports Anthropic's API origin without its versioned REST path. */ +export function normalizeOnboardingValidationProvider( + provider: StoredProvider, +): StoredProvider { + if ( + provider.id === "anthropic" && + /^https:\/\/api\.anthropic\.com\/?$/u.test(provider.baseUrl) + ) { + return { ...provider, baseUrl: "https://api.anthropic.com/v1" }; + } + return provider; +} + +/** Validate a non-generation catalog before replacing the stored credential. */ +export async function validateOnboardingProviderCredential({ + provider, + apiKey, + installedModelIds, + isCurrent, + commit, +}: OnboardingProviderValidationInput): Promise { + const result = await testConnection(normalizeOnboardingValidationProvider(provider), apiKey); + const accessible = new Set(result.models); + const usableModelIds = installedModelIds.filter((modelId) => accessible.has(modelId)); + if (usableModelIds.length === 0) { + throw new Error("Credentials were accepted, but no supported chat models are available."); + } + if (!isCurrent()) throw new Error("The onboarding window is no longer active."); + await commit(apiKey); + return usableModelIds; +} From 7f644a9567e2a443047f18110d6ebb7ee62b6028 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 110/266] test(main): add onboarding-state-core.test add main/services/onboarding-state-core.test.ts as part of the reviewed Aiden On The Go and Remote Access update. --- main/services/onboarding-state-core.test.ts | 96 +++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 main/services/onboarding-state-core.test.ts diff --git a/main/services/onboarding-state-core.test.ts b/main/services/onboarding-state-core.test.ts new file mode 100644 index 00000000..d1871990 --- /dev/null +++ b/main/services/onboarding-state-core.test.ts @@ -0,0 +1,96 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { + assertOnboardingCanComplete, + legacyOnboardingOutcome, + onboardingEvidenceProvider, + onboardingProgressState, + onboardingProviderReady, + reconcileOnboardingOutcome, +} from "./onboarding-state-core.js"; +import type { Provider } from "./types.js"; + +function provider(overrides: Partial = {}): Provider { + return { + id: "openai", + kind: "openai", + label: "OpenAI", + baseUrl: "https://api.openai.com/v1", + models: ["gpt-test"], + defaultModel: "gpt-test", + needsKey: true, + hasKey: true, + ...overrides, + }; +} + +test("provider readiness requires a chat model and the provider class's credential", () => { + assert.equal(onboardingProviderReady(provider()), true); + assert.equal(onboardingProviderReady(provider({ models: [] })), false); + assert.equal(onboardingProviderReady(provider({ hasKey: false })), false); + assert.equal( + onboardingProviderReady(provider({ needsKey: false, hasKey: false, models: ["local"] })), + true, + ); +}); + +test("an ambient or stored credential cannot skip validation after relaunch", () => { + const ambient = provider({ id: "groq", label: "Groq", models: ["static-model"] }); + assert.equal( + onboardingEvidenceProvider([ambient], { + version: 2, + outcome: "incomplete", + lastSatisfiedStep: "profile", + }), + undefined, + ); + assert.equal( + onboardingEvidenceProvider([ambient], { + version: 2, + outcome: "incomplete", + lastSatisfiedStep: "provider", + selectedProviderId: "groq", + })?.id, + "groq", + ); +}); + +test("legacy completion becomes deferred instead of lying about an unusable setup", () => { + assert.equal(legacyOnboardingOutcome(false, false), "incomplete"); + assert.equal(legacyOnboardingOutcome(true, false), "deferred"); + assert.equal(legacyOnboardingOutcome(true, true), "completed"); +}); + +test("progress is versioned and completion is readiness-gated", () => { + const ready = { + profileReady: true, + providerReady: true, + selectedProviderId: "openai", + }; + assert.deepEqual(onboardingProgressState("incomplete", ready), { + version: 2, + outcome: "incomplete", + lastSatisfiedStep: "provider", + selectedProviderId: "openai", + }); + assert.doesNotThrow(() => assertOnboardingCanComplete(ready)); + assert.throws( + () => assertOnboardingCanComplete({ ...ready, providerReady: false }), + /usable model provider/u, + ); +}); + +test("completed state reopens when authoritative setup readiness is lost", () => { + assert.equal( + reconcileOnboardingOutcome("completed", { profileReady: true, providerReady: false }), + "incomplete", + ); + assert.equal( + reconcileOnboardingOutcome("completed", { profileReady: true, providerReady: true }), + "completed", + ); + assert.equal( + reconcileOnboardingOutcome("deferred", { profileReady: true, providerReady: true }), + "deferred", + ); +}); From df79e07e360b4a99b83ea1ffca876602517c7431 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 111/266] feat(onboarding): add onboarding-state-core add main/services/onboarding-state-core.ts as part of the reviewed Aiden On The Go and Remote Access update. --- main/services/onboarding-state-core.ts | 64 ++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 main/services/onboarding-state-core.ts diff --git a/main/services/onboarding-state-core.ts b/main/services/onboarding-state-core.ts new file mode 100644 index 00000000..ae635a76 --- /dev/null +++ b/main/services/onboarding-state-core.ts @@ -0,0 +1,64 @@ +import type { Provider } from "./types.js"; +import { + ONBOARDING_STATE_VERSION, + type OnboardingOutcome, + type OnboardingState, +} from "../../renderer/shared/onboarding.js"; + +export interface OnboardingReadiness { + profileReady: boolean; + providerReady: boolean; + selectedProviderId?: string; +} + +export function onboardingProviderReady(provider: Provider): boolean { + return provider.models.length > 0 && (provider.needsKey ? provider.hasKey === true : true); +} + +/** Structural auth alone (including ambient env keys) is not first-run evidence. */ +export function onboardingEvidenceProvider( + providers: readonly Provider[], + state: OnboardingState | null, +): Provider | undefined { + const providerStepSatisfied = + state?.lastSatisfiedStep === "provider" || state?.lastSatisfiedStep === "tour"; + if (!providerStepSatisfied || !state.selectedProviderId) return undefined; + return providers.find( + (provider) => provider.id === state.selectedProviderId && onboardingProviderReady(provider), + ); +} + +export function legacyOnboardingOutcome( + legacyComplete: boolean, + setupReady: boolean, +): OnboardingOutcome { + if (!legacyComplete) return "incomplete"; + return setupReady ? "completed" : "deferred"; +} + +export function onboardingProgressState( + outcome: OnboardingOutcome, + ready: OnboardingReadiness, +): OnboardingState { + return { + version: ONBOARDING_STATE_VERSION, + outcome, + lastSatisfiedStep: !ready.profileReady ? "none" : ready.providerReady ? "provider" : "profile", + ...(ready.selectedProviderId ? { selectedProviderId: ready.selectedProviderId } : {}), + }; +} + +export function assertOnboardingCanComplete(ready: OnboardingReadiness): void { + if (!ready.profileReady || !ready.providerReady) { + throw new Error("Finish your profile and connect a usable model provider first."); + } +} + +export function reconcileOnboardingOutcome( + outcome: OnboardingOutcome, + ready: OnboardingReadiness, +): OnboardingOutcome { + return outcome === "completed" && (!ready.profileReady || !ready.providerReady) + ? "incomplete" + : outcome; +} From 2c7be5aaf1f1f6aa8189c860e9f0b2f42cf7b5d8 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 112/266] feat(onboarding): add onboarding-state add main/services/onboarding-state.ts as part of the reviewed Aiden On The Go and Remote Access update. --- main/services/onboarding-state.ts | 175 ++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 main/services/onboarding-state.ts diff --git a/main/services/onboarding-state.ts b/main/services/onboarding-state.ts new file mode 100644 index 00000000..190f4275 --- /dev/null +++ b/main/services/onboarding-state.ts @@ -0,0 +1,175 @@ +import { configStore } from "./config-store.js"; +import { listConfiguredProviders } from "./provider-list-main.js"; +import { + ONBOARDING_STATE_VERSION, + parseOnboardingState, + type OnboardingOutcome, + type OnboardingSnapshot, + type OnboardingState, +} from "../../renderer/shared/onboarding.js"; +import { + assertOnboardingCanComplete, + legacyOnboardingOutcome, + onboardingEvidenceProvider, + onboardingProgressState, + onboardingProviderReady, + reconcileOnboardingOutcome, +} from "./onboarding-state-core.js"; + +async function readiness(): Promise<{ + profileReady: boolean; + providerReady: boolean; + selectedProviderId?: string; +}> { + const [providers, settings] = await Promise.all([ + listConfiguredProviders(), + configStore.getSettings(), + ]); + const progress = parseOnboardingState(settings.onboarding); + // Static Pi catalogs and ambient credentials are not validation evidence. + // A provider becomes onboarding-ready only after the renderer completed one + // of the guarded validation/discovery flows and main recorded that exact ID. + const selected = onboardingEvidenceProvider(providers, progress); + return { + profileReady: + progress?.lastSatisfiedStep !== undefined && + progress.lastSatisfiedStep !== "none" && + typeof settings.profileName === "string" && + settings.profileName.trim().length > 0, + providerReady: selected !== undefined, + ...(selected ? { selectedProviderId: selected.id } : {}), + }; +} + +async function persist( + state: OnboardingState, + isCurrent: () => boolean = () => true, +): Promise { + if (!isCurrent()) throw new Error("The onboarding window is no longer active."); + const settings = await configStore.setSettings({ onboarding: state }); + const saved = parseOnboardingState(settings.onboarding); + if (!saved) throw new Error("Aiden couldn't save onboarding progress."); + return saved; +} + +export async function getOnboardingSnapshot( + legacyComplete = false, + isCurrent: () => boolean = () => true, +): Promise { + const [settings, ready] = await Promise.all([configStore.getSettings(), readiness()]); + let state = parseOnboardingState(settings.onboarding); + if (!state) { + // Preserve existing installs without trusting the legacy renderer marker as + // proof of readiness. An unusable legacy setup becomes deferred, not complete. + const migratedReady = { + ...ready, + profileReady: + legacyComplete && + typeof settings.profileName === "string" && + settings.profileName.trim().length > 0, + }; + state = await persist( + onboardingProgressState( + legacyOnboardingOutcome( + legacyComplete, + migratedReady.profileReady && migratedReady.providerReady, + ), + migratedReady, + ), + isCurrent, + ); + return { + ...state, + profileReady: migratedReady.profileReady, + providerReady: migratedReady.providerReady, + }; + } + const reconciledOutcome = reconcileOnboardingOutcome(state.outcome, ready); + if (reconciledOutcome !== state.outcome) { + state = await persist(onboardingProgressState(reconciledOutcome, ready), isCurrent); + } + return { ...state, profileReady: ready.profileReady, providerReady: ready.providerReady }; +} + +export async function setOnboardingProgress( + step: "profile" | "provider", + selectedProviderId?: string, + isCurrent: () => boolean = () => true, +): Promise { + const settings = await configStore.getSettings(); + const current = parseOnboardingState(settings.onboarding); + const profileConfigured = + typeof settings.profileName === "string" && settings.profileName.trim().length > 0; + if (!profileConfigured) throw new Error("Choose a profile name before continuing."); + if (step === "provider" && (!current || current.lastSatisfiedStep === "none")) { + throw new Error("Finish the profile step before connecting a model provider."); + } + const ready = await readiness(); + if (step === "provider") { + if (!selectedProviderId) throw new Error("A ready provider must be selected."); + const providers = await listConfiguredProviders(); + if ( + !providers.some( + (provider) => provider.id === selectedProviderId && onboardingProviderReady(provider), + ) + ) { + throw new Error("The selected model provider is not ready."); + } + } + const state = await persist({ + version: ONBOARDING_STATE_VERSION, + outcome: "incomplete", + lastSatisfiedStep: + step === "provider" || + current?.lastSatisfiedStep === "provider" || + current?.lastSatisfiedStep === "tour" + ? "provider" + : "profile", + ...((selectedProviderId ?? current?.selectedProviderId) + ? { selectedProviderId: selectedProviderId ?? current?.selectedProviderId } + : {}), + }, isCurrent); + return { + ...state, + profileReady: true, + providerReady: step === "provider" ? true : ready.providerReady, + }; +} + +export async function setOnboardingOutcome( + outcome: Exclude, + selectedProviderId?: string, + isCurrent: () => boolean = () => true, +): Promise { + if (outcome === "incomplete") { + const ready = await readiness(); + const state = await persist(onboardingProgressState("incomplete", ready), isCurrent); + return { ...state, profileReady: ready.profileReady, providerReady: ready.providerReady }; + } + + const ready = await readiness(); + if (outcome === "completed") assertOnboardingCanComplete(ready); + if ( + outcome === "completed" && + selectedProviderId && + selectedProviderId !== ready.selectedProviderId + ) { + const providers = await listConfiguredProviders(); + if ( + !providers.some( + (provider) => provider.id === selectedProviderId && onboardingProviderReady(provider), + ) + ) { + throw new Error("The selected model provider is not ready."); + } + } + const state = await persist( + { + ...onboardingProgressState(outcome, ready), + ...(selectedProviderId ? { selectedProviderId } : {}), + ...(outcome === "completed" ? { lastSatisfiedStep: "tour" as const } : {}), + }, + isCurrent, + ); + return { ...state, profileReady: ready.profileReady, providerReady: ready.providerReady }; +} From 88480510cf2d5cf258b1c8590ea1f415f1c8cfb4 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 113/266] feat(protocol): add manual-pairing-vector add protocol/aiden-remote/v1/fixtures/manual-pairing-vector.json as part of the reviewed Aiden On The Go and Remote Access update. --- .../v1/fixtures/manual-pairing-vector.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 protocol/aiden-remote/v1/fixtures/manual-pairing-vector.json diff --git a/protocol/aiden-remote/v1/fixtures/manual-pairing-vector.json b/protocol/aiden-remote/v1/fixtures/manual-pairing-vector.json new file mode 100644 index 00000000..e11380cd --- /dev/null +++ b/protocol/aiden-remote/v1/fixtures/manual-pairing-vector.json @@ -0,0 +1,14 @@ +{ + "code": "0123-4567-89AB-CDEF-GHJK", + "payload": "{\"kind\":\"aiden-pairing-v1\",\"bootstrap\":{\"protocolVersion\":1,\"instanceId\":\"instance_fixture_01\",\"endpoint\":\"https://aiden-fixture.example.test/api/aiden/v1\",\"serverSpkiSha256\":\"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\",\"secret\":\"AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE\",\"expiresAt\":\"2026-08-21T17:05:00.000Z\"},\"trust\":{\"mode\":\"system\"}}", + "bootstrap": { + "kind": "aiden-manual-pairing-v1", + "protocolVersion": 1, + "sessionId": "pairing_ssssssssssssssssssssssssssssssss", + "expiresAt": "2026-08-21T17:05:00.000Z", + "salt": "AAECAwQFBgcICQoLDA0ODw", + "nonce": "EBESExQVFhcYGRob", + "ciphertext": "rvjdeCbqBsel3faU24lEJ2YZUxyGXuh2qKSyWL_RUK3Hh9JiyfIoYqT3ismISu8duWMCkhKmDneYWswEOGG6l_2OADKUmsPtgdsYQ8UpAGnX_prF044mtYFBmf9-_z7DYQz0d5_uJ7RWg9JnI0K8lAuM-5nQYBV1eWgZZ0aclBLTzIGFP2N4Dd7szHapNNrJia_HoEoQ6wopSZoMAJrnkuJcxLtEVQrRla3YEWR4KAkwrGPP8HyZ5OQ6vQT9e9waInCqWCOq5Se9r8xZ-dL-l6P4XdfUHp74zUIYgNduqd2ZVs-9vIu3Y1WXlpkosTO4ulF4goF-JfiZp-QZlc_uo2299twQVTy2ZCGNuGwaOkF_yixW0i66X-H9ZmPdqDwToq0SahQ3Q8HXR2Aqx64Nz-ZBvJ0ESMz5SVlMIJfXKbuafRQR5Dy0Y42kqlt6rnh18_Jiz9N8N219M54Dqd8", + "tag": "P-ntrj59ZSt45orya5fSvQ" + } +} From 12e7e93d802145a77a486f5454b0aa1935b7c784 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 114/266] test(renderer): add remote-connection-popover.test add renderer/components/remote-connection-popover.test.tsx as part of the reviewed Aiden On The Go and Remote Access update. --- .../components/remote-connection-popover.test.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 renderer/components/remote-connection-popover.test.tsx diff --git a/renderer/components/remote-connection-popover.test.tsx b/renderer/components/remote-connection-popover.test.tsx new file mode 100644 index 00000000..57efaf54 --- /dev/null +++ b/renderer/components/remote-connection-popover.test.tsx @@ -0,0 +1,11 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import test from "node:test"; + +const source = readFileSync(new URL("./remote-connection-popover.tsx", import.meta.url), "utf8"); + +test("pending devices remain visible without formatting the zero last-seen timestamp", () => { + assert.match(source, /state === "pending"[\s\S]*Finishing connection/u); + assert.match(source, /label="Finishing" devices=\{groups\.pending\} state="pending"/u); + assert.match(source, /state === "pending" \? "Waiting for the first authenticated connection"/u); +}); From 2b224259513d5ea018e28ddbfe6ab82982811a39 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 115/266] feat(renderer): add remote-connection-popover add renderer/components/remote-connection-popover.tsx as part of the reviewed Aiden On The Go and Remote Access update. --- .../components/remote-connection-popover.tsx | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 renderer/components/remote-connection-popover.tsx diff --git a/renderer/components/remote-connection-popover.tsx b/renderer/components/remote-connection-popover.tsx new file mode 100644 index 00000000..965b5ca9 --- /dev/null +++ b/renderer/components/remote-connection-popover.tsx @@ -0,0 +1,207 @@ +import * as React from "react"; +import { useQueryClient } from "@tanstack/react-query"; +import { ChevronDown, Smartphone } from "lucide-react"; +import { + Button, + Popover, + PopoverContent, + PopoverTrigger, + Text, +} from "./ui"; +import { aidenRemoteApi } from "../lib/ipc"; +import { queryKeys, useAidenRemoteSettings } from "../lib/queries"; +import { + groupRemoteDevices, + remoteConnectionSummary, + type RemoteDeviceGroups, +} from "../lib/remote-connection-status"; +import type { AidenRemoteDeviceView } from "../shared/aiden-remote"; + +function relativeSeen(timestamp: number, now = Date.now()): string { + const elapsed = Math.max(0, now - timestamp); + if (elapsed < 60_000) return "Just now"; + const minutes = Math.floor(elapsed / 60_000); + if (minutes < 60) return `${minutes} min ago`; + const hours = Math.floor(minutes / 60); + if (hours < 24) return `${hours} hr${hours === 1 ? "" : "s"} ago`; + const days = Math.floor(hours / 24); + return `${days} day${days === 1 ? "" : "s"} ago`; +} + +function DeviceRow({ + device, + state, +}: { + device: AidenRemoteDeviceView; + state: keyof RemoteDeviceGroups; +}) { + const timestamp = state === "previous" ? (device.revokedAt ?? device.lastSeenAt) : device.lastSeenAt; + const detail = state === "previous" + ? `Removed ${relativeSeen(timestamp)}` + : state === "pending" + ? "Finishing connection" + : `Last seen ${relativeSeen(timestamp)}`; + + return ( +
    + +
    + {device.name} + + {device.type === "ipad" ? "iPad" : "iPhone"} · {detail} + +
    +
    + ); +} + +function DeviceGroup({ + label, + devices, + state, +}: { + label: string; + devices: AidenRemoteDeviceView[]; + state: keyof RemoteDeviceGroups; +}) { + if (devices.length === 0) return null; + return ( +
    +
    + {label} + {devices.length} +
    + {devices.map((device) => )} +
    + ); +} + +export function RemoteConnectionPopover({ + settingsBlockedReason, + onManage, +}: { + settingsBlockedReason?: string; + onManage: () => void; +}) { + const queryClient = useQueryClient(); + const settings = useAidenRemoteSettings(); + const [open, setOpen] = React.useState(false); + + React.useEffect(() => aidenRemoteApi.onChanged(() => { + void queryClient.invalidateQueries({ queryKey: queryKeys.aidenRemote }); + }), [queryClient]); + + const snapshot = settings.data; + const groups = snapshot + ? groupRemoteDevices(snapshot.devices, { serviceRunning: snapshot.status.running }) + : { active: [], pending: [], inactive: [], previous: [] }; + const summary = snapshot + ? remoteConnectionSummary({ + enabled: snapshot.status.enabled, + running: snapshot.status.running, + error: snapshot.status.error, + activeDeviceCount: groups.active.length, + }) + : settings.isLoading ? "Checking" : "Unavailable"; + const statusTone = snapshot?.status.error || (snapshot?.status.enabled && !snapshot.status.running) + ? "bg-red" + : groups.active.length > 0 + ? "bg-green" + : snapshot?.status.running + ? "bg-accent" + : "bg-tertiary"; + + const manage = () => { + setOpen(false); + onManage(); + }; + + return ( + + + + + +
    +
    +
    + Aiden On The Go + {summary} +
    +
    + {snapshot?.status.error ? ( + + {snapshot.status.error} + + ) : null} +
    + +
    + {settings.isLoading ? ( +

    Checking mobile connections…

    + ) : !snapshot ? ( +

    Connection status is unavailable.

    + ) : snapshot.devices.length === 0 ? ( +

    No devices have been paired with this Mac.

    + ) : ( + <> + + + + {groups.previous.length > 0 ? ( +
    + + Previous + + {groups.previous.length} + + + + {groups.previous.map((device) => ( + + ))} +
    + ) : null} + + )} +
    + +
    + +
    +
    +
    + ); +} From e89b22431dc7feca8c2e56501c03b607e680a7ce Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 116/266] test(renderer): add remote-connection-status.test add renderer/lib/remote-connection-status.test.ts as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/lib/remote-connection-status.test.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 renderer/lib/remote-connection-status.test.ts diff --git a/renderer/lib/remote-connection-status.test.ts b/renderer/lib/remote-connection-status.test.ts new file mode 100644 index 00000000..5f4970e6 --- /dev/null +++ b/renderer/lib/remote-connection-status.test.ts @@ -0,0 +1,54 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import type { AidenRemoteDeviceView } from "../shared/aiden-remote"; +import { + REMOTE_DEVICE_ACTIVE_WINDOW_MS, + groupRemoteDevices, + remoteConnectionSummary, +} from "./remote-connection-status"; + +const NOW = 2_000_000; + +function device( + id: string, + lastSeenAt: number, + revokedAt?: number, +): AidenRemoteDeviceView { + return { + id, + name: id, + type: "iphone", + clientVersion: "1", + capabilities: [], + createdAt: 1, + lastSeenAt, + ...(revokedAt === undefined ? {} : { revokedAt }), + }; +} + +test("groups recent, inactive, and revoked devices without overstating an offline service", () => { + const devices = [ + device("recent", NOW - REMOTE_DEVICE_ACTIVE_WINDOW_MS), + device("pending", 0), + device("inactive", NOW - REMOTE_DEVICE_ACTIVE_WINDOW_MS - 1), + device("revoked", NOW - 10, NOW - 5), + ]; + + const running = groupRemoteDevices(devices, { now: NOW, serviceRunning: true }); + assert.deepEqual(running.active.map(({ id }) => id), ["recent"]); + assert.deepEqual(running.pending.map(({ id }) => id), ["pending"]); + assert.deepEqual(running.inactive.map(({ id }) => id), ["inactive"]); + assert.deepEqual(running.previous.map(({ id }) => id), ["revoked"]); + + const stopped = groupRemoteDevices(devices, { now: NOW, serviceRunning: false }); + assert.deepEqual(stopped.active, []); + assert.deepEqual(stopped.pending.map(({ id }) => id), ["pending"]); + assert.deepEqual(stopped.inactive.map(({ id }) => id), ["recent", "inactive"]); +}); + +test("connection summary favors actionable service state over device counts", () => { + assert.equal(remoteConnectionSummary({ enabled: false, running: false, activeDeviceCount: 2 }), "Off"); + assert.equal(remoteConnectionSummary({ enabled: true, running: false, activeDeviceCount: 2 }), "Needs attention"); + assert.equal(remoteConnectionSummary({ enabled: true, running: true, activeDeviceCount: 0 }), "Ready for a device"); + assert.equal(remoteConnectionSummary({ enabled: true, running: true, activeDeviceCount: 2 }), "2 active"); +}); From 84b912ff775d64c281632e02a27efa85235a3546 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 117/266] feat(renderer): add remote-connection-status add renderer/lib/remote-connection-status.ts as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/lib/remote-connection-status.ts | 63 ++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 renderer/lib/remote-connection-status.ts diff --git a/renderer/lib/remote-connection-status.ts b/renderer/lib/remote-connection-status.ts new file mode 100644 index 00000000..84bf3438 --- /dev/null +++ b/renderer/lib/remote-connection-status.ts @@ -0,0 +1,63 @@ +import type { AidenRemoteDeviceView } from "../shared/aiden-remote"; + +/** + * Device last-seen writes are intentionally throttled to five minutes in the + * main process. A ten-minute window avoids flipping an actively used phone to + * inactive between those bounded writes. + */ +export const REMOTE_DEVICE_ACTIVE_WINDOW_MS = 10 * 60_000; + +export interface RemoteDeviceGroups { + active: AidenRemoteDeviceView[]; + pending: AidenRemoteDeviceView[]; + inactive: AidenRemoteDeviceView[]; + previous: AidenRemoteDeviceView[]; +} + +function newestFirst( + left: AidenRemoteDeviceView, + right: AidenRemoteDeviceView, +): number { + const leftTimestamp = left.revokedAt ?? left.lastSeenAt; + const rightTimestamp = right.revokedAt ?? right.lastSeenAt; + return rightTimestamp - leftTimestamp; +} + +export function groupRemoteDevices( + devices: readonly AidenRemoteDeviceView[], + options: { now?: number; serviceRunning: boolean }, +): RemoteDeviceGroups { + const now = options.now ?? Date.now(); + const groups: RemoteDeviceGroups = { active: [], pending: [], inactive: [], previous: [] }; + + for (const device of devices) { + if (device.revokedAt !== undefined) { + groups.previous.push(device); + continue; + } + if (device.lastSeenAt === 0) { + groups.pending.push(device); + continue; + } + const seenRecently = device.lastSeenAt >= now - REMOTE_DEVICE_ACTIVE_WINDOW_MS; + (options.serviceRunning && seenRecently ? groups.active : groups.inactive).push(device); + } + + groups.active.sort(newestFirst); + groups.pending.sort((left, right) => right.createdAt - left.createdAt); + groups.inactive.sort(newestFirst); + groups.previous.sort(newestFirst); + return groups; +} + +export function remoteConnectionSummary(input: { + enabled: boolean; + running: boolean; + error?: string; + activeDeviceCount: number; +}): string { + if (!input.enabled) return "Off"; + if (input.error || !input.running) return "Needs attention"; + if (input.activeDeviceCount === 0) return "Ready for a device"; + return `${input.activeDeviceCount} active`; +} From 2b3cc1c5bc336da0f511e8bc460c8e4ddd821429 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 118/266] test(renderer): add remote-pairing-lifecycle.test add renderer/lib/remote-pairing-lifecycle.test.ts as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/lib/remote-pairing-lifecycle.test.ts | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 renderer/lib/remote-pairing-lifecycle.test.ts diff --git a/renderer/lib/remote-pairing-lifecycle.test.ts b/renderer/lib/remote-pairing-lifecycle.test.ts new file mode 100644 index 00000000..17d20df3 --- /dev/null +++ b/renderer/lib/remote-pairing-lifecycle.test.ts @@ -0,0 +1,98 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import type { AidenRemoteDeviceView } from "../shared/aiden-remote"; +import { + effectiveRemotePairingLifecycle, + evaluateRemotePairingLifecycle, + remotePairingPresentation, +} from "./remote-pairing-lifecycle"; + +function device(id: string, lastSeenAt: number, revokedAt?: number): AidenRemoteDeviceView { + return { + id, + name: id, + type: "iphone", + clientVersion: "1", + capabilities: [], + createdAt: 10, + lastSeenAt, + ...(revokedAt === undefined ? {} : { revokedAt }), + }; +} + +test("only the exact main-owned pairing session and issued device can complete a dialog", () => { + const unrelatedDevice = device("device_other", 20); + assert.deepEqual(evaluateRemotePairingLifecycle({ + pairingSessionId: "pairing_current", + status: { sessionId: "pairing_other", state: "finishing", deviceId: unrelatedDevice.id }, + devices: [unrelatedDevice], + }), { state: "unrelated" }); + + assert.deepEqual(evaluateRemotePairingLifecycle({ + pairingSessionId: "pairing_current", + status: { sessionId: "pairing_current", state: "finishing", deviceId: "device_expected" }, + devices: [unrelatedDevice], + }), { state: "finishing" }); + + const pending = device("device_expected", 0); + assert.equal(evaluateRemotePairingLifecycle({ + pairingSessionId: "pairing_current", + status: { sessionId: "pairing_current", state: "finishing", deviceId: pending.id }, + devices: [pending, unrelatedDevice], + }).state, "finishing"); + + const connected = device("device_expected", 21); + assert.deepEqual(evaluateRemotePairingLifecycle({ + pairingSessionId: "pairing_current", + status: { sessionId: "pairing_current", state: "finishing", deviceId: connected.id }, + devices: [connected, unrelatedDevice], + }), { state: "connected", device: connected }); +}); + +test("failed, expired, absent, and revoked pairing states never report connected", () => { + const revoked = device("device_expected", 20, 21); + for (const state of ["failed", "expired"] as const) { + assert.deepEqual(evaluateRemotePairingLifecycle({ + pairingSessionId: "pairing_current", + status: { sessionId: "pairing_current", state }, + devices: [revoked], + }), { state }); + } + assert.deepEqual(evaluateRemotePairingLifecycle({ + pairingSessionId: "pairing_current", + devices: [revoked], + }), { state: "unrelated" }); + assert.equal(evaluateRemotePairingLifecycle({ + pairingSessionId: "pairing_current", + status: { sessionId: "pairing_current", state: "finishing", deviceId: revoked.id }, + devices: [revoked], + }).state, "cancelled"); +}); + +test("finishing presentation retains a disabled QR and ignores the elapsed bootstrap clock", () => { + assert.deepEqual(remotePairingPresentation({ state: "finishing" }, 0), { + badge: "Finishing connection", + tone: "blue", + qrDisabled: true, + }); + assert.deepEqual(remotePairingPresentation({ state: "awaiting_scan" }, 61), { + badge: "Expires in 1:01", + tone: "blue", + qrDisabled: false, + }); +}); + +test("an elapsed unconsumed pairing becomes expired without changing finishing work", () => { + assert.deepEqual(effectiveRemotePairingLifecycle({ state: "awaiting_scan" }, 0), { + state: "expired", + }); + assert.deepEqual(effectiveRemotePairingLifecycle({ state: "awaiting_scan" }, -1), { + state: "expired", + }); + assert.deepEqual(effectiveRemotePairingLifecycle({ state: "awaiting_scan" }, 1), { + state: "awaiting_scan", + }); + assert.deepEqual(effectiveRemotePairingLifecycle({ state: "finishing" }, 0), { + state: "finishing", + }); +}); From a96d35293453884e19995928ea71ac7698158454 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 119/266] feat(renderer): add remote-pairing-lifecycle add renderer/lib/remote-pairing-lifecycle.ts as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/lib/remote-pairing-lifecycle.ts | 71 ++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 renderer/lib/remote-pairing-lifecycle.ts diff --git a/renderer/lib/remote-pairing-lifecycle.ts b/renderer/lib/remote-pairing-lifecycle.ts new file mode 100644 index 00000000..3091d5bc --- /dev/null +++ b/renderer/lib/remote-pairing-lifecycle.ts @@ -0,0 +1,71 @@ +import type { + AidenRemoteDeviceView, + AidenRemotePairingStatusView, +} from "../shared/aiden-remote"; + +export type RemotePairingLifecycle = + | { state: "unrelated" } + | { state: "awaiting_scan" } + | { state: "finishing"; device?: AidenRemoteDeviceView } + | { state: "connected"; device: AidenRemoteDeviceView } + | { state: "cancelled"; device: AidenRemoteDeviceView } + | { state: "failed" } + | { state: "expired" }; + +export function evaluateRemotePairingLifecycle(input: { + pairingSessionId: string; + status?: AidenRemotePairingStatusView; + devices: readonly AidenRemoteDeviceView[]; +}): RemotePairingLifecycle { + if (!input.status || input.status.sessionId !== input.pairingSessionId) { + return { state: "unrelated" }; + } + if (input.status.state === "failed" || input.status.state === "expired") { + return { state: input.status.state }; + } + if (input.status.state === "awaiting_scan") { + return { state: "awaiting_scan" }; + } + const device = input.devices.find((candidate) => candidate.id === input.status?.deviceId); + if (device?.revokedAt !== undefined) { + return { state: "cancelled", device }; + } + if (!device || device.lastSeenAt === 0) { + return { state: "finishing", ...(device ? { device } : {}) }; + } + return { state: "connected", device }; +} + +export function effectiveRemotePairingLifecycle( + lifecycle: RemotePairingLifecycle, + secondsRemaining: number, +): RemotePairingLifecycle { + if (lifecycle.state === "awaiting_scan" && secondsRemaining <= 0) { + return { state: "expired" }; + } + return lifecycle; +} + +export function remotePairingPresentation( + lifecycle: RemotePairingLifecycle, + secondsRemaining: number, +): { badge: string; tone: "blue" | "red"; qrDisabled: boolean } { + switch (lifecycle.state) { + case "finishing": + return { badge: "Finishing connection", tone: "blue", qrDisabled: true }; + case "failed": + return { badge: "Pairing failed", tone: "red", qrDisabled: true }; + case "expired": + return { badge: "Expired", tone: "red", qrDisabled: true }; + case "cancelled": + return { badge: "Pairing cancelled", tone: "red", qrDisabled: true }; + default: + return secondsRemaining > 0 + ? { + badge: `Expires in ${Math.floor(secondsRemaining / 60)}:${String(secondsRemaining % 60).padStart(2, "0")}`, + tone: "blue", + qrDisabled: false, + } + : { badge: "Expired", tone: "red", qrDisabled: true }; + } +} From 91bcc5b886448034bb6760c4dd5e5654a8971d68 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:37:30 -0400 Subject: [PATCH 120/266] test(renderer): add onboarding.test add renderer/shared/onboarding.test.ts as part of the reviewed Aiden On The Go and Remote Access update. --- renderer/shared/onboarding.test.ts | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 renderer/shared/onboarding.test.ts diff --git a/renderer/shared/onboarding.test.ts b/renderer/shared/onboarding.test.ts new file mode 100644 index 00000000..75bc1631 --- /dev/null +++ b/renderer/shared/onboarding.test.ts @@ -0,0 +1,41 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { onboardingStepIndex, parseOnboardingState } from "./onboarding.js"; + +test("onboarding state accepts only the current bounded non-secret contract", () => { + assert.deepEqual( + parseOnboardingState({ + version: 2, + outcome: "deferred", + lastSatisfiedStep: "profile", + selectedProviderId: "openai", + }), + { + version: 2, + outcome: "deferred", + lastSatisfiedStep: "profile", + selectedProviderId: "openai", + }, + ); + assert.equal(parseOnboardingState({ version: 1, outcome: "completed" }), null); + assert.equal( + parseOnboardingState({ + version: 2, + outcome: "completed", + lastSatisfiedStep: "tour", + selectedProviderId: "x".repeat(129), + }), + null, + ); +}); + +test("resume position is derived from authoritative readiness", () => { + const base = { + version: 2 as const, + outcome: "incomplete" as const, + lastSatisfiedStep: "none" as const, + }; + assert.equal(onboardingStepIndex({ ...base, profileReady: false, providerReady: false }), 0); + assert.equal(onboardingStepIndex({ ...base, profileReady: true, providerReady: false }), 1); + assert.equal(onboardingStepIndex({ ...base, profileReady: true, providerReady: true }), 2); +}); From 6ad260f05d77b2af791d44ca4384a2e4e02532d9 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:41:35 -0400 Subject: [PATCH 121/266] test(e2e): scope remote access readiness status Assert the new primary Remote Access summary inside the exact This Mac control group. --- tests/e2e/remote-access-lifecycle.spec.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/e2e/remote-access-lifecycle.spec.ts b/tests/e2e/remote-access-lifecycle.spec.ts index 93f17c45..354ba1f3 100644 --- a/tests/e2e/remote-access-lifecycle.spec.ts +++ b/tests/e2e/remote-access-lifecycle.spec.ts @@ -38,7 +38,10 @@ test("Remote Access is opt-in and remains available after the main window closes await expect(remoteHealth()).rejects.toThrow(); await enabled.click(); await expect(enabled).toHaveAttribute("data-state", "checked"); - await expect(page.getByText("Ready", { exact: true })).toBeVisible(); + await expect( + page.getByRole("group").filter({ has: enabled }) + .getByText("Ready for a device", { exact: true }), + ).toBeVisible(); assertHealth(await remoteHealth()); await page.close(); From db366d784ff558f215b4c171434a06397bb9081c Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:41:35 -0400 Subject: [PATCH 122/266] test(e2e): scope disabled remote access status Avoid an ambiguous Off locator now that progressive connection details expose the same state. --- tests/e2e/settings-model-picker.spec.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/e2e/settings-model-picker.spec.ts b/tests/e2e/settings-model-picker.spec.ts index b2f7d658..94aacb6d 100644 --- a/tests/e2e/settings-model-picker.spec.ts +++ b/tests/e2e/settings-model-picker.spec.ts @@ -52,7 +52,11 @@ async function assertRenderedSettingsDestination( "data-state", "unchecked", ); - await expect(page.getByText("Off", { exact: true })).toBeVisible(); + await expect( + page.getByRole("group") + .filter({ has: page.getByRole("switch", { name: "Enable Aiden Remote Access" }) }) + .getByText("Off", { exact: true }), + ).toBeVisible(); return; case "Scheduled tasks": await expect( From b1292528a5f6793f8fd68f6d54a1a35fae10e94b Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Fri, 21 Aug 2026 21:50:59 -0400 Subject: [PATCH 123/266] fix(remote): close rollback sockets before listeners Stop new admissions, destroy Aiden-tracked raw sockets, and then await server closure so endpoint persistence failures cannot hang during a TLS handshake. --- main/services/aiden-remote-service.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/main/services/aiden-remote-service.ts b/main/services/aiden-remote-service.ts index 88db462e..3e4cbeb6 100644 --- a/main/services/aiden-remote-service.ts +++ b/main/services/aiden-remote-service.ts @@ -226,12 +226,15 @@ async function listen( async function closeServer(server: NetServer | null): Promise { if (!server) return; - if ("closeIdleConnections" in server) { - (server as HttpServer).closeIdleConnections?.(); - (server as HttpServer).closeAllConnections?.(); - } await new Promise((resolve) => { server.close(() => resolve()); + // Stop admitting sockets before forcing existing HTTP(S) connections + // closed. Reversing this order leaves a race where a new connection can + // arrive after closeAllConnections() and keep server.close() pending. + if ("closeIdleConnections" in server) { + (server as HttpServer).closeIdleConnections?.(); + (server as HttpServer).closeAllConnections?.(); + } }); } @@ -471,6 +474,8 @@ export class AidenRemoteService { selectedTailscaleServer = tailscaleServer; break; } catch (error) { + this.destroyConnections(this.lanConnections); + this.destroyConnections(this.tailscaleConnections); await Promise.all([ closeServer(lanServer), closeServer(tailscaleServer), @@ -487,6 +492,8 @@ export class AidenRemoteService { try { await this.options.state.commitLanPort(selectedPort); } catch (error) { + this.destroyConnections(this.lanConnections); + this.destroyConnections(this.tailscaleConnections); await Promise.all([ closeServer(selectedLanServer), closeServer(selectedTailscaleServer), From 5b75e4ecee9c62d3c965f30c80ba56faec3db19d Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Sat, 22 Aug 2026 09:21:10 -0400 Subject: [PATCH 124/266] docs: record catalog and device tooling papercuts --- .papercuts/troubleshooting.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.papercuts/troubleshooting.md b/.papercuts/troubleshooting.md index 598cbe8c..8b068c75 100644 --- a/.papercuts/troubleshooting.md +++ b/.papercuts/troubleshooting.md @@ -7,6 +7,8 @@ - Renderer-safe truncation markers must be normalized before their length is budgeted, then the truncated value must be sanitized again; NFKC expands `…` to `...` and can otherwise invalidate an exact-length snapshot. - Packaged builds do not initialize `aiden-dev.log`. For production subagent forensics, correlate the Pi session journal, the V2 run store, aggregate health metrics, and usage records without printing task or report contents. - Pi 0.80.10's public `AgentHarness` cannot express Aiden's global sequential-tool policy or resume an already-journaled user tail. Keep the low-level Agent behind `PiAgentRuntimeHarness` until Pi's durable Harness implements restore/resume, tool execution policy, and automatic between-turn compaction. +- Pi's remote model catalog can evolve independently of its agent/session runtime. A full package bump from 0.80.10 exposed breaking Session contracts, so backport the bounded provider-catalog wrapper and any explicitly reviewed transport adapters behind Aiden's registry; do not couple a model-list refresh to an unplanned journal/session migration. +- A provider-scoped catalog refresh must not call Pi's mutating `getAuth()` merely to test configuration: expired OAuth can rotate credentials after the caller's timeout. Use the non-refreshing auth check, resolve staleness before auth, keep provider-owned catalogs out of launch polling, cache valid empty/negative results, and bound HTTP generation timestamps against wall-clock skew so a far-future validator cannot freeze recovery. - Pi awaits `Agent.subscribe()` listeners. Never register an optional renderer/plugin observer as a critical listener: isolate it through the runtime contribution observer path, or its exception can alter the model lifecycle. - A Pi observer is not passive merely because its return value is ignored: a live `tool_execution_end` event contains mutable result references, and awaiting the observer blocks Pi settlement. Clone observer events and dispatch them outside Pi's serial lifecycle. - Global extension tools cannot be copied into a child Agent after authority is minted. Keep child contributions disabled until an adapter can include their names, effects, approvals, schemas, and budgets in the immutable child ceiling. @@ -61,3 +63,4 @@ - A physical XCTest run can pass every test and still print a `devicectl diagnose` collection error while Xcode archives partial diagnostics. Use the XCTest summary and final `TEST EXECUTE SUCCEEDED` result as the gate; treat diagnostic collection as a separate tooling warning rather than a test failure. - A pairing window can be closed or replaced while durable device issuance is awaiting storage. Rechecking only after issuance is too late because a hidden credential may already exist; pass an exact-session authorization fence into the serialized durable mutation and check it immediately before commit. - Re-pairing into one fixed Keychain scope makes registry rollback non-atomic because the old credential has already been overwritten. Write each device credential to a versioned scope, durably move the installation-registry pointer, and only then best-effort remove the prior scope. +- CoreDevice and `xcodebuild` can identify the same physical iPhone with different UUIDs. Use `devicectl list devices` only to confirm presence and unlock state, then resolve the actual Xcode destination identifier from `xcodebuild -showdestinations`; passing the CoreDevice UUID directly can report that an otherwise connected phone is unavailable. From 2cf4ce7251a782705669a499b7f92ef78bdcafcf Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Sat, 22 Aug 2026 09:21:34 -0400 Subject: [PATCH 125/266] docs: update dynamic catalog plan status --- docs/plans/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/README.md b/docs/plans/README.md index d1d133a8..b489e8ab 100644 --- a/docs/plans/README.md +++ b/docs/plans/README.md @@ -11,7 +11,7 @@ This directory is the source of truth for Aiden's implementation plans. The engi | [Aiden Manual Pairing](aiden-manual-pairing-plan.md) | Implemented | The reviewed 100-bit setup-code path, shared one-use QR window, staged iOS activation, and adversarial coverage ship; hands-on LAN/Tailscale UI and physical-iPad acceptance remain open. | | [Compaction](compaction-plan.md) | Partial | Pi-native checkpoints, lifecycle/crash recovery, and exact audited-upstream compatibility ship; durable memory and provider-native paths remain open. | | [Designer Mode](designer-mode-plan.md) | Planned | Phase 0 validation has not started in the runtime. | -| [Dynamic Model Catalog](dynamic-model-catalog-plan.md) | Partial | Stored Pi catalogs, cache-only hydration, and explicit provider refresh ship; remote overlays for otherwise-static providers remain open. | +| [Dynamic Model Catalog](dynamic-model-catalog-plan.md) | Implemented | Validated pi.dev overlays, offline `0600` cache hydration, scoped setup refresh, four-hour launch refresh, force refresh, Pi metadata fallback, and Mac/iOS projection ship on pinned Pi 0.80.10. | | [Generation Progress Notes](generation-progress-notes-plan.md) | Planned | No implementation yet. | | [Onboarding Authentication and Provider Validation](onboarding-auth-and-provider-validation-plan.md) | Active | Codex onboarding now uses its dedicated auth surface, completion state is main-owned, and hosted/local endpoint readiness validation is under focused verification and fresh review. | | [Performance, Stability, Battery, and Efficiency](performance-stability-efficiency-plan.md) | Planned | Whole-app source audit is complete; implementation starts with instrumentation, durable state, and hard memory bounds. | From ba2b8c9476e47e84fad8ef1d13cb1170e086a0f5 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Sat, 22 Aug 2026 09:21:34 -0400 Subject: [PATCH 126/266] docs: record first-class iOS pairing choices --- docs/plans/aiden-on-the-go-plan.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/plans/aiden-on-the-go-plan.md b/docs/plans/aiden-on-the-go-plan.md index 3ad0e006..3c2a5674 100644 --- a/docs/plans/aiden-on-the-go-plan.md +++ b/docs/plans/aiden-on-the-go-plan.md @@ -736,3 +736,9 @@ Completed and streaming assistant replies expose a Hermex-style long-press Copy 7. **Tailscale ownership:** Aiden provides explicit Connect/Disconnect, previews the exact non-Funnel Serve change, records ownership, and removes only the route it created without altering unrelated routes. Implementation may begin with Phase 0. Later phases remain gated by the acceptance criteria above rather than unresolved product decisions. + +## First-class pairing choices follow-up — 2026-08-22 + +The iOS pairing surface now exposes the same connection choices as Aiden Agent's Add Device flow. The primary list presents Scan QR Code, Nearby Mac + Setup Code for local Wi-Fi/Bonjour, and Private Address + Setup Code for Tailscale; Paste Pairing Payload remains an advanced camera-unavailable fallback. Each route reuses the reviewed five-minute, one-use trust protocol rather than defining a second authentication mechanism. The QR path explicitly states that it already carries the Mac-selected Local Network or Tailscale endpoint, while manual paths request the exact address and setup code shown on the Mac. + +Focused contract tests lock the complete method inventory and Mac/iOS labels, while the shipping-source gate requires both canonical endpoint forms and rejects the former segmented picker. The full Remote Access, release, repository, type, lint, build, generic iPhoneOS, and signed physical-iPhone gates pass. Remaining physical-iPad and hands-on cross-network acceptance stay open under the existing Phase 12 gates. From 507c329191eab7219d2bf4d98af16c471645dafa Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Sat, 22 Aug 2026 09:21:34 -0400 Subject: [PATCH 127/266] docs: finalize dynamic model catalog plan --- docs/plans/dynamic-model-catalog-plan.md | 46 ++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/docs/plans/dynamic-model-catalog-plan.md b/docs/plans/dynamic-model-catalog-plan.md index 1646e359..94ef9aee 100644 --- a/docs/plans/dynamic-model-catalog-plan.md +++ b/docs/plans/dynamic-model-catalog-plan.md @@ -1,8 +1,8 @@ # Dynamic Model Catalog Plan -Status: Partial — Pi's device-local models store, cache-only hydration, and explicit -provider refresh are implemented; the remote overlay for otherwise-static hosted -providers is not. +Status: Implemented — trusted pi.dev overlays, durable offline hydration, scoped +setup refresh, nonblocking four-hour launch refresh, explicit Settings/command +refresh, Pi-native metadata, and Mac/iOS projection ship on the pinned Pi runtime. Date: 2026-07-24 Source audited: local `/Users/sambitbiswas/projects/pi` (`packages/coding-agent` remote catalog + `packages/ai` Models/ModelsStore) Related: `docs/plans/pi-provider-integration-plan.md` (Phases 1–2 already call for `ModelsStore` + refresh; this plan owns the **remote overlay** that plan deferred) @@ -11,6 +11,40 @@ Related: `docs/plans/pi-provider-integration-plan.md` (Phases 1–2 already call Aiden users should get newly published hosted models (for example Opus 5) **without waiting for an Aiden app release**, by refreshing a device-local catalog overlay — the same product idea as Pi’s `pi update --models` / `/model` refresh. +## Implementation record (2026-08-22) + +- Option A is accepted: Aiden reads full executable model records only from the fixed + `https://pi.dev` provider endpoint. Requests carry a static versioned Aiden/Pi + User-Agent and never provider credentials, chat content, install IDs, or models.dev data. +- `@earendil-works/pi-ai` and agent-core remain deliberately pinned to `0.80.10`. + A full Pi package bump also changes Session/runtime contracts, so Aiden backports + only the reviewed remote-catalog behavior and OpenCode Go Responses transport. +- The main process restores normalized `0600` device-local catalogs offline before + first paint. A soft refresh runs after first paint when the four-hour TTL is stale; + Settings and the command palette provide force refresh. The stale launch pass includes + only stale Aiden pi.dev overlays, excluding Radius, Concentrate, and every other + provider-owned discovery path. Partial provider failures still publish + successes, retain each failed provider's last-known-good catalog, and surface a + nonfatal retry warning after credentials have already been saved. +- Remote routing fields are validated against each shipped provider's HTTPS origins + and executable API set. Responses are streamed through a 5 MiB limit, catalog/store + cardinality is bounded, validators are safe, downgrade and far-future timestamps are + rejected, and an already-poisoned future timestamp can be replaced by a current valid + generation, while a system-clock rollback retains the accepted catalog and downgrade + high-water mark. Scoped refresh uses Pi's non-refreshing auth check, so cancellation cannot + leave an OAuth rotation running after timeout. Empty 200 and negative 404/501 results + honor the same four-hour TTL; timeout/malformed responses never replace a good cache. + Renderer refresh results contain only bounded provider IDs and app-owned recovery copy; + raw upstream/auth error bodies remain main-process-private. +- Provider-owned metadata is the fallback when a new model is absent from the bundled + models.dev snapshot. OpenCode Go's `ox-alpha-free` therefore appears as + “Ox Alpha Free (Unlimited)” with low/high/max thinking on Mac and paired iOS. + The remote-v1 projection also carries the effective saved/default level and whether + “off” hides required thinking so both clients make the same initial selection. +- `npm run models:refresh` and `npm run dist` remain the only models.dev network paths. + The refreshed bundled capability snapshot is still offline display/ranking data, + not live selectable inventory. + When this ships: 1. Built-in Pi providers still ship with a static baseline catalog from `@earendil-works/pi-ai`. @@ -31,10 +65,10 @@ When this ships: | Layer | Today | Gap | | --- | --- | --- | -| Selectable hosted models | Pi `builtinModels()` is authoritative for built-ins and provider-owned dynamic catalogs | Providers without their own dynamic fetch still require a Pi pin bump **and** an Aiden release | +| Selectable hosted models | Pi `Models` plus Aiden's trusted pi.dev overlay are authoritative for built-ins | Shipped baseline remains the offline fallback; no release is required for compatible new remote records | | Capability metadata | Bundled `resources/model-capabilities.json` via `npm run models:refresh`; AA device cache on Connect & fetch | Offline by design; not the Opus-5 availability problem | -| Pi registry | `ProviderRegistry` + `builtinModels({ credentials, modelsStore })`; cache-only hydration and explicit force refresh are wired | No Aiden-owned remote overlay for otherwise-static providers | -| Device-local store | `pi-models-store.ts` persists Pi `ModelsStoreEntry` snapshots under Electron `userData` | The store can retain provider-owned dynamic results, but it cannot create a remote path for a static provider | +| Pi registry | `ProviderRegistry` wraps static providers with a validated overlay, compatible transports, offline hydration, scoped setup refresh, TTL launch refresh, and explicit force refresh | A future full Pi package upgrade remains a separate Session/runtime migration | +| Device-local store | `pi-models-store.ts` persists bounded normalized `ModelsStoreEntry` snapshots under Electron `userData` with mode `0600` | None for the implemented overlay scope | Pi coding-agent reference (do not import the package; mirror the pattern): From 4570d8cf841aa960461a7fb24ddb965f4985d79c Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Sat, 22 Aug 2026 09:21:35 -0400 Subject: [PATCH 128/266] docs: record physical pairing validation --- docs/testing/aiden-on-the-go/phase-12.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/testing/aiden-on-the-go/phase-12.md b/docs/testing/aiden-on-the-go/phase-12.md index 5b3930a9..db6c95c3 100644 --- a/docs/testing/aiden-on-the-go/phase-12.md +++ b/docs/testing/aiden-on-the-go/phase-12.md @@ -142,3 +142,9 @@ This audit found no additional locally actionable missing product surface. The r - Mobile-specific privacy-policy publication, visible support contact, owner publication of the drafted age/privacy answers, final physical-device screenshots, review phone/environment, remaining metadata approval, external TestFlight/Beta App Review, and public-release decisions. Phase 12 and the overall plan must remain active until those gates are completed or deliberately re-scoped by the owner. + +## First-class iOS pairing choices — 2026-08-22 + +The iOS pairing landing page now mirrors Aiden Agent's complete Add Device choice set: recommended QR for either Mac-selected endpoint, Nearby Mac + Setup Code for local Wi-Fi, Private Address + Setup Code for Tailscale, and an advanced full-payload paste fallback. The native contract test verifies that every method remains represented with the correct Local Network/Tailscale identity; the registered source gate also requires the canonical `.local:49220/api/aiden/v1` and `.tailnet.ts.net/api/aiden/v1` examples and rejects the prior segmented method picker. + +Validation passed with `npm run test:aiden-remote` (216 production Remote Access tests with one explicit skip, plus seven transport proofs), `npm run test:ios-release` (20 Ruby tests/42 assertions and 24 Node tests), `npm run test`, `npm run type-check`, `npm run lint`, `npm run build`, and the generic unsigned iPhoneOS test build. The complete Apple Development-signed XCTest target also passed on the connected physical iPhone 13 Pro, including `testPairingMethodsMirrorEveryMacConnectionChoice`; only explicit live-environment tests without an injected pairing payload skipped. No simulator or iPhone 16 Pro Max was used. From f7bc713d2cf495bb290cc6903db61501200ed121 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Sat, 22 Aug 2026 09:21:35 -0400 Subject: [PATCH 129/266] feat(models): coordinate Pi catalog refreshes --- main/services/pi-catalog-refresh.ts | 138 ++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 main/services/pi-catalog-refresh.ts diff --git a/main/services/pi-catalog-refresh.ts b/main/services/pi-catalog-refresh.ts new file mode 100644 index 00000000..e9173aa2 --- /dev/null +++ b/main/services/pi-catalog-refresh.ts @@ -0,0 +1,138 @@ +import type { + CredentialStore, + Models, + ProviderModelsStore, + Provider, +} from "@earendil-works/pi-ai"; +import { + isPiRemoteCatalogCacheFresh, + isPiRemoteCatalogProvider, +} from "./pi-remote-catalog.js"; + +export interface RefreshPiCatalogsOptions { + models: Models; + credentials: CredentialStore; + providerModelsStore: (providerId: string) => ProviderModelsStore; + providerIds?: readonly string[]; + force?: boolean; + signal?: AbortSignal; +} + +export interface RefreshPiCatalogsResult { + aborted: boolean; + errors: ReadonlyMap; +} + +export interface ProjectedPiCatalogRefreshError { + providerId: string; + message: string; +} + +export function projectPiCatalogRefreshErrors( + errors: ReadonlyMap, +): ProjectedPiCatalogRefreshError[] { + return [...errors.keys()].slice(0, 32).map((providerId) => ({ + providerId: providerId.replace(/[^a-zA-Z0-9._:-]/gu, "").slice(0, 128) || "provider", + // Never project upstream bodies or nested authentication failures across IPC. + message: "Catalog refresh failed. Cached models were kept.", + })); +} + +/** Return only stale Aiden pi.dev overlays; provider-owned catalogs keep their own refresh policy. */ +export async function staleCatalogProviderIds( + providers: readonly Provider[], + providerModelsStore: (providerId: string) => ProviderModelsStore, +): Promise { + const results = await Promise.all(providers.map(async (provider) => { + if (!provider.refreshModels || !isPiRemoteCatalogProvider(provider)) return undefined; + try { + const entry = await providerModelsStore(provider.id).read(); + return isPiRemoteCatalogCacheFresh(provider, entry) ? undefined : provider.id; + } catch { + return provider.id; + } + })); + return results.filter((providerId): providerId is string => providerId !== undefined); +} + +function abortError(): Error { + const error = new Error("Model catalog refresh was cancelled."); + error.name = "AbortError"; + return error; +} + +async function raceWithAbort(promise: Promise, signal?: AbortSignal): Promise { + if (!signal) return promise; + if (signal.aborted) throw abortError(); + return new Promise((resolve, reject) => { + const abort = () => reject(abortError()); + signal.addEventListener("abort", abort, { once: true }); + promise.then( + (value) => { + signal.removeEventListener("abort", abort); + resolve(value); + }, + (error: unknown) => { + signal.removeEventListener("abort", abort); + reject(error); + }, + ); + }); +} + +/** + * Backports provider-scoped catalog refresh to Aiden's pinned Pi runtime. + * Pi 0.80 refreshes every dynamic provider; setup needs isolation so one + * unrelated provider cannot make a newly configured provider appear broken. + */ +export async function refreshPiCatalogs({ + models, + credentials, + providerModelsStore, + providerIds, + force = true, + signal, +}: RefreshPiCatalogsOptions): Promise { + if (providerIds === undefined) { + const task = models.refresh({ force, signal }); + try { + return await raceWithAbort(task, signal); + } catch (error) { + if (signal?.aborted) return { aborted: true, errors: new Map() }; + throw error; + } + } + + const errors = new Map(); + await Promise.all( + [...new Set(providerIds)].map(async (providerId) => { + const provider = models.getProvider(providerId); + if (!provider?.refreshModels) return; + try { + const store = providerModelsStore(providerId); + if (!force && isPiRemoteCatalogProvider(provider)) { + const entry = await raceWithAbort(store.read(), signal); + if (isPiRemoteCatalogCacheFresh(provider, entry)) return; + } + // checkAuth is explicitly non-refreshing for OAuth. Catalog refresh + // must never outlive its timeout while secretly rotating credentials. + const auth = await raceWithAbort(models.checkAuth(providerId), signal); + if (!auth || signal?.aborted) return; + const credential = await raceWithAbort(credentials.read(providerId), signal); + await raceWithAbort(provider.refreshModels({ + credential, + store, + allowNetwork: true, + force, + signal, + }), signal); + } catch (error) { + errors.set( + providerId, + error instanceof Error ? error : new Error("Unknown model catalog refresh error."), + ); + } + }), + ); + return { aborted: signal?.aborted ?? false, errors }; +} From d4efea5a2bfa71798a4b1f303ac67ead9479f540 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Sat, 22 Aug 2026 09:21:35 -0400 Subject: [PATCH 130/266] feat(models): resolve provider model metadata --- main/services/pi-model-metadata.ts | 60 ++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 main/services/pi-model-metadata.ts diff --git a/main/services/pi-model-metadata.ts b/main/services/pi-model-metadata.ts new file mode 100644 index 00000000..d560fa7b --- /dev/null +++ b/main/services/pi-model-metadata.ts @@ -0,0 +1,60 @@ +import type { Api, Model } from "@earendil-works/pi-ai"; +import { + anthropicThinkingCanDisable, + anthropicThinkingLevelsForModel, +} from "../../renderer/shared/anthropic-thinking.js"; +import { + googleThinkingCanDisable, + googleThinkingLevelsForModel, +} from "../../renderer/shared/google-thinking.js"; +import { isGenerationThinkingLevel } from "../../renderer/shared/generation-thinking.js"; +import type { ProviderModelMetadata } from "./types.js"; + +const PI_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const; + +/** + * Pi treats an absent ordinary-level mapping as native support, an explicit + * null as unsupported, and requires an explicit mapping for xhigh/max. + * Keep Aiden's UI and runtime on that same contract while the Pi package is pinned. + */ +export function piThinkingLevelsForModel( + model: Pick, "reasoning" | "thinkingLevelMap">, +): ProviderModelMetadata["thinkingLevels"] { + if (!model.reasoning) return ["off"]; + return PI_THINKING_LEVELS.filter((level) => { + const mapped = model.thinkingLevelMap?.[level]; + if (mapped === null) return false; + if (level === "xhigh" || level === "max") return mapped !== undefined; + return true; + }).filter(isGenerationThinkingLevel); +} + +/** Project Pi's executable model contract into renderer and remote-client metadata. */ +export function piModelMetadataFor(providerId: string, model: Model): ProviderModelMetadata { + const thinking = + providerId === "anthropic" + ? { + thinkingLevels: anthropicThinkingLevelsForModel(model), + thinkingCanDisable: anthropicThinkingCanDisable(model), + } + : providerId === "google" + ? { + thinkingLevels: googleThinkingLevelsForModel(model), + thinkingCanDisable: googleThinkingCanDisable(model), + } + : model.reasoning + ? { + thinkingLevels: piThinkingLevelsForModel(model), + thinkingCanDisable: model.thinkingLevelMap?.off !== null, + } + : {}; + return { + source: "provider", + name: model.name, + type: "llm", + vision: model.input.includes("image"), + reasoning: model.reasoning, + ...thinking, + contextLength: model.contextWindow, + }; +} From 20b15d633a562baad677fce39aa24565d67adcb3 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Sat, 22 Aug 2026 09:21:35 -0400 Subject: [PATCH 131/266] feat(models): define Pi provider compatibility --- main/services/pi-provider-compatibility.ts | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 main/services/pi-provider-compatibility.ts diff --git a/main/services/pi-provider-compatibility.ts b/main/services/pi-provider-compatibility.ts new file mode 100644 index 00000000..7047c539 --- /dev/null +++ b/main/services/pi-provider-compatibility.ts @@ -0,0 +1,30 @@ +import type { Api, Model, Provider, ProviderStreams } from "@earendil-works/pi-ai"; +import { openAIResponsesApi } from "@earendil-works/pi-ai/api/openai-responses.lazy"; + +/** Add reviewed transports to an older pinned Pi provider without rebuilding its auth contract. */ +export function withProviderStreamOverrides( + provider: Provider, + overrides: Partial>, +): Provider { + const streamsFor = (model: Model) => overrides[model.api]; + return { + ...provider, + stream: (model, context, options) => + streamsFor(model)?.stream(model, context, options) ?? provider.stream(model, context, options), + streamSimple: (model, context, options) => + streamsFor(model)?.streamSimple(model, context, options) ?? + provider.streamSimple(model, context, options), + }; +} + +/** Pi 0.80 predates OpenCode Go's Responses-backed models published by pi.dev. */ +export function withAidenPiCompatibility(provider: Provider): Provider { + if (provider.id !== "opencode-go") return provider; + return withProviderStreamOverrides(provider, { + "openai-responses": openAIResponsesApi(), + }); +} + +export function additionalAidenPiApis(providerId: string): readonly string[] { + return providerId === "opencode-go" ? ["openai-responses"] : []; +} From ddfa90e4d6c526e083d55c94dbe1363084c5db50 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Sat, 22 Aug 2026 09:21:35 -0400 Subject: [PATCH 132/266] feat(models): fetch bounded Pi remote catalogs --- main/services/pi-remote-catalog.ts | 402 +++++++++++++++++++++++++++++ 1 file changed, 402 insertions(+) create mode 100644 main/services/pi-remote-catalog.ts diff --git a/main/services/pi-remote-catalog.ts b/main/services/pi-remote-catalog.ts new file mode 100644 index 00000000..84ed8c11 --- /dev/null +++ b/main/services/pi-remote-catalog.ts @@ -0,0 +1,402 @@ +import type { Api, Model, ModelsStoreEntry, Provider } from "@earendil-works/pi-ai"; + +const DEFAULT_CATALOG_BASE_URL = "https://pi.dev"; +const MAX_CATALOG_BYTES = 5 * 1024 * 1024; +const MAX_CATALOG_MODELS = 10_000; +const MAX_MODEL_ID_LENGTH = 256; +const MAX_MODEL_NAME_LENGTH = 256; +const MAX_TOKEN_LIMIT = 10_000_000; +const MAX_COST_RATE = 1_000_000; +const UNKNOWN_COST_SENTINEL = -1_000_000; +const MAX_CATALOG_CLOCK_SKEW_MS = 5 * 60 * 1000; + +/** Keep synchronized with the deliberately pinned @earendil-works/pi-ai dependency. */ +export const AIDEN_PI_CATALOG_VERSION = "0.80.10"; +export const AIDEN_PI_CATALOG_GENERATED_AT = Date.parse("2026-07-16T22:04:50.937Z"); +export const AIDEN_PI_CATALOG_USER_AGENT = `Aiden-Agent pi-ai/${AIDEN_PI_CATALOG_VERSION}`; +export const PI_REMOTE_CATALOG_REFRESH_INTERVAL_MS = 4 * 60 * 60 * 1000; + +export interface PiRemoteCatalogOptions { + catalogBaseUrl?: string; + fetchImpl?: typeof fetch; + now?: () => number; + supportedApis?: readonly string[]; + localGeneratedAt?: number; +} + +interface PersistedRemoteCatalog extends ModelsStoreEntry { + etag?: string; + lastModified?: number; +} + +const remoteCatalogFreshness = new WeakMap boolean>(); + +export function isPiRemoteCatalogProvider(provider: Provider): boolean { + return remoteCatalogFreshness.has(provider); +} + +export function isPiRemoteCatalogCacheFresh( + provider: Provider, + entry: ModelsStoreEntry | undefined, +): boolean { + return remoteCatalogFreshness.get(provider)?.(entry) ?? false; +} + +export interface CatalogPolicy { + allowedApis?: ReadonlySet; + allowedOrigins?: ReadonlySet; + allowEmptyBaseUrl?: boolean; +} + +function record(value: unknown): Record | undefined { + return value !== null && typeof value === "object" && !Array.isArray(value) + ? (value as Record) + : undefined; +} + +function catalogEntries(value: unknown): unknown[] | undefined { + if (Array.isArray(value)) return value; + const object = record(value); + if (!object) return undefined; + if ("models" in object) return Array.isArray(object.models) ? object.models : undefined; + return Object.values(object); +} + +function safePositiveInteger(value: unknown): value is number { + return Number.isSafeInteger(value) && (value as number) > 0 && (value as number) <= MAX_TOKEN_LIMIT; +} + +function safeCost(value: unknown): value is number { + return typeof value === "number" && Number.isFinite(value) && + (value === UNKNOWN_COST_SENTINEL || (value >= 0 && value <= MAX_COST_RATE)); +} + +function safeCostRecord(value: unknown): value is Model["cost"] { + const cost = record(value); + if (!cost || !safeCost(cost.input) || !safeCost(cost.output) || !safeCost(cost.cacheRead) || !safeCost(cost.cacheWrite)) { + return false; + } + if (cost.tiers === undefined) return true; + return Array.isArray(cost.tiers) && cost.tiers.length <= 16 && cost.tiers.every((tier) => { + const item = record(tier); + return item !== undefined && safePositiveInteger(item.inputTokensAbove) && safeCost(item.input) && safeCost(item.output) && safeCost(item.cacheRead) && safeCost(item.cacheWrite); + }); +} + +function safeThinkingLevelMap(value: unknown): boolean { + if (value === undefined) return true; + const map = record(value); + if (!map) return false; + const levels = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]); + return Object.entries(map).every(([key, entry]) => + levels.has(key) && (entry === null || (typeof entry === "string" && entry.length > 0 && entry.length <= 64)), + ); +} + +function safeCompat(value: unknown): boolean { + if (value === undefined) return true; + let nodes = 0; + const visit = (entry: unknown, depth: number): boolean => { + nodes += 1; + if (nodes > 512 || depth > 8) return false; + if (entry === null || typeof entry === "boolean") return true; + if (typeof entry === "number") return Number.isFinite(entry); + if (typeof entry === "string") return entry.length <= 1_024 && !/[\r\n]/u.test(entry); + if (Array.isArray(entry)) return entry.length <= 64 && entry.every((item) => visit(item, depth + 1)); + const object = record(entry); + if (!object || Object.keys(object).length > 64) return false; + return Object.entries(object).every(([key, item]) => + key.length > 0 && key.length <= 128 && key !== "__proto__" && key !== "prototype" && + key !== "constructor" && visit(item, depth + 1), + ); + }; + return visit(value, 0); +} + +function safeBaseUrl(value: unknown, policy: CatalogPolicy): value is string { + if (value === "" && policy.allowEmptyBaseUrl) return true; + if (typeof value !== "string" || value.length > 2_048) return false; + try { + const url = new URL(value); + return url.protocol === "https:" && url.username === "" && url.password === "" && + (policy.allowedOrigins === undefined || policy.allowedOrigins.has(url.origin)); + } catch { + return false; + } +} + +function safeHeaders(value: unknown): value is Record { + if (value === undefined) return true; + const headers = record(value); + if (!headers || Object.keys(headers).length > 32) return false; + const forbidden = new Set([ + "authorization", + "cookie", + "proxy-authorization", + "x-api-key", + "x-auth-token", + ]); + return Object.entries(headers).every(([name, entry]) => + /^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$/u.test(name) && + !forbidden.has(name.toLowerCase()) && + typeof entry === "string" && entry.length <= 1_024 && !/[\r\n]/u.test(entry), + ); +} + +function normalizeModel(providerId: string, value: unknown, policy: CatalogPolicy): Model | undefined { + const model = record(value); + if (!model) return undefined; + const id = typeof model.id === "string" ? model.id.trim() : ""; + const name = typeof model.name === "string" ? model.name.trim() : ""; + const api = typeof model.api === "string" ? model.api : ""; + const input = model.input; + const compat = model.compat; + if (!id || id.length > MAX_MODEL_ID_LENGTH || !name || name.length > MAX_MODEL_NAME_LENGTH || !api || + (policy.allowedApis !== undefined && !policy.allowedApis.has(api)) || + !safeBaseUrl(model.baseUrl, policy) || typeof model.reasoning !== "boolean" || + !Array.isArray(input) || input.length === 0 || input.length > 2 || new Set(input).size !== input.length || + !input.every((entry) => entry === "text" || entry === "image") || !safeCostRecord(model.cost) || + !safePositiveInteger(model.contextWindow) || !safePositiveInteger(model.maxTokens) || + !safeThinkingLevelMap(model.thinkingLevelMap) || !safeCompat(compat) || + !safeHeaders(model.headers)) { + return undefined; + } + return { + id, + name, + api, + provider: providerId, + baseUrl: model.baseUrl, + reasoning: model.reasoning, + ...(model.thinkingLevelMap === undefined ? {} : { thinkingLevelMap: structuredClone(model.thinkingLevelMap) }), + input: [...input], + cost: structuredClone(model.cost), + contextWindow: model.contextWindow, + maxTokens: model.maxTokens, + ...(model.headers === undefined ? {} : { headers: structuredClone(model.headers) }), + ...(compat === undefined ? {} : { compat: structuredClone(compat) }), + } as Model; +} + +export function parsePiRemoteCatalog(providerId: string, value: unknown, policy: CatalogPolicy = {}): Model[] { + const entries = catalogEntries(value); + if (!entries || entries.length > MAX_CATALOG_MODELS) { + throw new Error(`Invalid model catalog for provider "${providerId}".`); + } + const models: Model[] = []; + const ids = new Set(); + for (const entry of entries) { + const model = normalizeModel(providerId, entry, policy); + if (!model || ids.has(model.id)) { + throw new Error(`Invalid model catalog entry for provider "${providerId}".`); + } + ids.add(model.id); + models.push(model); + } + return models; +} + +function mergeModels(baseline: readonly Model[], dynamic: readonly Model[]): Model[] { + const merged = [...baseline]; + for (const model of dynamic) { + const index = merged.findIndex((entry) => entry.id === model.id); + if (index >= 0) merged[index] = model; + else merged.push(model); + } + return merged; +} + +function versionParts(value: string): number[] | undefined { + const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/u.exec(value.trim()); + return match ? match.slice(1).map(Number) : undefined; +} + +export function catalogVersionSupported(minimum: string | null): boolean { + if (!minimum) return true; + const required = versionParts(minimum); + const installed = versionParts(AIDEN_PI_CATALOG_VERSION); + if (!required || !installed) return false; + for (let index = 0; index < 3; index += 1) { + if (installed[index]! > required[index]!) return true; + if (installed[index]! < required[index]!) return false; + } + return true; +} + +async function readBoundedJson(response: Response): Promise { + const declared = Number(response.headers.get("content-length")); + if (Number.isFinite(declared) && declared > MAX_CATALOG_BYTES) { + await response.body?.cancel().catch(() => undefined); + throw new Error("Model catalog response is too large."); + } + if (!response.body) throw new Error("Model catalog response is empty."); + const reader = response.body.getReader(); + const chunks: Uint8Array[] = []; + let total = 0; + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + total += value.byteLength; + if (total > MAX_CATALOG_BYTES) { + await reader.cancel().catch(() => undefined); + throw new Error("Model catalog response is too large."); + } + chunks.push(value); + } + } finally { + reader.releaseLock(); + } + const bytes = new Uint8Array(total); + let offset = 0; + for (const chunk of chunks) { + bytes.set(chunk, offset); + offset += chunk.byteLength; + } + try { + return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)) as unknown; + } catch { + throw new Error("Model catalog response is invalid JSON."); + } +} + +/** Adds a durable, credential-free pi.dev overlay to a static built-in provider. */ +export function withPiRemoteCatalog(provider: Provider, options: PiRemoteCatalogOptions = {}): Provider { + const baseline = provider.getModels(); + const allowedApis = new Set([ + ...baseline.map((model) => model.api), + ...(options.supportedApis ?? []), + ]); + const allowedOrigins = new Set( + [provider.baseUrl, ...baseline.map((model) => model.baseUrl)] + .filter((value): value is string => typeof value === "string") + .flatMap((value) => { + try { return [new URL(value).origin]; } catch { return []; } + }), + ); + const policy = { + allowedApis, + allowedOrigins, + allowEmptyBaseUrl: provider.baseUrl === "" || baseline.some((model) => model.baseUrl === ""), + }; + const catalogBaseUrl = options.catalogBaseUrl ?? DEFAULT_CATALOG_BASE_URL; + const fetchImpl = options.fetchImpl ?? fetch; + const now = options.now ?? Date.now; + const localGeneratedAt = options.localGeneratedAt ?? AIDEN_PI_CATALOG_GENERATED_AT; + let dynamicModels: readonly Model[] = []; + let activeNetworkRefresh: Promise | null = null; + + const restoredCatalog = ( + entry: ModelsStoreEntry | undefined, + ): { models: readonly Model[]; lastModified: number } | undefined => { + if (!entry) return undefined; + const persisted = entry as PersistedRemoteCatalog; + const acceptedAt = persisted.checkedAt; + if (persisted.lastModified === undefined || persisted.lastModified <= localGeneratedAt || + !Number.isSafeInteger(acceptedAt) || + persisted.lastModified > acceptedAt! + MAX_CATALOG_CLOCK_SKEW_MS) return undefined; + try { + return { + models: parsePiRemoteCatalog(provider.id, entry.models, policy), + lastModified: persisted.lastModified, + }; + } catch { + return undefined; + } + }; + const cacheIsFresh = (entry: ModelsStoreEntry | undefined): boolean => { + if (!entry || !Number.isSafeInteger(entry.checkedAt)) return false; + const checkedNow = now(); + if (entry.checkedAt! > checkedNow + MAX_CATALOG_CLOCK_SKEW_MS || + checkedNow - entry.checkedAt! >= PI_REMOTE_CATALOG_REFRESH_INTERVAL_MS) return false; + const persisted = entry as PersistedRemoteCatalog; + if (restoredCatalog(entry)) return true; + // A 404/501 is a valid negative cache entry. It is deliberately distinct + // from a malformed catalog and from a poisoned future generation. + return entry.models.length === 0 && persisted.lastModified === undefined && persisted.etag === undefined; + }; + + const wrapped: Provider = { + ...provider, + getModels: () => mergeModels(baseline, dynamicModels), + refreshModels: async (context) => { + const stored = (await context.store.read()) as PersistedRemoteCatalog | undefined; + const restored = restoredCatalog(stored); + dynamicModels = restored?.models ?? []; + if (!context.allowNetwork || context.signal?.aborted) return; + if (!context.force && cacheIsFresh(stored)) return; + if (activeNetworkRefresh) return activeNetworkRefresh; + + const task = (async () => { + const validator = restored ? stored?.etag : undefined; + const url = new URL(`/api/models/providers/${encodeURIComponent(provider.id)}`, catalogBaseUrl); + const response = await fetchImpl(url, { + headers: { + accept: "application/json", + "user-agent": AIDEN_PI_CATALOG_USER_AGENT, + ...(validator ? { "if-none-match": validator } : {}), + }, + redirect: "error", + signal: context.signal, + }); + if (context.signal?.aborted) return; + const checkedAt = now(); + if (response.status === 304 && stored && restored) { + await context.store.write({ + ...stored, + // A local clock rollback must not invalidate the generation's + // original acceptance boundary on the next process launch. + checkedAt: Math.max(stored.checkedAt ?? 0, checkedAt), + }); + return; + } + if (response.status === 404 || response.status === 501) { + await response.body?.cancel().catch(() => undefined); + const retained: PersistedRemoteCatalog = { + ...(restored && stored ? stored : { models: [] }), + checkedAt: restored && stored + ? Math.max(stored.checkedAt ?? 0, checkedAt) + : checkedAt, + etag: undefined, + }; + await context.store.write(retained); + return; + } + if (!response.ok) { + await response.body?.cancel().catch(() => undefined); + throw new Error(`Model catalog request failed for ${provider.id}: ${response.status}`); + } + const minimum = response.headers.get("x-pi-model-catalog-minimum-version"); + if (!catalogVersionSupported(minimum)) { + await response.body?.cancel().catch(() => undefined); + throw new Error(`${provider.id} model catalog requires Pi ${minimum ?? "a newer version"}.`); + } + const refreshed = parsePiRemoteCatalog(provider.id, await readBoundedJson(response), policy); + if (context.signal?.aborted) return; + const lastModified = Date.parse(response.headers.get("last-modified") ?? ""); + if (Number.isNaN(lastModified) || lastModified <= localGeneratedAt) { + throw new Error(`${provider.id} model catalog is missing a newer generation timestamp.`); + } + const remoteModified = lastModified; + if (remoteModified > checkedAt + MAX_CATALOG_CLOCK_SKEW_MS) { + throw new Error(`${provider.id} model catalog has an invalid future generation timestamp.`); + } + if (restored && remoteModified < restored.lastModified) { + throw new Error(`${provider.id} model catalog is older than the cached generation.`); + } + const entry: PersistedRemoteCatalog = { + models: refreshed, + checkedAt, + lastModified: remoteModified, + etag: response.headers.get("etag") ?? undefined, + }; + await context.store.write(entry); + if (!context.signal?.aborted) dynamicModels = refreshed; + })(); + activeNetworkRefresh = task; + try { await task; } finally { if (activeNetworkRefresh === task) activeNetworkRefresh = null; } + }, + }; + remoteCatalogFreshness.set(wrapped, cacheIsFresh); + return wrapped; +} From 14f43978adef1ecb7e1d5eb3be0bbfe746cbad59 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Sat, 22 Aug 2026 09:21:35 -0400 Subject: [PATCH 133/266] test(models): cover Pi remote catalog hardening --- main/services/pi-remote-catalog.test.ts | 580 ++++++++++++++++++++++++ 1 file changed, 580 insertions(+) create mode 100644 main/services/pi-remote-catalog.test.ts diff --git a/main/services/pi-remote-catalog.test.ts b/main/services/pi-remote-catalog.test.ts new file mode 100644 index 00000000..70d75945 --- /dev/null +++ b/main/services/pi-remote-catalog.test.ts @@ -0,0 +1,580 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +import { + InMemoryCredentialStore, + type Api, + type Credential, + type CredentialStore, + type Model, + type ModelsStoreEntry, + type ProviderModelsStore, +} from "@earendil-works/pi-ai"; +import { builtinModels, builtinProviders } from "@earendil-works/pi-ai/providers/all"; + +import { + projectPiCatalogRefreshErrors, + refreshPiCatalogs, + staleCatalogProviderIds, +} from "./pi-catalog-refresh.js"; +import { + AIDEN_PI_CATALOG_USER_AGENT, + parsePiRemoteCatalog, + PI_REMOTE_CATALOG_REFRESH_INTERVAL_MS, + withPiRemoteCatalog, +} from "./pi-remote-catalog.js"; +import { normalizePiModelsDocument } from "./pi-models-store.js"; +import { concentrateProvider } from "./concentrate-provider.js"; +import { + additionalAidenPiApis, + withAidenPiCompatibility, + withProviderStreamOverrides, +} from "./pi-provider-compatibility.js"; +import { piModelMetadataFor } from "./pi-model-metadata.js"; + +function opencodeGoProvider() { + const provider = builtinProviders().find((entry) => entry.id === "opencode-go"); + assert.ok(provider, "pinned Pi must expose OpenCode Go"); + return provider; +} + +function oxAlphaModel(): Model { + const template = opencodeGoProvider().getModels()[0]; + assert.ok(template); + return { + ...template, + id: "ox-alpha-free", + name: "Ox Alpha Free (Unlimited)", + provider: "opencode-go", + reasoning: true, + input: ["text", "image"], + thinkingLevelMap: { + off: null, + minimal: null, + low: "low", + medium: null, + high: "high", + xhigh: null, + max: "max", + }, + contextWindow: 1_000_000, + maxTokens: 131_072, + }; +} + +function memoryProviderStore(initial?: ModelsStoreEntry): ProviderModelsStore & { + snapshot(): ModelsStoreEntry | undefined; +} { + let entry = initial === undefined ? undefined : structuredClone(initial); + return { + read: async () => (entry === undefined ? undefined : structuredClone(entry)), + write: async (next) => { + entry = structuredClone(next); + }, + delete: async () => { + entry = undefined; + }, + snapshot: () => (entry === undefined ? undefined : structuredClone(entry)), + }; +} + +test("remote catalog parser accepts Pi's keyed response and pins provider identity", () => { + const model = oxAlphaModel(); + const parsed = parsePiRemoteCatalog("opencode-go", { + [model.id]: { ...model, provider: "attacker-controlled" }, + }); + assert.equal(parsed.length, 1); + assert.equal(parsed[0]?.id, "ox-alpha-free"); + assert.equal(parsed[0]?.provider, "opencode-go"); + assert.throws(() => parsePiRemoteCatalog("opencode-go", { models: "wrong" })); +}); + +test("Ox Alpha publishes its native low, high, and max thinking contract", () => { + assert.deepEqual(piModelMetadataFor("opencode-go", oxAlphaModel()), { + source: "provider", + name: "Ox Alpha Free (Unlimited)", + type: "llm", + vision: true, + reasoning: true, + thinkingLevels: ["low", "high", "max"], + thinkingCanDisable: false, + contextLength: 1_000_000, + }); +}); + +test("provider stream overrides dispatch a newly cataloged API without altering other APIs", () => { + const original = opencodeGoProvider(); + const marker = {}; + let selected = ""; + const provider = withProviderStreamOverrides(original, { + "openai-responses": { + stream: (() => { selected = "responses"; return marker; }) as never, + streamSimple: (() => { selected = "responses-simple"; return marker; }) as never, + }, + }); + const responses = { ...oxAlphaModel(), api: "openai-responses" as const }; + assert.equal(provider.stream(responses, {} as never, {}), marker); + assert.equal(selected, "responses"); + assert.equal(provider.streamSimple(responses, {} as never, {}), marker); + assert.equal(selected, "responses-simple"); +}); + +test("OpenCode Go overlay publishes ox-alpha without sending provider credentials", async () => { + const store = memoryProviderStore(); + const requests: Array<{ url: string; headers: Headers }> = []; + const provider = withPiRemoteCatalog(opencodeGoProvider(), { + fetchImpl: async (input, init) => { + requests.push({ url: String(input), headers: new Headers(init?.headers) }); + return new Response(JSON.stringify({ "ox-alpha-free": oxAlphaModel() }), { + status: 200, + headers: { + etag: '"catalog-v1"', + "last-modified": "Thu, 20 Aug 2026 16:00:00 GMT", + }, + }); + }, + now: () => Date.parse("2026-08-20T16:01:00Z"), + }); + + await provider.refreshModels?.({ + credential: { type: "api_key", key: "must-not-leak" }, + store, + allowNetwork: true, + force: true, + signal: new AbortController().signal, + }); + + assert.equal(requests.length, 1); + assert.equal(requests[0]?.url, "https://pi.dev/api/models/providers/opencode-go"); + assert.equal(requests[0]?.headers.get("authorization"), null); + assert.equal(requests[0]?.headers.get("x-api-key"), null); + assert.equal(requests[0]?.headers.get("cookie"), null); + assert.equal(requests[0]?.headers.get("user-agent"), AIDEN_PI_CATALOG_USER_AGENT); + assert.ok(provider.getModels().some((model) => model.id === "ox-alpha-free")); + assert.ok(store.snapshot()?.models.some((model) => model.id === "ox-alpha-free")); +}); + +test("cached overlays hydrate offline and honor freshness unless force refreshed", async () => { + const checkedAt = Date.parse("2026-08-20T16:01:00Z"); + const store = memoryProviderStore({ + models: [oxAlphaModel()], + checkedAt, + lastModified: Date.parse("2026-08-20T16:00:00Z"), + } as ModelsStoreEntry); + let fetches = 0; + const provider = withPiRemoteCatalog(opencodeGoProvider(), { + fetchImpl: async () => { + fetches += 1; + return new Response(null, { status: 304 }); + }, + now: () => checkedAt + PI_REMOTE_CATALOG_REFRESH_INTERVAL_MS - 1, + }); + + await provider.refreshModels?.({ store, allowNetwork: false }); + assert.ok(provider.getModels().some((model) => model.id === "ox-alpha-free")); + + await provider.refreshModels?.({ store, allowNetwork: true }); + assert.equal(fetches, 0); + + await provider.refreshModels?.({ store, allowNetwork: true, force: true }); + assert.equal(fetches, 1); + assert.ok(provider.getModels().some((model) => model.id === "ox-alpha-free")); +}); + +test("fresh empty and negative catalog results do not refetch on every launch", async () => { + const checkedAt = Date.parse("2026-08-22T16:00:00Z"); + for (const firstResponse of [ + () => Response.json({}, { headers: { "last-modified": "Sat, 22 Aug 2026 15:59:00 GMT" } }), + () => new Response(null, { status: 404 }), + () => new Response(null, { status: 501 }), + ]) { + const store = memoryProviderStore(); + let fetches = 0; + const provider = withPiRemoteCatalog(opencodeGoProvider(), { + fetchImpl: async () => { + fetches += 1; + return firstResponse(); + }, + now: () => checkedAt, + }); + await provider.refreshModels!({ store, allowNetwork: true }); + await provider.refreshModels!({ store, allowNetwork: true }); + assert.equal(fetches, 1); + assert.deepEqual(store.snapshot()?.models, []); + } +}); + +test("conditional refresh sends only the safe ETag validator and keeps a 304 overlay", async () => { + const store = memoryProviderStore({ + models: [oxAlphaModel()], + checkedAt: Date.parse("2026-08-20T16:01:00Z"), + lastModified: Date.parse("2026-08-20T16:00:00Z"), + etag: '"catalog-v1"', + } as ModelsStoreEntry); + let headers = new Headers(); + const provider = withPiRemoteCatalog(opencodeGoProvider(), { + fetchImpl: async (_input, init) => { + headers = new Headers(init?.headers); + return new Response(null, { status: 304 }); + }, + }); + await provider.refreshModels!({ store, allowNetwork: true, force: true }); + assert.equal(headers.get("if-none-match"), '"catalog-v1"'); + assert.equal(headers.get("if-modified-since"), null); + assert.ok(provider.getModels().some((model) => model.id === "ox-alpha-free")); +}); + +test("configured provider refresh is isolated and publishes before its caller continues", async () => { + const credentials: CredentialStore = new InMemoryCredentialStore(); + const stores = new Map>(); + const storeFor = (providerId: string) => { + let store = stores.get(providerId); + if (!store) { + store = memoryProviderStore(); + stores.set(providerId, store); + } + return store; + }; + const models = builtinModels({ credentials }); + const original = models.getProvider("opencode-go"); + assert.ok(original); + const requested: string[] = []; + const compatible = withAidenPiCompatibility(original); + models.setProvider( + withPiRemoteCatalog(compatible, { + supportedApis: additionalAidenPiApis(original.id), + fetchImpl: async (input) => { + requested.push(String(input)); + return Response.json( + { + "ox-alpha-free": oxAlphaModel(), + "gpt-5.6-luna": { + ...oxAlphaModel(), + id: "gpt-5.6-luna", + name: "GPT-5.6 Luna", + api: "openai-responses", + }, + }, + { headers: { "last-modified": "Thu, 20 Aug 2026 16:00:00 GMT" } }, + ); + }, + }), + ); + await credentials.modify( + "opencode-go", + async () => ({ type: "api_key", key: "configured" }) satisfies Credential, + ); + const result = await refreshPiCatalogs({ + models, + credentials, + providerModelsStore: storeFor, + providerIds: ["opencode-go"], + }); + + assert.equal(result.aborted, false); + assert.equal(result.errors.size, 0); + assert.deepEqual(requested, ["https://pi.dev/api/models/providers/opencode-go"]); + assert.ok(models.getModel("opencode-go", "ox-alpha-free")); + assert.ok(models.getModel("opencode-go", "gpt-5.6-luna")); +}); + +test("catalog parser preserves OpenRouter's documented unknown-price sentinel", () => { + const model = oxAlphaModel(); + const parsed = parsePiRemoteCatalog("openrouter", [{ + ...model, + provider: "openrouter", + cost: { input: -1_000_000, output: -1_000_000, cacheRead: 0, cacheWrite: 0 }, + }]); + assert.equal(parsed[0]?.cost.input, -1_000_000); + assert.throws(() => parsePiRemoteCatalog("openrouter", [{ + ...model, + cost: { ...model.cost, input: -1 }, + }])); +}); + +test("catalog parser rejects duplicate ids, unsafe origins, credential headers, and unsupported APIs", () => { + const model = oxAlphaModel(); + assert.throws(() => parsePiRemoteCatalog("opencode-go", [model, model])); + assert.throws(() => parsePiRemoteCatalog("opencode-go", [{ ...model, baseUrl: "http://example.test/v1" }])); + assert.throws(() => parsePiRemoteCatalog("opencode-go", [{ ...model, headers: { Authorization: "secret" } }])); + assert.throws(() => parsePiRemoteCatalog("opencode-go", [{ ...model, headers: { "Key": "value" } }])); + assert.throws(() => parsePiRemoteCatalog("opencode-go", [{ + ...model, + compat: { nested: { constructor: "poison" } }, + }])); + assert.throws(() => parsePiRemoteCatalog("opencode-go", [{ ...model, api: "unknown-api" }], { + allowedApis: new Set(["openai-responses"]), + })); + assert.throws(() => parsePiRemoteCatalog("opencode-go", [{ ...model, baseUrl: "https://evil.example/v1" }], { + allowedOrigins: new Set([new URL(model.baseUrl).origin]), + })); +}); + +test("last-known-good catalog survives 404, 501, server errors, and malformed payloads", async () => { + const checkedAt = Date.parse("2026-08-20T16:01:00Z"); + for (const response of [ + () => new Response(null, { status: 404 }), + () => new Response(null, { status: 501 }), + () => new Response(null, { status: 500 }), + () => Response.json({ models: "invalid" }, { status: 200 }), + ]) { + const store = memoryProviderStore({ + models: [oxAlphaModel()], + checkedAt, + lastModified: Date.parse("2026-08-20T16:00:00Z"), + etag: '"old"', + } as ModelsStoreEntry); + const provider = withPiRemoteCatalog(opencodeGoProvider(), { + fetchImpl: async () => response(), + now: () => checkedAt + PI_REMOTE_CATALOG_REFRESH_INTERVAL_MS, + }); + await provider.refreshModels?.({ store, allowNetwork: false }); + await provider.refreshModels?.({ store, allowNetwork: true, force: true }).catch(() => undefined); + assert.ok(provider.getModels().some((entry) => entry.id === "ox-alpha-free")); + assert.ok(store.snapshot()?.models.some((entry) => entry.id === "ox-alpha-free")); + } +}); + +test("minimum-version and oversized responses fail closed without replacing the cache", async () => { + const store = memoryProviderStore({ + models: [oxAlphaModel()], + checkedAt: 1, + lastModified: Date.parse("2026-08-20T16:00:00Z"), + } as ModelsStoreEntry); + for (const response of [ + () => Response.json({ "ox-alpha-free": oxAlphaModel() }, { + headers: { "x-pi-model-catalog-minimum-version": "999.0.0" }, + }), + () => new Response("x".repeat(5 * 1024 * 1024 + 1), { + headers: { "content-type": "application/json" }, + }), + () => Response.json({ "ox-alpha-free": oxAlphaModel() }, { + headers: { "last-modified": "Thu, 01 Jan 2026 00:00:00 GMT" }, + }), + ]) { + const provider = withPiRemoteCatalog(opencodeGoProvider(), { + fetchImpl: async () => response(), + }); + await assert.rejects(provider.refreshModels!({ store, allowNetwork: true, force: true })); + assert.ok(store.snapshot()?.models.some((entry) => entry.id === "ox-alpha-free")); + } +}); + +test("an older valid catalog cannot roll back a newer cached generation", async () => { + const cached = oxAlphaModel(); + const store = memoryProviderStore({ + models: [cached], + checkedAt: Date.parse("2026-08-20T16:01:00Z"), + lastModified: Date.parse("2026-08-20T16:00:00Z"), + } as ModelsStoreEntry); + const provider = withPiRemoteCatalog(opencodeGoProvider(), { + fetchImpl: async () => Response.json({ + replacement: { ...cached, id: "replacement", name: "Older replacement" }, + }, { headers: { "last-modified": "Wed, 19 Aug 2026 16:00:00 GMT" } }), + }); + + await provider.refreshModels?.({ store, allowNetwork: false }); + await assert.rejects( + provider.refreshModels!({ store, allowNetwork: true, force: true }), + /older than the cached generation/u, + ); + assert.deepEqual(store.snapshot()?.models.map((model) => model.id), [cached.id]); + assert.deepEqual(provider.getModels().filter((model) => model.id === cached.id).map((model) => model.id), [cached.id]); +}); + +test("a future generation cannot poison the cache and a current catalog recovers it", async () => { + const now = Date.parse("2026-08-22T18:00:00Z"); + const poisoned = { ...oxAlphaModel(), id: "future-poison", name: "Future poison" }; + const store = memoryProviderStore({ + models: [poisoned], + checkedAt: now, + lastModified: Date.parse("9999-12-31T23:59:59Z"), + etag: '"future"', + } as ModelsStoreEntry); + const recovered = { ...oxAlphaModel(), id: "recovered", name: "Recovered" }; + const provider = withPiRemoteCatalog(opencodeGoProvider(), { + now: () => now, + fetchImpl: async () => Response.json({ recovered }, { + headers: { "last-modified": "Sat, 22 Aug 2026 17:59:00 GMT" }, + }), + }); + + await provider.refreshModels!({ store, allowNetwork: false }); + assert.equal(provider.getModels().some((model) => model.id === poisoned.id), false); + await provider.refreshModels!({ store, allowNetwork: true, force: true }); + assert.deepEqual(store.snapshot()?.models.map((model) => model.id), [recovered.id]); + assert.ok(provider.getModels().some((model) => model.id === recovered.id)); + + const futureResponse = withPiRemoteCatalog(opencodeGoProvider(), { + now: () => now, + fetchImpl: async () => Response.json({ poisoned }, { + headers: { "last-modified": "Fri, 31 Dec 9999 23:59:59 GMT" }, + }), + }); + await assert.rejects( + futureResponse.refreshModels!({ store: memoryProviderStore(), allowNetwork: true, force: true }), + /invalid future generation timestamp/u, + ); +}); + +test("clock rollback retains the last-known-good catalog and its downgrade fence", async () => { + const acceptedAt = Date.parse("2026-08-22T18:00:00Z"); + const cached = { ...oxAlphaModel(), id: "newer-cached", name: "Newer cached" }; + const store = memoryProviderStore({ + models: [cached], + checkedAt: acceptedAt, + lastModified: Date.parse("2026-08-22T17:59:00Z"), + } as ModelsStoreEntry); + const older = { ...oxAlphaModel(), id: "older-remote", name: "Older remote" }; + const provider = withPiRemoteCatalog(opencodeGoProvider(), { + now: () => Date.parse("2026-08-22T17:00:00Z"), + fetchImpl: async () => Response.json({ older }, { + headers: { "last-modified": "Sat, 01 Aug 2026 12:00:00 GMT" }, + }), + }); + + await provider.refreshModels!({ store, allowNetwork: false }); + assert.ok(provider.getModels().some((model) => model.id === cached.id)); + await assert.rejects( + provider.refreshModels!({ store, allowNetwork: true, force: true }), + /older than the cached generation/u, + ); + assert.deepEqual(store.snapshot()?.models.map((model) => model.id), [cached.id]); +}); + +test("clock-rollback revalidation retains its acceptance boundary across restart", async () => { + const acceptedAt = Date.parse("2026-08-22T18:00:00Z"); + const rolledBackNow = Date.parse("2026-08-22T17:00:00Z"); + const cached = { ...oxAlphaModel(), id: "rollback-cached", name: "Rollback cached" }; + for (const response of [ + () => new Response(null, { status: 304 }), + () => new Response(null, { status: 404 }), + () => new Response(null, { status: 501 }), + ]) { + const store = memoryProviderStore({ + models: [cached], + checkedAt: acceptedAt, + lastModified: Date.parse("2026-08-22T17:59:00Z"), + etag: '"accepted"', + } as ModelsStoreEntry); + const provider = withPiRemoteCatalog(opencodeGoProvider(), { + now: () => rolledBackNow, + fetchImpl: async () => response(), + }); + await provider.refreshModels!({ store, allowNetwork: true, force: true }); + assert.equal(store.snapshot()?.checkedAt, acceptedAt); + + const restarted = withPiRemoteCatalog(opencodeGoProvider(), { now: () => rolledBackNow }); + await restarted.refreshModels!({ store, allowNetwork: false }); + assert.ok(restarted.getModels().some((model) => model.id === cached.id)); + } +}); + +test("renderer catalog errors contain only bounded app-owned copy", () => { + const secret = "sk-upstream-token-canary"; + const projected = projectPiCatalogRefreshErrors(new Map([ + ["radius\n

    `9&^i(t?!mLoq<>Z*SRPn?Oq3bRbPP2qEIG?~ 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�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