experimental-a2 0.0.0 → 0.2.0
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.
- package/CHANGELOG.md +43 -0
- package/dist/ai-server.browser.js +2 -2
- package/dist/ai-server.d.ts +19 -7
- package/dist/ai-server.js +730 -96
- package/dist/ai.d.ts +32 -11
- package/dist/ai.js +253 -75
- package/dist/client.d.ts +1 -1
- package/dist/client.js +4 -4
- package/dist/{contract-B0kAXoaL.js → contract-CG_adnu_.js} +2 -1
- package/dist/{contract-DL8btVd9.d.ts → contract-C_3dIIEU.d.ts} +4 -1
- package/dist/devtools-server.browser.js +2 -2
- package/dist/devtools-server.js +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/http.js +4 -3
- package/dist/idempotent-replay-BMyHrP0L.js +19 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/{internal-Dm8Ejnud.js → internal-D6wNxTck.js} +3 -3
- package/dist/{log-Dg1I8NRr.d.ts → log-ldf5g8Cx.d.ts} +74 -56
- package/dist/log-memory.d.ts +1 -1
- package/dist/log-memory.js +173 -96
- package/dist/{log-polling-RO7kclzR.js → log-polling-6COoN60V.js} +1 -1
- package/dist/log-postgres.d.ts +1 -1
- package/dist/log-postgres.js +235 -192
- package/dist/log-redis.d.ts +1 -1
- package/dist/log-redis.js +453 -263
- package/dist/log-sqlite.d.ts +1 -1
- package/dist/log-sqlite.js +216 -127
- package/dist/otel.d.ts +1 -1
- package/dist/otel.js +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/recovery-vercel.d.ts +2 -2
- package/dist/recovery-vercel.js +9 -10
- package/dist/server-DJgD2YWP.js +877 -0
- package/dist/server.browser.js +4 -4
- package/dist/server.d.ts +46 -27
- package/dist/server.js +1 -1
- package/dist/{telemetry-C78al20p.d.ts → telemetry-Cso0qyHQ.d.ts} +1 -1
- package/dist/{wire-2QpU1EtJ.js → wire-BVsgR8o9.js} +1 -1
- package/docs/01-quickstart.mdx +7 -7
- package/docs/concepts/01-contracts.mdx +22 -22
- package/docs/concepts/02-handlers.mdx +223 -89
- package/docs/concepts/03-durability.mdx +199 -112
- package/docs/concepts/04-state.mdx +27 -1
- package/docs/guides/01-timers.mdx +4 -4
- package/docs/guides/02-cancellation.mdx +32 -4
- package/docs/guides/05-production.mdx +61 -27
- package/docs/guides/06-ai-agents.mdx +151 -70
- package/docs/guides/07-devtools.mdx +6 -3
- package/docs/guides/08-application-data.mdx +5 -6
- package/docs/index.mdx +30 -14
- package/docs/reference/01-api.mdx +305 -70
- package/package.json +31 -31
- package/dist/server-DYsnKTTy.js +0 -780
package/dist/ai.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as assertSyncSchema } from "./validate-XKT4FSNn.js";
|
|
2
|
-
import { t as contract } from "./contract-
|
|
2
|
+
import { t as contract } from "./contract-CG_adnu_.js";
|
|
3
3
|
//#region src/ai-projector.ts
|
|
4
4
|
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
5
|
const cloneMessage = (messageId, base) => structuredClone(base ?? {
|
|
@@ -442,7 +442,7 @@ const completedPayloadSchema = schema("ai.generation.completed", (value) => {
|
|
|
442
442
|
return { value };
|
|
443
443
|
});
|
|
444
444
|
const compactionPayloadSchema = (messageSchema) => schema("ai.compaction.completed", (value) => {
|
|
445
|
-
if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "generationId") || !stringField(value, "throughMessageId") || !Array.isArray(value["messages"])) return issue("invalid ai.compaction.completed payload");
|
|
445
|
+
if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "generationId") || !stringField(value, "throughMessageId") || !Array.isArray(value["messages"]) || value["retainedMessageIds"] !== void 0 && (!Array.isArray(value["retainedMessageIds"]) || !value["retainedMessageIds"].every((candidate) => typeof candidate === "string"))) return issue("invalid ai.compaction.completed payload");
|
|
446
446
|
const messages = [];
|
|
447
447
|
for (const candidate of value["messages"]) {
|
|
448
448
|
const result = validateMessage(candidate, messageSchema);
|
|
@@ -452,7 +452,8 @@ const compactionPayloadSchema = (messageSchema) => schema("ai.compaction.complet
|
|
|
452
452
|
return { value: {
|
|
453
453
|
generationId: value["generationId"],
|
|
454
454
|
throughMessageId: value["throughMessageId"],
|
|
455
|
-
messages
|
|
455
|
+
messages,
|
|
456
|
+
...value["retainedMessageIds"] === void 0 ? {} : { retainedMessageIds: value["retainedMessageIds"] }
|
|
456
457
|
} };
|
|
457
458
|
});
|
|
458
459
|
function createEvents(options) {
|
|
@@ -464,17 +465,18 @@ function createEvents(options) {
|
|
|
464
465
|
"ai.message.created": messagePayloadSchema(messageSchema),
|
|
465
466
|
"ai.message.completed": jsonObject("ai.message.completed", (value) => stringField(value, "messageId")),
|
|
466
467
|
"ai.message.interrupted": jsonObject("ai.message.interrupted", (value) => stringField(value, "messageId") && optionalStringField(value, "generationId") && optionalStringField(value, "reason") && (value["lastSeenIndex"] === void 0 || typeof value["lastSeenIndex"] === "number" && Number.isInteger(value["lastSeenIndex"]) && value["lastSeenIndex"] >= 0)),
|
|
467
|
-
"ai.generation.requested": jsonObject("ai.generation.requested", (value) => stringField(value, "messageId") && optionalStringField(value, "responseMessageId") && (value["reason"] === "message" || value["reason"] === "
|
|
468
|
+
"ai.generation.requested": jsonObject("ai.generation.requested", (value) => stringField(value, "messageId") && optionalStringField(value, "responseMessageId") && (value["reason"] === "message" || value["reason"] === "tool" || value["reason"] === "input" || value["reason"] === "retry")),
|
|
469
|
+
"ai.retry.requested": jsonObject("ai.retry.requested", (value) => stringField(value, "messageId") && stringField(value, "responseMessageId") && stringField(value, "retryId")),
|
|
468
470
|
"ai.generation.started": jsonObject("ai.generation.started", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "responseMessageId") && typeof value["attempt"] === "number" && Number.isInteger(value["attempt"]) && value["attempt"] > 0 && stringField(value, "model")),
|
|
469
471
|
"ai.generation.progress": progressPayloadSchema,
|
|
470
472
|
"ai.generation.completed": completedPayloadSchema,
|
|
471
|
-
"ai.generation.failed": jsonObject("ai.generation.failed", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "responseMessageId") && stringField(value, "error") && (value["superseded"] === void 0 || typeof value["superseded"] === "boolean")),
|
|
472
|
-
"ai.tool.called": jsonObject("ai.tool.called", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "toolCallId") && stringField(value, "toolName") && (value["dynamic"] === void 0 || typeof value["dynamic"] === "boolean") && (value["providerExecuted"] === void 0 || typeof value["providerExecuted"] === "boolean") && optionalStringField(value, "title")),
|
|
473
|
+
"ai.generation.failed": jsonObject("ai.generation.failed", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "responseMessageId") && stringField(value, "error") && (value["superseded"] === void 0 || typeof value["superseded"] === "boolean") && (value["stepLimit"] === void 0 || typeof value["stepLimit"] === "boolean")),
|
|
474
|
+
"ai.tool.called": jsonObject("ai.tool.called", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "toolCallId") && stringField(value, "toolName") && (value["dynamic"] === void 0 || typeof value["dynamic"] === "boolean") && (value["providerExecuted"] === void 0 || typeof value["providerExecuted"] === "boolean") && (value["supportsDeferredResults"] === void 0 || typeof value["supportsDeferredResults"] === "boolean") && optionalStringField(value, "title")),
|
|
473
475
|
"ai.tool.result": jsonObject("ai.tool.result", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "toolCallId") && optionalStringField(value, "toolName") && optionalStringField(value, "error") && (value["denied"] === void 0 || typeof value["denied"] === "boolean") && (value["preliminary"] === void 0 || typeof value["preliminary"] === "boolean") && (value["phase"] === void 0 || value["phase"] === "input" || value["phase"] === "execution") && (value["dynamic"] === void 0 || typeof value["dynamic"] === "boolean") && (value["providerExecuted"] === void 0 || typeof value["providerExecuted"] === "boolean")),
|
|
474
476
|
"ai.approval.requested": jsonObject("ai.approval.requested", (value) => stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "approvalId") && stringField(value, "toolCallId") && (value["isAutomatic"] === void 0 || typeof value["isAutomatic"] === "boolean") && optionalStringField(value, "signature")),
|
|
475
|
-
"ai.approval.responded": jsonObject("ai.approval.responded", (value) => stringField(value, "messageId") && stringField(value, "approvalId") && typeof value["approved"] === "boolean" && optionalStringField(value, "reason")),
|
|
476
|
-
"ai.input.requested": jsonObject("ai.input.requested", (value) => stringField(value, "messageId") &&
|
|
477
|
-
"ai.input.responded": jsonObject("ai.input.responded", (value) => stringField(value, "messageId") && stringField(value, "inputId") && stringField(value, "name")),
|
|
477
|
+
"ai.approval.responded": jsonObject("ai.approval.responded", (value) => stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "approvalId") && typeof value["approved"] === "boolean" && optionalStringField(value, "reason")),
|
|
478
|
+
"ai.input.requested": jsonObject("ai.input.requested", (value) => stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "inputId") && stringField(value, "name")),
|
|
479
|
+
"ai.input.responded": jsonObject("ai.input.responded", (value) => stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "inputId") && stringField(value, "name")),
|
|
478
480
|
"ai.compaction.requested": jsonObject("ai.compaction.requested", (value) => stringField(value, "generationId") && stringField(value, "throughMessageId")),
|
|
479
481
|
"ai.compaction.completed": compactionPayloadSchema(messageSchema)
|
|
480
482
|
};
|
|
@@ -485,7 +487,10 @@ const initialState = () => ({
|
|
|
485
487
|
messages: [],
|
|
486
488
|
status: "idle",
|
|
487
489
|
activeGeneration: null,
|
|
490
|
+
activeRequestId: null,
|
|
491
|
+
activeResponseMessageId: null,
|
|
488
492
|
activeProjection: null,
|
|
493
|
+
responseGenerationIds: {},
|
|
489
494
|
pendingApprovals: [],
|
|
490
495
|
pendingInputs: [],
|
|
491
496
|
tools: [],
|
|
@@ -522,12 +527,85 @@ const answerApproval = (messages, response) => messages.map((message) => {
|
|
|
522
527
|
parts
|
|
523
528
|
} : message;
|
|
524
529
|
});
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
+
const projectMessageChunks = (messages, messageId, chunks) => messages.map((message) => message.id === messageId ? projectUIMessage({
|
|
531
|
+
messageId,
|
|
532
|
+
base: message,
|
|
533
|
+
chunks
|
|
534
|
+
}) : message);
|
|
535
|
+
const approvalRequestedChunk = (payload) => ({
|
|
536
|
+
type: "tool-approval-request",
|
|
537
|
+
approvalId: payload.approvalId,
|
|
538
|
+
toolCallId: payload.toolCallId,
|
|
539
|
+
...payload.isAutomatic === void 0 ? {} : { isAutomatic: payload.isAutomatic },
|
|
540
|
+
...payload.signature === void 0 ? {} : { signature: payload.signature }
|
|
530
541
|
});
|
|
542
|
+
const approvalRespondedChunk = (payload) => ({
|
|
543
|
+
type: "tool-approval-response",
|
|
544
|
+
approvalId: payload.approvalId,
|
|
545
|
+
approved: payload.approved,
|
|
546
|
+
...payload.reason === void 0 ? {} : { reason: payload.reason }
|
|
547
|
+
});
|
|
548
|
+
const toolResultChunk = (payload) => {
|
|
549
|
+
if (payload.phase === "input") return {
|
|
550
|
+
type: "tool-input-error",
|
|
551
|
+
toolCallId: payload.toolCallId,
|
|
552
|
+
toolName: payload.toolName ?? "unknown",
|
|
553
|
+
input: payload.rawInput ?? payload.input,
|
|
554
|
+
errorText: payload.error ?? "Invalid tool input",
|
|
555
|
+
...payload.dynamic === void 0 ? {} : { dynamic: payload.dynamic },
|
|
556
|
+
...payload.providerExecuted === void 0 ? {} : { providerExecuted: payload.providerExecuted },
|
|
557
|
+
...payload.providerMetadata === void 0 ? {} : { providerMetadata: payload.providerMetadata },
|
|
558
|
+
...payload.toolMetadata === void 0 ? {} : { toolMetadata: payload.toolMetadata }
|
|
559
|
+
};
|
|
560
|
+
if (payload.denied === true) return {
|
|
561
|
+
type: "tool-output-denied",
|
|
562
|
+
toolCallId: payload.toolCallId
|
|
563
|
+
};
|
|
564
|
+
if (payload.error !== void 0) return {
|
|
565
|
+
type: "tool-output-error",
|
|
566
|
+
toolCallId: payload.toolCallId,
|
|
567
|
+
errorText: payload.error,
|
|
568
|
+
...payload.dynamic === void 0 ? {} : { dynamic: payload.dynamic },
|
|
569
|
+
...payload.providerExecuted === void 0 ? {} : { providerExecuted: payload.providerExecuted },
|
|
570
|
+
...payload.providerMetadata === void 0 ? {} : { providerMetadata: payload.providerMetadata },
|
|
571
|
+
...payload.toolMetadata === void 0 ? {} : { toolMetadata: payload.toolMetadata }
|
|
572
|
+
};
|
|
573
|
+
return {
|
|
574
|
+
type: "tool-output-available",
|
|
575
|
+
toolCallId: payload.toolCallId,
|
|
576
|
+
output: payload.output,
|
|
577
|
+
...payload.preliminary === void 0 ? {} : { preliminary: payload.preliminary },
|
|
578
|
+
...payload.dynamic === void 0 ? {} : { dynamic: payload.dynamic },
|
|
579
|
+
...payload.providerExecuted === void 0 ? {} : { providerExecuted: payload.providerExecuted },
|
|
580
|
+
...payload.providerMetadata === void 0 ? {} : { providerMetadata: payload.providerMetadata },
|
|
581
|
+
...payload.toolMetadata === void 0 ? {} : { toolMetadata: payload.toolMetadata }
|
|
582
|
+
};
|
|
583
|
+
};
|
|
584
|
+
const waitingStatus = (state) => {
|
|
585
|
+
if (state.pendingApprovals.length > 0 || state.pendingInputs.length > 0) return "waiting";
|
|
586
|
+
return state.activeGeneration !== null || state.tools.some((tool) => tool.status === "running") ? "generating" : "idle";
|
|
587
|
+
};
|
|
588
|
+
const lifecycleStatus = (previous, next) => previous.status === "failed" ? "failed" : waitingStatus(next);
|
|
589
|
+
const isStaleGeneration = (state, messageId, generationId) => {
|
|
590
|
+
const current = state.responseGenerationIds[messageId];
|
|
591
|
+
return current !== void 0 && current !== generationId;
|
|
592
|
+
};
|
|
593
|
+
const projectedMessage = (projection) => {
|
|
594
|
+
const projectionEvents = [...projection.batches];
|
|
595
|
+
for (const event of projection.toolEvents) if (event.type === "ai.tool.result") projectionEvents.push({
|
|
596
|
+
index: event.index,
|
|
597
|
+
chunks: [toolResultChunk(event.payload)]
|
|
598
|
+
});
|
|
599
|
+
for (const event of projection.approvalEvents) projectionEvents.push({
|
|
600
|
+
index: event.index,
|
|
601
|
+
chunks: [event.type === "ai.approval.requested" ? approvalRequestedChunk(event.payload) : approvalRespondedChunk(event.payload)]
|
|
602
|
+
});
|
|
603
|
+
return projectUIMessage({
|
|
604
|
+
messageId: projection.responseMessageId,
|
|
605
|
+
...projection.baseMessage === void 0 ? {} : { base: projection.baseMessage },
|
|
606
|
+
chunks: projectionEvents.toSorted((left, right) => left.index - right.index).flatMap((event) => event.chunks)
|
|
607
|
+
});
|
|
608
|
+
};
|
|
531
609
|
const reduceToolActivity = (tools, event) => {
|
|
532
610
|
if (event.type === "ai.tool.called") {
|
|
533
611
|
const payload = event.payload;
|
|
@@ -537,10 +615,10 @@ const reduceToolActivity = (tools, event) => {
|
|
|
537
615
|
...providerMetadata === void 0 ? {} : { callProviderMetadata: providerMetadata },
|
|
538
616
|
status: "running"
|
|
539
617
|
};
|
|
540
|
-
return [...tools.filter((candidate) => candidate.toolCallId !== payload.toolCallId), tool];
|
|
618
|
+
return [...tools.filter((candidate) => candidate.generationId !== payload.generationId || candidate.toolCallId !== payload.toolCallId), tool];
|
|
541
619
|
}
|
|
542
620
|
const payload = event.payload;
|
|
543
|
-
const existing = tools.find((candidate) => candidate.toolCallId === payload.toolCallId);
|
|
621
|
+
const existing = tools.find((candidate) => candidate.generationId === payload.generationId && candidate.toolCallId === payload.toolCallId);
|
|
544
622
|
const toolName = payload.toolName ?? existing?.toolName;
|
|
545
623
|
const input = payload.input === void 0 ? existing?.input : payload.input;
|
|
546
624
|
const dynamic = payload.dynamic ?? existing?.dynamic;
|
|
@@ -567,15 +645,19 @@ const reduceToolActivity = (tools, event) => {
|
|
|
567
645
|
...existing?.title === void 0 ? {} : { title: existing.title },
|
|
568
646
|
status: payload.denied ? "denied" : payload.error ? "failed" : payload.preliminary ? "running" : "completed"
|
|
569
647
|
};
|
|
570
|
-
return [...tools.filter((candidate) => candidate.toolCallId !== payload.toolCallId), tool];
|
|
648
|
+
return [...tools.filter((candidate) => candidate.generationId !== payload.generationId || candidate.toolCallId !== payload.toolCallId), tool];
|
|
571
649
|
};
|
|
572
650
|
const projectedTools = (projection) => projection.toolEvents.reduce((tools, event) => reduceToolActivity(tools, event), projection.baseTools);
|
|
573
|
-
const stopGenerationTools = (tools, generationId, responseMessageId, error) => tools.map((tool) =>
|
|
651
|
+
const stopGenerationTools = (tools, generationId, responseMessageId, error) => tools.map((tool) => tool.generationId === generationId || tool.messageId === responseMessageId ? {
|
|
574
652
|
...tool,
|
|
575
653
|
status: "failed",
|
|
576
654
|
error,
|
|
577
655
|
preliminary: false
|
|
578
656
|
} : tool);
|
|
657
|
+
const generationRequestId = (generationId) => {
|
|
658
|
+
const markerIndex = generationId.lastIndexOf(":generation:");
|
|
659
|
+
return markerIndex === -1 ? void 0 : generationId.slice(0, markerIndex);
|
|
660
|
+
};
|
|
579
661
|
/** Pure projection of the built-in AI protocol. Unknown extension events are ignored. */
|
|
580
662
|
function reduceAIState(state, event) {
|
|
581
663
|
if (state.status === "closed") return state;
|
|
@@ -594,6 +676,8 @@ function reduceAIState(state, event) {
|
|
|
594
676
|
...state,
|
|
595
677
|
status: "closed",
|
|
596
678
|
activeGeneration: null,
|
|
679
|
+
activeRequestId: null,
|
|
680
|
+
activeResponseMessageId: null,
|
|
597
681
|
activeProjection: null
|
|
598
682
|
};
|
|
599
683
|
case "ai.message.created": {
|
|
@@ -605,27 +689,46 @@ function reduceAIState(state, event) {
|
|
|
605
689
|
}
|
|
606
690
|
case "ai.generation.requested": {
|
|
607
691
|
const payload = event.payload;
|
|
692
|
+
const schedulesToolContinuation = payload.reason === "tool";
|
|
693
|
+
const responseMessageId = payload.responseMessageId ?? (payload.reason === "message" ? `${payload.messageId}:assistant` : payload.messageId);
|
|
694
|
+
const ownerGenerationId = state.responseGenerationIds[responseMessageId] ?? state.activeGeneration?.generationId;
|
|
695
|
+
const ownerRequestId = ownerGenerationId === void 0 ? void 0 : generationRequestId(ownerGenerationId);
|
|
696
|
+
const requestId = event.id;
|
|
697
|
+
if (!(payload.reason === "message" || (payload.reason === "tool" ? ownerGenerationId !== void 0 && requestId === `ai.generate:tools:${ownerGenerationId}` && state.activeRequestId === ownerRequestId && state.status !== "failed" : payload.reason === "retry" ? state.status === "failed" : ownerGenerationId !== void 0 && requestId?.startsWith(`ai.generate:input:${encodeURIComponent(responseMessageId)}:${encodeURIComponent(ownerGenerationId)}:`) === true && state.activeRequestId === ownerRequestId && state.status !== "failed"))) return state;
|
|
608
698
|
return {
|
|
609
699
|
...state,
|
|
610
700
|
messages: payload.reason === "retry" && payload.responseMessageId !== void 0 ? state.messages.filter((message) => message.id !== payload.responseMessageId) : state.messages,
|
|
611
|
-
status: "generating",
|
|
612
|
-
|
|
701
|
+
status: schedulesToolContinuation ? state.status : "generating",
|
|
702
|
+
activeRequestId: requestId ?? null,
|
|
703
|
+
activeResponseMessageId: responseMessageId,
|
|
704
|
+
error: schedulesToolContinuation ? state.error : null
|
|
613
705
|
};
|
|
614
706
|
}
|
|
615
707
|
case "ai.generation.started": {
|
|
616
708
|
const payload = event.payload;
|
|
709
|
+
if (state.activeRequestId !== null && state.activeRequestId !== payload.requestId) return state;
|
|
710
|
+
const currentOwner = state.responseGenerationIds[payload.responseMessageId];
|
|
711
|
+
const marker = ":generation:";
|
|
712
|
+
const currentAttempt = currentOwner?.startsWith(`${payload.requestId}${marker}`) ? Number(currentOwner.slice(`${payload.requestId}${marker}`.length)) : void 0;
|
|
713
|
+
if (currentAttempt !== void 0 && Number.isFinite(currentAttempt) && currentAttempt >= payload.attempt) return state;
|
|
617
714
|
const baseMessage = state.messages.find((message) => message.id === payload.responseMessageId);
|
|
618
715
|
return {
|
|
619
716
|
...state,
|
|
620
717
|
status: "generating",
|
|
621
718
|
activeGeneration: payload,
|
|
719
|
+
activeResponseMessageId: null,
|
|
720
|
+
responseGenerationIds: {
|
|
721
|
+
...state.responseGenerationIds,
|
|
722
|
+
[payload.responseMessageId]: payload.generationId
|
|
723
|
+
},
|
|
622
724
|
activeProjection: {
|
|
623
725
|
generationId: payload.generationId,
|
|
624
726
|
responseMessageId: payload.responseMessageId,
|
|
625
727
|
...baseMessage === void 0 ? {} : { baseMessage },
|
|
626
728
|
batches: [],
|
|
627
729
|
baseTools: state.tools,
|
|
628
|
-
toolEvents: []
|
|
730
|
+
toolEvents: [],
|
|
731
|
+
approvalEvents: []
|
|
629
732
|
},
|
|
630
733
|
error: null
|
|
631
734
|
};
|
|
@@ -668,13 +771,16 @@ function reduceAIState(state, event) {
|
|
|
668
771
|
}
|
|
669
772
|
case "ai.generation.failed": {
|
|
670
773
|
const payload = event.payload;
|
|
671
|
-
if (state.activeGeneration?.generationId !== payload.generationId) return state;
|
|
774
|
+
if (state.activeGeneration?.generationId !== payload.generationId && !(payload.stepLimit === true && state.responseGenerationIds[payload.responseMessageId] === payload.generationId)) return state;
|
|
672
775
|
if (payload.superseded === true) return {
|
|
673
776
|
...state,
|
|
674
777
|
messages: state.messages.filter((message) => message.id !== payload.responseMessageId),
|
|
675
778
|
tools: state.tools.filter((tool) => tool.generationId !== payload.generationId),
|
|
779
|
+
pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== payload.generationId),
|
|
780
|
+
pendingInputs: state.pendingInputs.filter((input) => input.generationId !== payload.generationId),
|
|
676
781
|
activeGeneration: null,
|
|
677
|
-
activeProjection: null
|
|
782
|
+
activeProjection: null,
|
|
783
|
+
compaction: state.compaction?.generationId === payload.generationId ? null : state.compaction
|
|
678
784
|
};
|
|
679
785
|
const projection = state.activeProjection;
|
|
680
786
|
const shouldProject = projection?.generationId === payload.generationId && (projection.baseMessage !== void 0 || projection.batches.length > 0);
|
|
@@ -683,7 +789,9 @@ function reduceAIState(state, event) {
|
|
|
683
789
|
messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(projection), `Generation failed before tool completion: ${payload.error}`)) : state.messages,
|
|
684
790
|
status: "failed",
|
|
685
791
|
activeGeneration: null,
|
|
792
|
+
activeResponseMessageId: null,
|
|
686
793
|
activeProjection: null,
|
|
794
|
+
compaction: state.compaction?.generationId === payload.generationId ? null : state.compaction,
|
|
687
795
|
pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== payload.generationId),
|
|
688
796
|
tools: stopGenerationTools(state.tools, payload.generationId, payload.responseMessageId, payload.error),
|
|
689
797
|
error: payload.error
|
|
@@ -692,32 +800,72 @@ function reduceAIState(state, event) {
|
|
|
692
800
|
case "ai.message.interrupted": {
|
|
693
801
|
const payload = event.payload;
|
|
694
802
|
const active = state.activeGeneration;
|
|
695
|
-
if (active === null
|
|
803
|
+
if (active === null) {
|
|
804
|
+
if (payload.generationId === void 0 && state.activeRequestId !== null && state.activeResponseMessageId === payload.messageId) return {
|
|
805
|
+
...state,
|
|
806
|
+
status: "idle",
|
|
807
|
+
activeRequestId: null,
|
|
808
|
+
activeResponseMessageId: null,
|
|
809
|
+
error: null
|
|
810
|
+
};
|
|
811
|
+
if (!(state.pendingApprovals.some((approval) => approval.messageId === payload.messageId) || state.pendingInputs.some((input) => input.messageId === payload.messageId) || state.tools.some((tool) => tool.messageId === payload.messageId && tool.status === "running"))) return state;
|
|
812
|
+
const next = {
|
|
813
|
+
...state,
|
|
814
|
+
activeRequestId: null,
|
|
815
|
+
activeResponseMessageId: null,
|
|
816
|
+
messages: state.messages.map((message) => message.id === payload.messageId ? interruptUIMessage(message) : message),
|
|
817
|
+
pendingApprovals: state.pendingApprovals.filter((approval) => approval.messageId !== payload.messageId),
|
|
818
|
+
pendingInputs: state.pendingInputs.filter((input) => input.messageId !== payload.messageId),
|
|
819
|
+
tools: stopGenerationTools(state.tools, payload.generationId ?? "", payload.messageId, "Tool execution was interrupted.")
|
|
820
|
+
};
|
|
821
|
+
return {
|
|
822
|
+
...next,
|
|
823
|
+
status: lifecycleStatus(state, next)
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
if (payload.generationId === void 0 ? payload.messageId !== active.responseMessageId : payload.generationId !== active.generationId) return state;
|
|
696
827
|
const projection = state.activeProjection;
|
|
697
828
|
const cutoff = payload.lastSeenIndex ?? Number.POSITIVE_INFINITY;
|
|
698
829
|
const retained = projection?.generationId === active.generationId ? {
|
|
699
830
|
...projection,
|
|
700
831
|
batches: projection.batches.filter((batch) => batch.index <= cutoff),
|
|
701
|
-
toolEvents: projection.toolEvents.filter((toolEvent) => toolEvent.index <= cutoff)
|
|
832
|
+
toolEvents: projection.toolEvents.filter((toolEvent) => toolEvent.index <= cutoff),
|
|
833
|
+
approvalEvents: projection.approvalEvents.filter((approvalEvent) => approvalEvent.index <= cutoff)
|
|
702
834
|
} : null;
|
|
703
835
|
const shouldProject = retained !== null && (retained.baseMessage !== void 0 || retained.batches.length > 0);
|
|
704
836
|
const next = {
|
|
705
837
|
...state,
|
|
706
838
|
messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(retained))) : state.messages,
|
|
707
839
|
activeGeneration: null,
|
|
840
|
+
activeRequestId: null,
|
|
841
|
+
activeResponseMessageId: null,
|
|
708
842
|
activeProjection: null,
|
|
709
843
|
pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== active.generationId),
|
|
710
844
|
tools: stopGenerationTools(retained === null ? state.tools : projectedTools(retained), active.generationId, active.responseMessageId, "Tool execution was interrupted.")
|
|
711
845
|
};
|
|
712
846
|
return {
|
|
713
847
|
...next,
|
|
714
|
-
status:
|
|
848
|
+
status: lifecycleStatus(state, next)
|
|
715
849
|
};
|
|
716
850
|
}
|
|
717
851
|
case "ai.tool.called": {
|
|
718
852
|
const payload = event.payload;
|
|
719
853
|
const projection = state.activeProjection;
|
|
720
|
-
if (
|
|
854
|
+
if (isStaleGeneration(state, payload.messageId, payload.generationId)) return state;
|
|
855
|
+
if (state.tools.some((tool) => tool.generationId === payload.generationId && tool.toolCallId === payload.toolCallId && tool.status === "failed" && state.activeProjection?.generationId !== payload.generationId)) return state;
|
|
856
|
+
if (projection?.generationId !== payload.generationId) {
|
|
857
|
+
const next = {
|
|
858
|
+
...state,
|
|
859
|
+
tools: reduceToolActivity(state.tools, {
|
|
860
|
+
type: "ai.tool.called",
|
|
861
|
+
payload
|
|
862
|
+
})
|
|
863
|
+
};
|
|
864
|
+
return {
|
|
865
|
+
...next,
|
|
866
|
+
status: lifecycleStatus(state, next)
|
|
867
|
+
};
|
|
868
|
+
}
|
|
721
869
|
const activeProjection = {
|
|
722
870
|
...projection,
|
|
723
871
|
toolEvents: [...projection.toolEvents, {
|
|
@@ -735,7 +883,22 @@ function reduceAIState(state, event) {
|
|
|
735
883
|
case "ai.tool.result": {
|
|
736
884
|
const payload = event.payload;
|
|
737
885
|
const projection = state.activeProjection;
|
|
738
|
-
if (
|
|
886
|
+
if (isStaleGeneration(state, payload.messageId, payload.generationId)) return state;
|
|
887
|
+
if (state.tools.some((tool) => tool.generationId === payload.generationId && tool.toolCallId === payload.toolCallId && tool.status === "failed" && state.activeProjection?.generationId !== payload.generationId)) return state;
|
|
888
|
+
if (projection?.generationId !== payload.generationId) {
|
|
889
|
+
const next = {
|
|
890
|
+
...state,
|
|
891
|
+
messages: projectMessageChunks(state.messages, payload.messageId, [toolResultChunk(payload)]),
|
|
892
|
+
tools: reduceToolActivity(state.tools, {
|
|
893
|
+
type: "ai.tool.result",
|
|
894
|
+
payload
|
|
895
|
+
})
|
|
896
|
+
};
|
|
897
|
+
return {
|
|
898
|
+
...next,
|
|
899
|
+
status: lifecycleStatus(state, next)
|
|
900
|
+
};
|
|
901
|
+
}
|
|
739
902
|
const activeProjection = {
|
|
740
903
|
...projection,
|
|
741
904
|
toolEvents: [...projection.toolEvents, {
|
|
@@ -746,50 +909,88 @@ function reduceAIState(state, event) {
|
|
|
746
909
|
};
|
|
747
910
|
return {
|
|
748
911
|
...state,
|
|
912
|
+
messages: upsertMessage(state.messages, projectedMessage(activeProjection)),
|
|
749
913
|
activeProjection,
|
|
750
914
|
tools: projectedTools(activeProjection)
|
|
751
915
|
};
|
|
752
916
|
}
|
|
753
917
|
case "ai.approval.requested": {
|
|
754
918
|
const payload = event.payload;
|
|
755
|
-
|
|
919
|
+
const projection = state.activeProjection;
|
|
920
|
+
if (isStaleGeneration(state, payload.messageId, payload.generationId)) return state;
|
|
921
|
+
if (state.tools.some((tool) => tool.generationId === payload.generationId && tool.toolCallId === payload.toolCallId && tool.status === "failed" && projection?.generationId !== payload.generationId)) return state;
|
|
922
|
+
const activeProjection = projection?.generationId === payload.generationId ? {
|
|
923
|
+
...projection,
|
|
924
|
+
approvalEvents: [...projection.approvalEvents, {
|
|
925
|
+
index: event.index,
|
|
926
|
+
type: "ai.approval.requested",
|
|
927
|
+
payload
|
|
928
|
+
}]
|
|
929
|
+
} : projection;
|
|
930
|
+
const next = {
|
|
756
931
|
...state,
|
|
757
|
-
|
|
932
|
+
messages: activeProjection === null || activeProjection === projection ? projectMessageChunks(state.messages, payload.messageId, [approvalRequestedChunk(payload)]) : upsertMessage(state.messages, projectedMessage(activeProjection)),
|
|
933
|
+
activeProjection,
|
|
934
|
+
pendingApprovals: payload.isAutomatic === true ? state.pendingApprovals : [...state.pendingApprovals.filter((approval) => approval.approvalId !== payload.approvalId || approval.messageId !== payload.messageId), payload]
|
|
935
|
+
};
|
|
936
|
+
return {
|
|
937
|
+
...next,
|
|
938
|
+
status: lifecycleStatus(state, next)
|
|
758
939
|
};
|
|
759
940
|
}
|
|
760
941
|
case "ai.approval.responded": {
|
|
761
942
|
const payload = event.payload;
|
|
943
|
+
if (isStaleGeneration(state, payload.messageId, payload.generationId)) return state;
|
|
944
|
+
const projection = state.activeProjection;
|
|
945
|
+
const pendingRequest = state.pendingApprovals.find((candidate) => candidate.approvalId === payload.approvalId && candidate.messageId === payload.messageId && candidate.generationId === payload.generationId);
|
|
946
|
+
const activeRequest = projection?.approvalEvents.findLast((candidate) => candidate.type === "ai.approval.requested" && candidate.payload.approvalId === payload.approvalId && candidate.payload.messageId === payload.messageId && candidate.payload.generationId === payload.generationId);
|
|
947
|
+
if (state.status === "failed" && pendingRequest === void 0 && activeRequest === void 0) return state;
|
|
948
|
+
const activeProjection = projection !== null && activeRequest !== void 0 ? {
|
|
949
|
+
...projection,
|
|
950
|
+
approvalEvents: [...projection.approvalEvents, {
|
|
951
|
+
index: event.index,
|
|
952
|
+
type: "ai.approval.responded",
|
|
953
|
+
payload
|
|
954
|
+
}]
|
|
955
|
+
} : projection;
|
|
762
956
|
const next = {
|
|
763
957
|
...state,
|
|
764
|
-
messages: answerApproval(state.messages, payload),
|
|
765
|
-
|
|
958
|
+
messages: activeProjection === null || activeProjection === projection ? answerApproval(state.messages, payload) : upsertMessage(state.messages, projectedMessage(activeProjection)),
|
|
959
|
+
activeProjection,
|
|
960
|
+
pendingApprovals: state.pendingApprovals.filter((approval) => approval.approvalId !== payload.approvalId || approval.messageId !== payload.messageId || approval.generationId !== payload.generationId)
|
|
766
961
|
};
|
|
767
962
|
return {
|
|
768
963
|
...next,
|
|
769
|
-
status:
|
|
964
|
+
status: lifecycleStatus(state, next)
|
|
770
965
|
};
|
|
771
966
|
}
|
|
772
967
|
case "ai.input.requested": {
|
|
773
968
|
const payload = event.payload;
|
|
774
|
-
return
|
|
969
|
+
if (isStaleGeneration(state, payload.messageId, payload.generationId)) return state;
|
|
970
|
+
const next = {
|
|
775
971
|
...state,
|
|
776
|
-
pendingInputs: [...state.pendingInputs.filter((input) => input.inputId !== payload.inputId), payload]
|
|
777
|
-
|
|
972
|
+
pendingInputs: [...state.pendingInputs.filter((input) => input.messageId !== payload.messageId || input.generationId !== payload.generationId || input.inputId !== payload.inputId), payload]
|
|
973
|
+
};
|
|
974
|
+
return {
|
|
975
|
+
...next,
|
|
976
|
+
status: lifecycleStatus(state, next)
|
|
778
977
|
};
|
|
779
978
|
}
|
|
780
979
|
case "ai.input.responded": {
|
|
781
980
|
const payload = event.payload;
|
|
981
|
+
if (isStaleGeneration(state, payload.messageId, payload.generationId)) return state;
|
|
782
982
|
const next = {
|
|
783
983
|
...state,
|
|
784
|
-
pendingInputs: state.pendingInputs.filter((input) => input.inputId !== payload.inputId)
|
|
984
|
+
pendingInputs: state.pendingInputs.filter((input) => input.inputId !== payload.inputId || input.messageId !== payload.messageId || input.generationId !== payload.generationId)
|
|
785
985
|
};
|
|
786
986
|
return {
|
|
787
987
|
...next,
|
|
788
|
-
status:
|
|
988
|
+
status: lifecycleStatus(state, next)
|
|
789
989
|
};
|
|
790
990
|
}
|
|
791
991
|
case "ai.compaction.requested": {
|
|
792
992
|
const payload = event.payload;
|
|
993
|
+
if (state.activeGeneration?.generationId !== payload.generationId) return state;
|
|
793
994
|
return {
|
|
794
995
|
...state,
|
|
795
996
|
compaction: {
|
|
@@ -800,6 +1001,7 @@ function reduceAIState(state, event) {
|
|
|
800
1001
|
}
|
|
801
1002
|
case "ai.compaction.completed": {
|
|
802
1003
|
const payload = event.payload;
|
|
1004
|
+
if (state.activeGeneration?.generationId !== payload.generationId || state.compaction?.status !== "running" || state.compaction.generationId !== payload.generationId) return state;
|
|
803
1005
|
return {
|
|
804
1006
|
...state,
|
|
805
1007
|
compaction: {
|
|
@@ -820,19 +1022,10 @@ function deriveUIMessages(history) {
|
|
|
820
1022
|
/** Build the standard AI state projection for any contract containing the protocol. */
|
|
821
1023
|
function createReducer(options) {
|
|
822
1024
|
return options.contract.reducer({
|
|
823
|
-
name: options.name ?? "a2.ai.state.
|
|
1025
|
+
name: options.name ?? "a2.ai.state.v6",
|
|
824
1026
|
initialState: initialState()
|
|
825
1027
|
}).fold((state, event) => reduceAIState(state, event));
|
|
826
1028
|
}
|
|
827
|
-
const generationRequest = (options) => ({
|
|
828
|
-
type: "ai.generation.requested",
|
|
829
|
-
id: options.id,
|
|
830
|
-
payload: {
|
|
831
|
-
messageId: options.messageId,
|
|
832
|
-
reason: options.reason,
|
|
833
|
-
...options.responseMessageId === void 0 ? {} : { responseMessageId: options.responseMessageId }
|
|
834
|
-
}
|
|
835
|
-
});
|
|
836
1029
|
const seedMessage = (message) => [{
|
|
837
1030
|
type: "ai.message.created",
|
|
838
1031
|
id: `ai.message:${message.id}`,
|
|
@@ -841,51 +1034,36 @@ const seedMessage = (message) => [{
|
|
|
841
1034
|
/** Pure append inputs for the built-in AI protocol. */
|
|
842
1035
|
const inputs = {
|
|
843
1036
|
message(message) {
|
|
844
|
-
|
|
845
|
-
if (message.role !== "user") return messageInputs;
|
|
846
|
-
return [...messageInputs, generationRequest({
|
|
847
|
-
id: `ai.generate:message:${message.id}`,
|
|
848
|
-
messageId: message.id,
|
|
849
|
-
reason: "message"
|
|
850
|
-
})];
|
|
1037
|
+
return seedMessage(message);
|
|
851
1038
|
},
|
|
852
1039
|
seed: seedMessage,
|
|
853
1040
|
approval(response) {
|
|
854
1041
|
return [{
|
|
855
1042
|
type: "ai.approval.responded",
|
|
856
|
-
id: `ai.approval:${response.approvalId}:response`,
|
|
1043
|
+
id: `ai.approval:${encodeURIComponent(response.messageId)}:${encodeURIComponent(response.generationId)}:${encodeURIComponent(response.approvalId)}:response`,
|
|
857
1044
|
payload: response
|
|
858
|
-
}
|
|
859
|
-
id: `ai.generate:approval:${response.approvalId}`,
|
|
860
|
-
messageId: response.messageId,
|
|
861
|
-
reason: "approval"
|
|
862
|
-
})];
|
|
1045
|
+
}];
|
|
863
1046
|
},
|
|
864
1047
|
input(response) {
|
|
865
1048
|
return [{
|
|
866
1049
|
type: "ai.input.responded",
|
|
867
|
-
id: `ai.input:${response.inputId}:response`,
|
|
1050
|
+
id: `ai.input:${encodeURIComponent(response.messageId)}:${encodeURIComponent(response.generationId)}:${encodeURIComponent(response.inputId)}:response`,
|
|
868
1051
|
payload: response
|
|
869
|
-
}
|
|
870
|
-
id: `ai.generate:input:${response.inputId}`,
|
|
871
|
-
messageId: response.messageId,
|
|
872
|
-
reason: "input"
|
|
873
|
-
})];
|
|
1052
|
+
}];
|
|
874
1053
|
},
|
|
875
1054
|
requestInput(request) {
|
|
876
1055
|
return [{
|
|
877
1056
|
type: "ai.input.requested",
|
|
878
|
-
id: `ai.input:${request.inputId}:request`,
|
|
1057
|
+
id: `ai.input:${encodeURIComponent(request.messageId)}:${encodeURIComponent(request.generationId)}:${encodeURIComponent(request.inputId)}:request`,
|
|
879
1058
|
payload: request
|
|
880
1059
|
}];
|
|
881
1060
|
},
|
|
882
1061
|
retry(retryOptions) {
|
|
883
|
-
return [
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
})];
|
|
1062
|
+
return [{
|
|
1063
|
+
type: "ai.retry.requested",
|
|
1064
|
+
id: `ai.retry:${retryOptions.retryId}`,
|
|
1065
|
+
payload: retryOptions
|
|
1066
|
+
}];
|
|
889
1067
|
},
|
|
890
1068
|
interrupt(interruption) {
|
|
891
1069
|
return [{
|
|
@@ -913,7 +1091,7 @@ function agent(options) {
|
|
|
913
1091
|
return {
|
|
914
1092
|
contract: agentContract,
|
|
915
1093
|
reducer: agentContract.reducer({
|
|
916
|
-
name: options.reducerName ?? "a2.ai.state.
|
|
1094
|
+
name: options.reducerName ?? "a2.ai.state.v6",
|
|
917
1095
|
initialState: initialState()
|
|
918
1096
|
}).fold((state, event) => reduceAIState(state, event))
|
|
919
1097
|
};
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as EventDefs, c as Reducer, r as ContractEvent, t as AppendInput } from "./contract-C_3dIIEU.js";
|
|
2
2
|
//#region src/client.d.ts
|
|
3
3
|
type ConnectionStatus = "idle" | "connecting" | "live" | "closed";
|
|
4
4
|
/**
|
package/dist/client.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { n as validateSync } from "./validate-XKT4FSNn.js";
|
|
2
2
|
import { t as A2Error } from "./errors-BJRMd-h6.js";
|
|
3
|
-
import { r as STREAM_TIMINGS } from "./internal-
|
|
4
|
-
import { i as eventFromWire, o as isWireEvent, t as errorFromWire } from "./wire-
|
|
3
|
+
import { r as STREAM_TIMINGS } from "./internal-D6wNxTck.js";
|
|
4
|
+
import { i as eventFromWire, o as isWireEvent, t as errorFromWire } from "./wire-BVsgR8o9.js";
|
|
5
5
|
//#region src/client.ts
|
|
6
6
|
/**
|
|
7
|
-
* a2/client — the framework-agnostic session client.
|
|
7
|
+
* experimental-a2/client — the framework-agnostic session client.
|
|
8
8
|
*
|
|
9
9
|
* Everything the browser needs to read a session live and push
|
|
10
10
|
* optimistically, with no framework attached: the SSE subscription with
|
|
11
11
|
* frontier resume and reconnection, the optimistic push queue with
|
|
12
12
|
* ack/rollback, and the local fold through the same reducer the server
|
|
13
|
-
* uses. `a2/react`'s `createReact` is a thin binding over it, and the
|
|
13
|
+
* uses. `experimental-a2/react`'s `createReact` is a thin binding over it, and the
|
|
14
14
|
* store contract (`subscribe`/`getSnapshot`) is exactly what
|
|
15
15
|
* `useSyncExternalStore` wants.
|
|
16
16
|
*
|
|
@@ -25,7 +25,7 @@ function makeReducerBuilder(events, options) {
|
|
|
25
25
|
//#region src/contract.ts
|
|
26
26
|
/**
|
|
27
27
|
* The contract — a2's primary noun. A named event vocabulary, shared by
|
|
28
|
-
* everything: the server implements it (a2/server's `createServer`),
|
|
28
|
+
* everything: the server implements it (experimental-a2/server's `createServer`),
|
|
29
29
|
* reducers derive from it (`contract.reducer`), the browser types its
|
|
30
30
|
* pushes off it. Isomorphic by construction: this module imports
|
|
31
31
|
* nothing but schema plumbing.
|
|
@@ -51,6 +51,7 @@ function contract(options) {
|
|
|
51
51
|
return {
|
|
52
52
|
name,
|
|
53
53
|
events,
|
|
54
|
+
batch: (...items) => items,
|
|
54
55
|
reducer(reducerOptions) {
|
|
55
56
|
return makeReducerBuilder(events, reducerOptions);
|
|
56
57
|
}
|
|
@@ -110,6 +110,7 @@ type AppendInput<D extends EventDefs> = { [T in keyof D & string]: {
|
|
|
110
110
|
payload: StandardSchemaV1.InferInput<D[T]>;
|
|
111
111
|
id?: string;
|
|
112
112
|
}; }[keyof D & string];
|
|
113
|
+
type EventBatchFactory<D extends EventDefs> = <const E extends readonly AppendInput<D>[]>(...events: E) => E;
|
|
113
114
|
type ReducerOptions<Seed, S> = {
|
|
114
115
|
/**
|
|
115
116
|
* Identity and snapshot cache key. Rename it when the fold's logic
|
|
@@ -134,6 +135,8 @@ type Contract<D extends EventDefs = EventDefs> = {
|
|
|
134
135
|
readonly name: string;
|
|
135
136
|
/** The vocabulary. */
|
|
136
137
|
readonly events: Readonly<D>;
|
|
138
|
+
/** Preserve literal types for a handler-returned event batch. */
|
|
139
|
+
readonly batch: EventBatchFactory<D>;
|
|
137
140
|
/**
|
|
138
141
|
* Derive a view from this contract. The reducer's `name` is its
|
|
139
142
|
* identity and snapshot cache key: rename it when the fold's logic
|
|
@@ -158,4 +161,4 @@ declare function contract<D extends EventDefs>(options: {
|
|
|
158
161
|
events: D;
|
|
159
162
|
}): Contract<D>;
|
|
160
163
|
//#endregion
|
|
161
|
-
export {
|
|
164
|
+
export { EventDefs as a, Reducer as c, EventBatchFactory as i, ReducerBuilder as l, Contract as n, ReducerOptions as o, ContractEvent as r, contract as s, AppendInput as t, StandardSchemaV1 as u };
|