@farukada/aws-langgraph-dynamodb-ts 0.7.0 → 0.9.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/README.md +52 -9
- package/dist/checkpointer/actions/delete-thread.d.ts +3 -4
- package/dist/checkpointer/actions/delete-thread.d.ts.map +1 -1
- package/dist/checkpointer/actions/delete-thread.js +21 -41
- package/dist/checkpointer/actions/delete-thread.js.map +1 -1
- package/dist/checkpointer/actions/list.d.ts.map +1 -1
- package/dist/checkpointer/actions/list.js +38 -4
- package/dist/checkpointer/actions/list.js.map +1 -1
- package/dist/checkpointer/actions/put-writes.d.ts.map +1 -1
- package/dist/checkpointer/actions/put-writes.js +15 -24
- package/dist/checkpointer/actions/put-writes.js.map +1 -1
- package/dist/checkpointer/internal/item-reader.d.ts +45 -0
- package/dist/checkpointer/internal/item-reader.d.ts.map +1 -1
- package/dist/checkpointer/internal/item-reader.js +63 -1
- package/dist/checkpointer/internal/item-reader.js.map +1 -1
- package/dist/checkpointer/internal/item-writer.d.ts +0 -8
- package/dist/checkpointer/internal/item-writer.d.ts.map +1 -1
- package/dist/checkpointer/internal/item-writer.js +16 -21
- package/dist/checkpointer/internal/item-writer.js.map +1 -1
- package/dist/checkpointer/internal/keys.d.ts +19 -3
- package/dist/checkpointer/internal/keys.d.ts.map +1 -1
- package/dist/checkpointer/internal/keys.js +41 -6
- package/dist/checkpointer/internal/keys.js.map +1 -1
- package/dist/checkpointer/internal/special-write-cas.d.ts +30 -0
- package/dist/checkpointer/internal/special-write-cas.d.ts.map +1 -0
- package/dist/checkpointer/internal/special-write-cas.js +104 -0
- package/dist/checkpointer/internal/special-write-cas.js.map +1 -0
- package/dist/checkpointer/internal/special-write-cleanup.d.ts +18 -12
- package/dist/checkpointer/internal/special-write-cleanup.d.ts.map +1 -1
- package/dist/checkpointer/internal/special-write-cleanup.js +31 -86
- package/dist/checkpointer/internal/special-write-cleanup.js.map +1 -1
- package/dist/checkpointer/internal/special-write-verify.d.ts +54 -0
- package/dist/checkpointer/internal/special-write-verify.d.ts.map +1 -0
- package/dist/checkpointer/internal/special-write-verify.js +65 -0
- package/dist/checkpointer/internal/special-write-verify.js.map +1 -0
- package/dist/checkpointer/internal/validation.d.ts +8 -4
- package/dist/checkpointer/internal/validation.d.ts.map +1 -1
- package/dist/checkpointer/internal/validation.js +12 -10
- package/dist/checkpointer/internal/validation.js.map +1 -1
- package/dist/checkpointer/internal/write-guard.d.ts +13 -0
- package/dist/checkpointer/internal/write-guard.d.ts.map +1 -0
- package/dist/checkpointer/internal/write-guard.js +39 -0
- package/dist/checkpointer/internal/write-guard.js.map +1 -0
- package/dist/checkpointer/internal/write-index.d.ts +37 -0
- package/dist/checkpointer/internal/write-index.d.ts.map +1 -0
- package/dist/checkpointer/internal/write-index.js +42 -0
- package/dist/checkpointer/internal/write-index.js.map +1 -0
- package/dist/checkpointer/types.d.ts +8 -0
- package/dist/checkpointer/types.d.ts.map +1 -1
- package/dist/history/actions/add-messages.d.ts.map +1 -1
- package/dist/history/actions/add-messages.js +24 -4
- package/dist/history/actions/add-messages.js.map +1 -1
- package/dist/history/actions/clear.d.ts +4 -6
- package/dist/history/actions/clear.d.ts.map +1 -1
- package/dist/history/actions/clear.js +18 -33
- package/dist/history/actions/clear.js.map +1 -1
- package/dist/history/actions/get-messages.d.ts +2 -1
- package/dist/history/actions/get-messages.d.ts.map +1 -1
- package/dist/history/actions/get-messages.js +28 -2
- package/dist/history/actions/get-messages.js.map +1 -1
- package/dist/history/actions/list-sessions.d.ts +2 -1
- package/dist/history/actions/list-sessions.d.ts.map +1 -1
- package/dist/history/actions/list-sessions.js +21 -2
- package/dist/history/actions/list-sessions.js.map +1 -1
- package/dist/history/actions/reconcile-count.d.ts.map +1 -1
- package/dist/history/actions/reconcile-count.js +2 -2
- package/dist/history/actions/reconcile-count.js.map +1 -1
- package/dist/history/internal/append-saga.d.ts.map +1 -1
- package/dist/history/internal/append-saga.js +2 -78
- package/dist/history/internal/append-saga.js.map +1 -1
- package/dist/history/internal/compensation.d.ts +21 -0
- package/dist/history/internal/compensation.d.ts.map +1 -0
- package/dist/history/internal/compensation.js +84 -0
- package/dist/history/internal/compensation.js.map +1 -0
- package/dist/history/internal/keys.d.ts +9 -1
- package/dist/history/internal/keys.d.ts.map +1 -1
- package/dist/history/internal/keys.js +21 -4
- package/dist/history/internal/keys.js.map +1 -1
- package/dist/history/internal/message-chunker.d.ts.map +1 -1
- package/dist/history/internal/message-chunker.js +13 -5
- package/dist/history/internal/message-chunker.js.map +1 -1
- package/dist/history/internal/session-count.d.ts +21 -0
- package/dist/history/internal/session-count.d.ts.map +1 -1
- package/dist/history/internal/session-count.js +51 -0
- package/dist/history/internal/session-count.js.map +1 -1
- package/dist/history/internal/session-title.d.ts +20 -0
- package/dist/history/internal/session-title.d.ts.map +1 -0
- package/dist/history/internal/session-title.js +44 -0
- package/dist/history/internal/session-title.js.map +1 -0
- package/dist/history/internal/setup.d.ts +2 -1
- package/dist/history/internal/setup.d.ts.map +1 -1
- package/dist/history/internal/setup.js +1 -0
- package/dist/history/internal/setup.js.map +1 -1
- package/dist/history/internal/title-generator.d.ts +8 -2
- package/dist/history/internal/title-generator.d.ts.map +1 -1
- package/dist/history/internal/title-generator.js +12 -3
- package/dist/history/internal/title-generator.js.map +1 -1
- package/dist/history/internal/validation.d.ts +9 -0
- package/dist/history/internal/validation.d.ts.map +1 -0
- package/dist/history/internal/validation.js +16 -0
- package/dist/history/internal/validation.js.map +1 -0
- package/dist/history/types.d.ts +12 -0
- package/dist/history/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/shared/codec/s3/offloader.d.ts +7 -1
- package/dist/shared/codec/s3/offloader.d.ts.map +1 -1
- package/dist/shared/codec/s3/offloader.js +14 -1
- package/dist/shared/codec/s3/offloader.js.map +1 -1
- package/dist/shared/constants.d.ts +15 -0
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +16 -1
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/dynamodb/conditional-put.d.ts +51 -0
- package/dist/shared/dynamodb/conditional-put.d.ts.map +1 -0
- package/dist/shared/dynamodb/conditional-put.js +59 -0
- package/dist/shared/dynamodb/conditional-put.js.map +1 -0
- package/dist/shared/dynamodb/partition-delete.d.ts +33 -0
- package/dist/shared/dynamodb/partition-delete.d.ts.map +1 -0
- package/dist/shared/dynamodb/partition-delete.js +70 -0
- package/dist/shared/dynamodb/partition-delete.js.map +1 -0
- package/dist/shared/dynamodb/retry-classifier.d.ts.map +1 -1
- package/dist/shared/dynamodb/retry-classifier.js +9 -1
- package/dist/shared/dynamodb/retry-classifier.js.map +1 -1
- package/dist/shared/logging/redaction-walk.d.ts +23 -0
- package/dist/shared/logging/redaction-walk.d.ts.map +1 -0
- package/dist/shared/logging/redaction-walk.js +92 -0
- package/dist/shared/logging/redaction-walk.js.map +1 -0
- package/dist/shared/logging/redaction.d.ts +26 -17
- package/dist/shared/logging/redaction.d.ts.map +1 -1
- package/dist/shared/logging/redaction.js +24 -48
- package/dist/shared/logging/redaction.js.map +1 -1
- package/dist/shared/logging/secret-patterns.d.ts +74 -0
- package/dist/shared/logging/secret-patterns.d.ts.map +1 -0
- package/dist/shared/logging/secret-patterns.js +107 -0
- package/dist/shared/logging/secret-patterns.js.map +1 -0
- package/dist/shared/validation/primitives.d.ts +9 -1
- package/dist/shared/validation/primitives.d.ts.map +1 -1
- package/dist/shared/validation/primitives.js +13 -0
- package/dist/shared/validation/primitives.js.map +1 -1
- package/dist/store/actions/get.d.ts.map +1 -1
- package/dist/store/actions/get.js +15 -1
- package/dist/store/actions/get.js.map +1 -1
- package/dist/store/actions/list-namespaces.d.ts.map +1 -1
- package/dist/store/actions/list-namespaces.js +1 -0
- package/dist/store/actions/list-namespaces.js.map +1 -1
- package/dist/store/actions/put.d.ts.map +1 -1
- package/dist/store/actions/put.js +24 -18
- package/dist/store/actions/put.js.map +1 -1
- package/dist/store/actions/search.d.ts.map +1 -1
- package/dist/store/actions/search.js +4 -35
- package/dist/store/actions/search.js.map +1 -1
- package/dist/store/internal/backend-search.d.ts +5 -0
- package/dist/store/internal/backend-search.d.ts.map +1 -0
- package/dist/store/internal/backend-search.js +68 -0
- package/dist/store/internal/backend-search.js.map +1 -0
- package/dist/store/internal/filter.d.ts.map +1 -1
- package/dist/store/internal/filter.js +46 -5
- package/dist/store/internal/filter.js.map +1 -1
- package/dist/store/internal/index-reconcile.d.ts.map +1 -1
- package/dist/store/internal/index-reconcile.js +41 -5
- package/dist/store/internal/index-reconcile.js.map +1 -1
- package/dist/store/internal/item-mapper.d.ts +1 -0
- package/dist/store/internal/item-mapper.d.ts.map +1 -1
- package/dist/store/internal/item-mapper.js +2 -0
- package/dist/store/internal/item-mapper.js.map +1 -1
- package/dist/store/internal/keys.d.ts +1 -1
- package/dist/store/internal/keys.d.ts.map +1 -1
- package/dist/store/internal/keys.js +8 -2
- package/dist/store/internal/keys.js.map +1 -1
- package/dist/store/internal/overwrite-swap.d.ts +33 -0
- package/dist/store/internal/overwrite-swap.d.ts.map +1 -0
- package/dist/store/internal/overwrite-swap.js +62 -0
- package/dist/store/internal/overwrite-swap.js.map +1 -0
- package/dist/store/internal/persist.d.ts +22 -6
- package/dist/store/internal/persist.d.ts.map +1 -1
- package/dist/store/internal/persist.js +41 -20
- package/dist/store/internal/persist.js.map +1 -1
- package/dist/store/internal/read-existing.d.ts +19 -0
- package/dist/store/internal/read-existing.d.ts.map +1 -0
- package/dist/store/internal/read-existing.js +29 -0
- package/dist/store/internal/read-existing.js.map +1 -0
- package/dist/store/internal/score-direction.d.ts +32 -0
- package/dist/store/internal/score-direction.d.ts.map +1 -0
- package/dist/store/internal/score-direction.js +39 -0
- package/dist/store/internal/score-direction.js.map +1 -0
- package/dist/store/internal/search-filter.d.ts +4 -0
- package/dist/store/internal/search-filter.d.ts.map +1 -0
- package/dist/store/internal/search-filter.js +11 -0
- package/dist/store/internal/search-filter.js.map +1 -0
- package/dist/store/internal/setup.d.ts +13 -1
- package/dist/store/internal/setup.d.ts.map +1 -1
- package/dist/store/internal/setup.js +57 -1
- package/dist/store/internal/setup.js.map +1 -1
- package/dist/store/internal/validation.d.ts +8 -2
- package/dist/store/internal/validation.d.ts.map +1 -1
- package/dist/store/internal/validation.js +15 -6
- package/dist/store/internal/validation.js.map +1 -1
- package/dist/store/internal/write-verify.d.ts +31 -6
- package/dist/store/internal/write-verify.d.ts.map +1 -1
- package/dist/store/internal/write-verify.js +48 -8
- package/dist/store/internal/write-verify.js.map +1 -1
- package/dist/store/types.d.ts +16 -0
- package/dist/store/types.d.ts.map +1 -1
- package/dist/store/vector-backend.d.ts +17 -0
- package/dist/store/vector-backend.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -3,13 +3,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.listSessions = listSessions;
|
|
4
4
|
const scan_1 = require("../../shared/dynamodb/scan");
|
|
5
5
|
const keys_1 = require("../internal/keys");
|
|
6
|
+
/**
|
|
7
|
+
* True when a session row is past its TTL. DynamoDB's background sweep can lag
|
|
8
|
+
* up to 48h, so an expired session would otherwise keep appearing in listings
|
|
9
|
+
* after `getMessages` had already stopped returning its messages — the same
|
|
10
|
+
* filter that read path applies.
|
|
11
|
+
*/
|
|
12
|
+
function isExpired(item, nowSeconds) {
|
|
13
|
+
return item.ttl !== undefined && item.ttl <= nowSeconds;
|
|
14
|
+
}
|
|
6
15
|
/**
|
|
7
16
|
* List all sessions as metadata summaries, newest-updated first. The scan is
|
|
8
17
|
* filtered to session items so the adapter works on a table shared with the
|
|
9
|
-
* checkpointer/store; foreign rows are also skipped defensively
|
|
18
|
+
* checkpointer/store; foreign rows are also skipped defensively, and rows past
|
|
19
|
+
* their TTL are filtered out exactly as `getMessages` filters expired messages.
|
|
10
20
|
*/
|
|
11
21
|
async function listSessions(context, options) {
|
|
12
22
|
const sessions = [];
|
|
23
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
13
24
|
for await (const raw of (0, scan_1.paginateScan)({
|
|
14
25
|
client: context.client,
|
|
15
26
|
params: {
|
|
@@ -24,6 +35,8 @@ async function listSessions(context, options) {
|
|
|
24
35
|
const item = raw;
|
|
25
36
|
if (item.SK !== keys_1.SESSION_SORT_KEY || typeof item.sessionId !== 'string')
|
|
26
37
|
continue;
|
|
38
|
+
if (isExpired(item, nowSeconds))
|
|
39
|
+
continue;
|
|
27
40
|
sessions.push({
|
|
28
41
|
sessionId: item.sessionId,
|
|
29
42
|
title: item.title,
|
|
@@ -32,7 +45,13 @@ async function listSessions(context, options) {
|
|
|
32
45
|
updatedAt: item.updatedAt,
|
|
33
46
|
});
|
|
34
47
|
}
|
|
35
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Ordinal, not `localeCompare`: these are ISO-8601 timestamps, whose
|
|
50
|
+
* byte order already is their chronological order. Locale-aware collation
|
|
51
|
+
* applies rules (case folding, punctuation weighting) that have no meaning
|
|
52
|
+
* here and are not guaranteed to agree with it in every locale.
|
|
53
|
+
*/
|
|
54
|
+
sessions.sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0));
|
|
36
55
|
return sessions;
|
|
37
56
|
}
|
|
38
57
|
//# sourceMappingURL=list-sessions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-sessions.js","sourceRoot":"","sources":["../../../src/history/actions/list-sessions.ts"],"names":[],"mappings":";;;AAAA,qDAA0D;AAC1D,2CAAoD;AAIpD
|
|
1
|
+
{"version":3,"file":"list-sessions.js","sourceRoot":"","sources":["../../../src/history/actions/list-sessions.ts"],"names":[],"mappings":";;;AAAA,qDAA0D;AAC1D,2CAAoD;AAIpD;;;;;GAKG;AACH,SAAS,SAAS,CAAC,IAAqB,EAAE,UAAkB;IAC1D,OAAO,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,IAAI,UAAU,CAAC;AAC1D,CAAC;AAED;;;;;GAKG;AACI,KAAK,uBACV,OAAuB,EACvB,OAAuD;IAEvD,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACjD,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,IAAA,mBAAY,EAAC;QACnC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE;YACN,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,gBAAgB,EAAE,gBAAgB;YAClC,wBAAwB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;YACzC,yBAAyB,EAAE,EAAE,UAAU,EAAE,uBAAgB,EAAE;SAC5D;QACD,aAAa,EAAE,OAAO,EAAE,aAAa;QACrC,QAAQ,EAAE,OAAO,EAAE,QAAQ;KAC5B,CAAC,EAAE,CAAC;QACH,MAAM,IAAI,GAAG,GAAsB,CAAC;QACpC,IAAI,IAAI,CAAC,EAAE,KAAK,uBAAgB,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ;YAAE,SAAS;QACjF,IAAI,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC;YAAE,SAAS;QAC1C,QAAQ,CAAC,IAAI,CAAC;YACZ,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC,CAAC;IACL,CAAC;IACD;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9F,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile-count.d.ts","sourceRoot":"","sources":["../../../src/history/actions/reconcile-count.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reconcile-count.d.ts","sourceRoot":"","sources":["../../../src/history/actions/reconcile-count.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAiBxD;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAwBjB"}
|
|
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.reconcileMessageCount = reconcileMessageCount;
|
|
4
4
|
const retry_1 = require("../../shared/dynamodb/retry");
|
|
5
5
|
const errors_1 = require("../../shared/errors/errors");
|
|
6
|
-
const primitives_1 = require("../../shared/validation/primitives");
|
|
7
6
|
const keys_1 = require("../internal/keys");
|
|
8
7
|
const query_1 = require("../internal/query");
|
|
8
|
+
const validation_1 = require("../internal/validation");
|
|
9
9
|
async function countMessages(context, sessionId) {
|
|
10
10
|
const base = (0, query_1.messageQuery)(context.tableName, sessionId);
|
|
11
11
|
let total = 0;
|
|
@@ -28,7 +28,7 @@ async function countMessages(context, sessionId) {
|
|
|
28
28
|
* reconciled count.
|
|
29
29
|
*/
|
|
30
30
|
async function reconcileMessageCount(context, sessionId) {
|
|
31
|
-
(0,
|
|
31
|
+
(0, validation_1.validateSessionId)(sessionId);
|
|
32
32
|
const count = await countMessages(context, sessionId);
|
|
33
33
|
try {
|
|
34
34
|
await (0, retry_1.withDynamoDBRetry)(() => context.client.update({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile-count.js","sourceRoot":"","sources":["../../../src/history/actions/reconcile-count.ts"],"names":[],"mappings":";;;AAEA,uDAAgE;AAChE,uDAA2D;AAC3D,
|
|
1
|
+
{"version":3,"file":"reconcile-count.js","sourceRoot":"","sources":["../../../src/history/actions/reconcile-count.ts"],"names":[],"mappings":";;;AAEA,uDAAgE;AAChE,uDAA2D;AAC3D,2CAAsE;AACtE,6CAAiD;AAEjD,uDAA2D;AAE3D,KAAK,UAAU,aAAa,CAAC,OAAuB,EAAE,SAAiB;IACrE,MAAM,IAAI,GAAG,IAAA,oBAAY,EAAC,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACxD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,QAA0D,CAAC;IAC/D,GAAG,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CACxC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAAC,CAChF,CAAC;QACF,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;QACzB,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC;IACnC,CAAC,QAAQ,QAAQ,EAAE;IACnB,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;GASG;AACI,KAAK,gCACV,OAAuB,EACvB,SAAiB;IAEjB,IAAA,8BAAiB,EAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACtD,IAAI,CAAC;QACH,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CAC3B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;YACpB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,GAAG,EAAE,EAAE,EAAE,EAAE,IAAA,uBAAgB,EAAC,SAAS,CAAC,EAAE,EAAE,EAAE,uBAAgB,EAAE;YAC9D,gBAAgB,EAAE,qBAAqB;YACvC,wBAAwB,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE;YACtD,yBAAyB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;YAC9C,mBAAmB,EAAE,sBAAsB;SAC5C,CAAC,CACH,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA2B,CAAC,IAAI,KAAK,iCAAiC,EAAE,CAAC;YAC5E,MAAM,IAAI,sBAAa,CACrB,2CAA2C,SAAS,kBAAkB,EACtE,KAAc,CACf,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"append-saga.d.ts","sourceRoot":"","sources":["../../../src/history/internal/append-saga.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"append-saga.d.ts","sourceRoot":"","sources":["../../../src/history/internal/append-saga.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAGhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,eAAe,EAAE,EAAE,EAC3B,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,IAAI,CAAC,CAyBf"}
|
|
@@ -1,85 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.appendChunks = appendChunks;
|
|
4
|
-
const descriptor_keys_1 = require("../../shared/codec/descriptor-keys");
|
|
5
|
-
const orphans_1 = require("../../shared/codec/s3/orphans");
|
|
6
|
-
const batch_write_1 = require("../../shared/dynamodb/batch-write");
|
|
7
|
-
const errors_1 = require("../../shared/errors/errors");
|
|
8
4
|
const wrap_error_1 = require("../../shared/errors/wrap-error");
|
|
5
|
+
const compensation_1 = require("./compensation");
|
|
9
6
|
const message_transaction_1 = require("./message-transaction");
|
|
10
|
-
const session_count_1 = require("./session-count");
|
|
11
|
-
/**
|
|
12
|
-
* Best-effort delete the offloaded S3 objects of the `chunks` slice given.
|
|
13
|
-
* {@link compensate} calls it once per commit status, never for the whole
|
|
14
|
-
* batch, so a committed chunk's objects arrive only once its rows are gone.
|
|
15
|
-
*/
|
|
16
|
-
async function cleanBatchS3(context, chunks) {
|
|
17
|
-
if (!context.offloader)
|
|
18
|
-
return;
|
|
19
|
-
const descriptors = chunks.flat().map((item) => item.message);
|
|
20
|
-
await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)(descriptors), 'history.addMessages', context.logger);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Delete every committed chunk's items, then revert the counted total by
|
|
24
|
-
* however many rows the delete actually persisted before failing (not
|
|
25
|
-
* necessarily every committed row — see the catch branch below).
|
|
26
|
-
*/
|
|
27
|
-
async function rollbackCommitted(context, sessionId, committed) {
|
|
28
|
-
const keys = committed.flatMap((chunk) => chunk.keys);
|
|
29
|
-
const total = committed.reduce((sum, chunk) => sum + chunk.count, 0);
|
|
30
|
-
if (keys.length === 0) {
|
|
31
|
-
await (0, session_count_1.revertSessionCount)(context, sessionId, total);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
try {
|
|
35
|
-
await (0, batch_write_1.batchWriteAll)(context.client, context.tableName, keys.map((Key) => ({ DeleteRequest: { Key } })));
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
/**
|
|
39
|
-
* batchWriteAll has exactly one throw site and it is always a
|
|
40
|
-
* BatchWriteAllIncompleteError (see Task 9) — asserted, not instanceof-
|
|
41
|
-
* checked, since the false case is unreachable and this project
|
|
42
|
-
* enforces 100% branch coverage with no exceptions.
|
|
43
|
-
*/
|
|
44
|
-
const deleted = error.succeededCount;
|
|
45
|
-
await (0, session_count_1.revertSessionCount)(context, sessionId, deleted);
|
|
46
|
-
throw error;
|
|
47
|
-
}
|
|
48
|
-
await (0, session_count_1.revertSessionCount)(context, sessionId, total);
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Undo a failed batch. Always throws. S3 cleanup is split by commit status so
|
|
52
|
-
* no live row is ever left pointing at a deleted object: the never-committed
|
|
53
|
-
* suffix is cleaned immediately, the committed prefix only after its rows are
|
|
54
|
-
* confirmed deleted. If the rollback itself fails, the committed chunks' S3
|
|
55
|
-
* objects are deliberately left in place (their rows may survive) and it
|
|
56
|
-
* raises {@link CompensationFailedError} carrying both the trigger and the
|
|
57
|
-
* rollback error; otherwise it rethrows the trigger.
|
|
58
|
-
*/
|
|
59
|
-
async function compensate(context, sessionId, chunks, committed, trigger) {
|
|
60
|
-
if (committed.length > 0) {
|
|
61
|
-
context.logger.warn('history.addMessages compensating committed chunks after a chunk failed', {
|
|
62
|
-
sessionId,
|
|
63
|
-
committedChunks: committed.length,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
/** The failed/never-attempted suffix never had a DynamoDB row, so it's safe to clean now. */
|
|
67
|
-
await cleanBatchS3(context, chunks.slice(committed.length));
|
|
68
|
-
try {
|
|
69
|
-
await rollbackCommitted(context, sessionId, committed);
|
|
70
|
-
}
|
|
71
|
-
catch (rollbackError) {
|
|
72
|
-
context.logger.error('history.addMessages rollback failed; messageCount may have drifted', {
|
|
73
|
-
sessionId,
|
|
74
|
-
committedChunks: committed.length,
|
|
75
|
-
});
|
|
76
|
-
/** Skip S3 cleanup here: rollback may have failed, so committed rows might still reference these objects. */
|
|
77
|
-
throw new errors_1.CompensationFailedError(trigger, (0, wrap_error_1.toError)(rollbackError));
|
|
78
|
-
}
|
|
79
|
-
/** Only now that committed rows are confirmed deleted is it safe to delete their S3 objects. */
|
|
80
|
-
await cleanBatchS3(context, chunks.slice(0, committed.length));
|
|
81
|
-
throw trigger;
|
|
82
|
-
}
|
|
83
7
|
/**
|
|
84
8
|
* Append message chunks with caller-observed atomicity. Each chunk commits its
|
|
85
9
|
* messages and count in one transaction; if a later chunk fails, every
|
|
@@ -104,7 +28,7 @@ async function appendChunks(context, sessionId, chunks, fields) {
|
|
|
104
28
|
});
|
|
105
29
|
}
|
|
106
30
|
catch (error) {
|
|
107
|
-
await compensate(context, sessionId, chunks, committed, (0, wrap_error_1.toError)(error));
|
|
31
|
+
await (0, compensation_1.compensate)(context, sessionId, chunks, committed, (0, wrap_error_1.toError)(error), fields.now, fields.title);
|
|
108
32
|
}
|
|
109
33
|
}
|
|
110
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"append-saga.js","sourceRoot":"","sources":["../../../src/history/internal/append-saga.ts"],"names":[],"mappings":";;;AAAA
|
|
1
|
+
{"version":3,"file":"append-saga.js","sourceRoot":"","sources":["../../../src/history/internal/append-saga.ts"],"names":[],"mappings":";;;AAAA,+DAAyD;AAEzD,iDAAiE;AACjE,+DAA0D;AAW1D;;;;;;;;GAQG;AACI,KAAK,uBACV,OAAuB,EACvB,SAAiB,EACjB,MAA2B,EAC3B,MAAoB;IAEpB,MAAM,SAAS,GAAqB,EAAE,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,IAAA,uCAAiB,EAAC,OAAO,EAAE,KAAK,EAAE;gBACtC,GAAG,MAAM;gBACT,SAAS;gBACT,KAAK,EAAE,KAAK,CAAC,MAAM;aACpB,CAAC,CAAC;YACH,SAAS,CAAC,IAAI,CAAC;gBACb,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;gBACzD,KAAK,EAAE,KAAK,CAAC,MAAM;aACpB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAA,yBAAU,EACd,OAAO,EACP,SAAS,EACT,MAAM,EACN,SAAS,EACT,IAAA,oBAAO,EAAC,KAAc,CAAC,EACvB,MAAM,CAAC,GAAG,EACV,MAAM,CAAC,KAAK,CACb,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ChatMessageItem } from '../types';
|
|
2
|
+
import type { HistoryContext } from './setup';
|
|
3
|
+
/** A chunk that committed, retained so it can be rolled back on a later failure. */
|
|
4
|
+
export interface CommittedChunk {
|
|
5
|
+
keys: {
|
|
6
|
+
PK: string;
|
|
7
|
+
SK: string;
|
|
8
|
+
}[];
|
|
9
|
+
count: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Undo a failed batch. Always throws. S3 cleanup is split by commit status so
|
|
13
|
+
* no live row is ever left pointing at a deleted object: the never-committed
|
|
14
|
+
* suffix is cleaned immediately, the committed prefix only after its rows are
|
|
15
|
+
* confirmed deleted. If the rollback itself fails, the committed chunks' S3
|
|
16
|
+
* objects are deliberately left in place (their rows may survive) and it
|
|
17
|
+
* raises {@link CompensationFailedError} carrying both the trigger and the
|
|
18
|
+
* rollback error; otherwise it rethrows the trigger.
|
|
19
|
+
*/
|
|
20
|
+
export declare function compensate(context: HistoryContext, sessionId: string, chunks: ChatMessageItem[][], committed: CommittedChunk[], trigger: Error, now: string, title: string | undefined): Promise<never>;
|
|
21
|
+
//# sourceMappingURL=compensation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compensation.d.ts","sourceRoot":"","sources":["../../../src/history/internal/compensation.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AA0DD;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,eAAe,EAAE,EAAE,EAC3B,SAAS,EAAE,cAAc,EAAE,EAC3B,OAAO,EAAE,KAAK,EACd,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAAC,KAAK,CAAC,CAsBhB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.compensate = compensate;
|
|
4
|
+
const descriptor_keys_1 = require("../../shared/codec/descriptor-keys");
|
|
5
|
+
const orphans_1 = require("../../shared/codec/s3/orphans");
|
|
6
|
+
const batch_write_1 = require("../../shared/dynamodb/batch-write");
|
|
7
|
+
const errors_1 = require("../../shared/errors/errors");
|
|
8
|
+
const wrap_error_1 = require("../../shared/errors/wrap-error");
|
|
9
|
+
const session_count_1 = require("./session-count");
|
|
10
|
+
/**
|
|
11
|
+
* Best-effort delete the offloaded S3 objects of the `chunks` slice given.
|
|
12
|
+
* {@link compensate} calls it once per commit status, never for the whole
|
|
13
|
+
* batch, so a committed chunk's objects arrive only once its rows are gone.
|
|
14
|
+
*/
|
|
15
|
+
async function cleanBatchS3(context, chunks) {
|
|
16
|
+
if (!context.offloader)
|
|
17
|
+
return;
|
|
18
|
+
const descriptors = chunks.flat().map((item) => item.message);
|
|
19
|
+
await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)(descriptors), 'history.addMessages', context.logger);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Delete every committed chunk's items, then undo their effect on the session
|
|
23
|
+
* row — deleting it outright when this call created it (see
|
|
24
|
+
* {@link revertSessionCreation}), so a failed first append leaves no ghost
|
|
25
|
+
* session holding the rolled-back message's title. A *partial* delete is not a
|
|
26
|
+
* clean creation to undo, so that branch reverts only the count.
|
|
27
|
+
*/
|
|
28
|
+
async function rollbackCommitted(context, sessionId, committed, now, title) {
|
|
29
|
+
const keys = committed.flatMap((chunk) => chunk.keys);
|
|
30
|
+
const total = committed.reduce((sum, chunk) => sum + chunk.count, 0);
|
|
31
|
+
if (keys.length === 0) {
|
|
32
|
+
await (0, session_count_1.revertSessionCreation)(context, sessionId, total, now, title);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
await (0, batch_write_1.batchWriteAll)(context.client, context.tableName, keys.map((Key) => ({ DeleteRequest: { Key } })));
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
/**
|
|
40
|
+
* batchWriteAll has exactly one throw site and it is always a
|
|
41
|
+
* BatchWriteAllIncompleteError (see Task 9) — asserted, not instanceof-
|
|
42
|
+
* checked, since the false case is unreachable and this project
|
|
43
|
+
* enforces 100% branch coverage with no exceptions.
|
|
44
|
+
*/
|
|
45
|
+
const deleted = error.succeededCount;
|
|
46
|
+
await (0, session_count_1.revertSessionCount)(context, sessionId, deleted);
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
await (0, session_count_1.revertSessionCreation)(context, sessionId, total, now, title);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Undo a failed batch. Always throws. S3 cleanup is split by commit status so
|
|
53
|
+
* no live row is ever left pointing at a deleted object: the never-committed
|
|
54
|
+
* suffix is cleaned immediately, the committed prefix only after its rows are
|
|
55
|
+
* confirmed deleted. If the rollback itself fails, the committed chunks' S3
|
|
56
|
+
* objects are deliberately left in place (their rows may survive) and it
|
|
57
|
+
* raises {@link CompensationFailedError} carrying both the trigger and the
|
|
58
|
+
* rollback error; otherwise it rethrows the trigger.
|
|
59
|
+
*/
|
|
60
|
+
async function compensate(context, sessionId, chunks, committed, trigger, now, title) {
|
|
61
|
+
if (committed.length > 0) {
|
|
62
|
+
context.logger.warn('history.addMessages compensating committed chunks after a chunk failed', {
|
|
63
|
+
sessionId,
|
|
64
|
+
committedChunks: committed.length,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** The failed/never-attempted suffix never had a DynamoDB row, so it's safe to clean now. */
|
|
68
|
+
await cleanBatchS3(context, chunks.slice(committed.length));
|
|
69
|
+
try {
|
|
70
|
+
await rollbackCommitted(context, sessionId, committed, now, title);
|
|
71
|
+
}
|
|
72
|
+
catch (rollbackError) {
|
|
73
|
+
context.logger.error('history.addMessages rollback failed; messageCount may have drifted', {
|
|
74
|
+
sessionId,
|
|
75
|
+
committedChunks: committed.length,
|
|
76
|
+
});
|
|
77
|
+
/** Skip S3 cleanup here: rollback may have failed, so committed rows might still reference these objects. */
|
|
78
|
+
throw new errors_1.CompensationFailedError(trigger, (0, wrap_error_1.toError)(rollbackError));
|
|
79
|
+
}
|
|
80
|
+
/** Only now that committed rows are confirmed deleted is it safe to delete their S3 objects. */
|
|
81
|
+
await cleanBatchS3(context, chunks.slice(0, committed.length));
|
|
82
|
+
throw trigger;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=compensation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compensation.js","sourceRoot":"","sources":["../../../src/history/internal/compensation.ts"],"names":[],"mappings":";;;AAAA,wEAAmE;AACnE,2DAAiE;AACjE,mEAAkE;AAClE,uDAGoC;AACpC,+DAAyD;AAEzD,mDAA4E;AAS5E;;;;GAIG;AACH,KAAK,UAAU,YAAY,CAAC,OAAuB,EAAE,MAA2B;IAC9E,IAAI,CAAC,OAAO,CAAC,SAAS;QAAE,OAAO;IAC/B,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9D,MAAM,IAAA,0BAAgB,EACpB,OAAO,CAAC,SAAS,EACjB,IAAA,+BAAa,EAAC,WAAW,CAAC,EAC1B,qBAAqB,EACrB,OAAO,CAAC,MAAM,CACf,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,iBAAiB,CAC9B,OAAuB,EACvB,SAAiB,EACjB,SAA2B,EAC3B,GAAW,EACX,KAAyB;IAEzB,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACrE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAA,qCAAqB,EAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACnE,OAAO;IACT,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAA,2BAAa,EACjB,OAAO,CAAC,MAAM,EACd,OAAO,CAAC,SAAS,EACjB,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,aAAa,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAChD,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf;;;;;WAKG;QACH,MAAM,OAAO,GAAI,KAAsC,CAAC,cAAc,CAAC;QACvE,MAAM,IAAA,kCAAkB,EAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QACtD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,IAAA,qCAAqB,EAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AACrE,CAAC;AAED;;;;;;;;GAQG;AACI,KAAK,qBACV,OAAuB,EACvB,SAAiB,EACjB,MAA2B,EAC3B,SAA2B,EAC3B,OAAc,EACd,GAAW,EACX,KAAyB;IAEzB,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,wEAAwE,EAAE;YAC5F,SAAS;YACT,eAAe,EAAE,SAAS,CAAC,MAAM;SAClC,CAAC,CAAC;IACL,CAAC;IACD,6FAA6F;IAC7F,MAAM,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,IAAI,CAAC;QACH,MAAM,iBAAiB,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,aAAa,EAAE,CAAC;QACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oEAAoE,EAAE;YACzF,SAAS;YACT,eAAe,EAAE,SAAS,CAAC,MAAM;SAClC,CAAC,CAAC;QACH,6GAA6G;QAC7G,MAAM,IAAI,gCAAuB,CAAC,OAAO,EAAE,IAAA,oBAAO,EAAC,aAAsB,CAAC,CAAC,CAAC;IAC9E,CAAC;IACD,gGAAgG;IAChG,MAAM,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,MAAM,OAAO,CAAC;AAChB,CAAC"}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
/** Reserved separator joining sort-key segments; forbidden inside a session id. */
|
|
2
|
+
export declare const SORT_KEY_SEPARATOR = "#";
|
|
1
3
|
/** Fixed sort key for the per-session metadata item. */
|
|
2
4
|
export declare const SESSION_SORT_KEY = "HISTORY#SESSION";
|
|
3
|
-
/** Partition key for a chat session: the session id
|
|
5
|
+
/** Partition key for a chat session: the adapter tag plus the session id. */
|
|
4
6
|
export declare function sessionPartition(sessionId: string): string;
|
|
5
7
|
/** Sort key for a single message item, ordered by its monotonic ULID. */
|
|
6
8
|
export declare function messageSortKey(ulid: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* True when `sortKey` is one this adapter writes. A partition query carries no
|
|
11
|
+
* sort-key condition, so a partition-wide delete uses this to leave any row it
|
|
12
|
+
* does not own in place rather than deleting the whole partition blindly.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isHistorySortKey(sortKey: string): boolean;
|
|
7
15
|
/** `begins_with` prefix selecting every message item in a session. */
|
|
8
16
|
export declare function messageSortKeyPrefix(): string;
|
|
9
17
|
//# sourceMappingURL=keys.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/history/internal/keys.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/history/internal/keys.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAatC,wDAAwD;AACxD,eAAO,MAAM,gBAAgB,oBAA6B,CAAC;AAW3D,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,sEAAsE;AACtE,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SESSION_SORT_KEY = void 0;
|
|
3
|
+
exports.SESSION_SORT_KEY = exports.SORT_KEY_SEPARATOR = void 0;
|
|
4
4
|
exports.sessionPartition = sessionPartition;
|
|
5
5
|
exports.messageSortKey = messageSortKey;
|
|
6
|
+
exports.isHistorySortKey = isHistorySortKey;
|
|
6
7
|
exports.messageSortKeyPrefix = messageSortKeyPrefix;
|
|
8
|
+
/** Reserved separator joining sort-key segments; forbidden inside a session id. */
|
|
9
|
+
exports.SORT_KEY_SEPARATOR = '#';
|
|
7
10
|
/**
|
|
8
11
|
* Item-kind tag distinguishing this adapter's sort keys from another
|
|
9
12
|
* adapter's on a table shared via `DynamoDBFactory.createAll()` — matches the
|
|
@@ -13,18 +16,32 @@ exports.messageSortKeyPrefix = messageSortKeyPrefix;
|
|
|
13
16
|
* `store.put([sessionId], 'SESSION', ...)`, since `sortKey` collapses a
|
|
14
17
|
* single-element namespace down to just the key).
|
|
15
18
|
*/
|
|
16
|
-
const ADAPTER_PREFIX =
|
|
19
|
+
const ADAPTER_PREFIX = `HISTORY${exports.SORT_KEY_SEPARATOR}`;
|
|
17
20
|
/** Fixed sort key for the per-session metadata item. */
|
|
18
21
|
exports.SESSION_SORT_KEY = `${ADAPTER_PREFIX}SESSION`;
|
|
19
22
|
const MESSAGE_PREFIX = `${ADAPTER_PREFIX}MSG#`;
|
|
20
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Adapter tag prefixed to every chat-history partition key — see the
|
|
25
|
+
* equivalent in checkpointer/internal/keys.ts for why the three adapters'
|
|
26
|
+
* partitions must not overlap on a shared table.
|
|
27
|
+
*/
|
|
28
|
+
const ADAPTER_PARTITION_PREFIX = `HIST${exports.SORT_KEY_SEPARATOR}`;
|
|
29
|
+
/** Partition key for a chat session: the adapter tag plus the session id. */
|
|
21
30
|
function sessionPartition(sessionId) {
|
|
22
|
-
return sessionId
|
|
31
|
+
return `${ADAPTER_PARTITION_PREFIX}${sessionId}`;
|
|
23
32
|
}
|
|
24
33
|
/** Sort key for a single message item, ordered by its monotonic ULID. */
|
|
25
34
|
function messageSortKey(ulid) {
|
|
26
35
|
return `${MESSAGE_PREFIX}${ulid}`;
|
|
27
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* True when `sortKey` is one this adapter writes. A partition query carries no
|
|
39
|
+
* sort-key condition, so a partition-wide delete uses this to leave any row it
|
|
40
|
+
* does not own in place rather than deleting the whole partition blindly.
|
|
41
|
+
*/
|
|
42
|
+
function isHistorySortKey(sortKey) {
|
|
43
|
+
return sortKey.startsWith(ADAPTER_PREFIX);
|
|
44
|
+
}
|
|
28
45
|
/** `begins_with` prefix selecting every message item in a session. */
|
|
29
46
|
function messageSortKeyPrefix() {
|
|
30
47
|
return MESSAGE_PREFIX;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../src/history/internal/keys.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../src/history/internal/keys.ts"],"names":[],"mappings":";;;;;;;AAAA,mFAAmF;AACtE,QAAA,kBAAkB,GAAG,GAAG,CAAC;AAEtC;;;;;;;;GAQG;AACH,MAAM,cAAc,GAAG,UAAU,QAAA,kBAAkB,EAAE,CAAC;AAEtD,wDAAwD;AAC3C,QAAA,gBAAgB,GAAG,GAAG,cAAc,SAAS,CAAC;AAE3D,MAAM,cAAc,GAAG,GAAG,cAAc,MAAM,CAAC;AAE/C;;;;GAIG;AACH,MAAM,wBAAwB,GAAG,OAAO,QAAA,kBAAkB,EAAE,CAAC;AAE7D,6EAA6E;AAC7E,0BAAiC,SAAiB;IAChD,OAAO,GAAG,wBAAwB,GAAG,SAAS,EAAE,CAAC;AACnD,CAAC;AAED,yEAAyE;AACzE,wBAA+B,IAAY;IACzC,OAAO,GAAG,cAAc,GAAG,IAAI,EAAE,CAAC;AACpC,CAAC;AAED;;;;GAIG;AACH,0BAAiC,OAAe;IAC9C,OAAO,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AAC5C,CAAC;AAED,sEAAsE;AACtE;IACE,OAAO,cAAc,CAAC;AACxB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-chunker.d.ts","sourceRoot":"","sources":["../../../src/history/internal/message-chunker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"message-chunker.d.ts","sourceRoot":"","sources":["../../../src/history/internal/message-chunker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AA4BhD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAQ/D;AAaD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,eAAe,EAAE,EACxB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,eAAe,EAAE,EAAE,CAgBrB"}
|
|
@@ -11,11 +11,19 @@ const codec_1 = require("../../shared/codec/codec");
|
|
|
11
11
|
* overshoot the transaction byte limit.
|
|
12
12
|
*/
|
|
13
13
|
const ITEM_OVERHEAD_BYTES = 256;
|
|
14
|
+
/**
|
|
15
|
+
* Byte length of a string as DynamoDB stores it. `String.length` counts UTF-16
|
|
16
|
+
* code units, which understates every non-ASCII character — the wrong
|
|
17
|
+
* direction for an estimate documented to sit at or above the real size.
|
|
18
|
+
*/
|
|
19
|
+
function utf8Bytes(value) {
|
|
20
|
+
return Buffer.byteLength(value, 'utf8');
|
|
21
|
+
}
|
|
14
22
|
function descriptorBytes(descriptor) {
|
|
15
23
|
const body = descriptor.location === codec_1.PayloadLocation.INLINE
|
|
16
24
|
? descriptor.bytes.length
|
|
17
|
-
: descriptor.s3Key
|
|
18
|
-
return body + descriptor.serdeType
|
|
25
|
+
: utf8Bytes(descriptor.s3Key);
|
|
26
|
+
return body + utf8Bytes(descriptor.serdeType);
|
|
19
27
|
}
|
|
20
28
|
/**
|
|
21
29
|
* Conservatively estimate a message item's stored size, so chunks can stay under
|
|
@@ -23,9 +31,9 @@ function descriptorBytes(descriptor) {
|
|
|
23
31
|
* on the safe side of the real marshalled size.
|
|
24
32
|
*/
|
|
25
33
|
function estimateItemBytes(item) {
|
|
26
|
-
return (item.PK
|
|
27
|
-
item.SK
|
|
28
|
-
item.sessionId
|
|
34
|
+
return (utf8Bytes(item.PK) +
|
|
35
|
+
utf8Bytes(item.SK) +
|
|
36
|
+
utf8Bytes(item.sessionId) +
|
|
29
37
|
descriptorBytes(item.message) +
|
|
30
38
|
ITEM_OVERHEAD_BYTES);
|
|
31
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-chunker.js","sourceRoot":"","sources":["../../../src/history/internal/message-chunker.ts"],"names":[],"mappings":";;;;AAAA,oDAAmF;AAGnF;;;;;;GAMG;AACH,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC,SAAS,eAAe,CAAC,UAA6B;IACpD,MAAM,IAAI,GACR,UAAU,CAAC,QAAQ,KAAK,uBAAe,CAAC,MAAM;QAC5C,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM;QACzB,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"message-chunker.js","sourceRoot":"","sources":["../../../src/history/internal/message-chunker.ts"],"names":[],"mappings":";;;;AAAA,oDAAmF;AAGnF;;;;;;GAMG;AACH,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC;;;;GAIG;AACH,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,eAAe,CAAC,UAA6B;IACpD,MAAM,IAAI,GACR,UAAU,CAAC,QAAQ,KAAK,uBAAe,CAAC,MAAM;QAC5C,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM;QACzB,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAClC,OAAO,IAAI,GAAG,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;AAChD,CAAC;AAED;;;;GAIG;AACH,2BAAkC,IAAqB;IACrD,OAAO,CACL,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAClB,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAClB,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC;QACzB,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC;QAC7B,mBAAmB,CACpB,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAClB,KAAa,EACb,KAAa,EACb,IAAY,EACZ,GAAW,EACX,QAAgB;IAEhB,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9B,OAAO,KAAK,IAAI,GAAG,IAAI,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,qBACE,KAAwB,EACxB,QAAgB,EAChB,QAAgB;IAEhB,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,IAAI,OAAO,GAAsB,EAAE,CAAC;IACpC,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;YACxE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACrB,OAAO,GAAG,EAAE,CAAC;YACb,YAAY,GAAG,CAAC,CAAC;QACnB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,YAAY,IAAI,IAAI,CAAC;IACvB,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -17,4 +17,25 @@ import type { HistoryContext } from './setup';
|
|
|
17
17
|
* "TTL expiry" section.
|
|
18
18
|
*/
|
|
19
19
|
export declare function revertSessionCount(context: HistoryContext, sessionId: string, delta: number): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Undo a rolled-back append's effect on the session row.
|
|
22
|
+
*
|
|
23
|
+
* When this call is the one that created the row — `createdAt` still equals
|
|
24
|
+
* this call's timestamp, and the only messages counted on it are the ones
|
|
25
|
+
* being reverted — the whole row is deleted. Without that, a failed first
|
|
26
|
+
* append left a "ghost session": `title`, `createdAt` and `sessionId` are all
|
|
27
|
+
* written via `if_not_exists`, so they were never reverted and never set
|
|
28
|
+
* again, leaving `listSessions()` reporting a session with `messageCount: 0`
|
|
29
|
+
* whose title still held up to 80 characters of a message the caller was told
|
|
30
|
+
* had not persisted, with no API to clear it.
|
|
31
|
+
*
|
|
32
|
+
* Both conditions are load-bearing. `createdAt = :now` establishes that this
|
|
33
|
+
* call created the row; `messageCount = :total` establishes that nothing else
|
|
34
|
+
* has added to it since. A concurrent append to the same brand-new session
|
|
35
|
+
* fails the count check, because deleting the row would destroy that caller's
|
|
36
|
+
* committed messages — so it falls through to the plain decrement, and then
|
|
37
|
+
* strips just the title this call contributed, which is the only part of the
|
|
38
|
+
* row still carrying rolled-back message content.
|
|
39
|
+
*/
|
|
40
|
+
export declare function revertSessionCreation(context: HistoryContext, sessionId: string, total: number, createdAt: string, title?: string): Promise<void>;
|
|
20
41
|
//# sourceMappingURL=session-count.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-count.d.ts","sourceRoot":"","sources":["../../../src/history/internal/session-count.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session-count.d.ts","sourceRoot":"","sources":["../../../src/history/internal/session-count.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAe9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAwBf"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.revertSessionCount = revertSessionCount;
|
|
4
|
+
exports.revertSessionCreation = revertSessionCreation;
|
|
4
5
|
const node_crypto_1 = require("node:crypto");
|
|
5
6
|
const cancellation_1 = require("../../shared/dynamodb/cancellation");
|
|
6
7
|
const retry_1 = require("../../shared/dynamodb/retry");
|
|
7
8
|
const keys_1 = require("./keys");
|
|
9
|
+
const session_title_1 = require("./session-title");
|
|
8
10
|
/**
|
|
9
11
|
* True when a TransactWriteItems cancellation was caused by a
|
|
10
12
|
* ConditionalCheckFailed reason. Named distinctly from
|
|
@@ -55,4 +57,53 @@ async function revertSessionCount(context, sessionId, delta) {
|
|
|
55
57
|
throw error;
|
|
56
58
|
}
|
|
57
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Undo a rolled-back append's effect on the session row.
|
|
62
|
+
*
|
|
63
|
+
* When this call is the one that created the row — `createdAt` still equals
|
|
64
|
+
* this call's timestamp, and the only messages counted on it are the ones
|
|
65
|
+
* being reverted — the whole row is deleted. Without that, a failed first
|
|
66
|
+
* append left a "ghost session": `title`, `createdAt` and `sessionId` are all
|
|
67
|
+
* written via `if_not_exists`, so they were never reverted and never set
|
|
68
|
+
* again, leaving `listSessions()` reporting a session with `messageCount: 0`
|
|
69
|
+
* whose title still held up to 80 characters of a message the caller was told
|
|
70
|
+
* had not persisted, with no API to clear it.
|
|
71
|
+
*
|
|
72
|
+
* Both conditions are load-bearing. `createdAt = :now` establishes that this
|
|
73
|
+
* call created the row; `messageCount = :total` establishes that nothing else
|
|
74
|
+
* has added to it since. A concurrent append to the same brand-new session
|
|
75
|
+
* fails the count check, because deleting the row would destroy that caller's
|
|
76
|
+
* committed messages — so it falls through to the plain decrement, and then
|
|
77
|
+
* strips just the title this call contributed, which is the only part of the
|
|
78
|
+
* row still carrying rolled-back message content.
|
|
79
|
+
*/
|
|
80
|
+
async function revertSessionCreation(context, sessionId, total, createdAt, title) {
|
|
81
|
+
if (total === 0)
|
|
82
|
+
return;
|
|
83
|
+
const input = {
|
|
84
|
+
TransactItems: [
|
|
85
|
+
{
|
|
86
|
+
Delete: {
|
|
87
|
+
TableName: context.tableName,
|
|
88
|
+
Key: { PK: (0, keys_1.sessionPartition)(sessionId), SK: keys_1.SESSION_SORT_KEY },
|
|
89
|
+
ConditionExpression: '#count = :total AND #c = :now',
|
|
90
|
+
ExpressionAttributeNames: { '#count': 'messageCount', '#c': 'createdAt' },
|
|
91
|
+
ExpressionAttributeValues: { ':total': total, ':now': createdAt },
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
ClientRequestToken: (0, node_crypto_1.randomUUID)(),
|
|
96
|
+
};
|
|
97
|
+
try {
|
|
98
|
+
await (0, retry_1.withDynamoDBRetry)(() => context.client.transactWrite(input));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (!isCancelledByCondition(error))
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
await revertSessionCount(context, sessionId, total);
|
|
106
|
+
if (title !== undefined)
|
|
107
|
+
await (0, session_title_1.removeRolledBackTitle)(context, sessionId, createdAt, title);
|
|
108
|
+
}
|
|
58
109
|
//# sourceMappingURL=session-count.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-count.js","sourceRoot":"","sources":["../../../src/history/internal/session-count.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session-count.js","sourceRoot":"","sources":["../../../src/history/internal/session-count.ts"],"names":[],"mappings":";;;;AAAA,6CAAyC;AAEzC,qEAA4E;AAC5E,uDAAgE;AAChE,iCAA4D;AAC5D,mDAAwD;AAGxD;;;;;;;GAOG;AACH,SAAS,sBAAsB,CAAC,KAAY;IAC1C,MAAM,OAAO,GAAG,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,wBAAwB,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACI,KAAK,6BACV,OAAuB,EACvB,SAAiB,EACjB,KAAa;IAEb,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO;IACxB,MAAM,MAAM,GAAG;QACb,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,GAAG,EAAE,EAAE,EAAE,EAAE,IAAA,uBAAgB,EAAC,SAAS,CAAC,EAAE,EAAE,EAAE,uBAAgB,EAAE;QAC9D,gBAAgB,EAAE,iBAAiB;QACnC,mBAAmB,EAAE,sBAAsB;QAC3C,wBAAwB,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE;QACtD,yBAAyB,EAAE,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE;KAC9C,CAAC;IACF,MAAM,KAAK,GAAG,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,kBAAkB,EAAE,IAAA,wBAAU,GAAE,EAAE,CAAC;IACxF,IAAI,CAAC;QACH,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,sBAAsB,CAAC,KAAc,CAAC;YAAE,OAAO;QACnD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACI,KAAK,gCACV,OAAuB,EACvB,SAAiB,EACjB,KAAa,EACb,SAAiB,EACjB,KAAc;IAEd,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO;IACxB,MAAM,KAAK,GAAG;QACZ,aAAa,EAAE;YACb;gBACE,MAAM,EAAE;oBACN,SAAS,EAAE,OAAO,CAAC,SAAS;oBAC5B,GAAG,EAAE,EAAE,EAAE,EAAE,IAAA,uBAAgB,EAAC,SAAS,CAAC,EAAE,EAAE,EAAE,uBAAgB,EAAE;oBAC9D,mBAAmB,EAAE,+BAA+B;oBACpD,wBAAwB,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,EAAE;oBACzE,yBAAyB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE;iBAClE;aACF;SACF;QACD,kBAAkB,EAAE,IAAA,wBAAU,GAAE;KACjC,CAAC;IACF,IAAI,CAAC;QACH,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;QACnE,OAAO;IACT,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,sBAAsB,CAAC,KAAc,CAAC;YAAE,MAAM,KAAK,CAAC;IAC3D,CAAC;IACD,MAAM,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IACpD,IAAI,KAAK,KAAK,SAAS;QAAE,MAAM,IAAA,qCAAqB,EAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;AAC7F,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HistoryContext } from './setup';
|
|
2
|
+
/**
|
|
3
|
+
* Strip a title this call contributed to a session row it created, when the
|
|
4
|
+
* row itself could not be deleted because a concurrent append has since added
|
|
5
|
+
* messages to it.
|
|
6
|
+
*
|
|
7
|
+
* Without this, that narrow window reopens exactly the leak C4 closes: the
|
|
8
|
+
* title is derived from the first human message of an append the caller was
|
|
9
|
+
* told had failed, and `if_not_exists` means nothing ever overwrites it — so
|
|
10
|
+
* a row that now belongs to a different caller keeps up to 80 characters of
|
|
11
|
+
* rolled-back message content.
|
|
12
|
+
*
|
|
13
|
+
* Both guards are load-bearing. `createdAt = :now` establishes that this call
|
|
14
|
+
* created the row, and `title = :title` that the title on it is still the one
|
|
15
|
+
* this call wrote — so a pre-existing title, or one a concurrent caller won
|
|
16
|
+
* the `if_not_exists` race for, is never removed. A condition rejection means
|
|
17
|
+
* exactly that and is not an error; anything else is.
|
|
18
|
+
*/
|
|
19
|
+
export declare function removeRolledBackTitle(context: HistoryContext, sessionId: string, createdAt: string, title: string): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=session-title.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-title.d.ts","sourceRoot":"","sources":["../../../src/history/internal/session-title.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAO9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAgBf"}
|