Workspace Orchestration
Workspace orchestration turns an agent workspace into an eventful operating surface. Agents and platform code still write ordinary files to S3, but a small set of prefixes have meaning: a markdown request under work/inbox/ wakes a folder-addressed agent, a lifecycle JSON file under work/runs/*/events/ advances a run, a review/ file asks a human to step in, and memory writes can trigger follow-up processing.
The important idea is that the folder is the routing boundary. The root agent is addressed as ., and specialist folders like expenses/ or customer-support/escalations/ are addressed by their relative path. That keeps async work aligned with the same workspace model used by the workspace overlay and Folder Is the Agent.
What it enables
Section titled “What it enables”Use workspace orchestration when work should outlive the current turn, needs a specialist folder, may wait on a person, or should resume another run later.
Common patterns:
- Async specialist work — a parent asks
legal/orexpenses/to handle work without blocking the current turn. - Human-in-the-loop review — an agent writes a review request, then resumes when the review is accepted or cancelled.
- Parent-managed fan-out — a parent wakes multiple specialist folders and tracks their completion in its own
status.jsonor run notes. - Long-running background work — scheduled jobs and connector events can write requests into the workspace instead of inventing a parallel queue.
- Memory and wiki follow-up —
memory/*.mdchanges can become canonical events that downstream compilers inspect.
Mental model
Section titled “Mental model”There are three layers:
| Layer | Purpose |
|---|---|
| S3 workspace files | The inspectable source files agents and operators can reason about. |
| Canonical database events | The source of truth for ordering, idempotency, status, and audit. |
| Wakeup requests | The bridge from a canonical event to an AgentCore invocation. |
S3 object events are at-least-once and can arrive out of order, so the dispatcher does not treat raw S3 events as the source of truth. It validates the key, checks suppression metadata, derives a canonical event, and records idempotency by tenant plus source key plus S3 sequencer.
The database tables are:
| Table | Purpose |
|---|---|
agent_workspace_runs | One logical run for a target folder and request. |
agent_workspace_events | Canonical event log with source object key, sequencer, reason, payload, and mirror status. |
agent_workspace_waits | Relationships where one run is waiting on another run or target path. |
Run statuses are pending, claimed, processing, completed, failed, awaiting_review, awaiting_subrun, cancelled, and expired.
Eventful prefixes
Section titled “Eventful prefixes”Only these prefixes are eventful. Everything else in the workspace remains a normal file.
| Workspace path | Canonical event | Notes |
|---|---|---|
work/inbox/*.md | work.requested | Root-agent async request. |
<target>/work/inbox/*.md | work.requested | Specialist-folder async request. |
work/runs/{runId}/events/*.json | run.started, run.blocked, run.completed, run.failed, review.requested | Lifecycle intent files for a run. |
<target>/work/runs/{runId}/events/*.json | Same lifecycle events | Folder-scoped run lifecycle. |
review/* | review.requested, review.responded, or event.rejected | Review files request human action; admin decisions record responses; direct S3 deletion is recorded as a rejected event. |
<target>/review/* | Same review behavior | Folder-scoped review. |
memory/* | memory.changed | Used by memory and wiki follow-up processors. |
<target>/memory/* | memory.changed | Folder-scoped memory. |
work/outbox/* | run.completed | Output files can complete the current run. |
<target>/work/outbox/* | run.completed | Folder-scoped output completion. |
errors/* | run.failed | Runtime or dispatcher error reporting. |
<target>/errors/* | run.failed | Folder-scoped error reporting. |
events/intents/*.json | event.rejected | Platform intent files must be written by orchestration writers. |
<target>/events/intents/*.json | event.rejected | Folder-scoped intent files must be written by orchestration writers. |
events/audit/* | event.rejected | Inspectable audit mirror files must be written by the platform. |
The generic workspace file API rejects direct writes to protected orchestration paths:
work/inbox/review/work/runs/*/events/events/intents/events/audit/
Use orchestration writers and tools instead. They validate the target, order parent-blocked writes before child inbox writes, and preserve audit metadata.
Configure it
Section titled “Configure it”Workspace orchestration has two gates: an infrastructure gate and a tenant gate. Both must be enabled before workspace events wake agents.
1. Enable the Terraform resources
Section titled “1. Enable the Terraform resources”Set the root module flag:
module "thinkwork" { source = "thinkwork-ai/thinkwork/aws"
# ... enable_workspace_orchestration = true}This wires the primary workspace bucket to EventBridge, creates SQS queues and DLQs for workspace events, and enables the workspace-event-dispatcher Lambda event source mapping.
2. Apply the database migrations
Section titled “2. Apply the database migrations”The migration packages/database-pg/drizzle/0034_agent_workspace_events.sql adds:
tenants.workspace_orchestration_enabledagent_workspace_runsagent_workspace_eventsagent_workspace_waits
The follow-up migration packages/database-pg/drizzle/0035_workspace_review_decisions.sql expands the canonical event vocabulary with review.responded, which backs the admin accept/resume path.
The normal deploy pipeline applies this with the rest of the database changes. For a manually managed environment, apply the migration before turning on the tenant flag.
3. Enable a tenant
Section titled “3. Enable a tenant”Enable the feature for one tenant after the infrastructure and schema are present:
UPDATE tenantsSET workspace_orchestration_enabled = trueWHERE slug = 'acme';Use a staged rollout: enable one tenant, confirm events land in agent_workspace_events, then expand.
4. Publish routeable folders
Section titled “4. Publish routeable folders”Targets are validated against the routing table in AGENTS.md. A target is valid only when it is listed in the Go to column or is the root target ..
## Routing
| Task | Go to | Read | Skills || ------------------------ | ---------------- | -------------------------- | --------------- || Expense policy questions | expenses/ | expenses/CONTEXT.md | expense-review || Legal review | legal/contracts/ | legal/contracts/CONTEXT.md | contract-review |Target rules:
| Rule | Meaning |
|---|---|
. | The root agent. |
| Lowercase, numbers, hyphens, slash separators | Valid folder syntax. |
| Max depth 4 | a/b/c/d is valid; deeper paths are rejected. |
No memory or skills segment | Those names are reserved at every depth. |
No absolute paths, .., backslashes, ?, or # | Prevents path traversal and ambiguous S3 keys. |
5. Deploy a runtime with wake_workspace
Section titled “5. Deploy a runtime with wake_workspace”Managed AgentCore runtimes register the wake_workspace Strands tool when the environment has:
| Environment variable | Purpose |
|---|---|
TENANT_ID or _MCP_TENANT_ID | Tenant identity for the write API. |
AGENT_ID or _MCP_AGENT_ID | Calling agent identity. |
THINKWORK_API_URL | Base URL for the API Gateway HTTP API. |
API_AUTH_SECRET or THINKWORK_API_SECRET | Service-auth secret for the orchestration write endpoint. |
WORKSPACE_ROUTE_TARGETS | Optional comma-separated routes. If absent, the runtime parses /tmp/workspace/AGENTS.md. |
Use it from an agent
Section titled “Use it from an agent”Agents should call wake_workspace for async folder-scoped work:
wake_workspace( target="expenses", request_md="# Expense policy review\n\nPlease compare the attached reimbursement note against the current travel policy.\n\nReturn findings in work/outbox/summary.md.", reason="expense-review", idempotency_key="thread-123:expense-note-456", wait_for_result=true)Arguments:
| Argument | Required | Purpose |
|---|---|---|
target | Yes | . for root or a route from AGENTS.md without a trailing slash. |
request_md | Yes | Markdown request body written to the target’s work/inbox/. |
reason | No | Human-readable reason for audit and debugging. |
idempotency_key | No | Caller-provided dedupe key for repeated attempts. |
wait_for_result | No | When true, the writer records the parent run as blocked before writing the child inbox request. |
The tool returns the S3 key that was queued. The dispatcher turns that object into a work.requested event and the wakeup processor invokes the target agent with source = "workspace_event" and the run context.
The wakeup payload includes:
| Field | Meaning |
|---|---|
workspaceRunId | The durable run row in agent_workspace_runs. |
workspaceEventId | The canonical event row that caused the wakeup. |
targetPath | The folder target, or an empty string for the root agent. |
sourceObjectKey | The S3 object key that started the run. |
causeType | The canonical event type, usually work.requested or review.responded. |
Request markdown guidance
Section titled “Request markdown guidance”Good requests are concrete and file-oriented:
# Review the vendor contract
Source files:
- docs/contracts/acme-master-services.md- docs/policy/vendor-risk.md
Please:
1. Identify clauses that conflict with the vendor-risk policy.2. Write findings to work/outbox/contract-review.md.3. If legal approval is required, create a review request instead of guessing.Prefer file paths, expected output paths, and the definition of done. Avoid prompts that depend on chat history the specialist folder may not have.
Operator and audit flow
Section titled “Operator and audit flow”When a request is written:
- The object lands in S3 under a target folder’s
work/inbox/. - S3 emits an EventBridge object event.
- EventBridge sends matching keys to the workspace orchestration queue.
- SQS invokes
workspace-event-dispatcher. - The dispatcher validates the key, suppresses intentional bulk writes, and canonicalizes the event.
- Canonical event handling records the event and creates or updates the run.
- The wakeup processor creates an agent wakeup request with
source = "workspace_event". - The AgentCore runtime receives the wakeup, reads the workspace, does the work, and writes lifecycle or output files.
The audit trail lives in agent_workspace_events. The optional S3 audit mirror is there for inspection; mirror_status tells you whether the mirror write succeeded. The database event remains canonical even if an audit mirror write fails.
Human review flow
Section titled “Human review flow”Agents ask for a human by writing a review file under review/ or <target>/review/. The dispatcher records that as review.requested and moves the run to awaiting_review when the run id can be resolved from the review filename.
Review filenames should start with the workspace run id:
review/3af1c5e8-0d93-4f64-b67d-53c08388a133.needs-human.mdRouting: who resolves the review
Section titled “Routing: who resolves the review”Reviews route to the human responsible for the agent. The platform walks the agent’s parent_agent_id chain to find the first ancestor with a human_pair_id; that user is the responsible human. If the chain terminates at a source='system' agent (eval test runtimes, platform-managed agents) the review is classified as system. If the chain has no human pair and no system terminator (orphan or cycle), the review is classified as unrouted.
agent_workspace_runs row (status='awaiting_review') │ ▼ classify by walking agents.parent_agent_id │ ├─ first ancestor with human_pair_id → paired (responsibleUserId set) ├─ chain terminates at source='system' → system (no human pair anywhere) └─ orphan / cycle / depth-cap → unrouted (with warning marker)Where reviews surface
Section titled “Where reviews surface”- Paired reviews surface only on the responsible human’s mobile Threads. Sub-agent reviews follow the parent chain — if a sub-agent of your agent pauses for review, it appears in your mobile threads, even though you’re not a participant in the sub-agent’s thread directly. The mobile thread row carries a
Sub-agent <name> needs your input on <target>label so the routing is visible. - System and unrouted reviews surface in the admin app’s Inbox as items with
type='workspace_review'. They share the same UI as other inbox items: a queue, detail view with review body and proposed-changes preview, and Approve / Reject / Request revision actions. Materialization happens automatically when a system run pauses; no operator action is required to populate the queue. - The admin app does not show paired-human reviews. They live on mobile only.
Operator actions (Inbox → workspace run)
Section titled “Operator actions (Inbox → workspace run)”When an operator decides on a workspace_review inbox item, the inbox mutation dispatches the matching workspace review action against the linked run:
| Inbox action | Workspace effect |
|---|---|
| Approve | Records review.responded (decision=accepted), moves the run back to pending, and enqueues a workspace_event wakeup. The agent resumes with approval. |
| Reject | Records run.failed (reason=review_cancelled) and marks the run cancelled. No wakeup. |
| Request revision | Records review.responded (decision=resumed) with the operator’s notes carried as the response payload, and enqueues a wakeup. The agent reads the notes and addresses them in the next turn. |
The legacy /workspace-reviews admin page is retired; navigating there now redirects to /inbox.
These mutations live on the GraphQL surface and can be called by trusted services, but no first-class agent tool wraps them today — review resolution is admin-UI-driven (or mobile-driven for paired-human reviews). A vetted Strands tool for review resolution would let an agent (e.g. a triage agent) drain its tenant’s queue programmatically; that’s plausible follow-up work, not part of v1.
Directly deleting a review object is not a cancellation mechanism. Deletions are recorded as event.rejected with review_deleted_directly; use the Inbox actions (or, on mobile for paired reviews, the in-thread confirmation card) so run state and audit history remain coherent.
Smoke test
Section titled “Smoke test”After deploying to dev, verify one routed folder before expanding the tenant rollout.
- Confirm Terraform has the infrastructure gate enabled:
enable_workspace_orchestration = true- Apply the database migrations and enable one tenant:
UPDATE tenantsSET workspace_orchestration_enabled = trueWHERE slug = 'acme';- Add a route to the agent’s
AGENTS.md:
## Routing
| Task | Go to | Read | Skills || -------------------- | ----------- | --------------------- | ---------- || Workspace smoke test | smoke-test/ | smoke-test/CONTEXT.md | smoke-test |-
Call
wake_workspace(target="smoke-test", request_md="...", wait_for_result=false). -
Confirm a canonical event landed:
SELECT id, event_type, source_object_key, created_atFROM agent_workspace_eventsWHERE event_type = 'work.requested'ORDER BY created_at DESCLIMIT 5;- Confirm the wakeup request exists:
SELECT id, source, trigger_detail, payload, statusFROM agent_wakeup_requestsWHERE source = 'workspace_event'ORDER BY created_at DESCLIMIT 5;-
Confirm the target agent was invoked by checking the wakeup processor logs and the agent run output.
-
Confirm protected generic writes are still refused by attempting a generic workspace file write to
smoke-test/work/inbox/manual.md. The API should return403withuse orchestration writer.
Safety model
Section titled “Safety model”- Canonical DB events win — S3 files are the operating surface, but the database event log is the ordering and audit source.
- Idempotency is required — duplicate S3 events dedupe by tenant and idempotency key.
- Out-of-order delivery is expected — processors should tolerate late or duplicate lifecycle files.
- Protected paths require writers — direct generic PUTs into orchestration paths return
403withuse orchestration writer. - Bulk writes can suppress events — platform maintenance jobs may set S3 metadata
thinkwork-suppress-event: true. - Targets are route-validated — a folder must be in
AGENTS.mdrouting before another agent can wake it. - Parent fan-out is agent-owned — v1 does not provide platform-level DAG fan-in. Agents should write their own status files when coordinating many subruns.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Checks |
|---|---|
No agent wakes after writing work/inbox/*.md | Confirm enable_workspace_orchestration = true, the tenant row has workspace_orchestration_enabled = true, the object key matches an eventful prefix, and the dispatcher Lambda has a live event source mapping. |
| Events are in SQS but not processed | Check the dispatcher logs, the workspace event DLQ, and whether the Lambda zip was built before Terraform apply in local-zip mode. |
wake_workspace says the target is invalid | Add the folder to the AGENTS.md routing table, remove reserved segments like memory or skills, and keep depth at four segments or fewer. |
Generic file save returns use orchestration writer | The path is protected. Use wake_workspace or the orchestration write endpoint rather than POST /api/workspaces/files. |
| Duplicate requests appear | Check the caller’s idempotency_key, S3 sequencer values, and agent_workspace_events.idempotency_key. Duplicate raw S3 delivery should not create duplicate canonical events. |
| Review deletion does not resume a run | Direct S3 deletion is recorded as event.rejected. Use Inbox (admin) for system reviews or the mobile in-thread confirmation card for paired reviews so the platform can write review.responded and update run state. |
| A system review is not listed in admin Inbox | Confirm the run is classified as system (chain terminates at source='system'), not paired-human (those live on mobile). Confirm the review filename starts with a UUID run id and the run status is awaiting_review. If the run pre-dates U4 of the workspace-reviews routing refactor, run the backfill script: pnpm tsx packages/api/scripts/backfill-system-reviews-to-inbox.ts. |
| A paired-human review is not visible on mobile | Confirm the responsible human is the agent’s human_pair_id (or, for sub-agent runs, the chain’s first paired ancestor). Confirm the user is signed into the right tenant on mobile. |
Related pages
Section titled “Related pages”- Workspace Overlay — how workspace files compose from agent, template, and defaults.
- Folder Is the Agent — why folder paths are the unit of specialization.
- Managed Agents — the AgentCore runtime that receives workspace wakeups.
- Scheduled and Event-driven Automations — other ways work enters the system.
- Compounding Memory Pipeline — downstream processing for memory and wiki updates.