experimental-a2 0.7.0 → 0.8.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 +19 -0
- package/dist/ai-server.d.ts +1 -1
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +13 -11
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.js +1 -1
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +1 -1
- package/dist/{server-286j79Mt.js → server-B2XNevQA.js} +123 -53
- package/dist/server-B2XNevQA.js.map +1 -0
- package/dist/{server-DgXmORIq.d.ts → server-DjPhHnbI.d.ts} +7 -4
- package/dist/server-DjPhHnbI.d.ts.map +1 -0
- package/dist/server.d.ts +3 -3
- package/dist/server.js +1 -1
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/{store-flRz1OWh.d.ts → store-RJO35BMj.d.ts} +25 -8
- package/dist/store-RJO35BMj.d.ts.map +1 -0
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +79 -19
- package/dist/store-memory.js.map +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +230 -101
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-redis-core-DEYO8Ryv.js → store-redis-core-DT01r4GZ.js} +167 -29
- package/dist/store-redis-core-DT01r4GZ.js.map +1 -0
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +2 -2
- package/dist/store-redis-http.js.map +1 -1
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +2 -2
- package/dist/store-redis.js.map +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +103 -19
- package/dist/store-sqlite.js.map +1 -1
- package/docs/concepts/02-handlers.mdx +4 -0
- package/docs/concepts/04-state.mdx +57 -9
- package/docs/guides/06-ai-agents.mdx +2 -1
- package/docs/reference/01-api.mdx +39 -16
- package/package.json +1 -1
- package/src/ai-server.ts +27 -10
- package/src/server.ts +242 -87
- package/src/store-memory.ts +138 -20
- package/src/store-postgres.ts +355 -138
- package/src/store-redis-core.ts +201 -27
- package/src/store-redis-http.ts +1 -1
- package/src/store-redis.ts +1 -1
- package/src/store-sqlite.ts +191 -34
- package/src/store.ts +27 -9
- package/dist/server-286j79Mt.js.map +0 -1
- package/dist/server-DgXmORIq.d.ts.map +0 -1
- package/dist/store-flRz1OWh.d.ts.map +0 -1
- package/dist/store-redis-core-DEYO8Ryv.js.map +0 -1
|
@@ -1123,21 +1123,45 @@ function createServer(options) {
|
|
|
1123
1123
|
throw asStoreUnavailable(err);
|
|
1124
1124
|
}
|
|
1125
1125
|
};
|
|
1126
|
-
const readCachedState = async (store, sessionId, reducerName) => {
|
|
1126
|
+
const readCachedState = async (store, sessionId, reducerName, throughIndex, snapshotThroughIndex) => {
|
|
1127
1127
|
try {
|
|
1128
|
-
return await store.readState(nsId(sessionId), reducerName
|
|
1128
|
+
return await store.readState(nsId(sessionId), reducerName, throughIndex === void 0 && snapshotThroughIndex === void 0 ? void 0 : {
|
|
1129
|
+
...throughIndex === void 0 ? {} : { throughIndex },
|
|
1130
|
+
...snapshotThroughIndex === void 0 ? {} : { snapshotThroughIndex }
|
|
1131
|
+
});
|
|
1129
1132
|
} catch {
|
|
1130
1133
|
try {
|
|
1131
1134
|
return {
|
|
1135
|
+
headIndex: null,
|
|
1132
1136
|
snapshot: null,
|
|
1133
|
-
events: await store.read(nsId(sessionId))
|
|
1137
|
+
events: await store.read(nsId(sessionId), throughIndex === void 0 ? void 0 : { throughIndex })
|
|
1134
1138
|
};
|
|
1135
1139
|
} catch (err) {
|
|
1136
1140
|
throw asStoreUnavailable(err);
|
|
1137
1141
|
}
|
|
1138
1142
|
}
|
|
1139
1143
|
};
|
|
1140
|
-
const
|
|
1144
|
+
const planStateReads = (batch) => {
|
|
1145
|
+
const bySession = /* @__PURE__ */ new Map();
|
|
1146
|
+
for (const entry of batch) {
|
|
1147
|
+
const entries = bySession.get(entry.sessionId);
|
|
1148
|
+
if (entries) entries.push(entry);
|
|
1149
|
+
else bySession.set(entry.sessionId, [entry]);
|
|
1150
|
+
}
|
|
1151
|
+
const plans = [];
|
|
1152
|
+
for (const [sessionId, entries] of bySession) {
|
|
1153
|
+
const boundaries = entries.flatMap((entry) => entry.throughIndex === void 0 ? [] : [entry.throughIndex]);
|
|
1154
|
+
const plan = {
|
|
1155
|
+
sessionId,
|
|
1156
|
+
entries
|
|
1157
|
+
};
|
|
1158
|
+
if (boundaries.length === entries.length) plan.throughIndex = Math.max(...boundaries);
|
|
1159
|
+
if (boundaries.length > 0) plan.snapshotThroughIndex = Math.min(...boundaries);
|
|
1160
|
+
plans.push(plan);
|
|
1161
|
+
}
|
|
1162
|
+
return plans;
|
|
1163
|
+
};
|
|
1164
|
+
const foldStatePlan = async (store, reducer, plan, stateRead) => {
|
|
1141
1165
|
let state = cloneInitial(reducer.initialState);
|
|
1142
1166
|
let index = 0;
|
|
1143
1167
|
let snapshotOutcome = "miss";
|
|
@@ -1158,84 +1182,120 @@ function createServer(options) {
|
|
|
1158
1182
|
snapshotOutcome = "hit";
|
|
1159
1183
|
}
|
|
1160
1184
|
}
|
|
1161
|
-
span.setAttribute("a2.state.snapshot", snapshotOutcome);
|
|
1162
1185
|
if (snapshotOutcome === "rejected") try {
|
|
1163
|
-
rows = await store.read(nsId(sessionId));
|
|
1186
|
+
rows = await store.read(nsId(plan.sessionId), plan.throughIndex === void 0 ? void 0 : { throughIndex: plan.throughIndex });
|
|
1164
1187
|
} catch (err) {
|
|
1165
1188
|
throw asStoreUnavailable(err);
|
|
1166
1189
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1190
|
+
const ordered = plan.entries.toSorted((a, b) => (a.throughIndex ?? Number.POSITIVE_INFINITY) - (b.throughIndex ?? Number.POSITIVE_INFINITY));
|
|
1191
|
+
const writes = /* @__PURE__ */ new Map();
|
|
1192
|
+
let rowPosition = 0;
|
|
1193
|
+
for (const entry of ordered) {
|
|
1194
|
+
const target = entry.throughIndex ?? Number.POSITIVE_INFINITY;
|
|
1195
|
+
while (rowPosition < rows.length && rows[rowPosition].index <= target) {
|
|
1196
|
+
const row = rows[rowPosition];
|
|
1197
|
+
state = reducer.fold(state, toPublic(row));
|
|
1198
|
+
index = row.index;
|
|
1199
|
+
rowPosition += 1;
|
|
1200
|
+
}
|
|
1201
|
+
const resultState = cloneInitial(state);
|
|
1202
|
+
const folded = rowPosition;
|
|
1203
|
+
entry.span.setAttribute("a2.state.snapshot", snapshotOutcome);
|
|
1204
|
+
entry.span.setAttribute("a2.state.folded", folded);
|
|
1205
|
+
entry.span.setAttribute("a2.state.index", index);
|
|
1206
|
+
if (index > 0 && (folded > 0 || entry.pinEventIndex !== void 0)) {
|
|
1207
|
+
const advancesHead = stateRead.headIndex === null || index > stateRead.headIndex;
|
|
1208
|
+
const repairsHead = snapshotOutcome === "rejected" && index === stateRead.headIndex;
|
|
1209
|
+
if (!advancesHead && !repairsHead && entry.pinEventIndex === void 0) {
|
|
1210
|
+
entry.resolve({
|
|
1211
|
+
state: resultState,
|
|
1212
|
+
index
|
|
1213
|
+
});
|
|
1214
|
+
continue;
|
|
1215
|
+
}
|
|
1216
|
+
let write = writes.get(index);
|
|
1217
|
+
if (!write) {
|
|
1218
|
+
write = {
|
|
1219
|
+
state: cloneInitial(resultState),
|
|
1220
|
+
pinEventIndexes: /* @__PURE__ */ new Set()
|
|
1221
|
+
};
|
|
1222
|
+
writes.set(index, write);
|
|
1223
|
+
}
|
|
1224
|
+
if (entry.pinEventIndex !== void 0) write.pinEventIndexes.add(entry.pinEventIndex);
|
|
1225
|
+
}
|
|
1226
|
+
entry.resolve({
|
|
1227
|
+
state: resultState,
|
|
1228
|
+
index
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
if (writes.size > 0) {
|
|
1232
|
+
const snapshots = [];
|
|
1233
|
+
for (const [snapshotIndex, write] of writes) {
|
|
1234
|
+
const snapshot = {
|
|
1235
|
+
index: snapshotIndex,
|
|
1236
|
+
state: write.state
|
|
1237
|
+
};
|
|
1238
|
+
if (write.pinEventIndexes.size > 0) snapshot.pinEventIndexes = [...write.pinEventIndexes];
|
|
1239
|
+
snapshots.push(snapshot);
|
|
1240
|
+
}
|
|
1241
|
+
const persistence = Promise.resolve().then(() => store.putSnapshots(nsId(plan.sessionId), reducer.name, snapshots)).catch(() => {});
|
|
1242
|
+
track(persistence);
|
|
1243
|
+
platformWaitUntil(persistence);
|
|
1170
1244
|
}
|
|
1171
|
-
span.setAttribute("a2.state.folded", rows.length);
|
|
1172
|
-
span.setAttribute("a2.state.index", index);
|
|
1173
|
-
return {
|
|
1174
|
-
state,
|
|
1175
|
-
index,
|
|
1176
|
-
folded: rows.length
|
|
1177
|
-
};
|
|
1178
1245
|
};
|
|
1179
1246
|
const pendingStateReads = /* @__PURE__ */ new Map();
|
|
1180
|
-
const flushStateReads = async (store,
|
|
1247
|
+
const flushStateReads = async (store, reducer, batch) => {
|
|
1248
|
+
const plans = planStateReads(batch);
|
|
1181
1249
|
let reads;
|
|
1182
1250
|
try {
|
|
1183
|
-
reads = store.readStates ? await store.readStates(
|
|
1251
|
+
reads = store.readStates ? await store.readStates(plans.map((plan) => ({
|
|
1252
|
+
sessionId: nsId(plan.sessionId),
|
|
1253
|
+
...plan.throughIndex === void 0 ? {} : { throughIndex: plan.throughIndex },
|
|
1254
|
+
...plan.snapshotThroughIndex === void 0 ? {} : { snapshotThroughIndex: plan.snapshotThroughIndex }
|
|
1255
|
+
})), reducer.name) : await Promise.all(plans.map((plan) => readCachedState(store, plan.sessionId, reducer.name, plan.throughIndex, plan.snapshotThroughIndex)));
|
|
1184
1256
|
} catch {
|
|
1185
1257
|
reads = null;
|
|
1186
1258
|
}
|
|
1187
|
-
if (!reads || reads.length !==
|
|
1188
|
-
|
|
1259
|
+
if (!reads || reads.length !== plans.length) try {
|
|
1260
|
+
reads = await Promise.all(plans.map((plan) => readCachedState(store, plan.sessionId, reducer.name, plan.throughIndex, plan.snapshotThroughIndex)));
|
|
1261
|
+
} catch (err) {
|
|
1262
|
+
for (const entry of batch) entry.reject(err);
|
|
1189
1263
|
return;
|
|
1190
1264
|
}
|
|
1191
|
-
const
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
try {
|
|
1196
|
-
const read = reads[position];
|
|
1197
|
-
entry.resolve(shared ? structuredClone(read) : read);
|
|
1198
|
-
} catch {
|
|
1199
|
-
entry.resolve(readCachedState(store, entry.sessionId, reducerName));
|
|
1200
|
-
}
|
|
1265
|
+
for (const [position, plan] of plans.entries()) try {
|
|
1266
|
+
await foldStatePlan(store, reducer, plan, reads[position]);
|
|
1267
|
+
} catch (err) {
|
|
1268
|
+
for (const entry of plan.entries) entry.reject(err);
|
|
1201
1269
|
}
|
|
1202
1270
|
};
|
|
1203
|
-
const enqueueStateRead = (store, sessionId,
|
|
1271
|
+
const enqueueStateRead = (store, sessionId, reducer, throughIndex, pinEventIndex, span) => new Promise((resolve, reject) => {
|
|
1272
|
+
const reducerName = reducer.name;
|
|
1204
1273
|
let batch = pendingStateReads.get(reducerName);
|
|
1205
1274
|
if (!batch) {
|
|
1206
1275
|
const opened = [];
|
|
1207
1276
|
pendingStateReads.set(reducerName, opened);
|
|
1208
1277
|
queueMicrotask(() => {
|
|
1209
1278
|
pendingStateReads.delete(reducerName);
|
|
1210
|
-
flushStateReads(store,
|
|
1279
|
+
flushStateReads(store, reducer, opened);
|
|
1211
1280
|
});
|
|
1212
1281
|
batch = opened;
|
|
1213
1282
|
}
|
|
1214
1283
|
batch.push({
|
|
1215
1284
|
sessionId,
|
|
1216
|
-
|
|
1285
|
+
...throughIndex === void 0 ? {} : { throughIndex },
|
|
1286
|
+
...pinEventIndex === void 0 ? {} : { pinEventIndex },
|
|
1287
|
+
span,
|
|
1288
|
+
resolve,
|
|
1289
|
+
reject
|
|
1217
1290
|
});
|
|
1218
1291
|
});
|
|
1219
|
-
const readState = async (sessionId, reducer) => {
|
|
1292
|
+
const readState = async (sessionId, reducer, throughIndex, pinEventIndex) => {
|
|
1220
1293
|
const store = await resolveStore();
|
|
1221
1294
|
return telemetry.span("a2.state", {
|
|
1222
1295
|
"a2.contract": name,
|
|
1223
1296
|
"a2.session_id": sessionId,
|
|
1224
1297
|
"a2.state.reducer": reducer.name
|
|
1225
|
-
},
|
|
1226
|
-
const stateRead = store.readStates ? await enqueueStateRead(store, sessionId, reducer.name) : await readCachedState(store, sessionId, reducer.name);
|
|
1227
|
-
const { state, index, folded } = await foldStateRead(store, sessionId, reducer, stateRead, span);
|
|
1228
|
-
if (folded > 0) {
|
|
1229
|
-
const snapshotState = cloneInitial(state);
|
|
1230
|
-
const write = Promise.resolve().then(() => store.putSnapshot(nsId(sessionId), reducer.name, index, snapshotState)).catch(() => {});
|
|
1231
|
-
track(write);
|
|
1232
|
-
platformWaitUntil(write);
|
|
1233
|
-
}
|
|
1234
|
-
return {
|
|
1235
|
-
state,
|
|
1236
|
-
index
|
|
1237
|
-
};
|
|
1238
|
-
});
|
|
1298
|
+
}, (span) => enqueueStateRead(store, sessionId, reducer, throughIndex, pinEventIndex, span));
|
|
1239
1299
|
};
|
|
1240
1300
|
const makeSchedule = (sessionId, trigger) => {
|
|
1241
1301
|
const triggerEventId = trigger?.id ?? null;
|
|
@@ -1246,11 +1306,14 @@ function createServer(options) {
|
|
|
1246
1306
|
if (events.length === 0) throw new TypeError("schedule requires at least one event");
|
|
1247
1307
|
const dueAt = scheduleDueAt(timing, triggerCreatedAt ?? Date.now());
|
|
1248
1308
|
const payloads = events.map((event) => cloneScheduledPayload(event.payload));
|
|
1249
|
-
const snapshottedEvents = events.map((event, index) =>
|
|
1309
|
+
const snapshottedEvents = events.map((event, index) => event.id === void 0 ? {
|
|
1310
|
+
type: event.type,
|
|
1311
|
+
payload: payloads[index]
|
|
1312
|
+
} : {
|
|
1250
1313
|
type: event.type,
|
|
1251
1314
|
payload: payloads[index],
|
|
1252
|
-
|
|
1253
|
-
})
|
|
1315
|
+
id: event.id
|
|
1316
|
+
});
|
|
1254
1317
|
const validated = validateEvents(sessionId, structuredClone(snapshottedEvents), false);
|
|
1255
1318
|
const scheduleId = await deterministicScheduleId(name, sessionId, triggerEventId, scheduleName);
|
|
1256
1319
|
const scheduledEvents = await Promise.all(validated.map(async (event, item) => ({
|
|
@@ -1512,7 +1575,11 @@ function createServer(options) {
|
|
|
1512
1575
|
append,
|
|
1513
1576
|
schedule: makeSchedule(id, trigger),
|
|
1514
1577
|
history: async (bounds) => (await readHistory(id, bounds)).map(toPublic),
|
|
1515
|
-
state: (reducer) =>
|
|
1578
|
+
state: (reducer, stateOptions) => {
|
|
1579
|
+
const through = stateOptions?.through ?? trigger?.index;
|
|
1580
|
+
if (through !== void 0 && through !== "latest") assertStateIndex(through);
|
|
1581
|
+
return readState(id, reducer, through === "latest" ? void 0 : through, trigger && through !== "latest" ? trigger.index : void 0);
|
|
1582
|
+
},
|
|
1516
1583
|
stream: (opts) => {
|
|
1517
1584
|
const startAfter = opts?.startAfter ?? 0;
|
|
1518
1585
|
assertStreamIndex(startAfter);
|
|
@@ -1998,6 +2065,9 @@ function assertHistoryIndex(name, value) {
|
|
|
1998
2065
|
function assertStreamIndex(value) {
|
|
1999
2066
|
if (!Number.isSafeInteger(value) || value < 0) throw new TypeError("stream.startAfter must be a non-negative safe integer");
|
|
2000
2067
|
}
|
|
2068
|
+
function assertStateIndex(value) {
|
|
2069
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new TypeError("state.through must be a non-negative safe integer");
|
|
2070
|
+
}
|
|
2001
2071
|
function assertAppendName(name) {
|
|
2002
2072
|
if (typeof name !== "string" || name.length === 0) throw new TypeError("handler append name must be a non-empty string");
|
|
2003
2073
|
}
|
|
@@ -2089,4 +2159,4 @@ function cloneInitial(initial) {
|
|
|
2089
2159
|
//#endregion
|
|
2090
2160
|
export { platformVercelOidcToken as i, deliverSchedulerAppend as n, setServerFetchHooks as r, createServer as t };
|
|
2091
2161
|
|
|
2092
|
-
//# sourceMappingURL=server-
|
|
2162
|
+
//# sourceMappingURL=server-B2XNevQA.js.map
|