@deepagents/context 7.1.1 → 7.1.3
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/dist/browser.js +7 -4
- package/dist/browser.js.map +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +695 -808
- package/dist/index.js.map +4 -4
- package/dist/lib/chain-summary.d.ts +2 -6
- package/dist/lib/chain-summary.d.ts.map +1 -1
- package/dist/lib/engine.d.ts +1 -1
- package/dist/lib/engine.d.ts.map +1 -1
- package/dist/lib/estimate.d.ts.map +1 -1
- package/dist/lib/fragments.d.ts +8 -6
- package/dist/lib/fragments.d.ts.map +1 -1
- package/dist/lib/resolvers/index.d.ts +0 -1
- package/dist/lib/resolvers/index.d.ts.map +1 -1
- package/dist/lib/save/save-pipeline.d.ts +2 -2
- package/dist/lib/save/save-pipeline.d.ts.map +1 -1
- package/dist/lib/store/ddl.sqlserver.d.ts.map +1 -1
- package/dist/lib/store/postgres-schema-lock.d.ts +12 -0
- package/dist/lib/store/postgres-schema-lock.d.ts.map +1 -0
- package/dist/lib/store/postgres.store.d.ts +3 -2
- package/dist/lib/store/postgres.store.d.ts.map +1 -1
- package/dist/lib/store/sqlite.store.d.ts +9 -0
- package/dist/lib/store/sqlite.store.d.ts.map +1 -1
- package/dist/lib/store/sqlserver-schema-lock.d.ts +14 -0
- package/dist/lib/store/sqlserver-schema-lock.d.ts.map +1 -0
- package/dist/lib/store/sqlserver.store.d.ts +3 -2
- package/dist/lib/store/sqlserver.store.d.ts.map +1 -1
- package/dist/lib/stream/postgres-notify-change-source.d.ts +3 -2
- package/dist/lib/stream/postgres-notify-change-source.d.ts.map +1 -1
- package/dist/lib/stream/postgres.stream-store.d.ts +3 -2
- package/dist/lib/stream/postgres.stream-store.d.ts.map +1 -1
- package/dist/lib/stream/sqlite.stream-store.d.ts.map +1 -1
- package/dist/lib/tracing/batch-processor.d.ts.map +1 -1
- package/dist/lib/tracing/index.js +49 -29
- package/dist/lib/tracing/index.js.map +2 -2
- package/package.json +17 -9
- package/dist/lib/resolvers/promise-resolver.d.ts +0 -7
- package/dist/lib/resolvers/promise-resolver.d.ts.map +0 -1
- package/dist/scripts/scripts/generate-models-type.d.ts +0 -2
- package/dist/scripts/scripts/generate-models-type.d.ts.map +0 -1
- package/dist/scripts/scripts/probe-tool-output-reminders.d.ts +0 -2
- package/dist/scripts/scripts/probe-tool-output-reminders.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -175,7 +175,7 @@ function fitsFragmentData(value, visited) {
|
|
|
175
175
|
if (Array.isArray(value)) {
|
|
176
176
|
return value.every((item) => fitsFragmentData(item, visited));
|
|
177
177
|
}
|
|
178
|
-
if (value instanceof Promise) return
|
|
178
|
+
if (value instanceof Promise) return false;
|
|
179
179
|
if (Symbol.asyncIterator in value || Symbol.iterator in value) return true;
|
|
180
180
|
if (isFragment(value)) return fitsFragmentData(value.data, visited);
|
|
181
181
|
return Object.values(value).every((item) => fitsFragmentData(item, visited));
|
|
@@ -2647,6 +2647,7 @@ import {
|
|
|
2647
2647
|
isStaticToolUIPart as isStaticToolUIPart2,
|
|
2648
2648
|
validateUIMessages
|
|
2649
2649
|
} from "ai";
|
|
2650
|
+
import pMemoize2 from "p-memoize";
|
|
2650
2651
|
|
|
2651
2652
|
// packages/context/src/lib/ui-message-guards.ts
|
|
2652
2653
|
function isRecord2(value) {
|
|
@@ -2679,75 +2680,63 @@ function mergeReply(segments) {
|
|
|
2679
2680
|
parts: segments.flatMap((segment) => segment.parts)
|
|
2680
2681
|
};
|
|
2681
2682
|
}
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
#firedOnceIds = /* @__PURE__ */ new Set();
|
|
2692
|
-
ingestStored(msg) {
|
|
2693
|
-
this.#messageCount++;
|
|
2683
|
+
function summarizeChain(stored, pending) {
|
|
2684
|
+
let turn = 0;
|
|
2685
|
+
let lastMessageAt;
|
|
2686
|
+
let lastMessage;
|
|
2687
|
+
const assistantMessages = [];
|
|
2688
|
+
let reply = [];
|
|
2689
|
+
const replies = [reply];
|
|
2690
|
+
const firedOnceIds = /* @__PURE__ */ new Set();
|
|
2691
|
+
for (const msg of stored) {
|
|
2694
2692
|
if (msg.name === "assistant") {
|
|
2695
2693
|
const message3 = requireUIMessage(
|
|
2696
2694
|
msg.data,
|
|
2697
2695
|
`Stored assistant message "${msg.id}"`
|
|
2698
2696
|
);
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
if (open && !this.#replyClosed) open.push(message3);
|
|
2703
|
-
else {
|
|
2704
|
-
this.#replies.push([message3]);
|
|
2705
|
-
this.#replyClosed = false;
|
|
2706
|
-
}
|
|
2707
|
-
return;
|
|
2708
|
-
}
|
|
2709
|
-
if (msg.name !== "user") {
|
|
2710
|
-
return;
|
|
2697
|
+
assistantMessages.push(message3);
|
|
2698
|
+
reply.push(message3);
|
|
2699
|
+
continue;
|
|
2711
2700
|
}
|
|
2701
|
+
if (msg.name !== "user") continue;
|
|
2712
2702
|
const message2 = requireUIMessage(
|
|
2713
2703
|
msg.data,
|
|
2714
2704
|
`Stored user message "${msg.id}"`
|
|
2715
2705
|
);
|
|
2716
2706
|
if (isSyntheticReminderMessage(message2)) {
|
|
2717
2707
|
for (const id of message2.metadata.synthetic.onceIds ?? []) {
|
|
2718
|
-
|
|
2708
|
+
firedOnceIds.add(id);
|
|
2719
2709
|
}
|
|
2720
|
-
|
|
2710
|
+
continue;
|
|
2721
2711
|
}
|
|
2722
|
-
for (const id of getReminderOnceIds(message2))
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2712
|
+
for (const id of getReminderOnceIds(message2)) firedOnceIds.add(id);
|
|
2713
|
+
turn++;
|
|
2714
|
+
lastMessageAt = msg.createdAt;
|
|
2715
|
+
lastMessage = message2;
|
|
2716
|
+
reply = [];
|
|
2717
|
+
replies.push(reply);
|
|
2727
2718
|
}
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
if (fragment2.name !== "user") return;
|
|
2719
|
+
for (const fragment2 of pending) {
|
|
2720
|
+
if (fragment2.name !== "user") continue;
|
|
2731
2721
|
const encoded = fragment2.codec?.encode();
|
|
2732
|
-
if (isUIMessage(encoded) && isSyntheticReminderMessage(encoded))
|
|
2733
|
-
|
|
2722
|
+
if (isUIMessage(encoded) && isSyntheticReminderMessage(encoded)) continue;
|
|
2723
|
+
turn++;
|
|
2734
2724
|
}
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
}
|
|
2747
|
-
};
|
|
2725
|
+
return {
|
|
2726
|
+
turn,
|
|
2727
|
+
messageCount: stored.length + pending.length,
|
|
2728
|
+
lastMessageAt,
|
|
2729
|
+
lastMessage,
|
|
2730
|
+
lastAssistantMessage: assistantMessages.at(-1),
|
|
2731
|
+
lastAssistantMessages: assistantMessages,
|
|
2732
|
+
lastAssistantReplies: replies.filter((segments) => segments.length > 0).map(mergeReply),
|
|
2733
|
+
firedOnceIds
|
|
2734
|
+
};
|
|
2735
|
+
}
|
|
2748
2736
|
|
|
2749
2737
|
// packages/context/src/lib/estimate.ts
|
|
2750
2738
|
import { encode } from "gpt-tokenizer";
|
|
2739
|
+
import pMemoize from "p-memoize";
|
|
2751
2740
|
import { z as z2 } from "zod";
|
|
2752
2741
|
var defaultTokenizer = {
|
|
2753
2742
|
encode(text) {
|
|
@@ -2789,14 +2778,17 @@ function inputPricePerMillion(model) {
|
|
|
2789
2778
|
}
|
|
2790
2779
|
var ModelsRegistry = class {
|
|
2791
2780
|
#cache = /* @__PURE__ */ new Map();
|
|
2792
|
-
#loaded = false;
|
|
2793
2781
|
#tokenizers = /* @__PURE__ */ new Map();
|
|
2794
2782
|
#defaultTokenizer = defaultTokenizer;
|
|
2783
|
+
/** Fetches once; concurrent calls share the fetch, and a failed one runs again. */
|
|
2784
|
+
#load = pMemoize(() => this.#fetchModels());
|
|
2795
2785
|
/**
|
|
2796
2786
|
* Load models data from models.dev API
|
|
2797
2787
|
*/
|
|
2798
2788
|
async load() {
|
|
2799
|
-
|
|
2789
|
+
await this.#load();
|
|
2790
|
+
}
|
|
2791
|
+
async #fetchModels() {
|
|
2800
2792
|
const response = await fetch("https://models.dev/api.json");
|
|
2801
2793
|
if (!response.ok) {
|
|
2802
2794
|
throw new Error(`Failed to fetch models: ${response.statusText}`);
|
|
@@ -2815,7 +2807,6 @@ var ModelsRegistry = class {
|
|
|
2815
2807
|
this.#cache.set(`${providerId}:${modelId}`, info);
|
|
2816
2808
|
}
|
|
2817
2809
|
}
|
|
2818
|
-
this.#loaded = true;
|
|
2819
2810
|
}
|
|
2820
2811
|
/**
|
|
2821
2812
|
* Get model info by ID
|
|
@@ -3059,20 +3050,6 @@ function pushLimited2(collected, chunk, maxItems, resolverName) {
|
|
|
3059
3050
|
collected.push(chunk);
|
|
3060
3051
|
}
|
|
3061
3052
|
|
|
3062
|
-
// packages/context/src/lib/resolvers/promise-resolver.ts
|
|
3063
|
-
var PromiseResolver = class {
|
|
3064
|
-
name = "PromiseResolver";
|
|
3065
|
-
canResolve(value) {
|
|
3066
|
-
return value instanceof Promise;
|
|
3067
|
-
}
|
|
3068
|
-
async resolve(value, _ctx) {
|
|
3069
|
-
if (!this.canResolve(value)) {
|
|
3070
|
-
throw new TypeError(`${this.name} cannot resolve a ${typeof value}`);
|
|
3071
|
-
}
|
|
3072
|
-
return value;
|
|
3073
|
-
}
|
|
3074
|
-
};
|
|
3075
|
-
|
|
3076
3053
|
// packages/context/src/lib/resolvers/loader-resolver.ts
|
|
3077
3054
|
var FragmentLoaderResolver = class {
|
|
3078
3055
|
#resolvers;
|
|
@@ -3207,7 +3184,6 @@ function defaultResolvers() {
|
|
|
3207
3184
|
new AsyncResolver(),
|
|
3208
3185
|
new GeneratorResolver(),
|
|
3209
3186
|
new FunctionResolver(),
|
|
3210
|
-
new PromiseResolver(),
|
|
3211
3187
|
new IterableResolver()
|
|
3212
3188
|
];
|
|
3213
3189
|
}
|
|
@@ -3224,14 +3200,14 @@ var HeadConflictError = class extends Error {
|
|
|
3224
3200
|
var SavePipeline = class _SavePipeline {
|
|
3225
3201
|
#engine;
|
|
3226
3202
|
#pending;
|
|
3227
|
-
#shouldBranch
|
|
3228
|
-
constructor(engine, pending) {
|
|
3203
|
+
#shouldBranch;
|
|
3204
|
+
constructor(engine, pending, shouldBranch) {
|
|
3229
3205
|
this.#engine = engine;
|
|
3230
3206
|
this.#pending = pending;
|
|
3231
|
-
}
|
|
3232
|
-
async applyUpdateBranching(shouldBranch) {
|
|
3233
3207
|
this.#shouldBranch = shouldBranch;
|
|
3234
|
-
|
|
3208
|
+
}
|
|
3209
|
+
async applyUpdateBranching() {
|
|
3210
|
+
if (!this.#shouldBranch) return this;
|
|
3235
3211
|
for (const fragment2 of this.#pending) {
|
|
3236
3212
|
if (!fragment2.id) continue;
|
|
3237
3213
|
const existing = await this.#engine.store.getMessage(fragment2.id);
|
|
@@ -3323,7 +3299,10 @@ var SavePipeline = class _SavePipeline {
|
|
|
3323
3299
|
};
|
|
3324
3300
|
|
|
3325
3301
|
// packages/context/src/lib/store/sqlite.store.ts
|
|
3326
|
-
import {
|
|
3302
|
+
import { transaction } from "@zukhruf/sqlite-transaction";
|
|
3303
|
+
import {
|
|
3304
|
+
DatabaseSync
|
|
3305
|
+
} from "node:sqlite";
|
|
3327
3306
|
import { z as z4 } from "zod";
|
|
3328
3307
|
|
|
3329
3308
|
// packages/context/src/lib/store/columns.ts
|
|
@@ -3472,92 +3451,82 @@ function toMessage(row) {
|
|
|
3472
3451
|
createdAt: row.createdAt
|
|
3473
3452
|
};
|
|
3474
3453
|
}
|
|
3454
|
+
var SearchQuerySyntaxError = class extends Error {
|
|
3455
|
+
constructor(query, options) {
|
|
3456
|
+
super(
|
|
3457
|
+
`The search query ${JSON.stringify(query)} is not valid FTS5 syntax. Put a term that has characters such as - or : in double quotes: "well-known" finds the term well-known.`,
|
|
3458
|
+
options
|
|
3459
|
+
);
|
|
3460
|
+
this.name = "SearchQuerySyntaxError";
|
|
3461
|
+
}
|
|
3462
|
+
};
|
|
3463
|
+
function isFts5QueryError(error) {
|
|
3464
|
+
return error instanceof Error && "errcode" in error && error.errcode === 1 && /^(fts5: syntax error near |no such column: |unterminated string$|unknown special query: |expected integer, got )/.test(
|
|
3465
|
+
error.message
|
|
3466
|
+
);
|
|
3467
|
+
}
|
|
3475
3468
|
var SqliteContextStore = class extends ContextStore {
|
|
3476
3469
|
#db;
|
|
3477
|
-
#
|
|
3478
|
-
/**
|
|
3479
|
-
* Get or create a prepared statement.
|
|
3480
|
-
* Statements are cached for the lifetime of the store to avoid
|
|
3481
|
-
* repeated SQL parsing and compilation overhead.
|
|
3482
|
-
*/
|
|
3483
|
-
#stmt(sql) {
|
|
3484
|
-
let stmt = this.#statements.get(sql);
|
|
3485
|
-
if (!stmt) {
|
|
3486
|
-
stmt = this.#db.prepare(sql);
|
|
3487
|
-
this.#statements.set(sql, stmt);
|
|
3488
|
-
}
|
|
3489
|
-
return stmt;
|
|
3490
|
-
}
|
|
3470
|
+
#sql;
|
|
3491
3471
|
constructor(pathOrDb) {
|
|
3492
3472
|
super();
|
|
3493
3473
|
this.#db = typeof pathOrDb === "string" ? new DatabaseSync(pathOrDb) : pathOrDb;
|
|
3494
3474
|
this.#db.exec(ddl_sqlite_default);
|
|
3495
|
-
|
|
3496
|
-
/**
|
|
3497
|
-
* Execute a function within a transaction.
|
|
3498
|
-
* Automatically commits on success or rolls back on error.
|
|
3499
|
-
*/
|
|
3500
|
-
#useTransaction(fn, mode = "deferred") {
|
|
3501
|
-
this.#db.exec(
|
|
3502
|
-
mode === "immediate" ? "BEGIN IMMEDIATE" : "BEGIN TRANSACTION"
|
|
3503
|
-
);
|
|
3504
|
-
try {
|
|
3505
|
-
const result = fn();
|
|
3506
|
-
this.#db.exec("COMMIT");
|
|
3507
|
-
return result;
|
|
3508
|
-
} catch (error) {
|
|
3509
|
-
try {
|
|
3510
|
-
this.#db.exec("ROLLBACK");
|
|
3511
|
-
} catch {
|
|
3512
|
-
}
|
|
3513
|
-
throw error;
|
|
3514
|
-
}
|
|
3475
|
+
this.#sql = this.#db.createTagStore();
|
|
3515
3476
|
}
|
|
3516
3477
|
// ==========================================================================
|
|
3517
3478
|
// Chat Operations
|
|
3518
3479
|
// ==========================================================================
|
|
3519
3480
|
async createChat(chat2) {
|
|
3520
|
-
return
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3481
|
+
return transaction(
|
|
3482
|
+
this.#db,
|
|
3483
|
+
() => {
|
|
3484
|
+
const created = storedChat.parse(
|
|
3485
|
+
this.#db.prepare(
|
|
3486
|
+
`INSERT INTO chats (id, userId, title, metadata)
|
|
3524
3487
|
VALUES (?, ?, ?, ?)
|
|
3525
3488
|
RETURNING *`
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3489
|
+
).get(
|
|
3490
|
+
chat2.id,
|
|
3491
|
+
chat2.userId,
|
|
3492
|
+
chat2.title ?? null,
|
|
3493
|
+
chat2.metadata ? JSON.stringify(chat2.metadata) : null
|
|
3494
|
+
)
|
|
3495
|
+
);
|
|
3496
|
+
this.#db.prepare(
|
|
3497
|
+
`INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
|
|
3535
3498
|
VALUES (?, ?, 'main', NULL, 1, ?)`
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3499
|
+
).run(crypto.randomUUID(), chat2.id, Date.now());
|
|
3500
|
+
return created;
|
|
3501
|
+
},
|
|
3502
|
+
{ mode: "deferred" }
|
|
3503
|
+
);
|
|
3539
3504
|
}
|
|
3540
3505
|
async upsertChat(chat2) {
|
|
3541
|
-
return
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3506
|
+
return transaction(
|
|
3507
|
+
this.#db,
|
|
3508
|
+
() => {
|
|
3509
|
+
const upserted = storedChat.parse(
|
|
3510
|
+
this.#db.prepare(
|
|
3511
|
+
`INSERT INTO chats (id, userId, title, metadata)
|
|
3545
3512
|
VALUES (?, ?, ?, ?)
|
|
3546
3513
|
ON CONFLICT(id) DO UPDATE SET id = excluded.id
|
|
3547
3514
|
RETURNING *`
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3515
|
+
).get(
|
|
3516
|
+
chat2.id,
|
|
3517
|
+
chat2.userId,
|
|
3518
|
+
chat2.title ?? null,
|
|
3519
|
+
chat2.metadata ? JSON.stringify(chat2.metadata) : null
|
|
3520
|
+
)
|
|
3521
|
+
);
|
|
3522
|
+
this.#db.prepare(
|
|
3523
|
+
`INSERT OR IGNORE INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
|
|
3557
3524
|
VALUES (?, ?, 'main', NULL, 1, ?)`
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3525
|
+
).run(crypto.randomUUID(), chat2.id, Date.now());
|
|
3526
|
+
return upserted;
|
|
3527
|
+
},
|
|
3528
|
+
{ mode: "deferred" }
|
|
3529
|
+
);
|
|
3561
3530
|
}
|
|
3562
3531
|
#getChat(chatId) {
|
|
3563
3532
|
return storedChat.optional().parse(this.#db.prepare("SELECT * FROM chats WHERE id = ?").get(chatId));
|
|
@@ -3566,30 +3535,36 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
3566
3535
|
return this.#getChat(chatId);
|
|
3567
3536
|
}
|
|
3568
3537
|
async updateChat(chatId, update) {
|
|
3569
|
-
return
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3538
|
+
return transaction(
|
|
3539
|
+
this.#db,
|
|
3540
|
+
() => {
|
|
3541
|
+
const current = this.#getChat(chatId);
|
|
3542
|
+
if (!current) {
|
|
3543
|
+
throw new Error(`updateChat: chat "${chatId}" not found`);
|
|
3544
|
+
}
|
|
3545
|
+
const updates = update(current);
|
|
3546
|
+
if (updates === void 0) return current;
|
|
3547
|
+
const setClauses = [
|
|
3548
|
+
"updatedAt = strftime('%s', 'now') * 1000"
|
|
3549
|
+
];
|
|
3550
|
+
const params = [];
|
|
3551
|
+
if (updates.title !== void 0) {
|
|
3552
|
+
setClauses.push("title = ?");
|
|
3553
|
+
params.push(updates.title);
|
|
3554
|
+
}
|
|
3555
|
+
if (updates.metadata !== void 0) {
|
|
3556
|
+
setClauses.push("metadata = ?");
|
|
3557
|
+
params.push(JSON.stringify(updates.metadata));
|
|
3558
|
+
}
|
|
3559
|
+
params.push(chatId);
|
|
3560
|
+
return storedChat.parse(
|
|
3561
|
+
this.#db.prepare(
|
|
3562
|
+
`UPDATE chats SET ${setClauses.join(", ")} WHERE id = ? RETURNING *`
|
|
3563
|
+
).get(...params)
|
|
3564
|
+
);
|
|
3565
|
+
},
|
|
3566
|
+
{ mode: "immediate" }
|
|
3567
|
+
);
|
|
3593
3568
|
}
|
|
3594
3569
|
async listChats(options) {
|
|
3595
3570
|
const params = [];
|
|
@@ -3636,25 +3611,29 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
3636
3611
|
);
|
|
3637
3612
|
}
|
|
3638
3613
|
async deleteChat(chatId, options) {
|
|
3639
|
-
return
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
const
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3614
|
+
return transaction(
|
|
3615
|
+
this.#db,
|
|
3616
|
+
() => {
|
|
3617
|
+
const messageIds = z4.array(idRow).parse(
|
|
3618
|
+
this.#db.prepare("SELECT id FROM messages WHERE chatId = ?").all(chatId)
|
|
3619
|
+
);
|
|
3620
|
+
let sql = "DELETE FROM chats WHERE id = ?";
|
|
3621
|
+
const params = [chatId];
|
|
3622
|
+
if (options?.userId !== void 0) {
|
|
3623
|
+
sql += " AND userId = ?";
|
|
3624
|
+
params.push(options.userId);
|
|
3625
|
+
}
|
|
3626
|
+
const result = this.#db.prepare(sql).run(...params);
|
|
3627
|
+
if (result.changes > 0 && messageIds.length > 0) {
|
|
3628
|
+
const placeholders = messageIds.map(() => "?").join(", ");
|
|
3629
|
+
this.#db.prepare(
|
|
3630
|
+
`DELETE FROM messages_fts WHERE messageId IN (${placeholders})`
|
|
3631
|
+
).run(...messageIds.map((m) => m.id));
|
|
3632
|
+
}
|
|
3633
|
+
return result.changes > 0;
|
|
3634
|
+
},
|
|
3635
|
+
{ mode: "deferred" }
|
|
3636
|
+
);
|
|
3658
3637
|
}
|
|
3659
3638
|
// ==========================================================================
|
|
3660
3639
|
// Message Operations (Graph Nodes)
|
|
@@ -3663,59 +3642,48 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
3663
3642
|
if (message2.parentId === message2.id) {
|
|
3664
3643
|
throw new Error(`Message ${message2.id} cannot be its own parent`);
|
|
3665
3644
|
}
|
|
3666
|
-
this.#
|
|
3667
|
-
|
|
3668
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
3645
|
+
this.#sql.run`INSERT INTO messages (id, chatId, parentId, name, type, data, createdAt)
|
|
3646
|
+
VALUES (${message2.id}, ${message2.chatId}, ${message2.parentId}, ${message2.name}, ${message2.type ?? null}, ${JSON.stringify(message2.data)}, ${message2.createdAt})
|
|
3669
3647
|
ON CONFLICT(id) DO UPDATE SET
|
|
3670
3648
|
name = excluded.name,
|
|
3671
3649
|
type = excluded.type,
|
|
3672
|
-
data = excluded.data
|
|
3673
|
-
).run(
|
|
3674
|
-
message2.id,
|
|
3675
|
-
message2.chatId,
|
|
3676
|
-
message2.parentId,
|
|
3677
|
-
message2.name,
|
|
3678
|
-
message2.type ?? null,
|
|
3679
|
-
JSON.stringify(message2.data),
|
|
3680
|
-
message2.createdAt
|
|
3681
|
-
);
|
|
3650
|
+
data = excluded.data`;
|
|
3682
3651
|
const content = typeof message2.data === "string" ? message2.data : JSON.stringify(message2.data);
|
|
3683
|
-
this.#
|
|
3684
|
-
this.#
|
|
3685
|
-
|
|
3686
|
-
VALUES (?, ?, ?, ?)`
|
|
3687
|
-
).run(message2.id, message2.chatId, message2.name, content);
|
|
3652
|
+
this.#sql.run`DELETE FROM messages_fts WHERE messageId = ${message2.id}`;
|
|
3653
|
+
this.#sql.run`INSERT INTO messages_fts(messageId, chatId, name, content)
|
|
3654
|
+
VALUES (${message2.id}, ${message2.chatId}, ${message2.name}, ${content})`;
|
|
3688
3655
|
}
|
|
3689
3656
|
async addMessage(message2) {
|
|
3690
3657
|
this.#insertMessage(message2);
|
|
3691
3658
|
}
|
|
3692
3659
|
async addMessages(messages) {
|
|
3693
3660
|
if (messages.length === 0) return;
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3661
|
+
transaction(
|
|
3662
|
+
this.#db,
|
|
3663
|
+
() => {
|
|
3664
|
+
for (const message2 of messages) {
|
|
3665
|
+
this.#insertMessage(message2);
|
|
3666
|
+
}
|
|
3667
|
+
},
|
|
3668
|
+
{ mode: "deferred" }
|
|
3669
|
+
);
|
|
3699
3670
|
}
|
|
3700
3671
|
async setMessageParent(messageId, parentId) {
|
|
3701
3672
|
if (parentId === messageId) {
|
|
3702
3673
|
throw new Error(`Message ${messageId} cannot be its own parent`);
|
|
3703
3674
|
}
|
|
3704
|
-
const result = this.#
|
|
3705
|
-
"UPDATE messages SET parentId = ? WHERE id = ?"
|
|
3706
|
-
).run(parentId, messageId);
|
|
3675
|
+
const result = this.#sql.run`UPDATE messages SET parentId = ${parentId} WHERE id = ${messageId}`;
|
|
3707
3676
|
if (result.changes === 0) {
|
|
3708
3677
|
throw new Error(`Message ${messageId} not found`);
|
|
3709
3678
|
}
|
|
3710
3679
|
}
|
|
3711
3680
|
async getMessage(messageId) {
|
|
3712
|
-
return storedMessage.optional().parse(this.#
|
|
3681
|
+
return storedMessage.optional().parse(this.#sql.get`SELECT * FROM messages WHERE id = ${messageId}`);
|
|
3713
3682
|
}
|
|
3714
3683
|
async getMessageChain(headId) {
|
|
3715
3684
|
return z4.array(storedMessage).parse(
|
|
3716
|
-
this.#
|
|
3717
|
-
|
|
3718
|
-
SELECT *, 0 as depth FROM messages WHERE id = ?
|
|
3685
|
+
this.#sql.all`WITH RECURSIVE chain AS (
|
|
3686
|
+
SELECT *, 0 as depth FROM messages WHERE id = ${headId}
|
|
3719
3687
|
UNION ALL
|
|
3720
3688
|
SELECT m.*, c.depth + 1 FROM messages m
|
|
3721
3689
|
INNER JOIN chain c ON m.id = c.parentId
|
|
@@ -3723,14 +3691,11 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
3723
3691
|
)
|
|
3724
3692
|
SELECT * FROM chain
|
|
3725
3693
|
ORDER BY depth DESC`
|
|
3726
|
-
).all(headId)
|
|
3727
3694
|
);
|
|
3728
3695
|
}
|
|
3729
3696
|
async hasChildren(messageId) {
|
|
3730
3697
|
const row = hasChildrenRow.parse(
|
|
3731
|
-
this.#
|
|
3732
|
-
"SELECT EXISTS(SELECT 1 FROM messages WHERE parentId = ?) as hasChildren"
|
|
3733
|
-
).get(messageId)
|
|
3698
|
+
this.#sql.get`SELECT EXISTS(SELECT 1 FROM messages WHERE parentId = ${messageId}) as hasChildren`
|
|
3734
3699
|
);
|
|
3735
3700
|
return row.hasChildren === 1;
|
|
3736
3701
|
}
|
|
@@ -3768,9 +3733,7 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
3768
3733
|
}
|
|
3769
3734
|
async getActiveBranch(chatId) {
|
|
3770
3735
|
return storedBranch.optional().parse(
|
|
3771
|
-
this.#
|
|
3772
|
-
"SELECT * FROM branches WHERE chatId = ? AND isActive = 1"
|
|
3773
|
-
).get(chatId)
|
|
3736
|
+
this.#sql.get`SELECT * FROM branches WHERE chatId = ${chatId} AND isActive = 1`
|
|
3774
3737
|
);
|
|
3775
3738
|
}
|
|
3776
3739
|
async setActiveBranch(chatId, branchId) {
|
|
@@ -3778,9 +3741,7 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
3778
3741
|
this.#db.prepare("UPDATE branches SET isActive = 1 WHERE id = ?").run(branchId);
|
|
3779
3742
|
}
|
|
3780
3743
|
async updateBranchHead(branchId, messageId, expectedHeadMessageId) {
|
|
3781
|
-
const result = this.#
|
|
3782
|
-
"UPDATE branches SET headMessageId = ? WHERE id = ? AND headMessageId IS ?"
|
|
3783
|
-
).run(messageId, branchId, expectedHeadMessageId);
|
|
3744
|
+
const result = this.#sql.run`UPDATE branches SET headMessageId = ${messageId} WHERE id = ${branchId} AND headMessageId IS ${expectedHeadMessageId}`;
|
|
3784
3745
|
return result.changes > 0;
|
|
3785
3746
|
}
|
|
3786
3747
|
async listBranches(chatId) {
|
|
@@ -3876,7 +3837,15 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
3876
3837
|
}
|
|
3877
3838
|
sql += " ORDER BY fts.rank LIMIT ?";
|
|
3878
3839
|
params.push(limit);
|
|
3879
|
-
|
|
3840
|
+
const statement2 = this.#db.prepare(sql);
|
|
3841
|
+
try {
|
|
3842
|
+
return z4.array(searchResult).parse(statement2.all(...params));
|
|
3843
|
+
} catch (error) {
|
|
3844
|
+
if (isFts5QueryError(error)) {
|
|
3845
|
+
throw new SearchQuerySyntaxError(query, { cause: error });
|
|
3846
|
+
}
|
|
3847
|
+
throw error;
|
|
3848
|
+
}
|
|
3880
3849
|
}
|
|
3881
3850
|
// ==========================================================================
|
|
3882
3851
|
// Visualization Operations
|
|
@@ -4101,6 +4070,25 @@ function mergeTerminalToolParts(message2, liveParts) {
|
|
|
4101
4070
|
parts.push(...remaining.values());
|
|
4102
4071
|
return { ...message2, parts };
|
|
4103
4072
|
}
|
|
4073
|
+
var UNOPENED = {
|
|
4074
|
+
chat: null,
|
|
4075
|
+
branchName: "main",
|
|
4076
|
+
headMessageId: void 0
|
|
4077
|
+
};
|
|
4078
|
+
var OpenChat = class {
|
|
4079
|
+
chat;
|
|
4080
|
+
branch;
|
|
4081
|
+
constructor(chat2, branch) {
|
|
4082
|
+
this.chat = chat2;
|
|
4083
|
+
this.branch = branch;
|
|
4084
|
+
}
|
|
4085
|
+
get branchName() {
|
|
4086
|
+
return this.branch.name;
|
|
4087
|
+
}
|
|
4088
|
+
get headMessageId() {
|
|
4089
|
+
return this.branch.headMessageId ?? void 0;
|
|
4090
|
+
}
|
|
4091
|
+
};
|
|
4104
4092
|
var ContextEngine = class _ContextEngine {
|
|
4105
4093
|
/** Non-message fragments (role, hints, etc.) - not persisted in graph */
|
|
4106
4094
|
#fragments = [];
|
|
@@ -4109,23 +4097,13 @@ var ContextEngine = class _ContextEngine {
|
|
|
4109
4097
|
#store;
|
|
4110
4098
|
#chatId;
|
|
4111
4099
|
#userId;
|
|
4112
|
-
|
|
4113
|
-
#
|
|
4114
|
-
|
|
4115
|
-
#
|
|
4116
|
-
/** Initial metadata to merge on first initialization */
|
|
4117
|
-
#initialMetadata;
|
|
4100
|
+
/** What this engine knows of its chat: nothing until the first store call opens it. */
|
|
4101
|
+
#session = UNOPENED;
|
|
4102
|
+
/** Opens the chat once; concurrent first calls share it, and a failed open runs again. */
|
|
4103
|
+
#open;
|
|
4118
4104
|
#loaderResolver;
|
|
4119
4105
|
/** Retained so forked children inherit the parent's resolver chain */
|
|
4120
4106
|
#resolvers;
|
|
4121
|
-
get #activeBranch() {
|
|
4122
|
-
if (!this.#branch) {
|
|
4123
|
-
throw new Error(
|
|
4124
|
-
"Branch not initialized. Call #ensureInitialized() first."
|
|
4125
|
-
);
|
|
4126
|
-
}
|
|
4127
|
-
return this.#branch;
|
|
4128
|
-
}
|
|
4129
4107
|
get #renderableFragments() {
|
|
4130
4108
|
return this.#fragments.filter((f) => !isConditionalReminder(f));
|
|
4131
4109
|
}
|
|
@@ -4139,38 +4117,30 @@ var ContextEngine = class _ContextEngine {
|
|
|
4139
4117
|
this.#store = options.store;
|
|
4140
4118
|
this.#chatId = options.chatId;
|
|
4141
4119
|
this.#userId = options.userId;
|
|
4142
|
-
this.#
|
|
4143
|
-
this.#initialMetadata = options.metadata;
|
|
4120
|
+
this.#open = pMemoize2(() => this.#openChat(options.metadata));
|
|
4144
4121
|
this.#resolvers = options.resolvers;
|
|
4145
4122
|
this.#loaderResolver = new FragmentLoaderResolver(
|
|
4146
4123
|
options.resolvers ?? defaultResolvers()
|
|
4147
4124
|
);
|
|
4148
4125
|
}
|
|
4149
4126
|
/**
|
|
4150
|
-
*
|
|
4127
|
+
* Creates the chat if it is new, checks its owner, merges the initial
|
|
4128
|
+
* metadata, and reads the active branch.
|
|
4151
4129
|
*/
|
|
4152
|
-
async #
|
|
4153
|
-
|
|
4154
|
-
return;
|
|
4155
|
-
}
|
|
4156
|
-
this.#chatData = await this.#store.upsertChat({
|
|
4130
|
+
async #openChat(metadata) {
|
|
4131
|
+
let chat2 = await this.#store.upsertChat({
|
|
4157
4132
|
id: this.#chatId,
|
|
4158
4133
|
userId: this.#userId
|
|
4159
4134
|
});
|
|
4160
|
-
if (
|
|
4135
|
+
if (chat2.userId !== this.#userId) {
|
|
4161
4136
|
throw new Error(
|
|
4162
|
-
`chat "${this.#chatId}" belongs to user "${
|
|
4137
|
+
`chat "${this.#chatId}" belongs to user "${chat2.userId}", not "${this.#userId}"`
|
|
4163
4138
|
);
|
|
4164
4139
|
}
|
|
4165
|
-
if (
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
({ metadata }) => ({
|
|
4170
|
-
metadata: { ...metadata, ...initialMetadata }
|
|
4171
|
-
})
|
|
4172
|
-
);
|
|
4173
|
-
this.#initialMetadata = void 0;
|
|
4140
|
+
if (metadata) {
|
|
4141
|
+
chat2 = await this.#store.updateChat(this.#chatId, (stored) => ({
|
|
4142
|
+
metadata: { ...stored.metadata, ...metadata }
|
|
4143
|
+
}));
|
|
4174
4144
|
}
|
|
4175
4145
|
const branch = await this.#store.getActiveBranch(this.#chatId);
|
|
4176
4146
|
if (!branch) {
|
|
@@ -4178,20 +4148,21 @@ var ContextEngine = class _ContextEngine {
|
|
|
4178
4148
|
`Active branch not found for chat "${this.#chatId}" after upsertChat`
|
|
4179
4149
|
);
|
|
4180
4150
|
}
|
|
4181
|
-
|
|
4182
|
-
this.#
|
|
4183
|
-
|
|
4151
|
+
const open = new OpenChat(chat2, branch);
|
|
4152
|
+
this.#session = open;
|
|
4153
|
+
return open;
|
|
4184
4154
|
}
|
|
4185
4155
|
/**
|
|
4186
4156
|
* Create a new branch from a specific message.
|
|
4187
4157
|
* Shared logic between rewind() and btw().
|
|
4188
4158
|
*/
|
|
4189
|
-
async #createBranchFrom(messageId, switchTo) {
|
|
4159
|
+
async #createBranchFrom(open, messageId, switchTo) {
|
|
4190
4160
|
const branches = await this.#store.listBranches(this.#chatId);
|
|
4161
|
+
const baseName = open.branch.name;
|
|
4191
4162
|
const samePrefix = branches.filter(
|
|
4192
|
-
(it) => it.name ===
|
|
4163
|
+
(it) => it.name === baseName || it.name.startsWith(`${baseName}-v`)
|
|
4193
4164
|
);
|
|
4194
|
-
const newBranchName = `${
|
|
4165
|
+
const newBranchName = `${baseName}-v${samePrefix.length + 1}`;
|
|
4195
4166
|
const newBranch = {
|
|
4196
4167
|
id: crypto.randomUUID(),
|
|
4197
4168
|
chatId: this.#chatId,
|
|
@@ -4203,8 +4174,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
4203
4174
|
await this.#store.createBranch(newBranch);
|
|
4204
4175
|
if (switchTo) {
|
|
4205
4176
|
await this.#store.setActiveBranch(this.#chatId, newBranch.id);
|
|
4206
|
-
|
|
4207
|
-
this.#branchName = newBranchName;
|
|
4177
|
+
open.branch = { ...newBranch, isActive: true };
|
|
4208
4178
|
this.#pendingMessages = [];
|
|
4209
4179
|
}
|
|
4210
4180
|
const chain = await this.#store.getMessageChain(messageId);
|
|
@@ -4236,47 +4206,40 @@ var ContextEngine = class _ContextEngine {
|
|
|
4236
4206
|
* Get the current branch name.
|
|
4237
4207
|
*/
|
|
4238
4208
|
get branch() {
|
|
4239
|
-
return this.#branchName;
|
|
4209
|
+
return this.#session.branchName;
|
|
4240
4210
|
}
|
|
4241
4211
|
/**
|
|
4242
4212
|
* Get the current branch head message ID.
|
|
4243
4213
|
* Returns undefined if no messages have been saved yet.
|
|
4244
4214
|
*/
|
|
4245
4215
|
get headMessageId() {
|
|
4246
|
-
return this.#
|
|
4216
|
+
return this.#session.headMessageId;
|
|
4247
4217
|
}
|
|
4248
4218
|
/**
|
|
4249
4219
|
* Get metadata for the current chat.
|
|
4250
4220
|
* Returns null if the chat hasn't been initialized yet.
|
|
4251
4221
|
*/
|
|
4252
4222
|
get chat() {
|
|
4253
|
-
return this.#
|
|
4223
|
+
return this.#session.chat;
|
|
4254
4224
|
}
|
|
4255
4225
|
/**
|
|
4256
4226
|
* Count user turns in the conversation and return the previous saved user message context.
|
|
4257
4227
|
* Includes persisted messages and pending messages in the turn count.
|
|
4258
4228
|
*/
|
|
4259
4229
|
async #getChainContext(pending = this.#pendingMessages) {
|
|
4260
|
-
await this.#
|
|
4261
|
-
const
|
|
4262
|
-
|
|
4263
|
-
const chain = await this.#store.getMessageChain(
|
|
4264
|
-
this.#branch.headMessageId
|
|
4265
|
-
);
|
|
4266
|
-
for (const msg of chain) builder.ingestStored(msg);
|
|
4267
|
-
}
|
|
4268
|
-
for (const fragment2 of pending) builder.ingestPending(fragment2);
|
|
4269
|
-
return builder.build();
|
|
4230
|
+
const open = await this.#open();
|
|
4231
|
+
const stored = open.branch.headMessageId ? await this.#store.getMessageChain(open.branch.headMessageId) : [];
|
|
4232
|
+
return summarizeChain(stored, pending);
|
|
4270
4233
|
}
|
|
4271
4234
|
async getTurnCount() {
|
|
4272
4235
|
const { turn } = await this.#getChainContext();
|
|
4273
4236
|
return turn;
|
|
4274
4237
|
}
|
|
4275
4238
|
async firstUserMessage() {
|
|
4276
|
-
await this.#
|
|
4277
|
-
if (
|
|
4239
|
+
const open = await this.#open();
|
|
4240
|
+
if (open.branch.headMessageId) {
|
|
4278
4241
|
const chain = await this.#store.getMessageChain(
|
|
4279
|
-
|
|
4242
|
+
open.branch.headMessageId
|
|
4280
4243
|
);
|
|
4281
4244
|
for (const msg of chain) {
|
|
4282
4245
|
if (msg.name === "user") {
|
|
@@ -4305,7 +4268,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
4305
4268
|
* @throws if the pending tail is missing an id (programming error).
|
|
4306
4269
|
*/
|
|
4307
4270
|
async headMessage() {
|
|
4308
|
-
await this.#
|
|
4271
|
+
const open = await this.#open();
|
|
4309
4272
|
if (this.#pendingMessages.length > 0) {
|
|
4310
4273
|
const tail = this.#pendingMessages[this.#pendingMessages.length - 1];
|
|
4311
4274
|
if (!tail.id) {
|
|
@@ -4315,8 +4278,8 @@ var ContextEngine = class _ContextEngine {
|
|
|
4315
4278
|
}
|
|
4316
4279
|
return { id: tail.id, name: tail.name };
|
|
4317
4280
|
}
|
|
4318
|
-
if (
|
|
4319
|
-
const msg = await this.#store.getMessage(
|
|
4281
|
+
if (open.branch.headMessageId) {
|
|
4282
|
+
const msg = await this.#store.getMessage(open.branch.headMessageId);
|
|
4320
4283
|
if (msg) return { id: msg.id, name: msg.name };
|
|
4321
4284
|
}
|
|
4322
4285
|
return void 0;
|
|
@@ -4336,12 +4299,12 @@ var ContextEngine = class _ContextEngine {
|
|
|
4336
4299
|
* For id-lookup use `headMessage()` instead — that one keeps placeholders.
|
|
4337
4300
|
*/
|
|
4338
4301
|
async getMessages() {
|
|
4339
|
-
await this.#
|
|
4302
|
+
const open = await this.#open();
|
|
4340
4303
|
const messages = [];
|
|
4341
4304
|
const indexById = /* @__PURE__ */ new Map();
|
|
4342
|
-
if (
|
|
4305
|
+
if (open.branch.headMessageId) {
|
|
4343
4306
|
const chain = await this.#store.getMessageChain(
|
|
4344
|
-
|
|
4307
|
+
open.branch.headMessageId
|
|
4345
4308
|
);
|
|
4346
4309
|
for (const msg of chain) {
|
|
4347
4310
|
const data = requireUIMessage(msg.data, `Stored message "${msg.id}"`);
|
|
@@ -4452,7 +4415,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
4452
4415
|
* ```
|
|
4453
4416
|
*/
|
|
4454
4417
|
async resolve(options) {
|
|
4455
|
-
await this.#
|
|
4418
|
+
await this.#open();
|
|
4456
4419
|
await this.#loaderResolver.resolve(this.#fragments, {
|
|
4457
4420
|
sandbox: options.sandbox,
|
|
4458
4421
|
context: this,
|
|
@@ -4477,15 +4440,19 @@ var ContextEngine = class _ContextEngine {
|
|
|
4477
4440
|
* ```
|
|
4478
4441
|
*/
|
|
4479
4442
|
async save(options) {
|
|
4480
|
-
await this.#
|
|
4443
|
+
const open = await this.#open();
|
|
4481
4444
|
if (this.#pendingMessages.length === 0) {
|
|
4482
|
-
return { headMessageId:
|
|
4445
|
+
return { headMessageId: open.branch.headMessageId ?? void 0 };
|
|
4483
4446
|
}
|
|
4484
4447
|
const pending = this.#pendingMessages;
|
|
4485
4448
|
this.#pendingMessages = [];
|
|
4486
|
-
const pipeline = new SavePipeline(
|
|
4449
|
+
const pipeline = new SavePipeline(
|
|
4450
|
+
this.#asSavePipelineEngine(open),
|
|
4451
|
+
pending,
|
|
4452
|
+
options?.branch ?? true
|
|
4453
|
+
);
|
|
4487
4454
|
try {
|
|
4488
|
-
await pipeline.applyUpdateBranching(
|
|
4455
|
+
await pipeline.applyUpdateBranching();
|
|
4489
4456
|
await this.#foldUserReminders(pending);
|
|
4490
4457
|
return await pipeline.persist();
|
|
4491
4458
|
} catch (error) {
|
|
@@ -4726,13 +4693,14 @@ var ContextEngine = class _ContextEngine {
|
|
|
4726
4693
|
return this.#fragments.filter(isConditionalReminder).map((fragment2) => fragment2.metadata.reminder).filter((config) => config.target === target);
|
|
4727
4694
|
}
|
|
4728
4695
|
#buildBaseWhenCtx(chain) {
|
|
4729
|
-
const
|
|
4730
|
-
|
|
4731
|
-
const elapsed = chain.lastMessageAt !== void 0 ? Date.now() - chain.lastMessageAt : void 0;
|
|
4732
|
-
const chatData = this.#chatData;
|
|
4733
|
-
if (!chatData) {
|
|
4696
|
+
const session = this.#session;
|
|
4697
|
+
if (!(session instanceof OpenChat)) {
|
|
4734
4698
|
throw new Error("ContextEngine must be initialized before reminders run");
|
|
4735
4699
|
}
|
|
4700
|
+
const chatData = session.chat;
|
|
4701
|
+
const rawUsage = chatData.metadata?.usage;
|
|
4702
|
+
const usage = isLanguageModelUsage(rawUsage) ? rawUsage : void 0;
|
|
4703
|
+
const elapsed = chain.lastMessageAt !== void 0 ? Date.now() - chain.lastMessageAt : void 0;
|
|
4736
4704
|
return {
|
|
4737
4705
|
turn: chain.turn,
|
|
4738
4706
|
messageCount: chain.messageCount,
|
|
@@ -4740,7 +4708,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
4740
4708
|
lastMessage: chain.lastMessage,
|
|
4741
4709
|
chat: chatData,
|
|
4742
4710
|
usage,
|
|
4743
|
-
branch:
|
|
4711
|
+
branch: session.branchName,
|
|
4744
4712
|
elapsed
|
|
4745
4713
|
};
|
|
4746
4714
|
}
|
|
@@ -4861,36 +4829,36 @@ var ContextEngine = class _ContextEngine {
|
|
|
4861
4829
|
}
|
|
4862
4830
|
return { texts, onceIds: [...onceIds] };
|
|
4863
4831
|
}
|
|
4864
|
-
#asSavePipelineEngine() {
|
|
4832
|
+
#asSavePipelineEngine(open) {
|
|
4865
4833
|
return {
|
|
4866
4834
|
store: this.#store,
|
|
4867
4835
|
chatId: this.#chatId,
|
|
4868
4836
|
getActiveBranch: () => ({
|
|
4869
|
-
id:
|
|
4870
|
-
headMessageId:
|
|
4837
|
+
id: open.branch.id,
|
|
4838
|
+
headMessageId: open.branch.headMessageId
|
|
4871
4839
|
}),
|
|
4872
4840
|
commitHead: async (headMessageId, expectedHeadMessageId) => {
|
|
4873
4841
|
const committed = await this.#store.updateBranchHead(
|
|
4874
|
-
|
|
4842
|
+
open.branch.id,
|
|
4875
4843
|
headMessageId,
|
|
4876
4844
|
expectedHeadMessageId
|
|
4877
4845
|
);
|
|
4878
4846
|
if (committed) {
|
|
4879
|
-
|
|
4847
|
+
open.branch.headMessageId = headMessageId;
|
|
4880
4848
|
}
|
|
4881
4849
|
return committed;
|
|
4882
4850
|
},
|
|
4883
4851
|
refreshBranch: async () => {
|
|
4884
4852
|
const fresh = await this.#store.getBranch(
|
|
4885
4853
|
this.#chatId,
|
|
4886
|
-
|
|
4854
|
+
open.branch.name
|
|
4887
4855
|
);
|
|
4888
4856
|
if (!fresh) {
|
|
4889
4857
|
throw new Error(
|
|
4890
|
-
`Branch "${
|
|
4858
|
+
`Branch "${open.branch.name}" not found for chat "${this.#chatId}"`
|
|
4891
4859
|
);
|
|
4892
4860
|
}
|
|
4893
|
-
|
|
4861
|
+
open.branch = fresh;
|
|
4894
4862
|
return { id: fresh.id, headMessageId: fresh.headMessageId };
|
|
4895
4863
|
},
|
|
4896
4864
|
rewindForUpdate: (parentId) => this.#rewindForUpdate(parentId)
|
|
@@ -4909,7 +4877,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
4909
4877
|
* @returns Estimate result with token counts, costs, and per-fragment breakdown
|
|
4910
4878
|
*/
|
|
4911
4879
|
async estimate(modelId, options = {}) {
|
|
4912
|
-
await this.#
|
|
4880
|
+
const open = await this.#open();
|
|
4913
4881
|
await this.#loaderResolver.resolve(this.#fragments, {
|
|
4914
4882
|
sandbox: options.sandbox,
|
|
4915
4883
|
context: this,
|
|
@@ -4938,9 +4906,9 @@ var ContextEngine = class _ContextEngine {
|
|
|
4938
4906
|
cost
|
|
4939
4907
|
});
|
|
4940
4908
|
}
|
|
4941
|
-
if (
|
|
4909
|
+
if (open.branch.headMessageId) {
|
|
4942
4910
|
const chain = await this.#store.getMessageChain(
|
|
4943
|
-
|
|
4911
|
+
open.branch.headMessageId
|
|
4944
4912
|
);
|
|
4945
4913
|
for (const msg of chain) {
|
|
4946
4914
|
const content = estimateMessageContent(msg.data);
|
|
@@ -5012,7 +4980,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
5012
4980
|
* ```
|
|
5013
4981
|
*/
|
|
5014
4982
|
async rewind(messageId) {
|
|
5015
|
-
await this.#
|
|
4983
|
+
const open = await this.#open();
|
|
5016
4984
|
const message2 = await this.#store.getMessage(messageId);
|
|
5017
4985
|
if (!message2) {
|
|
5018
4986
|
throw new Error(`Message "${messageId}" not found`);
|
|
@@ -5020,7 +4988,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
5020
4988
|
if (message2.chatId !== this.#chatId) {
|
|
5021
4989
|
throw new Error(`Message "${messageId}" belongs to a different chat`);
|
|
5022
4990
|
}
|
|
5023
|
-
return this.#createBranchFrom(messageId, true);
|
|
4991
|
+
return this.#createBranchFrom(open, messageId, true);
|
|
5024
4992
|
}
|
|
5025
4993
|
/**
|
|
5026
4994
|
* Create a checkpoint at the current position.
|
|
@@ -5042,15 +5010,15 @@ var ContextEngine = class _ContextEngine {
|
|
|
5042
5010
|
* ```
|
|
5043
5011
|
*/
|
|
5044
5012
|
async checkpoint(name) {
|
|
5045
|
-
await this.#
|
|
5046
|
-
if (!
|
|
5013
|
+
const open = await this.#open();
|
|
5014
|
+
if (!open.branch.headMessageId) {
|
|
5047
5015
|
throw new Error("Cannot create checkpoint: no messages in conversation");
|
|
5048
5016
|
}
|
|
5049
5017
|
const checkpoint = {
|
|
5050
5018
|
id: crypto.randomUUID(),
|
|
5051
5019
|
chatId: this.#chatId,
|
|
5052
5020
|
name,
|
|
5053
|
-
messageId:
|
|
5021
|
+
messageId: open.branch.headMessageId,
|
|
5054
5022
|
createdAt: Date.now()
|
|
5055
5023
|
};
|
|
5056
5024
|
await this.#store.createCheckpoint(checkpoint);
|
|
@@ -5078,7 +5046,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
5078
5046
|
* ```
|
|
5079
5047
|
*/
|
|
5080
5048
|
async restore(name) {
|
|
5081
|
-
await this.#
|
|
5049
|
+
await this.#open();
|
|
5082
5050
|
const checkpoint = await this.#store.getCheckpoint(this.#chatId, name);
|
|
5083
5051
|
if (!checkpoint) {
|
|
5084
5052
|
throw new Error(
|
|
@@ -5103,14 +5071,13 @@ var ContextEngine = class _ContextEngine {
|
|
|
5103
5071
|
* ```
|
|
5104
5072
|
*/
|
|
5105
5073
|
async switchBranch(name) {
|
|
5106
|
-
await this.#
|
|
5074
|
+
const open = await this.#open();
|
|
5107
5075
|
const branch = await this.#store.getBranch(this.#chatId, name);
|
|
5108
5076
|
if (!branch) {
|
|
5109
5077
|
throw new Error(`Branch "${name}" not found in chat "${this.#chatId}"`);
|
|
5110
5078
|
}
|
|
5111
5079
|
await this.#store.setActiveBranch(this.#chatId, branch.id);
|
|
5112
|
-
|
|
5113
|
-
this.#branchName = name;
|
|
5080
|
+
open.branch = { ...branch, isActive: true };
|
|
5114
5081
|
this.#pendingMessages = [];
|
|
5115
5082
|
}
|
|
5116
5083
|
/**
|
|
@@ -5145,11 +5112,11 @@ var ContextEngine = class _ContextEngine {
|
|
|
5145
5112
|
* ```
|
|
5146
5113
|
*/
|
|
5147
5114
|
async btw() {
|
|
5148
|
-
await this.#
|
|
5149
|
-
if (!
|
|
5115
|
+
const open = await this.#open();
|
|
5116
|
+
if (!open.branch.headMessageId) {
|
|
5150
5117
|
throw new Error("Cannot create btw branch: no messages in conversation");
|
|
5151
5118
|
}
|
|
5152
|
-
return this.#createBranchFrom(
|
|
5119
|
+
return this.#createBranchFrom(open, open.branch.headMessageId, false);
|
|
5153
5120
|
}
|
|
5154
5121
|
/**
|
|
5155
5122
|
* Update metadata for the current chat.
|
|
@@ -5165,14 +5132,11 @@ var ContextEngine = class _ContextEngine {
|
|
|
5165
5132
|
* ```
|
|
5166
5133
|
*/
|
|
5167
5134
|
async updateChat(updates) {
|
|
5168
|
-
await this.#
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
metadata: updates.metadata === void 0 ? void 0 : { ...metadata, ...updates.metadata }
|
|
5174
|
-
})
|
|
5175
|
-
);
|
|
5135
|
+
const open = await this.#open();
|
|
5136
|
+
open.chat = await this.#store.updateChat(this.#chatId, ({ metadata }) => ({
|
|
5137
|
+
title: updates.title,
|
|
5138
|
+
metadata: updates.metadata === void 0 ? void 0 : { ...metadata, ...updates.metadata }
|
|
5139
|
+
}));
|
|
5176
5140
|
}
|
|
5177
5141
|
/**
|
|
5178
5142
|
* Track token usage for the current chat.
|
|
@@ -5188,20 +5152,17 @@ var ContextEngine = class _ContextEngine {
|
|
|
5188
5152
|
* ```
|
|
5189
5153
|
*/
|
|
5190
5154
|
async trackUsage(usage) {
|
|
5191
|
-
await this.#
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
};
|
|
5203
|
-
}
|
|
5204
|
-
);
|
|
5155
|
+
const open = await this.#open();
|
|
5156
|
+
open.chat = await this.#store.updateChat(this.#chatId, ({ metadata }) => {
|
|
5157
|
+
const storedUsage = metadata?.usage;
|
|
5158
|
+
const currentUsage = isLanguageModelUsage(storedUsage) ? storedUsage : void 0;
|
|
5159
|
+
return {
|
|
5160
|
+
metadata: {
|
|
5161
|
+
...metadata,
|
|
5162
|
+
usage: mergeLanguageModelUsage(currentUsage, usage)
|
|
5163
|
+
}
|
|
5164
|
+
};
|
|
5165
|
+
});
|
|
5205
5166
|
}
|
|
5206
5167
|
/**
|
|
5207
5168
|
* Consolidate context fragments (no-op for now).
|
|
@@ -5267,7 +5228,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
5267
5228
|
* ```
|
|
5268
5229
|
*/
|
|
5269
5230
|
async inspect(options) {
|
|
5270
|
-
await this.#
|
|
5231
|
+
const open = await this.#open();
|
|
5271
5232
|
const { renderer } = options;
|
|
5272
5233
|
const estimateResult = await this.estimate(options.modelId, {
|
|
5273
5234
|
renderer,
|
|
@@ -5276,9 +5237,9 @@ var ContextEngine = class _ContextEngine {
|
|
|
5276
5237
|
});
|
|
5277
5238
|
const rendered = renderer.render(this.#renderableFragments);
|
|
5278
5239
|
const persistedMessages = [];
|
|
5279
|
-
if (
|
|
5240
|
+
if (open.branch.headMessageId) {
|
|
5280
5241
|
const chain = await this.#store.getMessageChain(
|
|
5281
|
-
|
|
5242
|
+
open.branch.headMessageId
|
|
5282
5243
|
);
|
|
5283
5244
|
persistedMessages.push(...chain);
|
|
5284
5245
|
}
|
|
@@ -5294,7 +5255,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
5294
5255
|
graph,
|
|
5295
5256
|
meta: {
|
|
5296
5257
|
chatId: this.#chatId,
|
|
5297
|
-
branch:
|
|
5258
|
+
branch: open.branch.name,
|
|
5298
5259
|
timestamp: Date.now()
|
|
5299
5260
|
}
|
|
5300
5261
|
};
|
|
@@ -10740,7 +10701,7 @@ async function assertMicrosandboxBash(vm) {
|
|
|
10740
10701
|
function normalizeMicrosandboxError(error, sdk) {
|
|
10741
10702
|
const err = toError2(error);
|
|
10742
10703
|
if (err instanceof MicrosandboxSandboxError) return err;
|
|
10743
|
-
if (err instanceof sdk.LibkrunfwNotFoundError) {
|
|
10704
|
+
if (err instanceof sdk.LibkrunfwNotFoundError || err instanceof sdk.RuntimeNotInstalledError || err instanceof sdk.RuntimeIncompleteError) {
|
|
10744
10705
|
return new MicrosandboxNotAvailableError(err);
|
|
10745
10706
|
}
|
|
10746
10707
|
if (err instanceof sdk.MicrosandboxError) {
|
|
@@ -11244,6 +11205,16 @@ function soul() {
|
|
|
11244
11205
|
return fragment("soul_protocol", ...children);
|
|
11245
11206
|
}
|
|
11246
11207
|
|
|
11208
|
+
// packages/context/src/lib/store/postgres-schema-lock.ts
|
|
11209
|
+
function lockSchemaDDL(ddl, schema) {
|
|
11210
|
+
if (schema !== void 0 && !/^[a-zA-Z_]\w*$/.test(schema)) {
|
|
11211
|
+
throw new Error(`Invalid schema name: "${schema}"`);
|
|
11212
|
+
}
|
|
11213
|
+
const key = schema === void 0 ? `coalesce(current_schema(), '')` : `'${schema}'`;
|
|
11214
|
+
return `SELECT pg_advisory_xact_lock(hashtextextended('deepagents:schema-ddl:' || ${key}, 0));
|
|
11215
|
+
${ddl}`;
|
|
11216
|
+
}
|
|
11217
|
+
|
|
11247
11218
|
// packages/context/src/lib/store/postgres.store.ts
|
|
11248
11219
|
import { createRequire } from "node:module";
|
|
11249
11220
|
import { z as z14 } from "zod";
|
|
@@ -11458,7 +11429,6 @@ var PostgresContextStore = class _PostgresContextStore extends ContextStore {
|
|
|
11458
11429
|
#pool;
|
|
11459
11430
|
#schema;
|
|
11460
11431
|
#ownsPool;
|
|
11461
|
-
#isInitialized = false;
|
|
11462
11432
|
constructor(options) {
|
|
11463
11433
|
super();
|
|
11464
11434
|
const schema = options.schema ?? "public";
|
|
@@ -11488,24 +11458,25 @@ var PostgresContextStore = class _PostgresContextStore extends ContextStore {
|
|
|
11488
11458
|
#t(name) {
|
|
11489
11459
|
return `"${this.#schema}"."${name}"`;
|
|
11490
11460
|
}
|
|
11491
|
-
|
|
11492
|
-
|
|
11493
|
-
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
throw
|
|
11499
|
-
"PostgresContextStore not initialized. Call await store.initialize() after construction."
|
|
11500
|
-
);
|
|
11461
|
+
/** Opens the store: creates its schema objects before it returns. */
|
|
11462
|
+
static async create(options) {
|
|
11463
|
+
const store = new _PostgresContextStore(options);
|
|
11464
|
+
try {
|
|
11465
|
+
await store.#initialize();
|
|
11466
|
+
} catch (error) {
|
|
11467
|
+
await store.close();
|
|
11468
|
+
throw error;
|
|
11501
11469
|
}
|
|
11470
|
+
return store;
|
|
11471
|
+
}
|
|
11472
|
+
async #initialize() {
|
|
11473
|
+
await this.#pool.query(lockSchemaDDL(storeDDL(this.#schema), this.#schema));
|
|
11502
11474
|
}
|
|
11503
11475
|
/**
|
|
11504
11476
|
* Execute a function within a transaction.
|
|
11505
11477
|
* Automatically commits on success or rolls back on error.
|
|
11506
11478
|
*/
|
|
11507
11479
|
async #useTransaction(fn) {
|
|
11508
|
-
this.#ensureInitialized();
|
|
11509
11480
|
const client = await this.#pool.connect();
|
|
11510
11481
|
try {
|
|
11511
11482
|
await client.query("BEGIN");
|
|
@@ -11523,7 +11494,6 @@ var PostgresContextStore = class _PostgresContextStore extends ContextStore {
|
|
|
11523
11494
|
* Execute a query using the pool (no transaction).
|
|
11524
11495
|
*/
|
|
11525
11496
|
async #query(sql, params) {
|
|
11526
|
-
this.#ensureInitialized();
|
|
11527
11497
|
const result = await this.#pool.query(sql, params);
|
|
11528
11498
|
return result.rows;
|
|
11529
11499
|
}
|
|
@@ -12072,6 +12042,11 @@ IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_${schema}_branches_ch
|
|
|
12072
12042
|
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_${schema}_branches_chatId_isActive' AND object_id = OBJECT_ID('[${schema}].[branches]'))
|
|
12073
12043
|
CREATE INDEX [idx_${schema}_branches_chatId_isActive] ON [${schema}].[branches](chatId, isActive);
|
|
12074
12044
|
|
|
12045
|
+
-- Deleting a message checks this foreign key. Without the index the check
|
|
12046
|
+
-- scans every chat's branches and waits on rows another chat delete holds.
|
|
12047
|
+
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_${schema}_branches_headMessageId' AND object_id = OBJECT_ID('[${schema}].[branches]'))
|
|
12048
|
+
CREATE INDEX [idx_${schema}_branches_headMessageId] ON [${schema}].[branches](headMessageId);
|
|
12049
|
+
|
|
12075
12050
|
IF OBJECT_ID('[${schema}].[checkpoints]', 'U') IS NULL
|
|
12076
12051
|
BEGIN
|
|
12077
12052
|
CREATE TABLE [${schema}].[checkpoints] (
|
|
@@ -12122,6 +12097,38 @@ END;
|
|
|
12122
12097
|
`;
|
|
12123
12098
|
}
|
|
12124
12099
|
|
|
12100
|
+
// packages/context/src/lib/store/sqlserver-schema-lock.ts
|
|
12101
|
+
async function withSqlServerSchemaLock(mssql, pool, schema, run) {
|
|
12102
|
+
const holder = new mssql.Transaction(pool);
|
|
12103
|
+
await holder.begin();
|
|
12104
|
+
let result;
|
|
12105
|
+
try {
|
|
12106
|
+
const lock = new mssql.Request(holder);
|
|
12107
|
+
lock.input(
|
|
12108
|
+
"resource",
|
|
12109
|
+
mssql.NVarChar(255),
|
|
12110
|
+
`deepagents:schema-ddl:${schema}`
|
|
12111
|
+
);
|
|
12112
|
+
const { recordset } = await lock.query(`
|
|
12113
|
+
DECLARE @status int;
|
|
12114
|
+
EXEC @status = sp_getapplock @Resource = @resource, @LockMode = 'Exclusive', @LockOwner = 'Transaction', @LockTimeout = -1;
|
|
12115
|
+
SELECT @status AS status;
|
|
12116
|
+
`);
|
|
12117
|
+
if (recordset[0].status < 0) {
|
|
12118
|
+
throw new Error(
|
|
12119
|
+
`Could not lock the DDL of schema "${schema}": sp_getapplock returned ${recordset[0].status}.`
|
|
12120
|
+
);
|
|
12121
|
+
}
|
|
12122
|
+
result = await run();
|
|
12123
|
+
} catch (error) {
|
|
12124
|
+
await holder.rollback().catch(() => {
|
|
12125
|
+
});
|
|
12126
|
+
throw error;
|
|
12127
|
+
}
|
|
12128
|
+
await holder.rollback();
|
|
12129
|
+
return result;
|
|
12130
|
+
}
|
|
12131
|
+
|
|
12125
12132
|
// packages/context/src/lib/store/sqlserver.store.ts
|
|
12126
12133
|
var chatColumns3 = z15.object({
|
|
12127
12134
|
id: z15.string(),
|
|
@@ -12225,7 +12232,6 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12225
12232
|
#pool;
|
|
12226
12233
|
#schema;
|
|
12227
12234
|
#ownsPool;
|
|
12228
|
-
#isInitialized = false;
|
|
12229
12235
|
constructor(options) {
|
|
12230
12236
|
super();
|
|
12231
12237
|
const schema = options.schema ?? "dbo";
|
|
@@ -12255,50 +12261,59 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12255
12261
|
#t(name) {
|
|
12256
12262
|
return `[${this.#schema}].[${name}]`;
|
|
12257
12263
|
}
|
|
12258
|
-
|
|
12264
|
+
/** Opens the store: connects a pool it owns and creates its schema objects before it returns. */
|
|
12265
|
+
static async create(options) {
|
|
12266
|
+
const store = new _SqlServerContextStore(options);
|
|
12267
|
+
try {
|
|
12268
|
+
await store.#initialize();
|
|
12269
|
+
} catch (error) {
|
|
12270
|
+
await store.close();
|
|
12271
|
+
throw error;
|
|
12272
|
+
}
|
|
12273
|
+
return store;
|
|
12274
|
+
}
|
|
12275
|
+
async #initialize() {
|
|
12259
12276
|
if (this.#ownsPool) {
|
|
12260
12277
|
await this.#pool.connect();
|
|
12261
12278
|
}
|
|
12262
|
-
|
|
12263
|
-
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12271
|
-
|
|
12272
|
-
|
|
12273
|
-
|
|
12274
|
-
|
|
12275
|
-
|
|
12279
|
+
await withSqlServerSchemaLock(
|
|
12280
|
+
_SqlServerContextStore.#requireMssql(),
|
|
12281
|
+
this.#pool,
|
|
12282
|
+
this.#schema,
|
|
12283
|
+
async () => {
|
|
12284
|
+
const schemaReq = this.#pool.request();
|
|
12285
|
+
schemaReq.input("schema", this.#schema);
|
|
12286
|
+
await schemaReq.query(`
|
|
12287
|
+
IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = @schema)
|
|
12288
|
+
BEGIN
|
|
12289
|
+
DECLARE @sql NVARCHAR(MAX) = N'CREATE SCHEMA ' + QUOTENAME(@schema);
|
|
12290
|
+
EXEC sp_executesql @sql;
|
|
12291
|
+
END
|
|
12292
|
+
`);
|
|
12293
|
+
const ddl = storeDDL2(this.#schema);
|
|
12294
|
+
const batches = ddl.split(/\bGO\b/i).filter((b) => b.trim());
|
|
12295
|
+
for (const batch of batches) {
|
|
12296
|
+
if (batch.trim()) {
|
|
12297
|
+
await this.#pool.request().batch(batch);
|
|
12298
|
+
}
|
|
12299
|
+
}
|
|
12276
12300
|
}
|
|
12277
|
-
|
|
12278
|
-
this.#isInitialized = true;
|
|
12279
|
-
}
|
|
12280
|
-
#ensureInitialized() {
|
|
12281
|
-
if (!this.#isInitialized) {
|
|
12282
|
-
throw new Error(
|
|
12283
|
-
"SqlServerContextStore not initialized. Call await store.initialize() after construction."
|
|
12284
|
-
);
|
|
12285
|
-
}
|
|
12301
|
+
);
|
|
12286
12302
|
}
|
|
12287
12303
|
/**
|
|
12288
12304
|
* Execute a function within a transaction.
|
|
12289
12305
|
* Automatically commits on success or rolls back on error.
|
|
12290
12306
|
*/
|
|
12291
12307
|
async #useTransaction(fn) {
|
|
12292
|
-
this.#ensureInitialized();
|
|
12293
12308
|
const mssql = _SqlServerContextStore.#requireMssql();
|
|
12294
|
-
const
|
|
12309
|
+
const transaction3 = new mssql.Transaction(this.#pool);
|
|
12295
12310
|
try {
|
|
12296
|
-
await
|
|
12297
|
-
const result = await fn(
|
|
12298
|
-
await
|
|
12311
|
+
await transaction3.begin();
|
|
12312
|
+
const result = await fn(transaction3);
|
|
12313
|
+
await transaction3.commit();
|
|
12299
12314
|
return result;
|
|
12300
12315
|
} catch (error) {
|
|
12301
|
-
await
|
|
12316
|
+
await transaction3.rollback();
|
|
12302
12317
|
throw error;
|
|
12303
12318
|
}
|
|
12304
12319
|
}
|
|
@@ -12307,7 +12322,6 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12307
12322
|
* Converts positional params to SQL Server named params (@p0, @p1, ...).
|
|
12308
12323
|
*/
|
|
12309
12324
|
async #query(sql, params) {
|
|
12310
|
-
this.#ensureInitialized();
|
|
12311
12325
|
const request = this.#pool.request();
|
|
12312
12326
|
params?.forEach((value, index) => {
|
|
12313
12327
|
request.input(`p${index}`, value);
|
|
@@ -12334,9 +12348,9 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12334
12348
|
// Chat Operations
|
|
12335
12349
|
// ==========================================================================
|
|
12336
12350
|
async createChat(chat2) {
|
|
12337
|
-
return this.#useTransaction(async (
|
|
12351
|
+
return this.#useTransaction(async (transaction3) => {
|
|
12338
12352
|
const mssql = _SqlServerContextStore.#requireMssql();
|
|
12339
|
-
const request =
|
|
12353
|
+
const request = transaction3.request();
|
|
12340
12354
|
request.input("p0", mssql.NVarChar, chat2.id);
|
|
12341
12355
|
request.input("p1", mssql.NVarChar, chat2.userId);
|
|
12342
12356
|
request.input("p2", mssql.NVarChar, chat2.title ?? null);
|
|
@@ -12351,7 +12365,7 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12351
12365
|
VALUES (@p0, @p1, @p2, @p3)
|
|
12352
12366
|
`);
|
|
12353
12367
|
const created = storedChat3.parse(result.recordset[0]);
|
|
12354
|
-
const branchRequest =
|
|
12368
|
+
const branchRequest = transaction3.request();
|
|
12355
12369
|
branchRequest.input("p0", mssql.NVarChar, crypto.randomUUID());
|
|
12356
12370
|
branchRequest.input("p1", mssql.NVarChar, chat2.id);
|
|
12357
12371
|
branchRequest.input("p2", mssql.BigInt, Date.now());
|
|
@@ -12363,9 +12377,9 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12363
12377
|
});
|
|
12364
12378
|
}
|
|
12365
12379
|
async upsertChat(chat2) {
|
|
12366
|
-
return this.#useTransaction(async (
|
|
12380
|
+
return this.#useTransaction(async (transaction3) => {
|
|
12367
12381
|
const mssql = _SqlServerContextStore.#requireMssql();
|
|
12368
|
-
const request =
|
|
12382
|
+
const request = transaction3.request();
|
|
12369
12383
|
request.input("p0", mssql.NVarChar, chat2.id);
|
|
12370
12384
|
request.input("p1", mssql.NVarChar, chat2.userId);
|
|
12371
12385
|
request.input("p2", mssql.NVarChar, chat2.title ?? null);
|
|
@@ -12387,7 +12401,7 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12387
12401
|
OUTPUT INSERTED.*;
|
|
12388
12402
|
`);
|
|
12389
12403
|
const upserted = storedChat3.parse(result.recordset[0]);
|
|
12390
|
-
const branchRequest =
|
|
12404
|
+
const branchRequest = transaction3.request();
|
|
12391
12405
|
branchRequest.input("p0", mssql.NVarChar, crypto.randomUUID());
|
|
12392
12406
|
branchRequest.input("p1", mssql.NVarChar, chat2.id);
|
|
12393
12407
|
branchRequest.input("p2", mssql.BigInt, Date.now());
|
|
@@ -12410,9 +12424,9 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12410
12424
|
return chat2;
|
|
12411
12425
|
}
|
|
12412
12426
|
async updateChat(chatId, update) {
|
|
12413
|
-
return this.#useTransaction(async (
|
|
12427
|
+
return this.#useTransaction(async (transaction3) => {
|
|
12414
12428
|
const mssql = _SqlServerContextStore.#requireMssql();
|
|
12415
|
-
const read =
|
|
12429
|
+
const read = transaction3.request();
|
|
12416
12430
|
read.input("chatId", mssql.NVarChar, chatId);
|
|
12417
12431
|
const result = await read.query(`
|
|
12418
12432
|
SELECT *
|
|
@@ -12429,7 +12443,7 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12429
12443
|
"updatedAt = DATEDIFF_BIG(ms, '1970-01-01', GETUTCDATE())"
|
|
12430
12444
|
];
|
|
12431
12445
|
let paramIndex = 0;
|
|
12432
|
-
const write =
|
|
12446
|
+
const write = transaction3.request();
|
|
12433
12447
|
if (updates.title !== void 0) {
|
|
12434
12448
|
const name = `p${paramIndex++}`;
|
|
12435
12449
|
setClauses.push(`title = @${name}`);
|
|
@@ -12494,9 +12508,9 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12494
12508
|
);
|
|
12495
12509
|
}
|
|
12496
12510
|
async deleteChat(chatId, options) {
|
|
12497
|
-
return this.#useTransaction(async (
|
|
12511
|
+
return this.#useTransaction(async (transaction3) => {
|
|
12498
12512
|
const mssql = _SqlServerContextStore.#requireMssql();
|
|
12499
|
-
const request =
|
|
12513
|
+
const request = transaction3.request();
|
|
12500
12514
|
request.input("p0", mssql.NVarChar, chatId);
|
|
12501
12515
|
let sql = `DELETE FROM ${this.#t("chats")} WHERE id = @p0`;
|
|
12502
12516
|
if (options?.userId !== void 0) {
|
|
@@ -12514,9 +12528,9 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12514
12528
|
if (message2.parentId === message2.id) {
|
|
12515
12529
|
throw new Error(`Message ${message2.id} cannot be its own parent`);
|
|
12516
12530
|
}
|
|
12517
|
-
await this.#useTransaction(async (
|
|
12531
|
+
await this.#useTransaction(async (transaction3) => {
|
|
12518
12532
|
const mssql = _SqlServerContextStore.#requireMssql();
|
|
12519
|
-
const request =
|
|
12533
|
+
const request = transaction3.request();
|
|
12520
12534
|
request.input("p0", mssql.NVarChar, message2.id);
|
|
12521
12535
|
request.input("p1", mssql.NVarChar, message2.chatId);
|
|
12522
12536
|
request.input("p2", mssql.NVarChar, message2.parentId);
|
|
@@ -12535,7 +12549,7 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12535
12549
|
VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6);
|
|
12536
12550
|
`);
|
|
12537
12551
|
const content = typeof message2.data === "string" ? message2.data : JSON.stringify(message2.data);
|
|
12538
|
-
const ftsRequest =
|
|
12552
|
+
const ftsRequest = transaction3.request();
|
|
12539
12553
|
ftsRequest.input("p0", mssql.NVarChar, message2.id);
|
|
12540
12554
|
ftsRequest.input("p1", mssql.NVarChar, message2.chatId);
|
|
12541
12555
|
ftsRequest.input("p2", mssql.NVarChar, message2.name);
|
|
@@ -12643,14 +12657,14 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12643
12657
|
return branch;
|
|
12644
12658
|
}
|
|
12645
12659
|
async setActiveBranch(chatId, branchId) {
|
|
12646
|
-
await this.#useTransaction(async (
|
|
12660
|
+
await this.#useTransaction(async (transaction3) => {
|
|
12647
12661
|
const mssql = _SqlServerContextStore.#requireMssql();
|
|
12648
|
-
const deactivateRequest =
|
|
12662
|
+
const deactivateRequest = transaction3.request();
|
|
12649
12663
|
deactivateRequest.input("p0", mssql.NVarChar, chatId);
|
|
12650
12664
|
await deactivateRequest.query(
|
|
12651
12665
|
`UPDATE ${this.#t("branches")} SET isActive = 0 WHERE chatId = @p0`
|
|
12652
12666
|
);
|
|
12653
|
-
const activateRequest =
|
|
12667
|
+
const activateRequest = transaction3.request();
|
|
12654
12668
|
activateRequest.input("p0", mssql.NVarChar, branchId);
|
|
12655
12669
|
await activateRequest.query(
|
|
12656
12670
|
`UPDATE ${this.#t("branches")} SET isActive = 1 WHERE id = @p0`
|
|
@@ -12713,9 +12727,9 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
|
|
|
12713
12727
|
// Checkpoint Operations
|
|
12714
12728
|
// ==========================================================================
|
|
12715
12729
|
async createCheckpoint(checkpoint) {
|
|
12716
|
-
await this.#useTransaction(async (
|
|
12730
|
+
await this.#useTransaction(async (transaction3) => {
|
|
12717
12731
|
const mssql = _SqlServerContextStore.#requireMssql();
|
|
12718
|
-
const request =
|
|
12732
|
+
const request = transaction3.request();
|
|
12719
12733
|
request.input("p0", mssql.NVarChar, checkpoint.id);
|
|
12720
12734
|
request.input("p1", mssql.NVarChar, checkpoint.chatId);
|
|
12721
12735
|
request.input("p2", mssql.NVarChar, checkpoint.name);
|
|
@@ -13159,7 +13173,9 @@ function staticPartText(part, options) {
|
|
|
13159
13173
|
}
|
|
13160
13174
|
|
|
13161
13175
|
// packages/context/src/lib/stream/postgres-notify-change-source.ts
|
|
13176
|
+
import { EventEmitter, on } from "node:events";
|
|
13162
13177
|
import { createRequire as createRequire3 } from "node:module";
|
|
13178
|
+
import pMemoize3 from "p-memoize";
|
|
13163
13179
|
import { z as z16 } from "zod";
|
|
13164
13180
|
|
|
13165
13181
|
// packages/context/src/lib/stream/ddl.stream.postgres-notify.ts
|
|
@@ -13220,28 +13236,73 @@ EXECUTE FUNCTION "${schema}"."notify_stream_status_update"();
|
|
|
13220
13236
|
}
|
|
13221
13237
|
|
|
13222
13238
|
// packages/context/src/lib/stream/postgres-notify-change-source.ts
|
|
13239
|
+
var NOT_LISTENING = {
|
|
13240
|
+
release: () => Promise.resolve(),
|
|
13241
|
+
fail: () => {
|
|
13242
|
+
}
|
|
13243
|
+
};
|
|
13244
|
+
var Listening = class {
|
|
13245
|
+
#client;
|
|
13246
|
+
#handlers;
|
|
13247
|
+
constructor(client, handlers) {
|
|
13248
|
+
this.#client = client;
|
|
13249
|
+
this.#handlers = handlers;
|
|
13250
|
+
client.on("notification", handlers.notification);
|
|
13251
|
+
client.on("error", handlers.error);
|
|
13252
|
+
client.on("end", handlers.end);
|
|
13253
|
+
}
|
|
13254
|
+
/** Stops listening and returns the connection to the pool. */
|
|
13255
|
+
async release(channel) {
|
|
13256
|
+
this.#detach();
|
|
13257
|
+
try {
|
|
13258
|
+
await this.#client.query(`UNLISTEN ${quoteIdentifier(channel)}`);
|
|
13259
|
+
} finally {
|
|
13260
|
+
this.#client.release();
|
|
13261
|
+
}
|
|
13262
|
+
}
|
|
13263
|
+
/** Gives a broken connection back for the pool to destroy. */
|
|
13264
|
+
fail(error) {
|
|
13265
|
+
this.#detach();
|
|
13266
|
+
this.#client.release(error);
|
|
13267
|
+
}
|
|
13268
|
+
#detach() {
|
|
13269
|
+
this.#client.off("notification", this.#handlers.notification);
|
|
13270
|
+
this.#client.off("error", this.#handlers.error);
|
|
13271
|
+
this.#client.off("end", this.#handlers.end);
|
|
13272
|
+
}
|
|
13273
|
+
};
|
|
13274
|
+
var ChangeDelivery = class {
|
|
13275
|
+
change;
|
|
13276
|
+
constructor(change) {
|
|
13277
|
+
this.change = change;
|
|
13278
|
+
}
|
|
13279
|
+
};
|
|
13223
13280
|
var PostgresNotifyChangeSource = class _PostgresNotifyChangeSource {
|
|
13224
13281
|
#pool;
|
|
13225
13282
|
#schema;
|
|
13226
13283
|
#channel;
|
|
13227
13284
|
#ownsPool;
|
|
13228
|
-
|
|
13229
|
-
#
|
|
13230
|
-
|
|
13231
|
-
#
|
|
13232
|
-
#
|
|
13233
|
-
#
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
|
|
13237
|
-
|
|
13238
|
-
|
|
13239
|
-
|
|
13240
|
-
|
|
13241
|
-
|
|
13242
|
-
|
|
13243
|
-
|
|
13244
|
-
)
|
|
13285
|
+
/** Aborts when close() starts: every subscription ends, and no LISTEN connection opens. */
|
|
13286
|
+
#closed = new AbortController();
|
|
13287
|
+
/** Emits `stream:<id>` with a ChangeDelivery; `error` fails every subscription. */
|
|
13288
|
+
#changes = new EventEmitter().setMaxListeners(0);
|
|
13289
|
+
#subscriptions = 0;
|
|
13290
|
+
#listener = NOT_LISTENING;
|
|
13291
|
+
/** Concurrent subscribers share one LISTEN setup; a failed one runs again. */
|
|
13292
|
+
#listen = pMemoize3(() => this.#openListener(), { cache: false });
|
|
13293
|
+
#handlers = {
|
|
13294
|
+
notification: (message2) => {
|
|
13295
|
+
this.#handleNotification(message2);
|
|
13296
|
+
},
|
|
13297
|
+
error: (error) => {
|
|
13298
|
+
this.#failListener(error);
|
|
13299
|
+
},
|
|
13300
|
+
end: () => {
|
|
13301
|
+
if (!this.#closed.signal.aborted) {
|
|
13302
|
+
this.#failListener(
|
|
13303
|
+
new Error("PostgreSQL stream notification listener ended")
|
|
13304
|
+
);
|
|
13305
|
+
}
|
|
13245
13306
|
}
|
|
13246
13307
|
};
|
|
13247
13308
|
constructor(options) {
|
|
@@ -13270,98 +13331,78 @@ var PostgresNotifyChangeSource = class _PostgresNotifyChangeSource {
|
|
|
13270
13331
|
);
|
|
13271
13332
|
}
|
|
13272
13333
|
}
|
|
13273
|
-
|
|
13274
|
-
|
|
13275
|
-
|
|
13276
|
-
|
|
13277
|
-
|
|
13334
|
+
/** Opens the source: installs the notify triggers before it returns. */
|
|
13335
|
+
static async create(options) {
|
|
13336
|
+
const source = new _PostgresNotifyChangeSource(options);
|
|
13337
|
+
try {
|
|
13338
|
+
await source.#pool.query(
|
|
13339
|
+
lockSchemaDDL(
|
|
13340
|
+
postgresStreamNotifyDDL(source.#schema, source.#channel),
|
|
13341
|
+
source.#schema
|
|
13342
|
+
)
|
|
13343
|
+
);
|
|
13344
|
+
} catch (error) {
|
|
13345
|
+
await source.close();
|
|
13346
|
+
throw error;
|
|
13347
|
+
}
|
|
13348
|
+
return source;
|
|
13278
13349
|
}
|
|
13279
13350
|
async *subscribe(streamId, signal) {
|
|
13280
|
-
this.#
|
|
13281
|
-
|
|
13282
|
-
|
|
13283
|
-
|
|
13284
|
-
const
|
|
13285
|
-
|
|
13286
|
-
|
|
13287
|
-
|
|
13288
|
-
signal.addEventListener("abort", cleanup, { once: true });
|
|
13351
|
+
if (this.#closed.signal.aborted) {
|
|
13352
|
+
throw new Error("PostgresNotifyChangeSource is closed.");
|
|
13353
|
+
}
|
|
13354
|
+
if (signal.aborted) return;
|
|
13355
|
+
const changes = on(this.#changes, `stream:${streamId}`, {
|
|
13356
|
+
signal: AbortSignal.any([signal, this.#closed.signal])
|
|
13357
|
+
});
|
|
13358
|
+
this.#subscriptions += 1;
|
|
13289
13359
|
try {
|
|
13290
13360
|
await this.#ensureListening();
|
|
13291
|
-
if (signal.aborted || this.#
|
|
13361
|
+
if (signal.aborted || this.#closed.signal.aborted) return;
|
|
13292
13362
|
yield { kind: "tick" };
|
|
13293
|
-
|
|
13294
|
-
|
|
13295
|
-
if (!change) return;
|
|
13296
|
-
yield change;
|
|
13363
|
+
for await (const [delivery] of changes) {
|
|
13364
|
+
if (delivery instanceof ChangeDelivery) yield delivery.change;
|
|
13297
13365
|
}
|
|
13366
|
+
} catch (error) {
|
|
13367
|
+
if (error instanceof Error && error.name === "AbortError") return;
|
|
13368
|
+
throw error;
|
|
13298
13369
|
} finally {
|
|
13299
|
-
|
|
13300
|
-
|
|
13370
|
+
this.#subscriptions -= 1;
|
|
13371
|
+
await changes.return?.();
|
|
13301
13372
|
await this.#releaseListenerIfIdle();
|
|
13302
13373
|
}
|
|
13303
13374
|
}
|
|
13304
13375
|
async close() {
|
|
13305
|
-
if (this.#
|
|
13306
|
-
this.#
|
|
13307
|
-
|
|
13308
|
-
|
|
13309
|
-
|
|
13310
|
-
subscriber.close();
|
|
13311
|
-
}
|
|
13312
|
-
}
|
|
13313
|
-
this.#subscribers.clear();
|
|
13314
|
-
if (this.#listenPromise) {
|
|
13315
|
-
try {
|
|
13316
|
-
await this.#listenPromise;
|
|
13317
|
-
} catch {
|
|
13318
|
-
}
|
|
13376
|
+
if (this.#closed.signal.aborted) return;
|
|
13377
|
+
this.#closed.abort();
|
|
13378
|
+
try {
|
|
13379
|
+
await this.#listen();
|
|
13380
|
+
} catch {
|
|
13319
13381
|
}
|
|
13320
13382
|
await this.#releaseListener();
|
|
13321
13383
|
if (this.#ownsPool) {
|
|
13322
13384
|
await this.#pool.end();
|
|
13323
13385
|
}
|
|
13324
13386
|
}
|
|
13325
|
-
#
|
|
13326
|
-
if (
|
|
13327
|
-
|
|
13328
|
-
"PostgresNotifyChangeSource not initialized. Call await source.initialize() after construction."
|
|
13329
|
-
);
|
|
13330
|
-
}
|
|
13387
|
+
async #ensureListening() {
|
|
13388
|
+
if (this.#listener instanceof Listening) return;
|
|
13389
|
+
await this.#listen();
|
|
13331
13390
|
}
|
|
13332
|
-
#
|
|
13333
|
-
if (this.#
|
|
13334
|
-
|
|
13391
|
+
async #openListener() {
|
|
13392
|
+
if (this.#closed.signal.aborted) return;
|
|
13393
|
+
const client = await this.#pool.connect();
|
|
13394
|
+
const listening = new Listening(client, this.#handlers);
|
|
13395
|
+
try {
|
|
13396
|
+
await client.query(`LISTEN ${quoteIdentifier(this.#channel)}`);
|
|
13397
|
+
} catch (error) {
|
|
13398
|
+
listening.fail(true);
|
|
13399
|
+
throw error;
|
|
13335
13400
|
}
|
|
13336
|
-
|
|
13337
|
-
|
|
13338
|
-
|
|
13339
|
-
|
|
13340
|
-
|
|
13341
|
-
this.#listenPromise = (async () => {
|
|
13342
|
-
const listener = await this.#pool.connect();
|
|
13343
|
-
let success = false;
|
|
13344
|
-
try {
|
|
13345
|
-
listener.on("notification", this.#onNotification);
|
|
13346
|
-
listener.on("error", this.#onListenerError);
|
|
13347
|
-
listener.on("end", this.#onListenerEnd);
|
|
13348
|
-
await listener.query(`LISTEN ${quoteIdentifier(this.#channel)}`);
|
|
13349
|
-
if (this.#isClosed) {
|
|
13350
|
-
return;
|
|
13351
|
-
}
|
|
13352
|
-
this.#listener = listener;
|
|
13353
|
-
success = true;
|
|
13354
|
-
} finally {
|
|
13355
|
-
this.#listenPromise = void 0;
|
|
13356
|
-
if (!success) {
|
|
13357
|
-
listener.off("notification", this.#onNotification);
|
|
13358
|
-
listener.off("error", this.#onListenerError);
|
|
13359
|
-
listener.off("end", this.#onListenerEnd);
|
|
13360
|
-
listener.release(true);
|
|
13361
|
-
}
|
|
13362
|
-
}
|
|
13363
|
-
})();
|
|
13364
|
-
return this.#listenPromise;
|
|
13401
|
+
if (this.#closed.signal.aborted) {
|
|
13402
|
+
listening.fail(true);
|
|
13403
|
+
return;
|
|
13404
|
+
}
|
|
13405
|
+
this.#listener = listening;
|
|
13365
13406
|
}
|
|
13366
13407
|
#handleNotification(message2) {
|
|
13367
13408
|
if (message2.channel !== this.#channel || !message2.payload) return;
|
|
@@ -13369,125 +13410,28 @@ var PostgresNotifyChangeSource = class _PostgresNotifyChangeSource {
|
|
|
13369
13410
|
if (!payload) return;
|
|
13370
13411
|
if (payload.schema !== this.#schema) return;
|
|
13371
13412
|
if (payload.kind !== "chunks" && payload.kind !== "status") return;
|
|
13372
|
-
const subscribers = this.#subscribers.get(payload.streamId);
|
|
13373
|
-
if (!subscribers) return;
|
|
13374
13413
|
const change = payload.kind === "chunks" ? { kind: "chunks" } : { kind: "status" };
|
|
13375
|
-
|
|
13376
|
-
|
|
13377
|
-
|
|
13414
|
+
this.#changes.emit(
|
|
13415
|
+
`stream:${payload.streamId}`,
|
|
13416
|
+
new ChangeDelivery(change)
|
|
13417
|
+
);
|
|
13378
13418
|
}
|
|
13379
13419
|
#failListener(error) {
|
|
13380
13420
|
const listener = this.#listener;
|
|
13381
|
-
this.#listener =
|
|
13382
|
-
|
|
13383
|
-
if (
|
|
13384
|
-
|
|
13385
|
-
listener.off("error", this.#onListenerError);
|
|
13386
|
-
listener.off("end", this.#onListenerEnd);
|
|
13387
|
-
listener.release(error);
|
|
13388
|
-
}
|
|
13389
|
-
for (const subscribers of this.#subscribers.values()) {
|
|
13390
|
-
for (const subscriber of subscribers) {
|
|
13391
|
-
subscriber.fail(error);
|
|
13392
|
-
}
|
|
13421
|
+
this.#listener = NOT_LISTENING;
|
|
13422
|
+
listener.fail(error);
|
|
13423
|
+
if (this.#changes.listenerCount("error") > 0) {
|
|
13424
|
+
this.#changes.emit("error", error);
|
|
13393
13425
|
}
|
|
13394
|
-
this.#subscribers.clear();
|
|
13395
13426
|
}
|
|
13396
13427
|
async #releaseListenerIfIdle() {
|
|
13397
|
-
if (this.#
|
|
13428
|
+
if (this.#subscriptions > 0) return;
|
|
13398
13429
|
await this.#releaseListener();
|
|
13399
13430
|
}
|
|
13400
13431
|
async #releaseListener() {
|
|
13401
13432
|
const listener = this.#listener;
|
|
13402
|
-
|
|
13403
|
-
this.#
|
|
13404
|
-
listener.off("notification", this.#onNotification);
|
|
13405
|
-
listener.off("error", this.#onListenerError);
|
|
13406
|
-
listener.off("end", this.#onListenerEnd);
|
|
13407
|
-
try {
|
|
13408
|
-
await listener.query(`UNLISTEN ${quoteIdentifier(this.#channel)}`);
|
|
13409
|
-
} finally {
|
|
13410
|
-
listener.release();
|
|
13411
|
-
}
|
|
13412
|
-
}
|
|
13413
|
-
#addSubscriber(streamId, subscriber) {
|
|
13414
|
-
let subscribers = this.#subscribers.get(streamId);
|
|
13415
|
-
if (!subscribers) {
|
|
13416
|
-
subscribers = /* @__PURE__ */ new Set();
|
|
13417
|
-
this.#subscribers.set(streamId, subscribers);
|
|
13418
|
-
}
|
|
13419
|
-
subscribers.add(subscriber);
|
|
13420
|
-
}
|
|
13421
|
-
#removeSubscriber(streamId, subscriber) {
|
|
13422
|
-
const subscribers = this.#subscribers.get(streamId);
|
|
13423
|
-
if (!subscribers) return;
|
|
13424
|
-
subscribers.delete(subscriber);
|
|
13425
|
-
if (subscribers.size === 0) {
|
|
13426
|
-
this.#subscribers.delete(streamId);
|
|
13427
|
-
}
|
|
13428
|
-
}
|
|
13429
|
-
};
|
|
13430
|
-
var ChangeQueue = class {
|
|
13431
|
-
#values = [];
|
|
13432
|
-
#waiters = /* @__PURE__ */ new Set();
|
|
13433
|
-
#closed = false;
|
|
13434
|
-
#error;
|
|
13435
|
-
push(value) {
|
|
13436
|
-
if (this.#closed) return;
|
|
13437
|
-
const waiter = this.#waiters.values().next().value;
|
|
13438
|
-
if (waiter) {
|
|
13439
|
-
this.#waiters.delete(waiter);
|
|
13440
|
-
waiter.resolve(value);
|
|
13441
|
-
return;
|
|
13442
|
-
}
|
|
13443
|
-
this.#values.push(value);
|
|
13444
|
-
}
|
|
13445
|
-
fail(error) {
|
|
13446
|
-
if (this.#closed) return;
|
|
13447
|
-
this.#closed = true;
|
|
13448
|
-
this.#error = error;
|
|
13449
|
-
this.#values = [];
|
|
13450
|
-
for (const waiter of this.#waiters) {
|
|
13451
|
-
waiter.reject(error);
|
|
13452
|
-
}
|
|
13453
|
-
this.#waiters.clear();
|
|
13454
|
-
}
|
|
13455
|
-
close() {
|
|
13456
|
-
if (this.#closed) return;
|
|
13457
|
-
this.#closed = true;
|
|
13458
|
-
this.#values = [];
|
|
13459
|
-
for (const waiter of this.#waiters) {
|
|
13460
|
-
waiter.resolve(void 0);
|
|
13461
|
-
}
|
|
13462
|
-
this.#waiters.clear();
|
|
13463
|
-
}
|
|
13464
|
-
async next(signal) {
|
|
13465
|
-
if (this.#error) throw this.#error;
|
|
13466
|
-
if (this.#closed || signal.aborted) return void 0;
|
|
13467
|
-
if (this.#values.length > 0) {
|
|
13468
|
-
return this.#values.shift();
|
|
13469
|
-
}
|
|
13470
|
-
return new Promise((resolve3, reject) => {
|
|
13471
|
-
const waiter = { resolve: resolve3, reject };
|
|
13472
|
-
const cleanup = () => {
|
|
13473
|
-
this.#waiters.delete(waiter);
|
|
13474
|
-
signal.removeEventListener("abort", onAbort);
|
|
13475
|
-
};
|
|
13476
|
-
const onAbort = () => {
|
|
13477
|
-
cleanup();
|
|
13478
|
-
resolve3(void 0);
|
|
13479
|
-
};
|
|
13480
|
-
waiter.resolve = (value) => {
|
|
13481
|
-
cleanup();
|
|
13482
|
-
resolve3(value);
|
|
13483
|
-
};
|
|
13484
|
-
waiter.reject = (error) => {
|
|
13485
|
-
cleanup();
|
|
13486
|
-
reject(error);
|
|
13487
|
-
};
|
|
13488
|
-
this.#waiters.add(waiter);
|
|
13489
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
13490
|
-
});
|
|
13433
|
+
this.#listener = NOT_LISTENING;
|
|
13434
|
+
await listener.release(this.#channel);
|
|
13491
13435
|
}
|
|
13492
13436
|
};
|
|
13493
13437
|
var notificationPayload = z16.object({
|
|
@@ -13610,8 +13554,6 @@ var PostgresStreamStore = class _PostgresStreamStore extends StreamStore {
|
|
|
13610
13554
|
#pool;
|
|
13611
13555
|
#schema;
|
|
13612
13556
|
#ownsPool;
|
|
13613
|
-
#isInitialized = false;
|
|
13614
|
-
#isClosed = false;
|
|
13615
13557
|
constructor(options) {
|
|
13616
13558
|
super();
|
|
13617
13559
|
const schema = options.schema ?? "public";
|
|
@@ -13639,26 +13581,28 @@ var PostgresStreamStore = class _PostgresStreamStore extends StreamStore {
|
|
|
13639
13581
|
#t(name) {
|
|
13640
13582
|
return `"${this.#schema}"."${name}"`;
|
|
13641
13583
|
}
|
|
13642
|
-
|
|
13643
|
-
|
|
13644
|
-
|
|
13584
|
+
/** Opens the store: creates its tables before it returns. */
|
|
13585
|
+
static async create(options) {
|
|
13586
|
+
const store = new _PostgresStreamStore(options);
|
|
13587
|
+
try {
|
|
13588
|
+
await store.#initialize();
|
|
13589
|
+
} catch (error) {
|
|
13590
|
+
await store.close();
|
|
13591
|
+
throw error;
|
|
13592
|
+
}
|
|
13593
|
+
return store;
|
|
13594
|
+
}
|
|
13595
|
+
async #initialize() {
|
|
13596
|
+
await this.#pool.query(
|
|
13597
|
+
lockSchemaDDL(postgresStreamDDL(this.#schema), this.#schema)
|
|
13598
|
+
);
|
|
13645
13599
|
}
|
|
13646
13600
|
async close() {
|
|
13647
|
-
if (this.#
|
|
13648
|
-
this.#isClosed = true;
|
|
13649
|
-
if (this.#ownsPool) {
|
|
13601
|
+
if (this.#ownsPool && !this.#pool.ending) {
|
|
13650
13602
|
await this.#pool.end();
|
|
13651
13603
|
}
|
|
13652
13604
|
}
|
|
13653
|
-
#ensureInitialized() {
|
|
13654
|
-
if (!this.#isInitialized) {
|
|
13655
|
-
throw new Error(
|
|
13656
|
-
"PostgresStreamStore not initialized. Call await store.initialize() after construction."
|
|
13657
|
-
);
|
|
13658
|
-
}
|
|
13659
|
-
}
|
|
13660
13605
|
async #query(sql, params) {
|
|
13661
|
-
this.#ensureInitialized();
|
|
13662
13606
|
const result = await this.#pool.query(sql, params);
|
|
13663
13607
|
return result.rows;
|
|
13664
13608
|
}
|
|
@@ -13666,7 +13610,6 @@ var PostgresStreamStore = class _PostgresStreamStore extends StreamStore {
|
|
|
13666
13610
|
return z18.array(row).parse(await this.#query(sql, params));
|
|
13667
13611
|
}
|
|
13668
13612
|
async #useTransaction(fn) {
|
|
13669
|
-
this.#ensureInitialized();
|
|
13670
13613
|
const client = await this.#pool.connect();
|
|
13671
13614
|
try {
|
|
13672
13615
|
await client.query("BEGIN");
|
|
@@ -13945,7 +13888,10 @@ function assertIdentifier2(value, label) {
|
|
|
13945
13888
|
}
|
|
13946
13889
|
|
|
13947
13890
|
// packages/context/src/lib/stream/sqlite.stream-store.ts
|
|
13948
|
-
import {
|
|
13891
|
+
import { transaction as transaction2 } from "@zukhruf/sqlite-transaction";
|
|
13892
|
+
import {
|
|
13893
|
+
DatabaseSync as DatabaseSync2
|
|
13894
|
+
} from "node:sqlite";
|
|
13949
13895
|
import { z as z19 } from "zod";
|
|
13950
13896
|
|
|
13951
13897
|
// packages/context/src/lib/stream/ddl.stream.sqlite.sql
|
|
@@ -13971,57 +13917,27 @@ var chunkRow2 = z19.object({
|
|
|
13971
13917
|
});
|
|
13972
13918
|
var SqliteStreamStore = class extends StreamStore {
|
|
13973
13919
|
#db;
|
|
13974
|
-
#
|
|
13975
|
-
#closed = false;
|
|
13976
|
-
#stmt(sql) {
|
|
13977
|
-
let stmt = this.#statements.get(sql);
|
|
13978
|
-
if (!stmt) {
|
|
13979
|
-
stmt = this.#db.prepare(sql);
|
|
13980
|
-
this.#statements.set(sql, stmt);
|
|
13981
|
-
}
|
|
13982
|
-
return stmt;
|
|
13983
|
-
}
|
|
13920
|
+
#sql;
|
|
13984
13921
|
close() {
|
|
13985
|
-
if (this.#
|
|
13986
|
-
this.#closed = true;
|
|
13987
|
-
this.#statements.clear();
|
|
13922
|
+
if (!this.#db.isOpen) return;
|
|
13988
13923
|
this.#db.close();
|
|
13989
13924
|
}
|
|
13990
13925
|
constructor(pathOrDb) {
|
|
13991
13926
|
super();
|
|
13992
13927
|
this.#db = typeof pathOrDb === "string" ? new DatabaseSync2(pathOrDb) : pathOrDb;
|
|
13993
13928
|
this.#db.exec(ddl_stream_sqlite_default);
|
|
13929
|
+
this.#sql = this.#db.createTagStore();
|
|
13994
13930
|
}
|
|
13995
13931
|
async createStream(stream) {
|
|
13996
|
-
this.#
|
|
13997
|
-
|
|
13998
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
13999
|
-
).run(
|
|
14000
|
-
stream.id,
|
|
14001
|
-
stream.status,
|
|
14002
|
-
stream.createdAt,
|
|
14003
|
-
stream.startedAt,
|
|
14004
|
-
stream.finishedAt,
|
|
14005
|
-
stream.cancelRequestedAt,
|
|
14006
|
-
stream.error
|
|
14007
|
-
);
|
|
13932
|
+
this.#sql.run`INSERT INTO streams (id, status, createdAt, startedAt, finishedAt, cancelRequestedAt, error)
|
|
13933
|
+
VALUES (${stream.id}, ${stream.status}, ${stream.createdAt}, ${stream.startedAt}, ${stream.finishedAt}, ${stream.cancelRequestedAt}, ${stream.error})`;
|
|
14008
13934
|
}
|
|
14009
13935
|
async upsertStream(stream) {
|
|
14010
13936
|
const created = streamRow.optional().parse(
|
|
14011
|
-
this.#
|
|
14012
|
-
|
|
14013
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
13937
|
+
this.#sql.get`INSERT INTO streams (id, status, createdAt, startedAt, finishedAt, cancelRequestedAt, error)
|
|
13938
|
+
VALUES (${stream.id}, ${stream.status}, ${stream.createdAt}, ${stream.startedAt}, ${stream.finishedAt}, ${stream.cancelRequestedAt}, ${stream.error})
|
|
14014
13939
|
ON CONFLICT(id) DO NOTHING
|
|
14015
13940
|
RETURNING *`
|
|
14016
|
-
).get(
|
|
14017
|
-
stream.id,
|
|
14018
|
-
stream.status,
|
|
14019
|
-
stream.createdAt,
|
|
14020
|
-
stream.startedAt,
|
|
14021
|
-
stream.finishedAt,
|
|
14022
|
-
stream.cancelRequestedAt,
|
|
14023
|
-
stream.error
|
|
14024
|
-
)
|
|
14025
13941
|
);
|
|
14026
13942
|
if (created) {
|
|
14027
13943
|
return { stream: created, created: true };
|
|
@@ -14035,137 +13951,107 @@ var SqliteStreamStore = class extends StreamStore {
|
|
|
14035
13951
|
return { stream: existing, created: false };
|
|
14036
13952
|
}
|
|
14037
13953
|
async getStream(streamId) {
|
|
14038
|
-
return streamRow.optional().parse(this.#
|
|
13954
|
+
return streamRow.optional().parse(this.#sql.get`SELECT * FROM streams WHERE id = ${streamId}`);
|
|
14039
13955
|
}
|
|
14040
13956
|
async getStreamStatus(streamId) {
|
|
14041
|
-
const row = statusRow2.optional().parse(
|
|
14042
|
-
this.#stmt("SELECT status FROM streams WHERE id = ?").get(streamId)
|
|
14043
|
-
);
|
|
13957
|
+
const row = statusRow2.optional().parse(this.#sql.get`SELECT status FROM streams WHERE id = ${streamId}`);
|
|
14044
13958
|
return row?.status;
|
|
14045
13959
|
}
|
|
14046
13960
|
async listStreamIds(options) {
|
|
14047
|
-
|
|
14048
|
-
|
|
14049
|
-
|
|
14050
|
-
sql += " WHERE status = ?";
|
|
14051
|
-
params.push(options.status);
|
|
14052
|
-
}
|
|
14053
|
-
sql += " ORDER BY createdAt ASC, id ASC";
|
|
14054
|
-
const rows = z19.array(idRow5).parse(this.#stmt(sql).all(...params));
|
|
13961
|
+
const rows = z19.array(idRow5).parse(
|
|
13962
|
+
options?.status ? this.#sql.all`SELECT id FROM streams WHERE status = ${options.status} ORDER BY createdAt ASC, id ASC` : this.#sql.all`SELECT id FROM streams ORDER BY createdAt ASC, id ASC`
|
|
13963
|
+
);
|
|
14055
13964
|
return rows.map((row) => row.id);
|
|
14056
13965
|
}
|
|
14057
13966
|
async updateStream(streamId, update) {
|
|
14058
|
-
return
|
|
14059
|
-
|
|
14060
|
-
|
|
14061
|
-
|
|
14062
|
-
|
|
14063
|
-
|
|
14064
|
-
|
|
14065
|
-
|
|
14066
|
-
|
|
14067
|
-
|
|
14068
|
-
|
|
14069
|
-
|
|
14070
|
-
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14074
|
-
|
|
14075
|
-
|
|
14076
|
-
|
|
14077
|
-
|
|
14078
|
-
|
|
14079
|
-
|
|
14080
|
-
|
|
14081
|
-
|
|
14082
|
-
|
|
14083
|
-
|
|
14084
|
-
|
|
14085
|
-
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
13967
|
+
return transaction2(
|
|
13968
|
+
this.#db,
|
|
13969
|
+
() => {
|
|
13970
|
+
const stream = streamRow.optional().parse(this.#sql.get`SELECT * FROM streams WHERE id = ${streamId}`);
|
|
13971
|
+
if (!stream) {
|
|
13972
|
+
throw new Error(`updateStream: stream "${streamId}" not found`);
|
|
13973
|
+
}
|
|
13974
|
+
const updates = update(stream);
|
|
13975
|
+
if (updates === void 0) return { stream, updated: false };
|
|
13976
|
+
const setClauses = [];
|
|
13977
|
+
const params = [];
|
|
13978
|
+
const set = (column, value) => {
|
|
13979
|
+
setClauses.push(`${column} = ?`);
|
|
13980
|
+
params.push(value);
|
|
13981
|
+
};
|
|
13982
|
+
if (updates.status !== void 0) set("status", updates.status);
|
|
13983
|
+
if (updates.startedAt !== void 0)
|
|
13984
|
+
set("startedAt", updates.startedAt);
|
|
13985
|
+
if (updates.finishedAt !== void 0) {
|
|
13986
|
+
set("finishedAt", updates.finishedAt);
|
|
13987
|
+
}
|
|
13988
|
+
if (updates.cancelRequestedAt !== void 0) {
|
|
13989
|
+
set("cancelRequestedAt", updates.cancelRequestedAt);
|
|
13990
|
+
}
|
|
13991
|
+
if (updates.error !== void 0) set("error", updates.error);
|
|
13992
|
+
if (setClauses.length === 0) return { stream, updated: false };
|
|
13993
|
+
params.push(streamId);
|
|
13994
|
+
const next = streamRow.parse(
|
|
13995
|
+
this.#db.prepare(
|
|
13996
|
+
`UPDATE streams SET ${setClauses.join(", ")} WHERE id = ? RETURNING *`
|
|
13997
|
+
).get(...params)
|
|
13998
|
+
);
|
|
13999
|
+
return { stream: next, updated: true };
|
|
14000
|
+
},
|
|
14001
|
+
{ mode: "immediate" }
|
|
14002
|
+
);
|
|
14089
14003
|
}
|
|
14090
14004
|
async updateStreamStatus(streamId, status, options) {
|
|
14091
14005
|
const now = Date.now();
|
|
14092
14006
|
switch (status) {
|
|
14093
14007
|
case "running":
|
|
14094
|
-
this.#
|
|
14095
|
-
|
|
14096
|
-
WHERE id = ? AND status = 'queued'`
|
|
14097
|
-
).run(status, now, streamId);
|
|
14008
|
+
this.#sql.run`UPDATE streams SET status = ${status}, startedAt = ${now}
|
|
14009
|
+
WHERE id = ${streamId} AND status = 'queued'`;
|
|
14098
14010
|
break;
|
|
14099
14011
|
case "completed":
|
|
14100
|
-
this.#
|
|
14101
|
-
|
|
14102
|
-
WHERE id = ? AND status IN ('queued', 'running')`
|
|
14103
|
-
).run(status, now, streamId);
|
|
14012
|
+
this.#sql.run`UPDATE streams SET status = ${status}, finishedAt = ${now}
|
|
14013
|
+
WHERE id = ${streamId} AND status IN ('queued', 'running')`;
|
|
14104
14014
|
break;
|
|
14105
14015
|
case "failed":
|
|
14106
|
-
this.#
|
|
14107
|
-
|
|
14108
|
-
WHERE id = ? AND status IN ('queued', 'running')`
|
|
14109
|
-
).run(status, now, options?.error ?? null, streamId);
|
|
14016
|
+
this.#sql.run`UPDATE streams SET status = ${status}, finishedAt = ${now}, error = ${options?.error ?? null}
|
|
14017
|
+
WHERE id = ${streamId} AND status IN ('queued', 'running')`;
|
|
14110
14018
|
break;
|
|
14111
14019
|
case "cancelled":
|
|
14112
|
-
this.#
|
|
14113
|
-
|
|
14114
|
-
WHERE id = ? AND status IN ('queued', 'running')`
|
|
14115
|
-
).run(status, now, now, streamId);
|
|
14020
|
+
this.#sql.run`UPDATE streams SET status = ${status}, cancelRequestedAt = ${now}, finishedAt = ${now}
|
|
14021
|
+
WHERE id = ${streamId} AND status IN ('queued', 'running')`;
|
|
14116
14022
|
break;
|
|
14117
14023
|
default:
|
|
14118
|
-
this.#
|
|
14119
|
-
|
|
14120
|
-
WHERE id = ? AND status = 'queued'`
|
|
14121
|
-
).run(status, streamId);
|
|
14024
|
+
this.#sql.run`UPDATE streams SET status = ${status}
|
|
14025
|
+
WHERE id = ${streamId} AND status = 'queued'`;
|
|
14122
14026
|
}
|
|
14123
14027
|
}
|
|
14124
14028
|
async appendChunks(chunks) {
|
|
14125
14029
|
if (chunks.length === 0) return;
|
|
14126
14030
|
const failures = collectStreamFailures(chunks);
|
|
14127
|
-
|
|
14128
|
-
|
|
14129
|
-
|
|
14130
|
-
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
14134
|
-
|
|
14135
|
-
|
|
14136
|
-
|
|
14137
|
-
|
|
14138
|
-
|
|
14139
|
-
|
|
14140
|
-
|
|
14141
|
-
|
|
14142
|
-
|
|
14143
|
-
WHERE id = ? AND status IN ('queued', 'running')`
|
|
14144
|
-
).run("failed", failedAt, failure.error, failure.streamId);
|
|
14145
|
-
if (result.changes !== 1) {
|
|
14146
|
-
const existing = this.#stmt(
|
|
14147
|
-
"SELECT id FROM streams WHERE id = ?"
|
|
14148
|
-
).get(failure.streamId);
|
|
14149
|
-
if (!existing) {
|
|
14150
|
-
throw new Error(`Stream "${failure.streamId}" not found`);
|
|
14031
|
+
transaction2(
|
|
14032
|
+
this.#db,
|
|
14033
|
+
() => {
|
|
14034
|
+
for (const chunk of chunks) {
|
|
14035
|
+
this.#sql.run`INSERT INTO stream_chunks (streamId, seq, data, createdAt)
|
|
14036
|
+
VALUES (${chunk.streamId}, ${chunk.seq}, ${JSON.stringify(chunk.data)}, ${chunk.createdAt})`;
|
|
14037
|
+
}
|
|
14038
|
+
const failedAt = Date.now();
|
|
14039
|
+
for (const failure of failures) {
|
|
14040
|
+
const result = this.#sql.run`UPDATE streams SET status = 'failed', finishedAt = ${failedAt}, error = ${failure.error}
|
|
14041
|
+
WHERE id = ${failure.streamId} AND status IN ('queued', 'running')`;
|
|
14042
|
+
if (result.changes !== 1) {
|
|
14043
|
+
const existing = this.#sql.get`SELECT id FROM streams WHERE id = ${failure.streamId}`;
|
|
14044
|
+
if (!existing) {
|
|
14045
|
+
throw new Error(`Stream "${failure.streamId}" not found`);
|
|
14046
|
+
}
|
|
14151
14047
|
}
|
|
14152
14048
|
}
|
|
14153
|
-
}
|
|
14154
|
-
|
|
14049
|
+
},
|
|
14050
|
+
{ mode: "immediate" }
|
|
14051
|
+
);
|
|
14155
14052
|
}
|
|
14156
14053
|
async getChunks(streamId, fromSeq, limit) {
|
|
14157
|
-
|
|
14158
|
-
const params = [streamId];
|
|
14159
|
-
if (fromSeq !== void 0) {
|
|
14160
|
-
sql += " AND seq >= ?";
|
|
14161
|
-
params.push(fromSeq);
|
|
14162
|
-
}
|
|
14163
|
-
sql += " ORDER BY seq ASC";
|
|
14164
|
-
if (limit !== void 0) {
|
|
14165
|
-
sql += " LIMIT ?";
|
|
14166
|
-
params.push(limit);
|
|
14167
|
-
}
|
|
14168
|
-
const rows = z19.array(chunkRow2).parse(this.#stmt(sql).all(...params));
|
|
14054
|
+
const rows = z19.array(chunkRow2).parse(this.#chunkRows(streamId, fromSeq, limit));
|
|
14169
14055
|
return Promise.all(
|
|
14170
14056
|
rows.map(async (row) => ({
|
|
14171
14057
|
streamId: row.streamId,
|
|
@@ -14175,50 +14061,50 @@ var SqliteStreamStore = class extends StreamStore {
|
|
|
14175
14061
|
}))
|
|
14176
14062
|
);
|
|
14177
14063
|
}
|
|
14064
|
+
#chunkRows(streamId, fromSeq, limit) {
|
|
14065
|
+
if (fromSeq !== void 0 && limit !== void 0) {
|
|
14066
|
+
return this.#sql.all`SELECT * FROM stream_chunks WHERE streamId = ${streamId} AND seq >= ${fromSeq} ORDER BY seq ASC LIMIT ${limit}`;
|
|
14067
|
+
}
|
|
14068
|
+
if (fromSeq !== void 0) {
|
|
14069
|
+
return this.#sql.all`SELECT * FROM stream_chunks WHERE streamId = ${streamId} AND seq >= ${fromSeq} ORDER BY seq ASC`;
|
|
14070
|
+
}
|
|
14071
|
+
if (limit !== void 0) {
|
|
14072
|
+
return this.#sql.all`SELECT * FROM stream_chunks WHERE streamId = ${streamId} ORDER BY seq ASC LIMIT ${limit}`;
|
|
14073
|
+
}
|
|
14074
|
+
return this.#sql.all`SELECT * FROM stream_chunks WHERE streamId = ${streamId} ORDER BY seq ASC`;
|
|
14075
|
+
}
|
|
14178
14076
|
async deleteStream(streamId) {
|
|
14179
|
-
this.#
|
|
14077
|
+
this.#sql.run`DELETE FROM streams WHERE id = ${streamId}`;
|
|
14180
14078
|
}
|
|
14181
14079
|
async reopenStream(streamId) {
|
|
14182
|
-
return
|
|
14183
|
-
|
|
14184
|
-
|
|
14185
|
-
|
|
14186
|
-
|
|
14187
|
-
|
|
14188
|
-
|
|
14189
|
-
|
|
14190
|
-
|
|
14191
|
-
|
|
14192
|
-
|
|
14193
|
-
|
|
14194
|
-
|
|
14195
|
-
|
|
14196
|
-
|
|
14197
|
-
|
|
14198
|
-
|
|
14199
|
-
|
|
14200
|
-
|
|
14201
|
-
|
|
14202
|
-
|
|
14203
|
-
|
|
14204
|
-
|
|
14205
|
-
|
|
14206
|
-
|
|
14207
|
-
|
|
14208
|
-
|
|
14209
|
-
|
|
14210
|
-
this.#db.exec("BEGIN IMMEDIATE");
|
|
14211
|
-
try {
|
|
14212
|
-
const result = callback();
|
|
14213
|
-
this.#db.exec("COMMIT");
|
|
14214
|
-
return result;
|
|
14215
|
-
} catch (error) {
|
|
14216
|
-
try {
|
|
14217
|
-
this.#db.exec("ROLLBACK");
|
|
14218
|
-
} catch {
|
|
14219
|
-
}
|
|
14220
|
-
throw error;
|
|
14221
|
-
}
|
|
14080
|
+
return transaction2(
|
|
14081
|
+
this.#db,
|
|
14082
|
+
() => {
|
|
14083
|
+
const row = statusRow2.optional().parse(this.#sql.get`SELECT * FROM streams WHERE id = ${streamId}`);
|
|
14084
|
+
if (!row) {
|
|
14085
|
+
throw new Error(`Stream "${streamId}" not found`);
|
|
14086
|
+
}
|
|
14087
|
+
if (row.status !== "completed" && row.status !== "failed" && row.status !== "cancelled") {
|
|
14088
|
+
throw new Error(
|
|
14089
|
+
`Cannot reopen stream "${streamId}" with status "${row.status}". Only terminal streams can be reopened.`
|
|
14090
|
+
);
|
|
14091
|
+
}
|
|
14092
|
+
this.#sql.run`DELETE FROM streams WHERE id = ${streamId}`;
|
|
14093
|
+
const now = Date.now();
|
|
14094
|
+
this.#sql.run`INSERT INTO streams (id, status, createdAt, startedAt, finishedAt, cancelRequestedAt, error)
|
|
14095
|
+
VALUES (${streamId}, 'queued', ${now}, NULL, NULL, NULL, NULL)`;
|
|
14096
|
+
return {
|
|
14097
|
+
id: streamId,
|
|
14098
|
+
status: "queued",
|
|
14099
|
+
createdAt: now,
|
|
14100
|
+
startedAt: null,
|
|
14101
|
+
finishedAt: null,
|
|
14102
|
+
cancelRequestedAt: null,
|
|
14103
|
+
error: null
|
|
14104
|
+
};
|
|
14105
|
+
},
|
|
14106
|
+
{ mode: "immediate" }
|
|
14107
|
+
);
|
|
14222
14108
|
}
|
|
14223
14109
|
};
|
|
14224
14110
|
|
|
@@ -14684,8 +14570,8 @@ export {
|
|
|
14684
14570
|
PostgresContextStore,
|
|
14685
14571
|
PostgresNotifyChangeSource,
|
|
14686
14572
|
PostgresStreamStore,
|
|
14687
|
-
PromiseResolver,
|
|
14688
14573
|
RuntimeStrategy,
|
|
14574
|
+
SearchQuerySyntaxError,
|
|
14689
14575
|
SqlServerContextStore,
|
|
14690
14576
|
SqliteContextStore,
|
|
14691
14577
|
SqliteStreamStore,
|
|
@@ -14792,6 +14678,7 @@ export {
|
|
|
14792
14678
|
isSyntheticReminderMessage,
|
|
14793
14679
|
lastAssistantLength,
|
|
14794
14680
|
localeReminder,
|
|
14681
|
+
lockSchemaDDL,
|
|
14795
14682
|
mapGenerateErrorToCode,
|
|
14796
14683
|
mergeMessageMetadata,
|
|
14797
14684
|
mergeReminderMetadata,
|