feat: project durable agent conversations
This commit is contained in:
16
packages/agents/src/admission-context.ts
Normal file
16
packages/agents/src/admission-context.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { AsyncLocalStorage } from "node:async_hooks";
|
||||
|
||||
export interface TurnAdmissionContext {
|
||||
readonly clientRequestId: string;
|
||||
readonly turnId: string;
|
||||
}
|
||||
|
||||
const turnAdmissionContext = new AsyncLocalStorage<TurnAdmissionContext>();
|
||||
|
||||
export const currentTurnAdmission = (): TurnAdmissionContext | undefined =>
|
||||
turnAdmissionContext.getStore();
|
||||
|
||||
export const withTurnAdmission = <T>(
|
||||
context: TurnAdmissionContext,
|
||||
run: () => T
|
||||
): T => turnAdmissionContext.run(context, run);
|
||||
@@ -80,6 +80,7 @@ app.post("/internal/work-attempts/:workspaceKey/cancel", async (context) => {
|
||||
});
|
||||
|
||||
app.all("/api/rivet/*", (context) => runtimeRegistry.handler(context.req.raw));
|
||||
|
||||
app.route("/", flue());
|
||||
|
||||
export default app satisfies Fetchable;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { parseAgentEnv } from "@code/env/agent";
|
||||
import type { AgentRouteHandler } from "@flue/runtime";
|
||||
|
||||
import { withTurnAdmission } from "./admission-context";
|
||||
|
||||
/** Only Convex may invoke or observe the organization-scoped Flue agent. */
|
||||
export const convexAgentRoute: AgentRouteHandler = async (context, next) => {
|
||||
const env = parseAgentEnv(process.env);
|
||||
@@ -14,5 +16,15 @@ export const convexAgentRoute: AgentRouteHandler = async (context, next) => {
|
||||
return context.json({ error: "Forbidden" }, 403);
|
||||
}
|
||||
|
||||
return await next().then(() => context.res);
|
||||
if (context.req.method !== "POST") {
|
||||
return await next();
|
||||
}
|
||||
|
||||
const clientRequestId = context.req.header("x-zopu-request-id");
|
||||
const turnId = context.req.header("x-zopu-turn-id");
|
||||
if (!clientRequestId || !turnId) {
|
||||
return context.json({ error: "Missing turn correlation headers" }, 400);
|
||||
}
|
||||
|
||||
return await withTurnAdmission({ clientRequestId, turnId }, () => next());
|
||||
};
|
||||
|
||||
@@ -26,11 +26,74 @@
|
||||
*/
|
||||
|
||||
import { env } from "@code/env/server";
|
||||
import { AttachmentConflictError, DEFAULT_LIST_LIMIT, DEFAULT_READ_LIMIT, MAX_LIST_LIMIT, MAX_READ_LIMIT, StreamListenerRegistry, assertSupportedFlueSchemaVersion, clampLimit, copyAttachmentBytes, createSessionStorageKey, decodeRunCursor, encodeRunCursor, formatOffset, hydratePersistedDirectSubmission, parseOffset, prepareDirectSubmission, verifyAttachmentBytes } from '@flue/runtime/adapter';
|
||||
import type { AgentAttemptMarker, AgentDispatchAdmission, AgentDispatchReceipt, AgentExecutionStore, AgentSubmission, AgentSubmissionStore, AttachmentRef, AttachmentStore, ConversationProducerClaim, ConversationRecord, ConversationStreamBatch, ConversationStreamIdentity, ConversationStreamMeta, ConversationStreamReadResult, ConversationStreamStore, CreateRunInput, DirectAgentSubmissionInput, DispatchAgentSubmissionInput, DispatchInput, EndRunInput, EventStreamMeta, EventStreamReadResult, EventStreamStore, GetAttachmentInput, PersistedChunkRow, PersistenceAdapter, PersistenceStores, PutAttachmentInput, RunPointer, RunRecord, RunStore, RunStatus, StoredAttachment, SubmissionAttemptRef, SubmissionClaimRef, SubmissionDurability, SubmissionSettlementObligation, SubmissionSettledRecord } from '@flue/runtime/adapter';
|
||||
import {
|
||||
AttachmentConflictError,
|
||||
DEFAULT_LIST_LIMIT,
|
||||
DEFAULT_READ_LIMIT,
|
||||
MAX_LIST_LIMIT,
|
||||
MAX_READ_LIMIT,
|
||||
StreamListenerRegistry,
|
||||
assertSupportedFlueSchemaVersion,
|
||||
clampLimit,
|
||||
copyAttachmentBytes,
|
||||
createSessionStorageKey,
|
||||
decodeRunCursor,
|
||||
encodeRunCursor,
|
||||
formatOffset,
|
||||
hydratePersistedDirectSubmission,
|
||||
parseOffset,
|
||||
prepareDirectSubmission,
|
||||
verifyAttachmentBytes,
|
||||
} from "@flue/runtime/adapter";
|
||||
import type {
|
||||
AgentAttemptMarker,
|
||||
AgentDispatchAdmission,
|
||||
AgentDispatchReceipt,
|
||||
AgentExecutionStore,
|
||||
AgentSubmission,
|
||||
AgentSubmissionStore,
|
||||
AttachmentRef,
|
||||
AttachmentStore,
|
||||
ConversationProducerClaim,
|
||||
ConversationRecord,
|
||||
ConversationStreamBatch,
|
||||
ConversationStreamIdentity,
|
||||
ConversationStreamMeta,
|
||||
ConversationStreamReadResult,
|
||||
ConversationStreamStore,
|
||||
CreateRunInput,
|
||||
DirectAgentSubmissionInput,
|
||||
DispatchAgentSubmissionInput,
|
||||
DispatchInput,
|
||||
EndRunInput,
|
||||
EventStreamMeta,
|
||||
EventStreamReadResult,
|
||||
EventStreamStore,
|
||||
GetAttachmentInput,
|
||||
PersistedChunkRow,
|
||||
PersistenceAdapter,
|
||||
PersistenceStores,
|
||||
PutAttachmentInput,
|
||||
RunPointer,
|
||||
RunRecord,
|
||||
RunStore,
|
||||
RunStatus,
|
||||
StoredAttachment,
|
||||
SubmissionAttemptRef,
|
||||
SubmissionClaimRef,
|
||||
SubmissionDurability,
|
||||
SubmissionSettlementObligation,
|
||||
SubmissionSettledRecord,
|
||||
} from "@flue/runtime/adapter";
|
||||
import { ConvexHttpClient } from "convex/browser";
|
||||
import { makeFunctionReference } from 'convex/server';
|
||||
import type { FunctionArgs, FunctionReference, FunctionReturnType } from 'convex/server';
|
||||
import { makeFunctionReference } from "convex/server";
|
||||
import type {
|
||||
FunctionArgs,
|
||||
FunctionReference,
|
||||
FunctionReturnType,
|
||||
} from "convex/server";
|
||||
|
||||
import { currentTurnAdmission } from "./admission-context";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Token + arg types
|
||||
@@ -42,7 +105,10 @@ import type { FunctionArgs, FunctionReference, FunctionReturnType } from 'convex
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Auth token present on every Convex function call. */
|
||||
interface TokenArgs { readonly token: string; readonly [key: string]: unknown }
|
||||
interface TokenArgs {
|
||||
readonly token: string;
|
||||
readonly [key: string]: unknown;
|
||||
}
|
||||
|
||||
const TOKEN = (): string => env.FLUE_DB_TOKEN;
|
||||
|
||||
@@ -152,7 +218,7 @@ interface AttachmentRefWire {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const checkSchemaVersion = makeFunctionReference<"query", TokenArgs, string>(
|
||||
"fluePersistence:checkSchemaVersion",
|
||||
"fluePersistence:checkSchemaVersion"
|
||||
);
|
||||
|
||||
const getSubmission = makeFunctionReference<
|
||||
@@ -221,7 +287,11 @@ interface AdmitSubmissionResponse {
|
||||
}
|
||||
const admitSubmission = makeFunctionReference<
|
||||
"mutation",
|
||||
TokenArgs & { readonly input: AdmitSubmissionEnvelope },
|
||||
TokenArgs & {
|
||||
readonly clientRequestId?: string;
|
||||
readonly input: AdmitSubmissionEnvelope;
|
||||
readonly turnId?: string;
|
||||
},
|
||||
AdmitSubmissionResponse
|
||||
>("fluePersistence:admitSubmission");
|
||||
|
||||
@@ -290,11 +360,9 @@ type SettleArgs = TokenArgs &
|
||||
readonly outcome: "completed" | "failed";
|
||||
readonly errorJson?: string;
|
||||
};
|
||||
const settleSubmission = makeFunctionReference<
|
||||
"mutation",
|
||||
SettleArgs,
|
||||
boolean
|
||||
>("fluePersistence:settleSubmission");
|
||||
const settleSubmission = makeFunctionReference<"mutation", SettleArgs, boolean>(
|
||||
"fluePersistence:settleSubmission"
|
||||
);
|
||||
|
||||
const insertAttemptMarker = makeFunctionReference<
|
||||
"mutation",
|
||||
@@ -349,10 +417,16 @@ type AppendBatchArgs = TokenArgs & {
|
||||
readonly producerEpoch: number;
|
||||
readonly incarnation: string;
|
||||
readonly producerSequence: number;
|
||||
readonly submission?: { readonly submissionId: string; readonly attemptId: string };
|
||||
readonly submission?: {
|
||||
readonly submissionId: string;
|
||||
readonly attemptId: string;
|
||||
};
|
||||
readonly recordsJson: string;
|
||||
};
|
||||
interface AppendResult { readonly offset: number; readonly appended: boolean }
|
||||
interface AppendResult {
|
||||
readonly offset: number;
|
||||
readonly appended: boolean;
|
||||
}
|
||||
const appendConversationBatch = makeFunctionReference<
|
||||
"mutation",
|
||||
AppendBatchArgs,
|
||||
@@ -438,7 +512,10 @@ const closeEventStream = makeFunctionReference<
|
||||
void
|
||||
>("fluePersistence:closeEventStream");
|
||||
|
||||
interface EventStreamMetaRow { readonly nextOffset: number; readonly closed: boolean }
|
||||
interface EventStreamMetaRow {
|
||||
readonly nextOffset: number;
|
||||
readonly closed: boolean;
|
||||
}
|
||||
const getEventStreamMeta = makeFunctionReference<
|
||||
"query",
|
||||
TokenArgs & { readonly path: string },
|
||||
@@ -454,7 +531,7 @@ type CreateRunArgs = TokenArgs & {
|
||||
readonly traceCarrierJson?: string;
|
||||
};
|
||||
const createRun = makeFunctionReference<"mutation", CreateRunArgs, void>(
|
||||
"fluePersistence:createRun",
|
||||
"fluePersistence:createRun"
|
||||
);
|
||||
|
||||
type EndRunArgs = TokenArgs & {
|
||||
@@ -466,7 +543,7 @@ type EndRunArgs = TokenArgs & {
|
||||
readonly errorJson?: string;
|
||||
};
|
||||
const endRun = makeFunctionReference<"mutation", EndRunArgs, void>(
|
||||
"fluePersistence:endRun",
|
||||
"fluePersistence:endRun"
|
||||
);
|
||||
|
||||
const getRun = makeFunctionReference<
|
||||
@@ -492,7 +569,7 @@ interface ListRunsResult {
|
||||
readonly hasMore: boolean;
|
||||
}
|
||||
const listRuns = makeFunctionReference<"query", ListRunsArgs, ListRunsResult>(
|
||||
"fluePersistence:listRuns",
|
||||
"fluePersistence:listRuns"
|
||||
);
|
||||
|
||||
// Attachments
|
||||
@@ -534,7 +611,9 @@ const deleteAttachmentsForInstance = makeFunctionReference<
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const safeJsonParse = <T>(text: string | null | undefined): T | undefined => {
|
||||
if (text === null || text === undefined || text === "") {return undefined;}
|
||||
if (text === null || text === undefined || text === "") {
|
||||
return undefined;
|
||||
}
|
||||
return JSON.parse(text) as T;
|
||||
};
|
||||
|
||||
@@ -544,7 +623,7 @@ const parseAcceptedAt = (value: string, label: string): number => {
|
||||
const ms = Date.parse(value);
|
||||
if (!Number.isFinite(ms)) {
|
||||
throw new TypeError(
|
||||
`[flue] ${label} produced non-finite acceptedAt: ${value}`,
|
||||
`[flue] ${label} produced non-finite acceptedAt: ${value}`
|
||||
);
|
||||
}
|
||||
return ms;
|
||||
@@ -610,7 +689,7 @@ const hydrateSubmission = (row: SubmissionRow): AgentSubmission => {
|
||||
const input = safeJsonParse<DispatchAgentSubmissionInput>(row.inputJson);
|
||||
if (input === undefined) {
|
||||
throw new Error(
|
||||
`[flue] persisted dispatch submission ${row.submissionId} has empty inputJson`,
|
||||
`[flue] persisted dispatch submission ${row.submissionId} has empty inputJson`
|
||||
);
|
||||
}
|
||||
const inputWithCarrier =
|
||||
@@ -621,7 +700,7 @@ const hydrateSubmission = (row: SubmissionRow): AgentSubmission => {
|
||||
const stripped = safeJsonParse<DirectAgentSubmissionInput>(row.inputJson);
|
||||
if (stripped === undefined) {
|
||||
throw new Error(
|
||||
`[flue] persisted direct submission ${row.submissionId} has empty inputJson`,
|
||||
`[flue] persisted direct submission ${row.submissionId} has empty inputJson`
|
||||
);
|
||||
}
|
||||
const chunks = safeJsonParse<PersistedChunkRow[]>(row.chunksJson) ?? [];
|
||||
@@ -632,12 +711,12 @@ const hydrateSubmission = (row: SubmissionRow): AgentSubmission => {
|
||||
};
|
||||
|
||||
const hydrateObligation = (
|
||||
row: SettlementObligationRow,
|
||||
row: SettlementObligationRow
|
||||
): SubmissionSettlementObligation => {
|
||||
const record = safeJsonParse<SubmissionSettledRecord>(row.recordJson);
|
||||
if (record === undefined) {
|
||||
throw new Error(
|
||||
`[flue] persisted settlement obligation ${row.submissionId} has empty recordJson`,
|
||||
`[flue] persisted settlement obligation ${row.submissionId} has empty recordJson`
|
||||
);
|
||||
}
|
||||
return {
|
||||
@@ -692,14 +771,14 @@ class ConvexClient {
|
||||
|
||||
query<F extends FunctionReference<"query">>(
|
||||
ref: F,
|
||||
args: FunctionArgs<F>,
|
||||
args: FunctionArgs<F>
|
||||
): Promise<FunctionReturnType<F>> {
|
||||
return this.client.query(ref, args);
|
||||
}
|
||||
|
||||
mutation<F extends FunctionReference<"mutation">>(
|
||||
ref: F,
|
||||
args: FunctionArgs<F>,
|
||||
args: FunctionArgs<F>
|
||||
): Promise<FunctionReturnType<F>> {
|
||||
return this.client.mutation(ref, args);
|
||||
}
|
||||
@@ -757,7 +836,7 @@ class ConvexAgentSubmissionStore implements AgentSubmissionStore {
|
||||
async replaceSubmissionAttempt(
|
||||
attempt: SubmissionAttemptRef,
|
||||
nextAttemptId: string,
|
||||
lease?: { ownerId: string; leaseExpiresAt: number },
|
||||
lease?: { ownerId: string; leaseExpiresAt: number }
|
||||
): Promise<AgentSubmission | null> {
|
||||
const row = await this.convex.mutation(replaceSubmissionAttempt, {
|
||||
attemptId: attempt.attemptId,
|
||||
@@ -808,10 +887,11 @@ class ConvexAgentSubmissionStore implements AgentSubmissionStore {
|
||||
}
|
||||
|
||||
async admitDirect(
|
||||
input: DirectAgentSubmissionInput,
|
||||
input: DirectAgentSubmissionInput
|
||||
): Promise<AgentSubmission> {
|
||||
const sessionKey = createSessionStorageKey(input.id, "default", "default");
|
||||
const extracted = prepareDirectSubmission(input);
|
||||
const admission = currentTurnAdmission();
|
||||
const res = await this.convex.mutation(admitSubmission, {
|
||||
input: {
|
||||
acceptedAt: parseAcceptedAt(input.acceptedAt, "admitDirect"),
|
||||
@@ -825,6 +905,12 @@ class ConvexAgentSubmissionStore implements AgentSubmissionStore {
|
||||
: { traceCarrierJson: jsonStringify(input.traceCarrier) }),
|
||||
},
|
||||
token: TOKEN(),
|
||||
...(admission === undefined
|
||||
? {}
|
||||
: {
|
||||
clientRequestId: admission.clientRequestId,
|
||||
turnId: admission.turnId,
|
||||
}),
|
||||
});
|
||||
if (res.kind === "submission" && res.submission !== undefined) {
|
||||
return hydrateSubmission(res.submission);
|
||||
@@ -833,12 +919,12 @@ class ConvexAgentSubmissionStore implements AgentSubmissionStore {
|
||||
// submission; if the backend signals a non-submission result we cannot
|
||||
// satisfy the `AgentSubmission` return type.
|
||||
throw new Error(
|
||||
`[flue] admitDirect for ${input.submissionId} did not return a submission`,
|
||||
`[flue] admitDirect for ${input.submissionId} did not return a submission`
|
||||
);
|
||||
}
|
||||
|
||||
async markSubmissionCanonicalReady(
|
||||
submissionId: string,
|
||||
submissionId: string
|
||||
): Promise<AgentSubmission | null> {
|
||||
const row = await this.convex.mutation(markSubmissionCanonicalReady, {
|
||||
submissionId,
|
||||
@@ -848,7 +934,7 @@ class ConvexAgentSubmissionStore implements AgentSubmissionStore {
|
||||
}
|
||||
|
||||
async claimSubmission(
|
||||
claim: SubmissionClaimRef,
|
||||
claim: SubmissionClaimRef
|
||||
): Promise<AgentSubmission | null> {
|
||||
const row = await this.convex.mutation(claimSubmission, {
|
||||
attemptId: claim.attemptId,
|
||||
@@ -862,7 +948,7 @@ class ConvexAgentSubmissionStore implements AgentSubmissionStore {
|
||||
|
||||
markSubmissionInputApplied(
|
||||
attempt: SubmissionAttemptRef,
|
||||
durability?: SubmissionDurability,
|
||||
durability?: SubmissionDurability
|
||||
): Promise<boolean> {
|
||||
return this.convex.mutation(markSubmissionInputApplied, {
|
||||
attemptId: attempt.attemptId,
|
||||
@@ -872,9 +958,7 @@ class ConvexAgentSubmissionStore implements AgentSubmissionStore {
|
||||
});
|
||||
}
|
||||
|
||||
requestSubmissionRecovery(
|
||||
attempt: SubmissionAttemptRef,
|
||||
): Promise<boolean> {
|
||||
requestSubmissionRecovery(attempt: SubmissionAttemptRef): Promise<boolean> {
|
||||
return this.convex.mutation(requestSubmissionRecovery, {
|
||||
attemptId: attempt.attemptId,
|
||||
submissionId: attempt.submissionId,
|
||||
@@ -890,7 +974,7 @@ class ConvexAgentSubmissionStore implements AgentSubmissionStore {
|
||||
}
|
||||
|
||||
requeueSubmissionBeforeInputApplied(
|
||||
attempt: SubmissionAttemptRef,
|
||||
attempt: SubmissionAttemptRef
|
||||
): Promise<boolean> {
|
||||
return this.convex.mutation(requeueSubmissionBeforeInputApplied, {
|
||||
attemptId: attempt.attemptId,
|
||||
@@ -901,7 +985,7 @@ class ConvexAgentSubmissionStore implements AgentSubmissionStore {
|
||||
|
||||
async reserveSubmissionSettlement(
|
||||
attempt: SubmissionAttemptRef,
|
||||
settlement: { recordId: string; record: SubmissionSettledRecord },
|
||||
settlement: { recordId: string; record: SubmissionSettledRecord }
|
||||
): Promise<SubmissionSettlementObligation | null> {
|
||||
const row = await this.convex.mutation(reserveSubmissionSettlement, {
|
||||
attemptId: attempt.attemptId,
|
||||
@@ -915,7 +999,7 @@ class ConvexAgentSubmissionStore implements AgentSubmissionStore {
|
||||
|
||||
finalizeSubmissionSettlement(
|
||||
attempt: SubmissionAttemptRef,
|
||||
recordId: string,
|
||||
recordId: string
|
||||
): Promise<boolean> {
|
||||
return this.convex.mutation(finalizeSubmissionSettlement, {
|
||||
attemptId: attempt.attemptId,
|
||||
@@ -936,7 +1020,7 @@ class ConvexAgentSubmissionStore implements AgentSubmissionStore {
|
||||
|
||||
failSubmission(
|
||||
attempt: SubmissionAttemptRef,
|
||||
error: unknown,
|
||||
error: unknown
|
||||
): Promise<boolean> {
|
||||
return this.convex.mutation(settleSubmission, {
|
||||
attemptId: attempt.attemptId,
|
||||
@@ -1001,7 +1085,7 @@ class ConvexConversationStreamStore implements ConversationStreamStore {
|
||||
|
||||
async createStream(
|
||||
path: string,
|
||||
identity: ConversationStreamIdentity,
|
||||
identity: ConversationStreamIdentity
|
||||
): Promise<void> {
|
||||
await this.convex.mutation(createConversationStream, {
|
||||
identity,
|
||||
@@ -1012,7 +1096,7 @@ class ConvexConversationStreamStore implements ConversationStreamStore {
|
||||
|
||||
acquireProducer(
|
||||
path: string,
|
||||
producerId: string,
|
||||
producerId: string
|
||||
): Promise<ConversationProducerClaim> {
|
||||
return this.convex.mutation(acquireConversationProducer, {
|
||||
path,
|
||||
@@ -1038,7 +1122,9 @@ class ConvexConversationStreamStore implements ConversationStreamStore {
|
||||
producerSequence: input.producerSequence,
|
||||
recordsJson: jsonStringify(input.records),
|
||||
token: TOKEN(),
|
||||
...(input.submission === undefined ? {} : { submission: input.submission }),
|
||||
...(input.submission === undefined
|
||||
? {}
|
||||
: { submission: input.submission }),
|
||||
});
|
||||
if (result.appended) {
|
||||
this.listeners.notify(input.path);
|
||||
@@ -1048,12 +1134,12 @@ class ConvexConversationStreamStore implements ConversationStreamStore {
|
||||
|
||||
async read(
|
||||
path: string,
|
||||
options?: { offset?: string; limit?: number },
|
||||
options?: { offset?: string; limit?: number }
|
||||
): Promise<ConversationStreamReadResult> {
|
||||
const limit = clampLimit(
|
||||
options?.limit,
|
||||
DEFAULT_READ_LIMIT,
|
||||
MAX_READ_LIMIT,
|
||||
MAX_READ_LIMIT
|
||||
);
|
||||
const result = await this.convex.query(readConversationBatches, {
|
||||
afterOffset: afterOffset(options?.offset),
|
||||
@@ -1077,7 +1163,9 @@ class ConvexConversationStreamStore implements ConversationStreamStore {
|
||||
path,
|
||||
token: TOKEN(),
|
||||
});
|
||||
if (row === null) {return null;}
|
||||
if (row === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
identity: row.identity,
|
||||
incarnation: row.incarnation,
|
||||
@@ -1131,7 +1219,7 @@ class ConvexEventStreamStore implements EventStreamStore {
|
||||
async appendEventOnce(
|
||||
path: string,
|
||||
key: string,
|
||||
event: unknown,
|
||||
event: unknown
|
||||
): Promise<string> {
|
||||
const result = await this.convex.mutation(appendEventOnce, {
|
||||
dataJson: jsonStringify(event),
|
||||
@@ -1147,7 +1235,7 @@ class ConvexEventStreamStore implements EventStreamStore {
|
||||
|
||||
async readEvents(
|
||||
path: string,
|
||||
opts?: { offset?: string; limit?: number },
|
||||
opts?: { offset?: string; limit?: number }
|
||||
): Promise<EventStreamReadResult> {
|
||||
const limit = clampLimit(opts?.limit, DEFAULT_READ_LIMIT, MAX_READ_LIMIT);
|
||||
const result = await this.convex.query(readEventsFn, {
|
||||
@@ -1179,7 +1267,9 @@ class ConvexEventStreamStore implements EventStreamStore {
|
||||
path,
|
||||
token: TOKEN(),
|
||||
});
|
||||
if (row === null) {return null;}
|
||||
if (row === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
closed: row.closed,
|
||||
nextOffset: formatOffset(row.nextOffset),
|
||||
@@ -1233,7 +1323,7 @@ class ConvexRunStore implements RunStore {
|
||||
}
|
||||
|
||||
lookupRun(
|
||||
runId: string,
|
||||
runId: string
|
||||
): Promise<{ runId: string; workflowName: string } | null> {
|
||||
return this.convex.query(lookupRun, { runId, token: TOKEN() });
|
||||
}
|
||||
@@ -1281,7 +1371,7 @@ class ConvexAttachmentStore implements AttachmentStore {
|
||||
attachment: attachmentRefToWire(input.attachment),
|
||||
bytes: bytes.buffer.slice(
|
||||
bytes.byteOffset,
|
||||
bytes.byteOffset + bytes.byteLength,
|
||||
bytes.byteOffset + bytes.byteLength
|
||||
) as ArrayBuffer,
|
||||
conversationId: input.conversationId,
|
||||
streamPath: input.streamPath,
|
||||
@@ -1302,7 +1392,9 @@ class ConvexAttachmentStore implements AttachmentStore {
|
||||
streamPath: input.streamPath,
|
||||
token: TOKEN(),
|
||||
});
|
||||
if (row === null) {return null;}
|
||||
if (row === null) {
|
||||
return null;
|
||||
}
|
||||
const attachment = wireRefToAttachmentRef(row.attachment);
|
||||
const bytes = new Uint8Array(row.bytes);
|
||||
// Verify integrity before handing bytes back to the runtime; throws
|
||||
@@ -1345,7 +1437,7 @@ class ConvexPersistenceAdapterImpl implements PersistenceAdapter {
|
||||
connect(): PersistenceStores {
|
||||
if (!this.migrated) {
|
||||
throw new Error(
|
||||
"[flue] ConvexPersistenceAdapter.connect() called before migrate() completed",
|
||||
"[flue] ConvexPersistenceAdapter.connect() called before migrate() completed"
|
||||
);
|
||||
}
|
||||
const submissions = new ConvexAgentSubmissionStore(this.convex);
|
||||
|
||||
Reference in New Issue
Block a user