experimental-a2 0.2.0 → 0.4.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 +169 -0
- package/dist/ai-B4YhEnfw.d.ts +333 -0
- package/dist/ai-server.d.ts +48 -9
- package/dist/ai-server.js +121 -49
- package/dist/ai.d.ts +2 -303
- package/dist/ai.js +231 -86
- package/dist/cli-B3VuxoDe.js +597 -0
- package/dist/cli-bin.d.ts +1 -0
- package/dist/cli-bin.js +5 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.js +2 -0
- package/dist/client-BrfDXQ8A.d.ts +155 -0
- package/dist/client-Bt4tAKi9.js +798 -0
- package/dist/client.d.ts +2 -90
- package/dist/client.js +1 -409
- package/dist/{contract-CG_adnu_.js → contract-48bUMgcL.js} +10 -2
- package/dist/{contract-C_3dIIEU.d.ts → contract-jIfaR085.d.ts} +62 -8
- package/dist/devtools-J_jZ2vQf.d.ts +151 -0
- package/dist/devtools-kJJaORn-.js +338 -0
- package/dist/devtools-server.browser.js +1 -1
- package/dist/devtools-server.d.ts +2 -2
- package/dist/devtools-server.js +224 -43
- package/dist/devtools.d.ts +2 -0
- package/dist/devtools.js +2 -0
- package/dist/{errors-BJRMd-h6.js → errors-BQuJpe82.js} +4 -4
- package/dist/{errors-xL_JTXsY.d.ts → errors-W6nwJ-fm.d.ts} +1 -1
- package/dist/http.d.ts +71 -13
- package/dist/http.js +302 -41
- package/dist/{idempotent-replay-BMyHrP0L.js → idempotent-replay-DuqEkYA7.js} +2 -2
- package/dist/index.d.ts +5 -5
- package/dist/index.js +2 -2
- package/dist/{inspection-E7qbD0Xj.js → inspection-DaxB5jM2.js} +2 -1
- package/dist/internal-aEotMzu_.js +209 -0
- package/dist/otel.d.ts +1 -1
- package/dist/platform-B4TnJtWu.js +32 -0
- package/dist/react.d.ts +37 -14
- package/dist/react.js +26 -15
- package/dist/scheduler-qstash.d.ts +78 -0
- package/dist/scheduler-qstash.js +499 -0
- package/dist/scheduler-task-BpzhPnRS.js +54 -0
- package/dist/{recovery-vercel.d.ts → scheduler-vercel.d.ts} +17 -24
- package/dist/scheduler-vercel.js +226 -0
- package/dist/server-CcNnFnoW.js +1405 -0
- package/dist/server-YtPq7hjw.d.ts +260 -0
- package/dist/server.d.ts +4 -155
- package/dist/server.js +2 -2
- package/dist/{log-ldf5g8Cx.d.ts → store-C3sNAaBT.d.ts} +111 -35
- package/dist/{log-yJbXUf72.js → store-N8PXxDAS.js} +1 -1
- package/dist/store-codec-DTG0Ftek.js +8 -0
- package/dist/store-memory.d.ts +11 -0
- package/dist/{log-memory.js → store-memory.js} +127 -24
- package/dist/{log-polling-6COoN60V.js → store-polling-DgrrAE3d.js} +7 -6
- package/dist/{log-postgres.d.ts → store-postgres.d.ts} +6 -6
- package/dist/{log-postgres.js → store-postgres.js} +158 -24
- package/dist/{log-redis.js → store-redis-core-DWqx3F47.js} +294 -156
- package/dist/store-redis-http.d.ts +21 -0
- package/dist/store-redis-http.js +70 -0
- package/dist/store-redis.d.ts +37 -0
- package/dist/store-redis.js +298 -0
- package/dist/{log-sqlite.d.ts → store-sqlite.d.ts} +6 -6
- package/dist/{log-sqlite.js → store-sqlite.js} +116 -22
- package/dist/{telemetry-Cso0qyHQ.d.ts → telemetry-BjYHTfh2.d.ts} +1 -1
- package/dist/testing.browser.d.ts +1 -0
- package/dist/testing.browser.js +4 -0
- package/dist/testing.d.ts +31 -0
- package/dist/testing.js +101 -0
- package/dist/wire-DCUZBUlT.js +222 -0
- package/docs/01-quickstart.mdx +4 -5
- package/docs/concepts/01-contracts.mdx +21 -17
- package/docs/concepts/02-handlers.mdx +7 -7
- package/docs/concepts/03-durability.mdx +26 -29
- package/docs/concepts/04-state.mdx +18 -21
- package/docs/guides/01-timers.mdx +154 -54
- package/docs/guides/02-cancellation.mdx +30 -4
- package/docs/guides/03-react.mdx +20 -21
- package/docs/guides/04-local-first.mdx +1 -1
- package/docs/guides/05-production.mdx +321 -60
- package/docs/guides/06-ai-agents.mdx +249 -49
- package/docs/guides/07-devtools.mdx +137 -12
- package/docs/guides/08-application-data.mdx +1 -1
- package/docs/guides/09-presence.mdx +284 -0
- package/docs/guides/10-transports.mdx +131 -0
- package/docs/index.mdx +22 -46
- package/docs/reference/01-api.mdx +751 -102
- package/docs/reference/02-errors.mdx +10 -5
- package/package.json +23 -6
- package/dist/internal-D6wNxTck.js +0 -36
- package/dist/log-memory.d.ts +0 -11
- package/dist/log-redis.d.ts +0 -31
- package/dist/recovery-vercel.js +0 -119
- package/dist/server-DJgD2YWP.js +0 -877
- package/dist/wire-BVsgR8o9.js +0 -62
package/dist/ai.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { t as assertSyncSchema } from "./validate-XKT4FSNn.js";
|
|
2
|
-
import { t as contract } from "./contract-
|
|
1
|
+
import { n as validateSync, t as assertSyncSchema } from "./validate-XKT4FSNn.js";
|
|
2
|
+
import { t as contract } from "./contract-48bUMgcL.js";
|
|
3
|
+
import { l as ambientToolScopeStorage } from "./internal-aEotMzu_.js";
|
|
3
4
|
//#region src/ai-projector.ts
|
|
4
5
|
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
6
|
const cloneMessage = (messageId, base) => structuredClone(base ?? {
|
|
@@ -423,15 +424,18 @@ const messageValue = (value) => isRecord(value) && typeof value["id"] === "strin
|
|
|
423
424
|
const validateMessage = (value, messageSchema) => {
|
|
424
425
|
if (!messageValue(value)) return issue("invalid AI SDK UIMessage");
|
|
425
426
|
if (!messageSchema) return { value };
|
|
426
|
-
|
|
427
|
-
if (result instanceof Promise) throw new TypeError("the AI message schema must validate synchronously");
|
|
428
|
-
return result;
|
|
427
|
+
return validateSync(messageSchema, value, "the AI message");
|
|
429
428
|
};
|
|
430
429
|
const messagePayloadSchema = (messageSchema) => schema("ai.message.created", (value) => {
|
|
431
430
|
if (!isRecord(value)) return issue("invalid ai.message.created payload");
|
|
432
431
|
const message = validateMessage(value["message"], messageSchema);
|
|
433
432
|
if (message.issues) return { issues: message.issues };
|
|
434
|
-
|
|
433
|
+
const generate = value["generate"];
|
|
434
|
+
if (generate !== void 0 && typeof generate !== "boolean") return issue("invalid ai.message.created generate flag");
|
|
435
|
+
return { value: {
|
|
436
|
+
message: message.value,
|
|
437
|
+
...generate === false ? { generate: false } : {}
|
|
438
|
+
} };
|
|
435
439
|
});
|
|
436
440
|
const progressPayloadSchema = schema("ai.generation.progress", (value) => {
|
|
437
441
|
if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "requestId") || !stringField(value, "messageId") || !stringField(value, "generationId") || !stringField(value, "responseMessageId") || typeof value["sequence"] !== "number" || !Number.isInteger(value["sequence"]) || value["sequence"] < 0 || !Array.isArray(value["chunks"]) || !value["chunks"].every((chunk) => isRecord(chunk) && typeof chunk["type"] === "string")) return issue("invalid ai.generation.progress payload");
|
|
@@ -464,7 +468,7 @@ function createEvents(options) {
|
|
|
464
468
|
"ai.session.closed": jsonObject("ai.session.closed", (value) => optionalStringField(value, "reason")),
|
|
465
469
|
"ai.message.created": messagePayloadSchema(messageSchema),
|
|
466
470
|
"ai.message.completed": jsonObject("ai.message.completed", (value) => stringField(value, "messageId")),
|
|
467
|
-
"ai.message.interrupted": jsonObject("ai.message.interrupted", (value) => stringField(value, "messageId") &&
|
|
471
|
+
"ai.message.interrupted": jsonObject("ai.message.interrupted", (value) => stringField(value, "messageId") && (stringField(value, "generationId") && value["requestId"] === void 0 || stringField(value, "requestId") && value["generationId"] === void 0) && optionalStringField(value, "reason") && (value["lastSeenIndex"] === void 0 || typeof value["lastSeenIndex"] === "number" && Number.isInteger(value["lastSeenIndex"]) && value["lastSeenIndex"] >= 0)),
|
|
468
472
|
"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
473
|
"ai.retry.requested": jsonObject("ai.retry.requested", (value) => stringField(value, "messageId") && stringField(value, "responseMessageId") && stringField(value, "retryId")),
|
|
470
474
|
"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")),
|
|
@@ -583,7 +587,7 @@ const toolResultChunk = (payload) => {
|
|
|
583
587
|
};
|
|
584
588
|
const waitingStatus = (state) => {
|
|
585
589
|
if (state.pendingApprovals.length > 0 || state.pendingInputs.length > 0) return "waiting";
|
|
586
|
-
return state.activeGeneration !== null || state.tools.some((
|
|
590
|
+
return state.activeGeneration !== null || state.tools.some((activity) => activity.status === "running") ? "generating" : "idle";
|
|
587
591
|
};
|
|
588
592
|
const lifecycleStatus = (previous, next) => previous.status === "failed" ? "failed" : waitingStatus(next);
|
|
589
593
|
const isStaleGeneration = (state, messageId, generationId) => {
|
|
@@ -610,12 +614,12 @@ const reduceToolActivity = (tools, event) => {
|
|
|
610
614
|
if (event.type === "ai.tool.called") {
|
|
611
615
|
const payload = event.payload;
|
|
612
616
|
const { providerMetadata, ...called } = payload;
|
|
613
|
-
const
|
|
617
|
+
const activity = {
|
|
614
618
|
...called,
|
|
615
619
|
...providerMetadata === void 0 ? {} : { callProviderMetadata: providerMetadata },
|
|
616
620
|
status: "running"
|
|
617
621
|
};
|
|
618
|
-
return [...tools.filter((candidate) => candidate.generationId !== payload.generationId || candidate.toolCallId !== payload.toolCallId),
|
|
622
|
+
return [...tools.filter((candidate) => candidate.generationId !== payload.generationId || candidate.toolCallId !== payload.toolCallId), activity];
|
|
619
623
|
}
|
|
620
624
|
const payload = event.payload;
|
|
621
625
|
const existing = tools.find((candidate) => candidate.generationId === payload.generationId && candidate.toolCallId === payload.toolCallId);
|
|
@@ -625,7 +629,7 @@ const reduceToolActivity = (tools, event) => {
|
|
|
625
629
|
const providerExecuted = payload.providerExecuted ?? existing?.providerExecuted;
|
|
626
630
|
const toolMetadata = payload.toolMetadata ?? existing?.toolMetadata;
|
|
627
631
|
const resultProviderMetadata = payload.providerMetadata ?? existing?.resultProviderMetadata;
|
|
628
|
-
const
|
|
632
|
+
const activity = {
|
|
629
633
|
requestId: payload.requestId,
|
|
630
634
|
messageId: payload.messageId,
|
|
631
635
|
generationId: payload.generationId,
|
|
@@ -645,19 +649,60 @@ const reduceToolActivity = (tools, event) => {
|
|
|
645
649
|
...existing?.title === void 0 ? {} : { title: existing.title },
|
|
646
650
|
status: payload.denied ? "denied" : payload.error ? "failed" : payload.preliminary ? "running" : "completed"
|
|
647
651
|
};
|
|
648
|
-
return [...tools.filter((candidate) => candidate.generationId !== payload.generationId || candidate.toolCallId !== payload.toolCallId),
|
|
652
|
+
return [...tools.filter((candidate) => candidate.generationId !== payload.generationId || candidate.toolCallId !== payload.toolCallId), activity];
|
|
649
653
|
};
|
|
650
654
|
const projectedTools = (projection) => projection.toolEvents.reduce((tools, event) => reduceToolActivity(tools, event), projection.baseTools);
|
|
651
|
-
const
|
|
652
|
-
|
|
655
|
+
const restoreGenerationBase = (state, generationId, responseMessageId, reason) => {
|
|
656
|
+
const projection = state.activeProjection?.generationId === generationId ? state.activeProjection : null;
|
|
657
|
+
return {
|
|
658
|
+
...state,
|
|
659
|
+
messages: projection?.baseMessage === void 0 ? state.messages.filter((message) => message.id !== responseMessageId) : upsertMessage(state.messages, projection.baseMessage),
|
|
660
|
+
tools: projection === null ? state.tools.filter((activity) => activity.generationId !== generationId) : projection.baseTools,
|
|
661
|
+
pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== generationId),
|
|
662
|
+
pendingInputs: state.pendingInputs.filter((input) => input.generationId !== generationId),
|
|
663
|
+
activeGeneration: null,
|
|
664
|
+
activeProjection: null,
|
|
665
|
+
terminalGenerations: {
|
|
666
|
+
...state.terminalGenerations,
|
|
667
|
+
[generationId]: reason
|
|
668
|
+
},
|
|
669
|
+
compaction: projection === null ? state.compaction?.generationId === generationId ? null : state.compaction : projection.baseCompaction ?? null
|
|
670
|
+
};
|
|
671
|
+
};
|
|
672
|
+
const stopGenerationTools = (tools, generationId, responseMessageId, error) => tools.map((activity) => activity.status === "running" && (generationId === void 0 ? activity.messageId === responseMessageId : activity.generationId === generationId) ? {
|
|
673
|
+
...activity,
|
|
653
674
|
status: "failed",
|
|
654
675
|
error,
|
|
655
676
|
preliminary: false
|
|
656
|
-
} :
|
|
677
|
+
} : activity);
|
|
678
|
+
const interruptGenerationProjection = (state, generationId, responseMessageId, cutoff) => {
|
|
679
|
+
const projection = state.activeProjection?.generationId === generationId ? state.activeProjection : null;
|
|
680
|
+
if (projection === null) return null;
|
|
681
|
+
const retained = {
|
|
682
|
+
...projection,
|
|
683
|
+
batches: projection.batches.filter((batch) => batch.index <= cutoff),
|
|
684
|
+
toolEvents: projection.toolEvents.filter((toolEvent) => toolEvent.index <= cutoff),
|
|
685
|
+
approvalEvents: projection.approvalEvents.filter((approvalEvent) => approvalEvent.index <= cutoff)
|
|
686
|
+
};
|
|
687
|
+
const shouldProject = retained.baseMessage !== void 0 || retained.batches.length > 0;
|
|
688
|
+
const restored = restoreGenerationBase(state, generationId, responseMessageId, "interrupted");
|
|
689
|
+
return {
|
|
690
|
+
...restored,
|
|
691
|
+
messages: shouldProject ? upsertMessage(restored.messages, interruptUIMessage(projectedMessage(retained))) : restored.messages,
|
|
692
|
+
tools: stopGenerationTools(projectedTools(retained), generationId, responseMessageId, "Tool execution was interrupted."),
|
|
693
|
+
usage: projection.completionIndex === void 0 || projection.completionIndex <= cutoff ? restored.usage : restored.usage.filter((entry) => entry.generationId !== generationId)
|
|
694
|
+
};
|
|
695
|
+
};
|
|
657
696
|
const generationRequestId = (generationId) => {
|
|
658
697
|
const markerIndex = generationId.lastIndexOf(":generation:");
|
|
659
698
|
return markerIndex === -1 ? void 0 : generationId.slice(0, markerIndex);
|
|
660
699
|
};
|
|
700
|
+
const terminalGenerationReason = (state, generationId) => {
|
|
701
|
+
const terminal = state.terminalGenerations?.[generationId];
|
|
702
|
+
if (terminal !== void 0) return terminal;
|
|
703
|
+
const requestId = generationRequestId(generationId);
|
|
704
|
+
return requestId !== void 0 && state.terminalRequestIds?.[requestId] === true ? "interrupted" : void 0;
|
|
705
|
+
};
|
|
661
706
|
/** Pure projection of the built-in AI protocol. Unknown extension events are ignored. */
|
|
662
707
|
function reduceAIState(state, event) {
|
|
663
708
|
if (state.status === "closed") return state;
|
|
@@ -687,6 +732,26 @@ function reduceAIState(state, event) {
|
|
|
687
732
|
messages: upsertMessage(state.messages, message)
|
|
688
733
|
};
|
|
689
734
|
}
|
|
735
|
+
case "ai.message.completed": {
|
|
736
|
+
const { messageId } = event.payload;
|
|
737
|
+
const generationId = state.responseGenerationIds[messageId];
|
|
738
|
+
if (generationId === void 0 || terminalGenerationReason(state, generationId) !== void 0) return state;
|
|
739
|
+
const requestId = generationRequestId(generationId);
|
|
740
|
+
return {
|
|
741
|
+
...state,
|
|
742
|
+
activeRequestId: null,
|
|
743
|
+
activeResponseMessageId: null,
|
|
744
|
+
activeProjection: state.activeProjection?.generationId === generationId ? null : state.activeProjection,
|
|
745
|
+
terminalGenerations: {
|
|
746
|
+
...state.terminalGenerations,
|
|
747
|
+
[generationId]: "completed"
|
|
748
|
+
},
|
|
749
|
+
...requestId === void 0 ? {} : { terminalRequestIds: {
|
|
750
|
+
...state.terminalRequestIds,
|
|
751
|
+
[requestId]: true
|
|
752
|
+
} }
|
|
753
|
+
};
|
|
754
|
+
}
|
|
690
755
|
case "ai.generation.requested": {
|
|
691
756
|
const payload = event.payload;
|
|
692
757
|
const schedulesToolContinuation = payload.reason === "tool";
|
|
@@ -694,10 +759,12 @@ function reduceAIState(state, event) {
|
|
|
694
759
|
const ownerGenerationId = state.responseGenerationIds[responseMessageId] ?? state.activeGeneration?.generationId;
|
|
695
760
|
const ownerRequestId = ownerGenerationId === void 0 ? void 0 : generationRequestId(ownerGenerationId);
|
|
696
761
|
const requestId = event.id;
|
|
762
|
+
if (requestId !== void 0 && state.terminalRequestIds?.[requestId] === true) return state;
|
|
697
763
|
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;
|
|
698
764
|
return {
|
|
699
765
|
...state,
|
|
700
766
|
messages: payload.reason === "retry" && payload.responseMessageId !== void 0 ? state.messages.filter((message) => message.id !== payload.responseMessageId) : state.messages,
|
|
767
|
+
activeProjection: payload.reason === "retry" ? null : state.activeProjection,
|
|
701
768
|
status: schedulesToolContinuation ? state.status : "generating",
|
|
702
769
|
activeRequestId: requestId ?? null,
|
|
703
770
|
activeResponseMessageId: responseMessageId,
|
|
@@ -706,27 +773,33 @@ function reduceAIState(state, event) {
|
|
|
706
773
|
}
|
|
707
774
|
case "ai.generation.started": {
|
|
708
775
|
const payload = event.payload;
|
|
776
|
+
if (state.terminalRequestIds?.[payload.requestId] === true || terminalGenerationReason(state, payload.generationId) !== void 0) return state;
|
|
709
777
|
if (state.activeRequestId !== null && state.activeRequestId !== payload.requestId) return state;
|
|
710
778
|
const currentOwner = state.responseGenerationIds[payload.responseMessageId];
|
|
779
|
+
const currentTerminal = currentOwner === void 0 ? void 0 : terminalGenerationReason(state, currentOwner);
|
|
780
|
+
if (currentTerminal === "completed" || currentTerminal === "interrupted") return state;
|
|
711
781
|
const marker = ":generation:";
|
|
712
782
|
const currentAttempt = currentOwner?.startsWith(`${payload.requestId}${marker}`) ? Number(currentOwner.slice(`${payload.requestId}${marker}`.length)) : void 0;
|
|
713
783
|
if (currentAttempt !== void 0 && Number.isFinite(currentAttempt) && currentAttempt >= payload.attempt) return state;
|
|
714
|
-
const
|
|
784
|
+
const activeGeneration = state.activeGeneration;
|
|
785
|
+
const baseState = activeGeneration !== null && activeGeneration.requestId === payload.requestId && activeGeneration.responseMessageId === payload.responseMessageId && activeGeneration.attempt < payload.attempt && state.activeProjection?.generationId === activeGeneration.generationId ? restoreGenerationBase(state, activeGeneration.generationId, activeGeneration.responseMessageId, "superseded") : state;
|
|
786
|
+
const baseMessage = baseState.messages.find((message) => message.id === payload.responseMessageId);
|
|
715
787
|
return {
|
|
716
|
-
...
|
|
788
|
+
...baseState,
|
|
717
789
|
status: "generating",
|
|
718
790
|
activeGeneration: payload,
|
|
719
791
|
activeResponseMessageId: null,
|
|
720
792
|
responseGenerationIds: {
|
|
721
|
-
...
|
|
793
|
+
...baseState.responseGenerationIds,
|
|
722
794
|
[payload.responseMessageId]: payload.generationId
|
|
723
795
|
},
|
|
724
796
|
activeProjection: {
|
|
725
797
|
generationId: payload.generationId,
|
|
726
798
|
responseMessageId: payload.responseMessageId,
|
|
727
799
|
...baseMessage === void 0 ? {} : { baseMessage },
|
|
800
|
+
baseCompaction: baseState.compaction,
|
|
728
801
|
batches: [],
|
|
729
|
-
baseTools:
|
|
802
|
+
baseTools: baseState.tools,
|
|
730
803
|
toolEvents: [],
|
|
731
804
|
approvalEvents: []
|
|
732
805
|
},
|
|
@@ -735,6 +808,7 @@ function reduceAIState(state, event) {
|
|
|
735
808
|
}
|
|
736
809
|
case "ai.generation.progress": {
|
|
737
810
|
const payload = event.payload;
|
|
811
|
+
if (terminalGenerationReason(state, payload.generationId) !== void 0) return state;
|
|
738
812
|
const activeProjection = state.activeProjection;
|
|
739
813
|
if (state.activeGeneration?.generationId !== payload.generationId || activeProjection?.generationId !== payload.generationId) return state;
|
|
740
814
|
const projection = {
|
|
@@ -753,6 +827,7 @@ function reduceAIState(state, event) {
|
|
|
753
827
|
}
|
|
754
828
|
case "ai.generation.completed": {
|
|
755
829
|
const payload = event.payload;
|
|
830
|
+
if (terminalGenerationReason(state, payload.generationId) !== void 0) return state;
|
|
756
831
|
if (state.activeGeneration?.generationId !== payload.generationId) return state;
|
|
757
832
|
const usage = payload.usage ? [...state.usage.filter((entry) => entry.generationId !== payload.generationId), {
|
|
758
833
|
generationId: payload.generationId,
|
|
@@ -761,7 +836,10 @@ function reduceAIState(state, event) {
|
|
|
761
836
|
const next = {
|
|
762
837
|
...state,
|
|
763
838
|
activeGeneration: null,
|
|
764
|
-
activeProjection:
|
|
839
|
+
activeProjection: state.activeProjection?.generationId === payload.generationId ? {
|
|
840
|
+
...state.activeProjection,
|
|
841
|
+
completionIndex: event.index
|
|
842
|
+
} : state.activeProjection,
|
|
765
843
|
usage
|
|
766
844
|
};
|
|
767
845
|
return {
|
|
@@ -771,77 +849,111 @@ function reduceAIState(state, event) {
|
|
|
771
849
|
}
|
|
772
850
|
case "ai.generation.failed": {
|
|
773
851
|
const payload = event.payload;
|
|
774
|
-
if (state
|
|
775
|
-
if (payload.
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== payload.generationId),
|
|
780
|
-
pendingInputs: state.pendingInputs.filter((input) => input.generationId !== payload.generationId),
|
|
781
|
-
activeGeneration: null,
|
|
782
|
-
activeProjection: null,
|
|
783
|
-
compaction: state.compaction?.generationId === payload.generationId ? null : state.compaction
|
|
784
|
-
};
|
|
785
|
-
const projection = state.activeProjection;
|
|
786
|
-
const shouldProject = projection?.generationId === payload.generationId && (projection.baseMessage !== void 0 || projection.batches.length > 0);
|
|
852
|
+
if (terminalGenerationReason(state, payload.generationId) !== void 0) return state;
|
|
853
|
+
if (state.activeGeneration?.generationId !== payload.generationId && !(payload.stepLimit === true && state.responseGenerationIds[payload.responseMessageId] === payload.generationId && state.activeRequestId === `ai.generate:tools:${payload.generationId}` && state.activeResponseMessageId === payload.responseMessageId)) return state;
|
|
854
|
+
if (payload.superseded === true) return restoreGenerationBase(state, payload.generationId, payload.responseMessageId, "superseded");
|
|
855
|
+
const projection = state.activeProjection?.generationId === payload.generationId ? state.activeProjection : null;
|
|
856
|
+
const shouldProject = projection !== null && (projection.baseMessage !== void 0 || projection.batches.length > 0);
|
|
787
857
|
return {
|
|
788
858
|
...state,
|
|
789
859
|
messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(projection), `Generation failed before tool completion: ${payload.error}`)) : state.messages,
|
|
790
860
|
status: "failed",
|
|
791
861
|
activeGeneration: null,
|
|
792
862
|
activeResponseMessageId: null,
|
|
793
|
-
activeProjection:
|
|
794
|
-
|
|
863
|
+
activeProjection: projection,
|
|
864
|
+
terminalGenerations: {
|
|
865
|
+
...state.terminalGenerations,
|
|
866
|
+
[payload.generationId]: "failed"
|
|
867
|
+
},
|
|
868
|
+
terminalRequestIds: {
|
|
869
|
+
...state.terminalRequestIds,
|
|
870
|
+
[payload.requestId]: true,
|
|
871
|
+
...state.activeRequestId === null ? {} : { [state.activeRequestId]: true }
|
|
872
|
+
},
|
|
873
|
+
compaction: projection?.generationId === payload.generationId ? projection.baseCompaction ?? null : state.compaction?.generationId === payload.generationId ? null : state.compaction,
|
|
795
874
|
pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== payload.generationId),
|
|
875
|
+
pendingInputs: state.pendingInputs.filter((input) => input.generationId !== payload.generationId),
|
|
796
876
|
tools: stopGenerationTools(state.tools, payload.generationId, payload.responseMessageId, payload.error),
|
|
797
877
|
error: payload.error
|
|
798
878
|
};
|
|
799
879
|
}
|
|
800
880
|
case "ai.message.interrupted": {
|
|
801
881
|
const payload = event.payload;
|
|
882
|
+
const currentGenerationId = state.responseGenerationIds[payload.messageId];
|
|
883
|
+
const targetRequestId = payload.requestId ?? generationRequestId(payload.generationId);
|
|
884
|
+
const ownsRequestPhase = payload.requestId !== void 0 && state.activeGeneration === null && state.activeRequestId === payload.requestId && state.activeResponseMessageId === payload.messageId;
|
|
885
|
+
if (currentGenerationId !== void 0 && !ownsRequestPhase && (payload.generationId !== void 0 ? currentGenerationId !== payload.generationId : generationRequestId(currentGenerationId) !== payload.requestId)) return state;
|
|
886
|
+
if (state.activeGeneration === null && state.activeRequestId !== null && state.activeResponseMessageId === payload.messageId && targetRequestId !== state.activeRequestId) return state;
|
|
887
|
+
const currentTerminal = currentGenerationId === void 0 ? void 0 : terminalGenerationReason(state, currentGenerationId);
|
|
888
|
+
if (currentTerminal === "completed" || currentTerminal === "interrupted" || currentTerminal === "superseded") return state;
|
|
802
889
|
const active = state.activeGeneration;
|
|
803
890
|
if (active === null) {
|
|
804
|
-
if (payload.
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
891
|
+
if (payload.requestId !== void 0 && state.activeRequestId !== null && state.activeRequestId === payload.requestId && state.activeResponseMessageId === payload.messageId) {
|
|
892
|
+
const baseState = (currentGenerationId === void 0 ? null : interruptGenerationProjection(state, currentGenerationId, payload.messageId, payload.lastSeenIndex ?? Number.POSITIVE_INFINITY)) ?? state;
|
|
893
|
+
return {
|
|
894
|
+
...baseState,
|
|
895
|
+
status: "idle",
|
|
896
|
+
activeRequestId: null,
|
|
897
|
+
activeResponseMessageId: null,
|
|
898
|
+
activeProjection: null,
|
|
899
|
+
terminalRequestIds: {
|
|
900
|
+
...baseState.terminalRequestIds,
|
|
901
|
+
[payload.requestId]: true
|
|
902
|
+
},
|
|
903
|
+
...currentGenerationId === void 0 ? {} : { terminalGenerations: {
|
|
904
|
+
...baseState.terminalGenerations,
|
|
905
|
+
[currentGenerationId]: "interrupted"
|
|
906
|
+
} },
|
|
907
|
+
error: null
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
const targetGenerationId = payload.generationId ?? (currentGenerationId !== void 0 && generationRequestId(currentGenerationId) === payload.requestId ? currentGenerationId : void 0);
|
|
911
|
+
if (targetGenerationId === void 0) return state;
|
|
912
|
+
if (currentGenerationId !== targetGenerationId) return state;
|
|
913
|
+
const matchesGeneration = (generationId) => targetGenerationId === generationId;
|
|
914
|
+
const hasMessageWork = state.pendingApprovals.some((approval) => approval.messageId === payload.messageId && matchesGeneration(approval.generationId)) || state.pendingInputs.some((input) => input.messageId === payload.messageId && matchesGeneration(input.generationId)) || state.tools.some((activity) => activity.messageId === payload.messageId && activity.status === "running" && matchesGeneration(activity.generationId));
|
|
915
|
+
const resolvesFailedGeneration = state.status === "failed" && currentGenerationId === targetGenerationId && (state.activeRequestId === null || state.activeRequestId === targetRequestId || state.activeRequestId === `ai.generate:tools:${targetGenerationId}`);
|
|
916
|
+
if (!hasMessageWork && !resolvesFailedGeneration && !(currentGenerationId === targetGenerationId && currentTerminal === void 0)) return state;
|
|
917
|
+
const pendingRequestId = state.activeRequestId;
|
|
918
|
+
const projectedState = interruptGenerationProjection(state, targetGenerationId, payload.messageId, payload.lastSeenIndex ?? Number.POSITIVE_INFINITY);
|
|
919
|
+
const baseState = projectedState ?? state;
|
|
812
920
|
const next = {
|
|
813
|
-
...
|
|
921
|
+
...baseState,
|
|
814
922
|
activeRequestId: null,
|
|
815
923
|
activeResponseMessageId: null,
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
924
|
+
activeProjection: null,
|
|
925
|
+
messages: projectedState === null ? state.messages.map((message) => message.id === payload.messageId ? interruptUIMessage(message) : message) : baseState.messages,
|
|
926
|
+
pendingApprovals: baseState.pendingApprovals.filter((approval) => approval.messageId !== payload.messageId || !matchesGeneration(approval.generationId)),
|
|
927
|
+
pendingInputs: baseState.pendingInputs.filter((input) => input.messageId !== payload.messageId || !matchesGeneration(input.generationId)),
|
|
928
|
+
tools: projectedState === null ? stopGenerationTools(state.tools, targetGenerationId, payload.messageId, "Tool execution was interrupted.") : baseState.tools,
|
|
929
|
+
terminalGenerations: {
|
|
930
|
+
...baseState.terminalGenerations,
|
|
931
|
+
[targetGenerationId]: "interrupted"
|
|
932
|
+
},
|
|
933
|
+
...pendingRequestId === null ? {} : { terminalRequestIds: {
|
|
934
|
+
...baseState.terminalRequestIds,
|
|
935
|
+
[pendingRequestId]: true
|
|
936
|
+
} },
|
|
937
|
+
error: resolvesFailedGeneration ? null : state.error
|
|
820
938
|
};
|
|
821
939
|
return {
|
|
822
940
|
...next,
|
|
823
|
-
status:
|
|
941
|
+
status: waitingStatus(next)
|
|
824
942
|
};
|
|
825
943
|
}
|
|
826
|
-
if (payload.
|
|
827
|
-
const projection = state.activeProjection;
|
|
944
|
+
if (payload.messageId !== active.responseMessageId || payload.generationId !== active.generationId && payload.requestId !== active.requestId) return state;
|
|
828
945
|
const cutoff = payload.lastSeenIndex ?? Number.POSITIVE_INFINITY;
|
|
829
|
-
const
|
|
830
|
-
|
|
831
|
-
batches: projection.batches.filter((batch) => batch.index <= cutoff),
|
|
832
|
-
toolEvents: projection.toolEvents.filter((toolEvent) => toolEvent.index <= cutoff),
|
|
833
|
-
approvalEvents: projection.approvalEvents.filter((approvalEvent) => approvalEvent.index <= cutoff)
|
|
834
|
-
} : null;
|
|
835
|
-
const shouldProject = retained !== null && (retained.baseMessage !== void 0 || retained.batches.length > 0);
|
|
946
|
+
const projectedState = interruptGenerationProjection(state, active.generationId, active.responseMessageId, cutoff);
|
|
947
|
+
const baseState = projectedState ?? restoreGenerationBase(state, active.generationId, active.responseMessageId, "interrupted");
|
|
836
948
|
const next = {
|
|
837
|
-
...
|
|
838
|
-
messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(retained))) : state.messages,
|
|
839
|
-
activeGeneration: null,
|
|
949
|
+
...baseState,
|
|
840
950
|
activeRequestId: null,
|
|
841
951
|
activeResponseMessageId: null,
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
952
|
+
terminalRequestIds: {
|
|
953
|
+
...baseState.terminalRequestIds,
|
|
954
|
+
[active.requestId]: true
|
|
955
|
+
},
|
|
956
|
+
tools: projectedState === null ? stopGenerationTools(baseState.tools, active.generationId, active.responseMessageId, "Tool execution was interrupted.") : baseState.tools
|
|
845
957
|
};
|
|
846
958
|
return {
|
|
847
959
|
...next,
|
|
@@ -851,8 +963,8 @@ function reduceAIState(state, event) {
|
|
|
851
963
|
case "ai.tool.called": {
|
|
852
964
|
const payload = event.payload;
|
|
853
965
|
const projection = state.activeProjection;
|
|
854
|
-
if (isStaleGeneration(state, payload.messageId, payload.generationId)) return state;
|
|
855
|
-
if (state.tools.some((
|
|
966
|
+
if (isStaleGeneration(state, payload.messageId, payload.generationId) || terminalGenerationReason(state, payload.generationId) !== void 0) return state;
|
|
967
|
+
if (state.tools.some((activity) => activity.generationId === payload.generationId && activity.toolCallId === payload.toolCallId && activity.status === "failed" && state.activeGeneration?.generationId !== payload.generationId)) return state;
|
|
856
968
|
if (projection?.generationId !== payload.generationId) {
|
|
857
969
|
const next = {
|
|
858
970
|
...state,
|
|
@@ -874,17 +986,23 @@ function reduceAIState(state, event) {
|
|
|
874
986
|
payload
|
|
875
987
|
}]
|
|
876
988
|
};
|
|
877
|
-
|
|
989
|
+
const next = {
|
|
878
990
|
...state,
|
|
879
991
|
activeProjection,
|
|
880
992
|
tools: projectedTools(activeProjection)
|
|
881
993
|
};
|
|
994
|
+
return {
|
|
995
|
+
...next,
|
|
996
|
+
status: lifecycleStatus(state, next)
|
|
997
|
+
};
|
|
882
998
|
}
|
|
883
999
|
case "ai.tool.result": {
|
|
884
1000
|
const payload = event.payload;
|
|
885
1001
|
const projection = state.activeProjection;
|
|
886
|
-
|
|
887
|
-
|
|
1002
|
+
const terminalReason = terminalGenerationReason(state, payload.generationId);
|
|
1003
|
+
const completesFailedTool = terminalReason === "failed" && state.status === "failed" && payload.preliminary !== true && projection?.generationId === payload.generationId && projectedTools(projection).some((activity) => activity.generationId === payload.generationId && activity.toolCallId === payload.toolCallId && activity.status === "running");
|
|
1004
|
+
if (isStaleGeneration(state, payload.messageId, payload.generationId) || terminalReason !== void 0 && !completesFailedTool) return state;
|
|
1005
|
+
if (state.tools.some((activity) => activity.generationId === payload.generationId && activity.toolCallId === payload.toolCallId && activity.status === "failed" && state.activeGeneration?.generationId !== payload.generationId && !completesFailedTool)) return state;
|
|
888
1006
|
if (projection?.generationId !== payload.generationId) {
|
|
889
1007
|
const next = {
|
|
890
1008
|
...state,
|
|
@@ -907,18 +1025,25 @@ function reduceAIState(state, event) {
|
|
|
907
1025
|
payload
|
|
908
1026
|
}]
|
|
909
1027
|
};
|
|
910
|
-
|
|
1028
|
+
const projected = projectedMessage(activeProjection);
|
|
1029
|
+
const tools = projectedTools(activeProjection);
|
|
1030
|
+
const failureError = state.error ?? "Generation failed.";
|
|
1031
|
+
const next = {
|
|
911
1032
|
...state,
|
|
912
|
-
messages: upsertMessage(state.messages,
|
|
1033
|
+
messages: upsertMessage(state.messages, terminalReason === "failed" ? interruptUIMessage(projected, `Generation failed before tool completion: ${failureError}`) : projected),
|
|
913
1034
|
activeProjection,
|
|
914
|
-
tools:
|
|
1035
|
+
tools: terminalReason === "failed" ? stopGenerationTools(tools, payload.generationId, payload.messageId, failureError) : tools
|
|
1036
|
+
};
|
|
1037
|
+
return {
|
|
1038
|
+
...next,
|
|
1039
|
+
status: lifecycleStatus(state, next)
|
|
915
1040
|
};
|
|
916
1041
|
}
|
|
917
1042
|
case "ai.approval.requested": {
|
|
918
1043
|
const payload = event.payload;
|
|
919
1044
|
const projection = state.activeProjection;
|
|
920
|
-
if (isStaleGeneration(state, payload.messageId, payload.generationId)) return state;
|
|
921
|
-
if (state.tools.some((
|
|
1045
|
+
if (isStaleGeneration(state, payload.messageId, payload.generationId) || terminalGenerationReason(state, payload.generationId) !== void 0) return state;
|
|
1046
|
+
if (state.tools.some((activity) => activity.generationId === payload.generationId && activity.toolCallId === payload.toolCallId && activity.status === "failed" && state.activeGeneration?.generationId !== payload.generationId)) return state;
|
|
922
1047
|
const activeProjection = projection?.generationId === payload.generationId ? {
|
|
923
1048
|
...projection,
|
|
924
1049
|
approvalEvents: [...projection.approvalEvents, {
|
|
@@ -940,7 +1065,7 @@ function reduceAIState(state, event) {
|
|
|
940
1065
|
}
|
|
941
1066
|
case "ai.approval.responded": {
|
|
942
1067
|
const payload = event.payload;
|
|
943
|
-
if (isStaleGeneration(state, payload.messageId, payload.generationId)) return state;
|
|
1068
|
+
if (isStaleGeneration(state, payload.messageId, payload.generationId) || terminalGenerationReason(state, payload.generationId) !== void 0) return state;
|
|
944
1069
|
const projection = state.activeProjection;
|
|
945
1070
|
const pendingRequest = state.pendingApprovals.find((candidate) => candidate.approvalId === payload.approvalId && candidate.messageId === payload.messageId && candidate.generationId === payload.generationId);
|
|
946
1071
|
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);
|
|
@@ -966,7 +1091,7 @@ function reduceAIState(state, event) {
|
|
|
966
1091
|
}
|
|
967
1092
|
case "ai.input.requested": {
|
|
968
1093
|
const payload = event.payload;
|
|
969
|
-
if (isStaleGeneration(state, payload.messageId, payload.generationId)) return state;
|
|
1094
|
+
if (isStaleGeneration(state, payload.messageId, payload.generationId) || terminalGenerationReason(state, payload.generationId) !== void 0) return state;
|
|
970
1095
|
const next = {
|
|
971
1096
|
...state,
|
|
972
1097
|
pendingInputs: [...state.pendingInputs.filter((input) => input.messageId !== payload.messageId || input.generationId !== payload.generationId || input.inputId !== payload.inputId), payload]
|
|
@@ -978,7 +1103,7 @@ function reduceAIState(state, event) {
|
|
|
978
1103
|
}
|
|
979
1104
|
case "ai.input.responded": {
|
|
980
1105
|
const payload = event.payload;
|
|
981
|
-
if (isStaleGeneration(state, payload.messageId, payload.generationId)) return state;
|
|
1106
|
+
if (isStaleGeneration(state, payload.messageId, payload.generationId) || terminalGenerationReason(state, payload.generationId) !== void 0) return state;
|
|
982
1107
|
const next = {
|
|
983
1108
|
...state,
|
|
984
1109
|
pendingInputs: state.pendingInputs.filter((input) => input.inputId !== payload.inputId || input.messageId !== payload.messageId || input.generationId !== payload.generationId)
|
|
@@ -1022,21 +1147,26 @@ function deriveUIMessages(history) {
|
|
|
1022
1147
|
/** Build the standard AI state projection for any contract containing the protocol. */
|
|
1023
1148
|
function createReducer(options) {
|
|
1024
1149
|
return options.contract.reducer({
|
|
1025
|
-
name: options.name ?? "a2.ai.state.
|
|
1150
|
+
name: options.name ?? "a2.ai.state.v8",
|
|
1026
1151
|
initialState: initialState()
|
|
1027
1152
|
}).fold((state, event) => reduceAIState(state, event));
|
|
1028
1153
|
}
|
|
1029
|
-
const
|
|
1154
|
+
const messageInput = (message, generate) => [{
|
|
1030
1155
|
type: "ai.message.created",
|
|
1031
1156
|
id: `ai.message:${message.id}`,
|
|
1032
|
-
payload: {
|
|
1157
|
+
payload: {
|
|
1158
|
+
message,
|
|
1159
|
+
...generate === false ? { generate: false } : {}
|
|
1160
|
+
}
|
|
1033
1161
|
}];
|
|
1034
1162
|
/** Pure append inputs for the built-in AI protocol. */
|
|
1035
1163
|
const inputs = {
|
|
1036
|
-
message(message) {
|
|
1037
|
-
return
|
|
1164
|
+
message(message, options) {
|
|
1165
|
+
return messageInput(message, options?.generate);
|
|
1166
|
+
},
|
|
1167
|
+
seed(message) {
|
|
1168
|
+
return messageInput(message, false);
|
|
1038
1169
|
},
|
|
1039
|
-
seed: seedMessage,
|
|
1040
1170
|
approval(response) {
|
|
1041
1171
|
return [{
|
|
1042
1172
|
type: "ai.approval.responded",
|
|
@@ -1068,12 +1198,27 @@ const inputs = {
|
|
|
1068
1198
|
interrupt(interruption) {
|
|
1069
1199
|
return [{
|
|
1070
1200
|
type: "ai.message.interrupted",
|
|
1071
|
-
id: `ai.interrupt:${interruption.generationId ?? interruption.
|
|
1201
|
+
id: `ai.interrupt:${interruption.generationId ?? interruption.requestId}`,
|
|
1072
1202
|
payload: interruption
|
|
1073
1203
|
}];
|
|
1074
1204
|
}
|
|
1075
1205
|
};
|
|
1076
1206
|
/**
|
|
1207
|
+
* Read the durable handler context ambient to the current AI tool
|
|
1208
|
+
* execution. A2 runs each local tool's `execute` inside this scope, so
|
|
1209
|
+
* plain AI SDK `tool()` definitions reach their `event`, `attempt`,
|
|
1210
|
+
* `session`, and `signal` without an A2-specific wrapper. The agent (or
|
|
1211
|
+
* contract) argument carries the event types and is verified against
|
|
1212
|
+
* the executing server's contract.
|
|
1213
|
+
*/
|
|
1214
|
+
function handlerContext(agentOrContract) {
|
|
1215
|
+
const contract = "contract" in agentOrContract ? agentOrContract.contract : agentOrContract;
|
|
1216
|
+
const scope = ambientToolScopeStorage()?.getStore();
|
|
1217
|
+
if (scope === void 0) throw new TypeError(`handlerContext('${contract.name}') requires an ambient A2 tool execution: call it inside a local tool's execute, or wrap direct tool tests with runWithHandlerContext`);
|
|
1218
|
+
if (scope.contract !== void 0 && scope.contract.name !== contract.name) throw new TypeError(`handlerContext('${contract.name}') was called during a tool execution for agent '${scope.contract.name}'`);
|
|
1219
|
+
return scope.context;
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1077
1222
|
* Define an agent: built-in AI protocol + application events and one state
|
|
1078
1223
|
* reducer. The result is isomorphic.
|
|
1079
1224
|
*/
|
|
@@ -1091,10 +1236,10 @@ function agent(options) {
|
|
|
1091
1236
|
return {
|
|
1092
1237
|
contract: agentContract,
|
|
1093
1238
|
reducer: agentContract.reducer({
|
|
1094
|
-
name: options.reducerName ?? "a2.ai.state.
|
|
1239
|
+
name: options.reducerName ?? "a2.ai.state.v8",
|
|
1095
1240
|
initialState: initialState()
|
|
1096
1241
|
}).fold((state, event) => reduceAIState(state, event))
|
|
1097
1242
|
};
|
|
1098
1243
|
}
|
|
1099
1244
|
//#endregion
|
|
1100
|
-
export { agent, createEvents, createReducer, deriveUIMessages, events, inputs, reduceAIState };
|
|
1245
|
+
export { agent, createEvents, createReducer, deriveUIMessages, events, handlerContext, inputs, reduceAIState };
|