Skip to content

Chat with a project

You drive a project by chatting with it. Aura reads files, edits inputs, runs the solver, and replies. One message plus everything it triggers is a turn.

const reply = await aura.chat(project.id, "Solve this and summarize the result.");
console.log(reply.content);

chat() connects to the project WebSocket, dispatches the message, and returns the completed reply. In an environment that blocks WebSockets it falls back to polling /chat/sync — same result, later. It gives up after fifteen minutes; the timeout and the fallback’s poll interval are both adjustable. That bound is total duration, not inactivity, so a long solve can exceed it while perfectly healthy — raise it rather than re-sending the message.

Two details matter on a project several callers share:

  • You get your own reply. The match is on the dispatched message’s sequence number, not on a message count, so another caller’s turn is never handed back to you.
  • The wait is pinned to one conversation — the one your dispatch landed in. Activating or creating another conversation mid-turn does not redirect it.

A timeout means the turn is genuinely still running. A socket that dies announces itself, but one that goes half-open just stops delivering — and a turn’s terminal event with it. So the wait never concludes anything from silence: whenever the stream has been quiet for quietProbeMs / quiet_probe (30 seconds by default, floored at 1 second), and again before the deadline expires, the turn’s state is read back from the server. A turn that finished is returned however its terminal event was lost, so a timeout is never a finished turn you missed.

const reply = await aura.chat(project.id, "Re-solve with a 30 minute budget.", {
conversationId, // omit to use the project's active conversation
pollIntervalMs: 2000,
timeoutMs: 15 * 60_000, // long solves need a longer wait
});

onDelta / on_delta receives assistant text as it is generated, and onToolActivity / on_tool_activity fires as the agent starts and finishes tools. Both are no-ops on the poll fallback, so treat them as progress reporting rather than a delivery guarantee — chat() still returns the completed reply.

await aura.chat(project.id, "Solve this and summarize the result.", {
onDelta: (text) => process.stdout.write(text),
onToolActivity: (activity) => console.error(`[${activity.status}] ${activity.tool}`),
});

A project can hold several conversations. Without one named, chat uses the project’s active conversation.

const conversation = await aura.createConversation(project.id, "budget sweep");
await aura.chat(project.id, "Try a 20% larger fleet.", { conversationId: conversation.id });
const all = await aura.listConversations(project.id);
import { AuraTurnCancelledError } from "@strangeworks-inc/strangeworks-aura-sdk";
const controller = new AbortController();
// e.g. a Ctrl-C handler — stop both sides
process.on("SIGINT", () => {
void aura.cancel(project.id, conversationId);
controller.abort();
});
const reply = await aura
.chat(project.id, "Solve this.", { conversationId, signal: controller.signal })
.catch((err) => {
if (err instanceof AuraTurnCancelledError) return err.partialContent;
throw err;
});

cancel(projectId, conversationId) returns whether anything was stopped. Omitting the conversation id cancels any running turn in the project, including one someone else started.

To avoid blocking, dispatch the message and read state on your own schedule.

const dispatched = await aura.sendMessage(project.id, { content: "Solve this." });
const conversationId = dispatched.message.conversation_id;
let reply;
for (;;) {
const state = await aura.getConversationState(project.id, conversationId);
reply = [...state.messages]
.reverse()
.find((m) => m.role === "assistant" && m.sequence > dispatched.message.sequence);
if (reply && state.turn.state === "idle") break;
await new Promise((r) => setTimeout(r, 2000));
}

Match the reply the way chat() does — an assistant message whose sequence is above your dispatch’s — rather than taking the newest row, which may be a system notice or another caller’s message. Read the pinned conversation’s turn.state; getAgentStatus / get_agent_status is project-wide, so on a shared project it also reports other people’s turns.

aura chat without --message opens a full-screen terminal UI: a pinned header, a scrolling transcript, and a composer at the bottom. It uses the alternate screen buffer, so your shell’s scrollback is untouched, and prints the command to resume the session on exit.

Key Does
enter Send
shift+enter (or alt+enter) Newline — compose a multi-line message
Recall history; move between lines within a multi-line message
pgup pgdn, wheel Scroll the transcript
drag with the mouse Select text — releasing copies it to the clipboard
click a ⧉ n badge Copy that message; /copy n does the same, /copy takes the whole thread
esc Cancel the running turn, stay in the session
/help, /exit In-session commands — a leading / acts locally rather than sending a turn
ctrl-c twice, or exit Quit

The chat session page has the full key list, including word motion, line editing and the quick-reply picker.

  • Read results — executions, output files, the solution.
  • Errors — the three ways a turn can end badly, and which is which.