Skip to content

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.

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/ or expenses/ 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.json or 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-upmemory/*.md changes can become canonical events that downstream compilers inspect.

There are three layers:

LayerPurpose
S3 workspace filesThe inspectable source files agents and operators can reason about.
Canonical database eventsThe source of truth for ordering, idempotency, status, and audit.
Wakeup requestsThe 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:

TablePurpose
agent_workspace_runsOne logical run for a target folder and request.
agent_workspace_eventsCanonical event log with source object key, sequencer, reason, payload, and mirror status.
agent_workspace_waitsRelationships 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.

Only these prefixes are eventful. Everything else in the workspace remains a normal file.

Workspace pathCanonical eventNotes
work/inbox/*.mdwork.requestedRoot-agent async request.
<target>/work/inbox/*.mdwork.requestedSpecialist-folder async request.
work/runs/{runId}/events/*.jsonrun.started, run.blocked, run.completed, run.failed, review.requestedLifecycle intent files for a run.
<target>/work/runs/{runId}/events/*.jsonSame lifecycle eventsFolder-scoped run lifecycle.
review/*review.requested, review.responded, or event.rejectedReview files request human action; admin decisions record responses; direct S3 deletion is recorded as a rejected event.
<target>/review/*Same review behaviorFolder-scoped review.
memory/*memory.changedUsed by memory and wiki follow-up processors.
<target>/memory/*memory.changedFolder-scoped memory.
work/outbox/*run.completedOutput files can complete the current run.
<target>/work/outbox/*run.completedFolder-scoped output completion.
errors/*run.failedRuntime or dispatcher error reporting.
<target>/errors/*run.failedFolder-scoped error reporting.
events/intents/*.jsonevent.rejectedPlatform intent files must be written by orchestration writers.
<target>/events/intents/*.jsonevent.rejectedFolder-scoped intent files must be written by orchestration writers.
events/audit/*event.rejectedInspectable 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.

Workspace orchestration has two gates: an infrastructure gate and a tenant gate. Both must be enabled before workspace events wake agents.

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.

The migration packages/database-pg/drizzle/0034_agent_workspace_events.sql adds:

  • tenants.workspace_orchestration_enabled
  • agent_workspace_runs
  • agent_workspace_events
  • agent_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.

Enable the feature for one tenant after the infrastructure and schema are present:

UPDATE tenants
SET workspace_orchestration_enabled = true
WHERE slug = 'acme';

Use a staged rollout: enable one tenant, confirm events land in agent_workspace_events, then expand.

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:

RuleMeaning
.The root agent.
Lowercase, numbers, hyphens, slash separatorsValid folder syntax.
Max depth 4a/b/c/d is valid; deeper paths are rejected.
No memory or skills segmentThose names are reserved at every depth.
No absolute paths, .., backslashes, ?, or #Prevents path traversal and ambiguous S3 keys.

Managed AgentCore runtimes register the wake_workspace Strands tool when the environment has:

Environment variablePurpose
TENANT_ID or _MCP_TENANT_IDTenant identity for the write API.
AGENT_ID or _MCP_AGENT_IDCalling agent identity.
THINKWORK_API_URLBase URL for the API Gateway HTTP API.
API_AUTH_SECRET or THINKWORK_API_SECRETService-auth secret for the orchestration write endpoint.
WORKSPACE_ROUTE_TARGETSOptional comma-separated routes. If absent, the runtime parses /tmp/workspace/AGENTS.md.

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:

ArgumentRequiredPurpose
targetYes. for root or a route from AGENTS.md without a trailing slash.
request_mdYesMarkdown request body written to the target’s work/inbox/.
reasonNoHuman-readable reason for audit and debugging.
idempotency_keyNoCaller-provided dedupe key for repeated attempts.
wait_for_resultNoWhen 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:

FieldMeaning
workspaceRunIdThe durable run row in agent_workspace_runs.
workspaceEventIdThe canonical event row that caused the wakeup.
targetPathThe folder target, or an empty string for the root agent.
sourceObjectKeyThe S3 object key that started the run.
causeTypeThe canonical event type, usually work.requested or review.responded.

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.

When a request is written:

  1. The object lands in S3 under a target folder’s work/inbox/.
  2. S3 emits an EventBridge object event.
  3. EventBridge sends matching keys to the workspace orchestration queue.
  4. SQS invokes workspace-event-dispatcher.
  5. The dispatcher validates the key, suppresses intentional bulk writes, and canonicalizes the event.
  6. Canonical event handling records the event and creates or updates the run.
  7. The wakeup processor creates an agent wakeup request with source = "workspace_event".
  8. 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.

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.md

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)
  • 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 actionWorkspace effect
ApproveRecords review.responded (decision=accepted), moves the run back to pending, and enqueues a workspace_event wakeup. The agent resumes with approval.
RejectRecords run.failed (reason=review_cancelled) and marks the run cancelled. No wakeup.
Request revisionRecords 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.

After deploying to dev, verify one routed folder before expanding the tenant rollout.

  1. Confirm Terraform has the infrastructure gate enabled:
enable_workspace_orchestration = true
  1. Apply the database migrations and enable one tenant:
UPDATE tenants
SET workspace_orchestration_enabled = true
WHERE slug = 'acme';
  1. 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 |
  1. Call wake_workspace(target="smoke-test", request_md="...", wait_for_result=false).

  2. Confirm a canonical event landed:

SELECT id, event_type, source_object_key, created_at
FROM agent_workspace_events
WHERE event_type = 'work.requested'
ORDER BY created_at DESC
LIMIT 5;
  1. Confirm the wakeup request exists:
SELECT id, source, trigger_detail, payload, status
FROM agent_wakeup_requests
WHERE source = 'workspace_event'
ORDER BY created_at DESC
LIMIT 5;
  1. Confirm the target agent was invoked by checking the wakeup processor logs and the agent run output.

  2. Confirm protected generic writes are still refused by attempting a generic workspace file write to smoke-test/work/inbox/manual.md. The API should return 403 with use orchestration writer.

  • 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 403 with use 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.md routing 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.
SymptomChecks
No agent wakes after writing work/inbox/*.mdConfirm 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 processedCheck 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 invalidAdd 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 writerThe path is protected. Use wake_workspace or the orchestration write endpoint rather than POST /api/workspaces/files.
Duplicate requests appearCheck 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 runDirect 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 InboxConfirm 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 mobileConfirm 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.