cloudflare / cloudflare/agents

Think: no public wait for a submission to finish

Open
#2,265 0 comments 0 reactions 1 assignee Claimed by @threepointone View on GitHub
enhancement
Dominant language
TypeScript
Stars
5.6k
Forks
711
Avg merge
1d 20h
Merged PRs (30d)
53

Description

## Summary

Durable turns are `submitMessages` (returns immediately) plus `inspectSubmission` (a status read). There is no public promise or event that resolves when that submission reaches a terminal status. Workflows that admit a Think turn have to poll.

`onSubmissionStatus` is a protected hook, not a waiter. `waitUntilStable` is chat-recovery / client-tool drain, not submission completion.

## Environment

- `@cloudflare/think@0.17.0`
- Cloudflare Workflows calling a Think Durable Object via `submitMessages`

## What the types allow

```ts
submitMessages(
messages: UIMessage[],
options?: SubmitMessagesOptions
): Promise;

inspectSubmission(
submissionId: string
): Promise;

type ThinkSubmissionStatus =
| "pending"
| "running"
| "completed"
| "aborted"
| "skipped"
| "error";
```

`SubmitMessagesResult` is `ThinkSubmissionInspection & { accepted: boolean }` — typically `pending` / `running` at return. There is no `waitForSubmission(submissionId)` (or equivalent) on the public `Think` class.

`runTurn({ mode: "wait" })` blocks, but it is the wrong admission for a Workflow step: a wait-mode turn dies with the isolate, and `step.prompt` is hard-wired to `this.agent`. Parallel Capturer/Builder turns have to be `submitMessages` on other DOs.

## Reproduction

A Workflow step that must not return until a role DO has finished:

```ts
const { submissionId } = await agent.submitMessages([userMessage], {
idempotencyKey: `${workflowName}:${instanceId}:${stepName}`,
});

// No:
// await agent.waitForSubmission(submissionId)
// Only:
let inspection = await agent.inspectSubmission(submissionId);
while (inspection?.status === "pending" || inspection?.status === "running") {
// poll — in a Workflow, throw so the step retries
inspection = await agent.inspectSubmission(submissionId);
}
```

That poll is what `admitTurn` in a Workflow does today: `submitMessages` in one `step.do`, then a second `step.do` that calls `inspectSubmission` and throws while `pending`/`running` so the engine retries.

## Suggested fix

A public waiter, e.g. `waitForSubmission(submissionId): Promise`, that resolves on `completed` / `aborted` / `skipped` / `error` (and rejects or returns the error payload on `error`). An event / `onSubmissionStatus` subscription that a Workflow can await would also remove the poll.

`mode: "wait"` can stay for in-process callers; this is for the submit path a Workflow actually uses.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.