@cognition-ai/cloudflare-agents 0.0.1-beta.3 → 0.0.1-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -230,7 +230,6 @@ let DevinChatAgent = (() => {
230
230
  writer.merge(toUIMessageStream(turn.events(prompt, toolCallIds(this.messages)), {
231
231
  approvals: turn.approvals,
232
232
  messageMetadata: devinSessionMetadata(session),
233
- onError: errorText,
234
233
  onEvent: (event) => {
235
234
  this.#project(epoch, session, event);
236
235
  },
@@ -340,14 +339,11 @@ class Turn {
340
339
  async *events(prompt, earlierToolCalls) {
341
340
  this.#started = true;
342
341
  const turn = this.#session.stream(prompt, { signal: this.#stop.signal });
343
- let finished = false;
344
342
  try {
345
343
  for (;;) {
346
344
  const next = await turn.next();
347
- if (next.done) {
348
- finished = true;
345
+ if (next.done)
349
346
  return next.value;
350
- }
351
347
  const event = next.value;
352
348
  if (event.type === "tool_call" && earlierToolCalls.has(event.call.id))
353
349
  continue;
@@ -355,9 +351,8 @@ class Turn {
355
351
  }
356
352
  }
357
353
  finally {
358
- // The consumer stopped early: let the SDK's generator run its cleanup too.
359
- if (!finished)
360
- await turn.return(undefined);
354
+ // If the consumer stopped early, let the SDK's generator run its cleanup too.
355
+ await turn.return(undefined);
361
356
  this.#close();
362
357
  }
363
358
  }
@@ -1 +1 @@
1
- {"version":3,"file":"devin-chat-agent.js","sourceRoot":"","sources":["../src/devin-chat-agent.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AIChatAgent,\n type ChatRecoveryContext,\n type ChatRecoveryOptions,\n type OnChatMessageOptions,\n} from \"@cloudflare/ai-chat\";\nimport {\n devinSessionMetadata,\n toPromptInput,\n toUIMessages,\n toUIMessageStream,\n ToolApprovals,\n type DevinMessageMetadata,\n type DevinUIMessage,\n} from \"@cognition-ai/ai-sdk\";\nimport {\n DevinError,\n SessionEventReducer,\n type CloudDevin,\n type CloudSession,\n type CloudSessionOptions,\n type PromptInput,\n type PromptResult,\n type SessionEvent,\n} from \"@cognition-ai/sdk\";\nimport { callable } from \"agents\";\nimport {\n createUIMessageStream,\n createUIMessageStreamResponse,\n isToolUIPart,\n type InferUIMessageChunk,\n type UIMessage,\n} from \"ai\";\n\nimport { connectDevin as connectFromEnv, type DevinEnv } from \"./connect.js\";\nimport { initialDevinChatState, reduceDevinChatState, type DevinChatState } from \"./state.js\";\n\n/** The messages a {@link DevinChatAgent} streams: `useAgentChat<DevinChatMessage>()`. */\nexport type DevinChatMessage = DevinUIMessage<DevinMessageMetadata>;\n\ntype DevinChatChunk = InferUIMessageChunk<DevinChatMessage>;\n\n/** What {@link DevinChatAgent.importSession} rebuilt. */\nexport interface ImportedSession {\n sessionId: string;\n sessionUrl: string | null;\n /** The first user message's text, e.g. for a chat title. */\n title: string | null;\n /** How many messages the replay produced. */\n messages: number;\n}\n\n/**\n * An `AIChatAgent` backed by a Devin cloud session instead of a model, so\n * `useAgentChat` drives Devin unchanged.\n *\n * - Each chat message prompts the chat's Devin session and streams the turn\n * with `@cognition-ai/ai-sdk`'s `toUIMessageStream`: text, reasoning, tool\n * calls, plans and pull requests.\n * - The session id lives in Agent state, so later turns attach to the same\n * session after hibernation, a deploy or from another client. Devin keeps\n * working in the cloud between turns.\n * - Permission requests are AI SDK tool approvals. `addToolApprovalResponse`\n * answers them and Cloudflare's auto-continuation resumes the turn.\n * - Stopping the client cancels the Devin turn; {@link startOver} detaches.\n *\n * ```ts\n * export class DevinChat extends DevinChatAgent<Env> {}\n * ```\n *\n * reads `DEVIN_API_KEY` (and optional `DEVIN_ORG_ID`, `DEVIN_BASE_URL`) from\n * the Worker's bindings. Override {@link connectDevin} for per-user tokens.\n */\nexport class DevinChatAgent<\n Env extends Cloudflare.Env & DevinEnv = Cloudflare.Env & DevinEnv,\n> extends AIChatAgent<Env, DevinChatState> {\n override initialState: DevinChatState = initialDevinChatState;\n /** A Devin session runs one turn at a time, so messages sent mid-turn wait their turn. */\n override messageConcurrency = \"queue\" as const;\n\n #turn: Turn | undefined;\n /** Bumped whenever the running turn is replaced, so its late events can't touch state. */\n #epoch = 0;\n\n /** Open the Devin connection a turn uses. Override for per-user tokens or other options. */\n protected connectDevin(): Promise<CloudDevin> {\n return connectFromEnv(this.env);\n }\n\n /** Options for the session the first turn creates, e.g. `{ repos: [\"acme/app\"] }`. */\n protected sessionOptions(): Omit<CloudSessionOptions, \"onPermission\"> {\n return {};\n }\n\n override onChatMessage(_onFinish: unknown, options?: OnChatMessageOptions): Promise<Response> {\n const signal = options?.abortSignal;\n const turn = this.#turn;\n const resumed = turn?.approvals.respond(this.messages);\n if (turn !== undefined && resumed !== undefined) {\n turn.follow(signal);\n return Promise.resolve(createUIMessageStreamResponse({ stream: resumed }));\n }\n if (options?.continuation === true) {\n return Promise.resolve(createUIMessageStreamResponse({ stream: this.#settleOrphans() }));\n }\n this.#replaceTurn();\n return Promise.resolve(createUIMessageStreamResponse({ stream: this.#prompt(signal) }));\n }\n\n /**\n * Detach from the Devin session so the next message starts a new one, and\n * stop the running turn. The old session keeps running in the cloud. Clear\n * the transcript on the client with `clearHistory()`.\n */\n @callable()\n startOver(): void {\n this.#replaceTurn();\n this.resetTurnState();\n this.setState(initialDevinChatState);\n }\n\n /**\n * Start this chat from an existing cloud session: rebuild its transcript\n * from the replayed tail and attach future turns to it.\n *\n * @throws {DevinError} when the chat already has a session or messages, or\n * the session can't be loaded.\n */\n async importSession(sessionId: string): Promise<ImportedSession> {\n if (this.state.sessionId !== null || this.messages.length > 0) {\n throw new DevinError(\"This chat already has a Devin session.\");\n }\n const loaded = await this.#load(sessionId);\n const messages = await toUIMessages(loaded.replay);\n const reducer = new SessionEventReducer();\n let state: DevinChatState = {\n ...initialDevinChatState,\n sessionId: loaded.sessionId,\n sessionUrl: loaded.sessionUrl,\n };\n for (const update of loaded.replay) state = reduceDevinChatState(state, reducer.handle(update));\n\n await this.persistMessages(messages);\n this.setState(state);\n const first = messages.find((message) => message.role === \"user\");\n const title = first?.parts\n .flatMap((part) => (part.type === \"text\" ? [part.text] : []))\n .join(\" \");\n return {\n sessionId: loaded.sessionId,\n sessionUrl: loaded.sessionUrl,\n title: title === undefined || title === \"\" ? null : title,\n messages: messages.length,\n };\n }\n\n /**\n * A Durable Object restart interrupted a turn. Keep what streamed, but don't\n * send the prompt again: Devin already has it and keeps working in the cloud.\n */\n protected override onChatRecovery(_ctx: ChatRecoveryContext): Promise<ChatRecoveryOptions> {\n return Promise.resolve({ persist: true, continue: false });\n }\n\n #prompt(signal: AbortSignal | undefined): ReadableStream<DevinChatChunk> {\n const epoch = this.#epoch;\n return createUIMessageStream<DevinChatMessage>({\n onError: errorText,\n execute: async ({ writer }) => {\n const prompt = toPromptInput(this.messages);\n const devin = await this.connectDevin();\n let session: CloudSession;\n try {\n session = await this.#openSession(devin, epoch);\n } catch (error) {\n devin.close();\n throw error;\n }\n if (signal?.aborted === true || epoch !== this.#epoch) {\n devin.close();\n return;\n }\n const turn = new Turn(devin, session, () => {\n if (this.#turn === turn) this.#turn = undefined;\n });\n this.#turn = turn;\n turn.follow(signal);\n writer.merge(\n toUIMessageStream(turn.events(prompt, toolCallIds(this.messages)), {\n approvals: turn.approvals,\n messageMetadata: devinSessionMetadata(session),\n onError: errorText,\n onEvent: (event) => {\n this.#project(epoch, session, event);\n },\n }),\n );\n },\n });\n }\n\n async #openSession(devin: CloudDevin, epoch: number): Promise<CloudSession> {\n const { sessionId } = this.state;\n if (sessionId !== null) return devin.attach(sessionId);\n const session = await devin.createSession(this.sessionOptions());\n if (epoch === this.#epoch) {\n this.setState({ ...this.state, sessionId: session.id, sessionUrl: session.url ?? null });\n }\n return session;\n }\n\n #project(epoch: number, session: CloudSession, event: SessionEvent): void {\n if (epoch !== this.#epoch || this.state.sessionId !== session.id) return;\n let next = reduceDevinChatState(this.state, event);\n const url = session.url ?? next.sessionUrl;\n if (url !== next.sessionUrl) next = { ...next, sessionUrl: url };\n if (next !== this.state) this.setState(next);\n }\n\n /** Stop the running turn and fence off its late events. */\n #replaceTurn(): void {\n this.#epoch++;\n const turn = this.#turn;\n this.#turn = undefined;\n turn?.stop();\n }\n\n /**\n * An approval arrived for a turn this agent no longer holds (it was stopped,\n * or the Durable Object restarted). End those tool calls instead of leaving\n * them spinning.\n */\n #settleOrphans(): ReadableStream<DevinChatChunk> {\n const last = this.messages.at(-1);\n const parts = last?.role === \"assistant\" ? last.parts.filter(isToolUIPart) : [];\n return createUIMessageStream<DevinChatMessage>({\n execute: ({ writer }) => {\n writer.write({ type: \"start\" });\n for (const part of parts) {\n if (part.state !== \"approval-responded\") continue;\n writer.write({\n type: \"tool-output-error\",\n toolCallId: part.toolCallId,\n errorText: \"This Devin turn ended before the approval arrived.\",\n dynamic: true,\n });\n }\n writer.write({ type: \"finish\", finishReason: \"other\" });\n },\n });\n }\n\n async #load(\n sessionId: string,\n ): Promise<{ sessionId: string; sessionUrl: string | null; replay: LoadedReplay }> {\n await using devin = await this.connectDevin();\n try {\n const { session, replay } = await devin.loadSession(sessionId, { replay: \"tail\" });\n return { sessionId: session.id, sessionUrl: session.url ?? null, replay };\n } catch (error) {\n throw new DevinError(`Could not load Devin session ${sessionId}: ${errorText(error)}`);\n }\n }\n}\n\ntype LoadedReplay = Awaited<ReturnType<CloudDevin[\"loadSession\"]>>[\"replay\"];\n\n/** One Devin turn: its connection, its approvals, and a way to stop it. */\nclass Turn {\n readonly approvals = new ToolApprovals();\n readonly #devin: CloudDevin;\n readonly #session: CloudSession;\n readonly #stop = new AbortController();\n readonly #onClose: () => void;\n #started = false;\n #closed = false;\n\n constructor(devin: CloudDevin, session: CloudSession, onClose: () => void) {\n this.#devin = devin;\n this.#session = session;\n this.#onClose = onClose;\n session.onPermission = this.approvals.onPermission;\n }\n\n /**\n * The turn's events, minus updates for tool calls that belong to an earlier\n * message: stopping a turn does not stop the command Devin was running, so\n * it can finish during a later turn. Its part lives in a message this\n * stream can't reach, and `AIChatAgent` folds messages that share a tool\n * call id into one, which would overwrite the earlier reply.\n */\n async *events(\n prompt: PromptInput,\n earlierToolCalls: ReadonlySet<string>,\n ): AsyncGenerator<SessionEvent, PromptResult> {\n this.#started = true;\n const turn = this.#session.stream(prompt, { signal: this.#stop.signal });\n let finished = false;\n try {\n for (;;) {\n const next = await turn.next();\n if (next.done) {\n finished = true;\n return next.value;\n }\n const event = next.value;\n if (event.type === \"tool_call\" && earlierToolCalls.has(event.call.id)) continue;\n yield event;\n }\n } finally {\n // The consumer stopped early: let the SDK's generator run its cleanup too.\n if (!finished) await turn.return(undefined as never);\n this.#close();\n }\n }\n\n /** Stop the turn when this request's signal aborts (the client pressed stop). */\n follow(signal: AbortSignal | undefined): void {\n if (signal === undefined) return;\n if (signal.aborted) {\n this.stop();\n return;\n }\n signal.addEventListener(\n \"abort\",\n () => {\n this.stop();\n },\n { once: true },\n );\n }\n\n stop(): void {\n this.#stop.abort(new DevinError(\"The Devin turn was stopped.\"));\n this.approvals.cancel();\n if (!this.#started) this.#close();\n }\n\n #close(): void {\n if (this.#closed) return;\n this.#closed = true;\n this.approvals.cancel();\n this.#devin.close();\n this.#onClose();\n }\n}\n\n/** Ids of every tool call in the transcript so far. */\nfunction toolCallIds(messages: readonly UIMessage[]): Set<string> {\n const ids = new Set<string>();\n for (const message of messages) {\n for (const part of message.parts) if (isToolUIPart(part)) ids.add(part.toolCallId);\n }\n return ids;\n}\n\nfunction errorText(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
1
+ {"version":3,"file":"devin-chat-agent.js","sourceRoot":"","sources":["../src/devin-chat-agent.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AIChatAgent,\n type ChatRecoveryContext,\n type ChatRecoveryOptions,\n type OnChatMessageOptions,\n} from \"@cloudflare/ai-chat\";\nimport {\n devinSessionMetadata,\n toPromptInput,\n toUIMessages,\n toUIMessageStream,\n ToolApprovals,\n type DevinMessageMetadata,\n type DevinUIMessage,\n} from \"@cognition-ai/ai-sdk\";\nimport {\n DevinError,\n SessionEventReducer,\n type CloudDevin,\n type CloudSession,\n type CloudSessionOptions,\n type PromptInput,\n type PromptResult,\n type SessionEvent,\n type SessionUpdate,\n} from \"@cognition-ai/sdk\";\nimport { callable } from \"agents\";\nimport {\n createUIMessageStream,\n createUIMessageStreamResponse,\n isToolUIPart,\n type InferUIMessageChunk,\n type UIMessage,\n} from \"ai\";\n\nimport { connectDevin as connectFromEnv, type DevinEnv } from \"./connect.js\";\nimport { initialDevinChatState, reduceDevinChatState, type DevinChatState } from \"./state.js\";\n\n/** The messages a {@link DevinChatAgent} streams: `useAgentChat<DevinChatMessage>()`. */\nexport type DevinChatMessage = DevinUIMessage<DevinMessageMetadata>;\n\ntype DevinChatChunk = InferUIMessageChunk<DevinChatMessage>;\n\n/** What {@link DevinChatAgent.importSession} rebuilt. */\nexport interface ImportedSession {\n sessionId: string;\n sessionUrl: string | null;\n /** The first user message's text, e.g. for a chat title. */\n title: string | null;\n /** How many messages the replay produced. */\n messages: number;\n}\n\n/**\n * An `AIChatAgent` backed by a Devin cloud session instead of a model, so\n * `useAgentChat` drives Devin unchanged.\n *\n * - Each chat message prompts the chat's Devin session and streams the turn\n * with `@cognition-ai/ai-sdk`'s `toUIMessageStream`: text, reasoning, tool\n * calls, plans and pull requests.\n * - The session id lives in Agent state, so later turns attach to the same\n * session after hibernation, a deploy or from another client. Devin keeps\n * working in the cloud between turns.\n * - Permission requests are AI SDK tool approvals. `addToolApprovalResponse`\n * answers them and Cloudflare's auto-continuation resumes the turn.\n * - Stopping the client cancels the Devin turn; {@link startOver} detaches.\n *\n * ```ts\n * export class DevinChat extends DevinChatAgent<Env> {}\n * ```\n *\n * reads `DEVIN_API_KEY` (and optional `DEVIN_ORG_ID`, `DEVIN_BASE_URL`) from\n * the Worker's bindings. Override {@link connectDevin} for per-user tokens.\n */\nexport class DevinChatAgent<\n Env extends Cloudflare.Env & DevinEnv = Cloudflare.Env & DevinEnv,\n> extends AIChatAgent<Env, DevinChatState> {\n override initialState: DevinChatState = initialDevinChatState;\n /** A Devin session runs one turn at a time, so messages sent mid-turn wait their turn. */\n override messageConcurrency = \"queue\" as const;\n\n #turn: Turn | undefined;\n /** Bumped whenever the running turn is replaced, so its late events can't touch state. */\n #epoch = 0;\n\n /** Open the Devin connection a turn uses. Override for per-user tokens or other options. */\n protected connectDevin(): Promise<CloudDevin> {\n return connectFromEnv(this.env);\n }\n\n /** Options for the session the first turn creates, e.g. `{ repos: [\"acme/app\"] }`. */\n protected sessionOptions(): Omit<CloudSessionOptions, \"onPermission\"> {\n return {};\n }\n\n override onChatMessage(_onFinish: unknown, options?: OnChatMessageOptions): Promise<Response> {\n const signal = options?.abortSignal;\n const turn = this.#turn;\n const resumed = turn?.approvals.respond(this.messages);\n if (turn !== undefined && resumed !== undefined) {\n turn.follow(signal);\n return Promise.resolve(createUIMessageStreamResponse({ stream: resumed }));\n }\n if (options?.continuation === true) {\n return Promise.resolve(createUIMessageStreamResponse({ stream: this.#settleOrphans() }));\n }\n this.#replaceTurn();\n return Promise.resolve(createUIMessageStreamResponse({ stream: this.#prompt(signal) }));\n }\n\n /**\n * Detach from the Devin session so the next message starts a new one, and\n * stop the running turn. The old session keeps running in the cloud. Clear\n * the transcript on the client with `clearHistory()`.\n */\n @callable()\n startOver(): void {\n this.#replaceTurn();\n this.resetTurnState();\n this.setState(initialDevinChatState);\n }\n\n /**\n * Start this chat from an existing cloud session: rebuild its transcript\n * from the replayed tail and attach future turns to it.\n *\n * @throws {DevinError} when the chat already has a session or messages, or\n * the session can't be loaded.\n */\n async importSession(sessionId: string): Promise<ImportedSession> {\n if (this.state.sessionId !== null || this.messages.length > 0) {\n throw new DevinError(\"This chat already has a Devin session.\");\n }\n const loaded = await this.#load(sessionId);\n const messages = await toUIMessages(loaded.replay);\n const reducer = new SessionEventReducer();\n let state: DevinChatState = {\n ...initialDevinChatState,\n sessionId: loaded.sessionId,\n sessionUrl: loaded.sessionUrl,\n };\n for (const update of loaded.replay) state = reduceDevinChatState(state, reducer.handle(update));\n\n await this.persistMessages(messages);\n this.setState(state);\n const first = messages.find((message) => message.role === \"user\");\n const title = first?.parts\n .flatMap((part) => (part.type === \"text\" ? [part.text] : []))\n .join(\" \");\n return {\n sessionId: loaded.sessionId,\n sessionUrl: loaded.sessionUrl,\n title: title === undefined || title === \"\" ? null : title,\n messages: messages.length,\n };\n }\n\n /**\n * A Durable Object restart interrupted a turn. Keep what streamed, but don't\n * send the prompt again: Devin already has it and keeps working in the cloud.\n */\n protected override onChatRecovery(_ctx: ChatRecoveryContext): Promise<ChatRecoveryOptions> {\n return Promise.resolve({ persist: true, continue: false });\n }\n\n #prompt(signal: AbortSignal | undefined): ReadableStream<DevinChatChunk> {\n const epoch = this.#epoch;\n return createUIMessageStream<DevinChatMessage>({\n onError: errorText,\n execute: async ({ writer }) => {\n const prompt = toPromptInput(this.messages);\n const devin = await this.connectDevin();\n let session: CloudSession;\n try {\n session = await this.#openSession(devin, epoch);\n } catch (error) {\n devin.close();\n throw error;\n }\n if (signal?.aborted === true || epoch !== this.#epoch) {\n devin.close();\n return;\n }\n const turn = new Turn(devin, session, () => {\n if (this.#turn === turn) this.#turn = undefined;\n });\n this.#turn = turn;\n turn.follow(signal);\n writer.merge(\n toUIMessageStream(turn.events(prompt, toolCallIds(this.messages)), {\n approvals: turn.approvals,\n messageMetadata: devinSessionMetadata(session),\n onEvent: (event) => {\n this.#project(epoch, session, event);\n },\n }),\n );\n },\n });\n }\n\n async #openSession(devin: CloudDevin, epoch: number): Promise<CloudSession> {\n const { sessionId } = this.state;\n if (sessionId !== null) return devin.attach(sessionId);\n const session = await devin.createSession(this.sessionOptions());\n if (epoch === this.#epoch) {\n this.setState({ ...this.state, sessionId: session.id, sessionUrl: session.url ?? null });\n }\n return session;\n }\n\n #project(epoch: number, session: CloudSession, event: SessionEvent): void {\n if (epoch !== this.#epoch || this.state.sessionId !== session.id) return;\n let next = reduceDevinChatState(this.state, event);\n const url = session.url ?? next.sessionUrl;\n if (url !== next.sessionUrl) next = { ...next, sessionUrl: url };\n if (next !== this.state) this.setState(next);\n }\n\n /** Stop the running turn and fence off its late events. */\n #replaceTurn(): void {\n this.#epoch++;\n const turn = this.#turn;\n this.#turn = undefined;\n turn?.stop();\n }\n\n /**\n * An approval arrived for a turn this agent no longer holds (it was stopped,\n * or the Durable Object restarted). End those tool calls instead of leaving\n * them spinning.\n */\n #settleOrphans(): ReadableStream<DevinChatChunk> {\n const last = this.messages.at(-1);\n const parts = last?.role === \"assistant\" ? last.parts.filter(isToolUIPart) : [];\n return createUIMessageStream<DevinChatMessage>({\n execute: ({ writer }) => {\n writer.write({ type: \"start\" });\n for (const part of parts) {\n if (part.state !== \"approval-responded\") continue;\n writer.write({\n type: \"tool-output-error\",\n toolCallId: part.toolCallId,\n errorText: \"This Devin turn ended before the approval arrived.\",\n dynamic: true,\n });\n }\n writer.write({ type: \"finish\", finishReason: \"other\" });\n },\n });\n }\n\n async #load(\n sessionId: string,\n ): Promise<{ sessionId: string; sessionUrl: string | null; replay: SessionUpdate[] }> {\n await using devin = await this.connectDevin();\n try {\n const { session, replay } = await devin.loadSession(sessionId, { replay: \"tail\" });\n return { sessionId: session.id, sessionUrl: session.url ?? null, replay };\n } catch (error) {\n throw new DevinError(`Could not load Devin session ${sessionId}: ${errorText(error)}`);\n }\n }\n}\n\n/** One Devin turn: its connection, its approvals, and a way to stop it. */\nclass Turn {\n readonly approvals = new ToolApprovals();\n readonly #devin: CloudDevin;\n readonly #session: CloudSession;\n readonly #stop = new AbortController();\n readonly #onClose: () => void;\n #started = false;\n #closed = false;\n\n constructor(devin: CloudDevin, session: CloudSession, onClose: () => void) {\n this.#devin = devin;\n this.#session = session;\n this.#onClose = onClose;\n session.onPermission = this.approvals.onPermission;\n }\n\n /**\n * The turn's events, minus updates for tool calls that belong to an earlier\n * message: stopping a turn does not stop the command Devin was running, so\n * it can finish during a later turn. Its part lives in a message this\n * stream can't reach, and `AIChatAgent` folds messages that share a tool\n * call id into one, which would overwrite the earlier reply.\n */\n async *events(\n prompt: PromptInput,\n earlierToolCalls: ReadonlySet<string>,\n ): AsyncGenerator<SessionEvent, PromptResult> {\n this.#started = true;\n const turn = this.#session.stream(prompt, { signal: this.#stop.signal });\n try {\n for (;;) {\n const next = await turn.next();\n if (next.done) return next.value;\n const event = next.value;\n if (event.type === \"tool_call\" && earlierToolCalls.has(event.call.id)) continue;\n yield event;\n }\n } finally {\n // If the consumer stopped early, let the SDK's generator run its cleanup too.\n await turn.return(undefined as never);\n this.#close();\n }\n }\n\n /** Stop the turn when this request's signal aborts (the client pressed stop). */\n follow(signal: AbortSignal | undefined): void {\n if (signal === undefined) return;\n if (signal.aborted) {\n this.stop();\n return;\n }\n signal.addEventListener(\n \"abort\",\n () => {\n this.stop();\n },\n { once: true },\n );\n }\n\n stop(): void {\n this.#stop.abort(new DevinError(\"The Devin turn was stopped.\"));\n this.approvals.cancel();\n if (!this.#started) this.#close();\n }\n\n #close(): void {\n if (this.#closed) return;\n this.#closed = true;\n this.approvals.cancel();\n this.#devin.close();\n this.#onClose();\n }\n}\n\n/** Ids of every tool call in the transcript so far. */\nfunction toolCallIds(messages: readonly UIMessage[]): Set<string> {\n const ids = new Set<string>();\n for (const message of messages) {\n for (const part of message.parts) if (isToolUIPart(part)) ids.add(part.toolCallId);\n }\n return ids;\n}\n\nfunction errorText(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cognition-ai/cloudflare-agents",
3
- "version": "0.0.1-beta.3",
3
+ "version": "0.0.1-beta.5",
4
4
  "description": "Cloudflare Agents adapter for the Devin SDK: a Devin cloud session behind an AIChatAgent, for useAgentChat.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,8 +27,8 @@
27
27
  "test": "vitest run --config test/vitest.config.ts"
28
28
  },
29
29
  "dependencies": {
30
- "@cognition-ai/ai-sdk": "0.0.1-beta.3",
31
- "@cognition-ai/sdk": "0.0.1-beta.3"
30
+ "@cognition-ai/ai-sdk": "0.0.1-beta.5",
31
+ "@cognition-ai/sdk": "0.0.1-beta.5"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "@cloudflare/ai-chat": ">=0.11.0 <1.0.0",