---
title: "ThinkWork Computer v1 autopilot status"
type: status
status: active
date: 2026-05-09
plan: docs/plans/2026-05-08-014-feat-thinkwork-computer-v1-consolidated-plan.md
---

# ThinkWork Computer v1 autopilot status

This file records implementation decisions, blockers, and follow-up notes while executing the consolidated Computer v1 plan without stopping for interactive questions.

## 2026-05-09

- **Decision:** Computer thread turns must force the Python Strands runtime whenever `computerId` and `computerTaskId` are present, even if the backing `agents.runtime` row still says `flue`. This avoids the legacy TypeScript/Flue path and lets Strands complete the `computer_tasks` row and persist the assistant response.
- **Verification note:** After the Strands routing fix deployed, a fresh deployed GraphQL-created thread produced a `thread_turn_dispatched` event with `runtime: "strands"`, a completed `computer_tasks` row, and an assistant message from `sender_type=computer`.
- **Blocker worked around:** Computer Use could not reacquire the browser window after deployment because the macOS accessibility bridge returned `cgWindowNotFound`. The deployed path was verified through GraphQL/API/DB and CloudWatch instead. Continue to use Computer Use again when the bridge is healthy.
- **Decision:** Keep `apps/computer` approval routes available for push deep-links, but do not expose Approvals/Inbox as sidebar destinations. Pending `computer_approval` count belongs on the Computer nav item per the latest UX direction.
- **Decision:** The dashboard refresh UI must stop simulating progress with timers. The next M5 unit wires `apps/computer` to `dashboardArtifact(id)` and `refreshDashboardArtifact(id)` so the refresh bar reflects the real Computer task state. Runtime execution of `dashboard_artifact_refresh` is tracked as the next remaining gap after this UI/API wiring.
- **Verification note:** Browser-level smoke for the dashboard artifact route passed on `http://127.0.0.1:5180` using a temporary Playwright browser with mocked tenant/GraphQL responses: the artifact loaded, the deterministic refresh copy rendered, clicking Refresh called the refresh mutation once, and the bar showed `Queued`.
- **Decision:** Execute `dashboard_artifact_refresh` deterministically inside the API mutation for v1, then complete/fail the Computer task there. This avoids depending on the legacy TypeScript `packages/computer-runtime` container while preserving task auditability and the UI's task-state contract.
- **Verification note:** API refresh executor coverage passes for success and S3 failure paths; `refreshDashboardArtifact` now writes the refreshed manifest, completes the Computer task with deterministic refresh metadata, or marks the task failed before surfacing the error.
- **Blocker worked around:** Computer Use still cannot inspect Brave or Chrome (`cgWindowNotFound` from the macOS accessibility bridge). Streaming verification continues with automated browser/unit coverage until Computer Use can reacquire browser windows.
- **Decision:** apps/computer must use the explicit `VITE_GRAPHQL_WS_URL` AppSync realtime endpoint for subscriptions while keeping the AppSync authorization `host` set to the GraphQL API host from `VITE_GRAPHQL_URL`. The build already writes both env vars; the client was only reading `VITE_GRAPHQL_URL`, which could route live subscriptions to the wrong host and make token streaming appear dead.
- **Decision:** AppSync subscription `start` payloads must send a printed GraphQL document string. `urql` can pass a `DocumentNode` through `forwardSubscription`, so apps/computer now serializes subscription queries before opening the AppSync realtime operation.
- **Decision:** The live streaming buffer resets only at clear turn boundaries: route/thread change, explicit follow-up submit, or durable assistant persistence. It no longer clears when the latest user message refetches, because that can erase early chunks during fast streams and make the UI appear stuck on the processing state.
- **Verification note:** The AppSync realtime transport was smoke-tested directly against dev: subscribe to `onComputerThreadChunk(threadId)`, publish `publishComputerThreadChunk` for the same temporary thread id, and assert the socket receives the chunk. This isolates AppSync transport health from browser auth and the agent runtime.
- **Live probe finding:** A real Computer thread turn completed and persisted the assistant response, and AppSync chunks arrived, but the Strands callback supplied the same text delta through multiple aliases in one callback invocation. The Python AppSync publisher now dedupes identical text parts per callback so live UI chunks do not repeat while the durable answer remains unchanged.
- **Decision:** Keep the Computer shell mounted for generated app routes instead of bypassing the sidebar/topbar layout. This removes the route-switch remount/flicker path when moving between Computer, Apps, and generated app detail pages; generated apps now render inside the same persistent shell.
- **Decision:** Thread detail renders the latest thinking/tool activity directly after the latest user message, before streaming or durable assistant text. Computer task output already includes model, token usage, and Strands tool invocation previews, so the UI surfaces those as Perplexity-style expandable activity rows instead of a separate task-looking block.
- **Live probe finding:** After the per-callback alias dedupe deployed, a deterministic blank-thread → subscribe → send-message probe confirmed AppSync streaming and durable persistence both work, but Strands can still emit the same completed text as separate full-buffer callbacks. The publisher now normalizes turn-wide full-buffer repeats into append-only deltas before publishing.
- **Verification note:** After the full-buffer normalization deployed, the deterministic live probe passed against dev: a blank thread subscribed before `sendMessage`, received one AppSync chunk, persisted the matching assistant message, and completed the Computer task without duplicate chunks. This probe is being promoted to `scripts/smoke/computer-thread-streaming-smoke.mjs`.
- **Decision:** Computer thread-list subscriptions must request `tenantId` on `onThreadTurnUpdated`; the list already filters by tenant, but the document omitted the field, so completed turns could fail to trigger a table refresh.
- **Decision:** Promote the deterministic thread streaming smoke into the main deploy workflow as `computer-thread-streaming-smoke`. The job runs after Strands runtime update, Computer SPA deploy, and bootstrap, then creates a real thread, subscribes to AppSync before `sendMessage`, and fails the deploy if live chunks, durable assistant text, or `computer_tasks` completion diverge.
- **Decision:** Add the plan-facing `scripts/smoke-computer.sh dev` entrypoint and have deploy call it. The lower-level post-deploy wrapper remains the implementation detail; the plan, README, and CI now share one public smoke command.
- **Decision:** Browser automation must leave durable Computer evidence, not only cost rows. The Python Strands browser tool now appends ordered `browser_automation_started`, `browser_automation_completed`, `browser_automation_failed`, or `browser_automation_unavailable` events through the existing Computer runtime task-event endpoint whenever it runs inside a Computer thread turn.
- **Decision:** Thread-detail source badges must be derived from actual evidence instead of a placeholder. The apps/computer header now counts source metadata, tool results, artifact source statuses, and source-like turn tool invocations rather than hardcoding `4 sources` whenever an artifact exists.
- **Decision:** Remove the legacy `/tasks` Computer routes and task-named route helper from apps/computer. User-facing navigation now stays on `/threads` for blank chat and thread detail, matching the product language correction away from "task" as the primary UI noun.
- **Decision:** Broaden `scripts/smoke-computer.sh dev` beyond token streaming without making every deploy depend on a flaky live browser session. The smoke now keeps the deterministic streaming turn, then verifies `myComputer`, thread-table loading, a temporary `computer_approval` create/list/detail/cancel round-trip, `memoryRecords` listing, and browser-evidence event observability. Setting `SMOKE_REQUIRE_BROWSER_EVIDENCE=1` turns the browser-evidence observation into a hard gate after a reliable browser-backed scenario is available.
- **Decision:** Increase the streaming smoke subscription warm-up from 1s to 3s. The deterministic answer can complete as a single fast chunk, and AppSync does not give us a distinct `start_ack`; the smoke should wait long enough for the subscription operation to settle before sending the message.
- **Verification note:** The broadened `scripts/smoke-computer.sh dev` passed against dev after the warm-up increase. It streamed one live chunk, persisted the matching assistant answer, completed the Computer task, loaded the caller's Computer and thread table, created/listed/loaded/cancelled a temporary `computer_approval`, and listed 500 memory records for the selected user namespace. Browser evidence was observable but absent for the selected Computer, so it remains a soft observation until a deterministic browser-backed smoke lands.
- **Blocker still open:** Retried Computer Use against Brave and Chrome after the broadened smoke; both still fail before page inspection with `Apple event error -10005: cgWindowNotFound`. Continue using deployed API/AppSync/DB verification until the macOS accessibility bridge can reacquire browser windows.
- **Decision:** Add an opt-in browser-backed smoke path instead of enabling browser automation in every deploy by default. `SMOKE_BROWSER_SCENARIO=1` temporarily enables `browser_automation` for the selected Computer's backing agent, asks for one deterministic `https://example.com` browser action, waits for durable `browser_automation_*` Computer events, and restores the prior capability state. This gives us a real M5 browser evidence switch while keeping the default deploy gate stable.
- **Verification note:** The opt-in browser smoke passed as an evidence gate on dev with `SMOKE_BROWSER_SCENARIO=1 SMOKE_REQUIRE_BROWSER_EVIDENCE=1`. It created a browser smoke thread and captured a durable `browser_automation_unavailable` event, then restored the temporary `agent_capabilities` row; no `browser_automation` capability rows remained afterward.
- **Blocker worked through:** Browser Automation could not complete a live browser action because `nova_act` was intentionally absent from the Python Strands image after an earlier dependency conflict. The container requirements now include Nova Act, and the follow-up strict smoke work updated that dependency to the supported 3.x line.
- **Verification note:** Python dependency resolution with the updated Strands requirements succeeds on the supported Nova Act 3.x line. Focused Python browser/tool-event tests pass.
- **Live probe finding:** After the Nova Act dependency image deployed, `SMOKE_BROWSER_SCENARIO=1 SMOKE_REQUIRE_BROWSER_EVIDENCE=1 SMOKE_REQUIRE_BROWSER_COMPLETED=1 scripts/smoke-computer.sh dev` proved the runtime can import and invoke the browser tool, but it returned `browser_automation_unavailable` with `reason: "nova_act_api_key_missing"`. No `/thinkwork/dev/nova-act-api-key` or Nova-related secret existed in SSM/Secrets Manager, and the Strands runtime IAM role only allowed `/thinkwork/<stage>/agentcore/*`, so the old loader path would have been inaccessible even if populated.
- **Decision:** Own Nova Act configuration as a stage AgentCore secret. Terraform now creates `/thinkwork/<stage>/agentcore/nova-act-api-key` as a SecureString placeholder with `ignore_changes`, injects its name as `NOVA_ACT_SSM_PARAM_NAME`, and the Python loader prefers that path while ignoring `PLACEHOLDER_SET_VIA_CLI`. Operators rotate the real key with `aws ssm put-parameter --overwrite ...`; completion-required browser smoke remains blocked until the real secret is populated.
- **Verification note:** After the managed-secret fix deployed, `/thinkwork/dev/agentcore/nova-act-api-key` exists as a SecureString and still contains the Terraform placeholder. A completion-required browser smoke rerun passed the deterministic streaming leg on retry, then failed the browser leg with `browser_automation_unavailable` / `nova_act_api_key_missing`. Current state: code, image, IAM path, and SSM slot are in place; a real Nova Act key is required before `SMOKE_REQUIRE_BROWSER_COMPLETED=1` can pass.
- **Blocker still open:** Retried Computer Use after the #1034 deploy; Brave still fails before inspection with `Apple event error -10005: cgWindowNotFound`. Continue verifying deployed behavior through API/AppSync/DB smoke until the macOS accessibility bridge can reacquire a browser window.
- **Decision:** Delete the placeholder `/inbox` route from apps/computer. Inbox is no longer part of the v1 navigation model; approvals remain as hidden deep-link routes for push/open-in-browser flows, and the sidebar exposes only the pending-approval count on Computer.
- **Blocker still open:** Retried Computer Use during the `/inbox` cleanup checkpoint; Brave and Chrome both still fail before inspection with `Apple event error -10005: cgWindowNotFound`.
- **Decision:** Thread detail should surface durable `computer_events` for the loaded Computer turn instead of relying only on task output. Browser automation events now render as Perplexity-style activity rows such as "Opening browser", "Browser completed", or "Browser unavailable", so tool progress is visible in the thread UI even when the live browser action is unavailable.
- **Decision:** Thread activity detail should translate raw backend payload keys before rendering expandable JSON. User-facing activity now says `instruction`, `runId`, `runType`, and `computerRunId` instead of leaking task terminology from durable event payloads.
- **Blocker still open:** Retried Computer Use again after opening PR #1040; Brave and Chrome both still fail before inspection with `Apple event error -10005: cgWindowNotFound`. Continue using in-app Browser/scripted smoke coverage for E2E until the macOS accessibility bridge can reacquire a browser window.
- **Verification note:** PR #1040 deployed successfully. `scripts/smoke-computer.sh dev` passed after the deploy: live AppSync streaming produced one chunk, the durable assistant response matched, the Computer task completed, thread-table loading worked, the approval create/list/detail/cancel round-trip worked, and memory listing returned user-scoped Hindsight records.
- **Verification note:** `SMOKE_BROWSER_SCENARIO=1 SMOKE_REQUIRE_BROWSER_EVIDENCE=1 scripts/smoke-computer.sh dev` passed after the deploy. It created a browser-backed Computer turn and observed durable browser evidence events, with the runtime returning `browser_automation_unavailable` because Nova Act is not configured.
- **Blocker still open:** `SMOKE_BROWSER_SCENARIO=1 SMOKE_REQUIRE_BROWSER_EVIDENCE=1 SMOKE_REQUIRE_BROWSER_COMPLETED=1 scripts/smoke-computer.sh dev` still fails only at the browser-completion requirement with `browser_automation_unavailable`. The remaining step is rotating a real value into `/thinkwork/dev/agentcore/nova-act-api-key`; code, IAM, event evidence, and UI rendering are in place.
- **Verification note:** The Nova Act API key was rotated into `/thinkwork/dev/agentcore/nova-act-api-key` and the completion-required browser smoke advanced past the missing-key state. The browser tool emitted `browser_automation_started`, proving the runtime can load the key and call the managed browser path.
- **Blocker worked through:** The next strict browser smoke failure was an IAM denial on `bedrock-agentcore:StartBrowserSession` for the Strands AgentCore runtime role. The fix grants the runtime role the AgentCore Browser session lifecycle actions and extends the AgentCore IAM lint guard so dependency-helper browser calls cannot be missed again.
- **Blocker worked through:** After the browser IAM grant deployed, the strict browser smoke advanced again but failed while initializing Nova Act/Playwright. Local reproduction against AgentCore Browser showed the managed CDP connection starts without any local browser install, then exposed two SDK issues: the 2.x Nova Act SDK is now rejected by the service, and the wrapper should call `act(..., schema=...)` for compatibility. The Strands browser tool now calls `act`, the runtime requires Nova Act 3.x, and the Docker image no longer runs `playwright install chromium --with-deps`; Computer should connect to AgentCore Browser, not bake Chromium into the runtime image.
- **Verification note:** A direct local AgentCore Browser + Nova Act 3.x probe against `https://example.com` returned `"Example Domain"` through the managed CDP endpoint using the SSM-provided key. This proves the key, IAM, AgentCore Browser session, Nova Act 3.x, and CDP headers path work before the container image PR/deploy.
- **Blocker worked through:** The first Nova Act 3.x image build failed because Nova Act declares `strands-agents<=1.29` while the Python runtime requires newer Strands. A local reproduction proved Nova Act 3.4 works against AgentCore Browser with `strands-agents==1.39.0` when its non-Strands dependencies are installed and Nova Act itself is installed with `--no-deps`. The Dockerfile now preserves the runtime's Strands version and installs Nova Act without dependency backtracking.
- **Decision:** Nova Act is not required for Computer v1 browser evidence. Browser Automation now defaults to direct Playwright-over-AgentCore-Browser CDP, records only AgentCore Browser cost for that path, and keeps Nova Act behind an explicit `BROWSER_AUTOMATION_ENGINE=nova_act` switch for a later natural-language browser-operator layer.
- **Verification note:** A local direct AgentCore Browser CDP probe through the Browser Automation tool returned `Example Domain`, emitted `browser_automation_started` and `browser_automation_completed`, and did not require a Nova Act API key or any installed browser binary.
- **Live probe finding:** After the direct-CDP image deployed, the strict browser smoke advanced past package/runtime startup and emitted `browser_automation_started`, then failed on the Playwright CDP websocket with `403 Forbidden: User is not authorized to access automation stream`. The Strands runtime role had Browser session lifecycle permissions but not the stream permissions AWS documents for AgentCore Browser. The runtime IAM policy now grants `UpdateBrowserStream`, `ConnectBrowserAutomationStream`, and `ConnectBrowserLiveViewStream` for the managed browser ARN and the account-scoped browser ARN shape.
- **Verification note:** After the Browser stream IAM patch deployed, `SMOKE_BROWSER_SCENARIO=1 SMOKE_REQUIRE_BROWSER_EVIDENCE=1 SMOKE_REQUIRE_BROWSER_COMPLETED=1 scripts/smoke-computer.sh dev` passed. The smoke streamed the deterministic thread chunk, completed the browser scenario with `browser_automation_started` and `browser_automation_completed`, and persisted the assistant answer `Example Domain`. Browser Automation is now using AgentCore Browser directly without Nova Act or a runtime-installed browser binary.
