@ykurilov/agent-chat-storage-sqlite 0.83.3 → 0.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/dist/external.js +72 -0
- package/dist/external.js.map +1 -0
- package/dist/types/external.d.ts +5 -0
- package/package.json +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @ykurilov/agent-chat-storage-sqlite
|
|
2
2
|
|
|
3
|
+
## 0.85.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [1f7a921]
|
|
8
|
+
- @ykurilov/agent-chat-core@0.85.0
|
|
9
|
+
|
|
10
|
+
## 0.84.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- 5e90be0: Добавлен opt-in первый срез бесед с внешним исполнителем: текст сохраняется до отправки, недоступный исполнитель получает очередь после восстановления, ответ и разрешения приходят через сохранённый журнал. Исходный clientMessageId проверяется при повторе. Новые публичные входы core/external, server/external и storage-sqlite/external не меняют прежний ChatService.
|
|
15
|
+
|
|
16
|
+
В checkout SDK добавлены локальные SPM-продукты AgentChatClient и AgentChatOduvanchik для iOS 17/macOS 14: история, поток, отдельные разрешения, «Стоп», переподключение и очередь на устройстве. Канонический YKDesign подключается отдельно по локальному пути. Переносимого публичного SPM URL пока нет.
|
|
17
|
+
|
|
18
|
+
### Как обновить приложение
|
|
19
|
+
|
|
20
|
+
1. Старые приложения не требуют изменений. Для внешнего исполнителя в серверном composition root подключите новые /external входы, создайте SQLite conversation store, сервис и fetch handler. Существующее ChatStorage не заменяйте.
|
|
21
|
+
2. При старте provision разрешённых бесед и service.start; при завершении await service.close до store.close. Адаптер обязан подтверждать async sink после записи, поддерживать reconcile и отдельный stopTurn. Полный договор - docs/external-native.md.
|
|
22
|
+
3. В Xcode подключите локальный SDK package и канонический YKDesign, передайте endpoint, accountID, cacheURL и credential closure в AgentChatClient. Авторизация, список разрешённых привязок и политика кеша остаются приложению.
|
|
23
|
+
4. Проверьте недоступного исполнителя, повтор одного clientMessageId, переподключение, разрешение и остановку. Не включайте kind:model: встроенный нативный модельный исполнитель отложен. Перед production подтвердите на Мосте поиск исходного ID при неопределённой отправке; без доказательства SDK не повторяет внешнее действие.
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- Updated dependencies [5e90be0]
|
|
28
|
+
- @ykurilov/agent-chat-core@0.84.0
|
|
29
|
+
|
|
3
30
|
## 0.83.3
|
|
4
31
|
|
|
5
32
|
### Patch Changes
|
package/dist/external.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/external.ts
|
|
2
|
+
import Database from "better-sqlite3";
|
|
3
|
+
import { conversationView, initialConversation } from "@ykurilov/agent-chat-core/external";
|
|
4
|
+
function createSqliteConversationStore(path) {
|
|
5
|
+
const db = new Database(path);
|
|
6
|
+
db.pragma("journal_mode = WAL");
|
|
7
|
+
db.pragma("busy_timeout = 5000");
|
|
8
|
+
db.pragma("foreign_keys = ON");
|
|
9
|
+
db.transaction(() => db.exec(`
|
|
10
|
+
CREATE TABLE IF NOT EXISTS sdk_external_schema (id INTEGER PRIMARY KEY CHECK(id=1), version INTEGER NOT NULL);
|
|
11
|
+
INSERT OR IGNORE INTO sdk_external_schema VALUES (1,1);
|
|
12
|
+
CREATE TABLE IF NOT EXISTS sdk_external_conversations (id TEXT PRIMARY KEY, owner_id TEXT NOT NULL, metadata TEXT NOT NULL);
|
|
13
|
+
CREATE TABLE IF NOT EXISTS sdk_external_messages (chat_id TEXT NOT NULL REFERENCES sdk_external_conversations(id) ON DELETE CASCADE, id TEXT NOT NULL, position INTEGER NOT NULL, data TEXT NOT NULL, PRIMARY KEY(chat_id,id));
|
|
14
|
+
CREATE TABLE IF NOT EXISTS sdk_external_events (chat_id TEXT NOT NULL REFERENCES sdk_external_conversations(id) ON DELETE CASCADE, seq INTEGER NOT NULL, data TEXT NOT NULL, PRIMARY KEY(chat_id,seq));
|
|
15
|
+
`)).immediate();
|
|
16
|
+
const version = db.prepare("SELECT version FROM sdk_external_schema WHERE id=1").get();
|
|
17
|
+
if (version.version !== 1) {
|
|
18
|
+
db.close();
|
|
19
|
+
throw Error("unsupported_external_schema");
|
|
20
|
+
}
|
|
21
|
+
function read(ownerId, id) {
|
|
22
|
+
const row = db.prepare("SELECT metadata FROM sdk_external_conversations WHERE id=? AND owner_id=?").get(id, ownerId);
|
|
23
|
+
if (!row) throw Error("not_found");
|
|
24
|
+
return { ...JSON.parse(row.metadata), messages: db.prepare("SELECT data FROM sdk_external_messages WHERE chat_id=? ORDER BY position").all(id).map((r) => JSON.parse(r.data)), events: db.prepare("SELECT data FROM sdk_external_events WHERE chat_id=? ORDER BY seq").all(id).map((r) => JSON.parse(r.data)) };
|
|
25
|
+
}
|
|
26
|
+
function write(s) {
|
|
27
|
+
const { messages, events, ...metadata } = s;
|
|
28
|
+
db.prepare("UPDATE sdk_external_conversations SET metadata=? WHERE id=? AND owner_id=?").run(JSON.stringify(metadata), s.definition.id, s.definition.ownerId);
|
|
29
|
+
const put = db.prepare("INSERT INTO sdk_external_messages(chat_id,id,position,data) VALUES(?,?,?,?) ON CONFLICT(chat_id,id) DO UPDATE SET position=excluded.position,data=excluded.data WHERE data<>excluded.data OR position<>excluded.position");
|
|
30
|
+
messages.forEach((m, index) => put.run(s.definition.id, m.id, index, JSON.stringify(m)));
|
|
31
|
+
const event = db.prepare("INSERT OR IGNORE INTO sdk_external_events(chat_id,seq,data) VALUES(?,?,?)");
|
|
32
|
+
for (const e of events) event.run(s.definition.id, e.seq, JSON.stringify(e));
|
|
33
|
+
db.prepare("DELETE FROM sdk_external_events WHERE chat_id=? AND seq<?").run(s.definition.id, events[0]?.seq ?? s.cursor + 1);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
guarded: true,
|
|
37
|
+
close() {
|
|
38
|
+
db.close();
|
|
39
|
+
},
|
|
40
|
+
async create(d) {
|
|
41
|
+
db.transaction(() => {
|
|
42
|
+
const row = db.prepare("SELECT owner_id,metadata FROM sdk_external_conversations WHERE id=?").get(d.id);
|
|
43
|
+
if (row) {
|
|
44
|
+
if (row.owner_id !== d.ownerId) throw Error("not_found");
|
|
45
|
+
if (JSON.stringify(JSON.parse(row.metadata).definition.execution) !== JSON.stringify(d.execution)) throw Error("execution_conflict");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const { messages: _m, events: _e, ...s } = initialConversation(d);
|
|
49
|
+
db.prepare("INSERT INTO sdk_external_conversations VALUES(?,?,?)").run(d.id, d.ownerId, JSON.stringify(s));
|
|
50
|
+
}).immediate();
|
|
51
|
+
},
|
|
52
|
+
async read(ownerId, id) {
|
|
53
|
+
return db.transaction(() => read(ownerId, id))();
|
|
54
|
+
},
|
|
55
|
+
async list(ownerId) {
|
|
56
|
+
return db.prepare("SELECT metadata FROM sdk_external_conversations WHERE owner_id=?").all(ownerId).map((r) => conversationView(JSON.parse(r.metadata))).sort((a, b) => Number(b.pinned) - Number(a.pinned) || b.updatedAt.localeCompare(a.updatedAt));
|
|
57
|
+
},
|
|
58
|
+
async transaction(ownerId, id, change) {
|
|
59
|
+
return db.transaction(() => {
|
|
60
|
+
const s = read(ownerId, id);
|
|
61
|
+
const result = change(s);
|
|
62
|
+
if (result && typeof result === "object" && "then" in result) throw Error("async_transaction_forbidden");
|
|
63
|
+
write(s);
|
|
64
|
+
return structuredClone(result);
|
|
65
|
+
}).immediate();
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
createSqliteConversationStore
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=external.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/external.ts"],"sourcesContent":["import Database from \"better-sqlite3\";\nimport { conversationView, initialConversation } from \"@ykurilov/agent-chat-core/external\";\nimport type { ConversationDefinition, ConversationState, ConversationStore, ConversationFeedEvent, ConversationMessage } from \"@ykurilov/agent-chat-core/external\";\n\n/** Opt-in external/native projection, isolated tables; does not change ordinary ChatStorage. */\nexport function createSqliteConversationStore(path:string):ConversationStore&{close():void} {\n const db=new Database(path);\n db.pragma(\"journal_mode = WAL\");db.pragma(\"busy_timeout = 5000\");db.pragma(\"foreign_keys = ON\");\n db.transaction(()=>db.exec(`\n CREATE TABLE IF NOT EXISTS sdk_external_schema (id INTEGER PRIMARY KEY CHECK(id=1), version INTEGER NOT NULL);\n INSERT OR IGNORE INTO sdk_external_schema VALUES (1,1);\n CREATE TABLE IF NOT EXISTS sdk_external_conversations (id TEXT PRIMARY KEY, owner_id TEXT NOT NULL, metadata TEXT NOT NULL);\n CREATE TABLE IF NOT EXISTS sdk_external_messages (chat_id TEXT NOT NULL REFERENCES sdk_external_conversations(id) ON DELETE CASCADE, id TEXT NOT NULL, position INTEGER NOT NULL, data TEXT NOT NULL, PRIMARY KEY(chat_id,id));\n CREATE TABLE IF NOT EXISTS sdk_external_events (chat_id TEXT NOT NULL REFERENCES sdk_external_conversations(id) ON DELETE CASCADE, seq INTEGER NOT NULL, data TEXT NOT NULL, PRIMARY KEY(chat_id,seq));\n `)).immediate();\n const version=db.prepare(\"SELECT version FROM sdk_external_schema WHERE id=1\").get() as {version:number};\n if(version.version!==1){db.close();throw Error(\"unsupported_external_schema\");}\n function read(ownerId:string,id:string):ConversationState {\n const row=db.prepare(\"SELECT metadata FROM sdk_external_conversations WHERE id=? AND owner_id=?\").get(id,ownerId) as {metadata:string}|undefined;\n if(!row)throw Error(\"not_found\");\n return {...JSON.parse(row.metadata),messages:(db.prepare(\"SELECT data FROM sdk_external_messages WHERE chat_id=? ORDER BY position\").all(id) as Array<{data:string}>).map(r=>JSON.parse(r.data) as ConversationMessage),events:(db.prepare(\"SELECT data FROM sdk_external_events WHERE chat_id=? ORDER BY seq\").all(id) as Array<{data:string}>).map(r=>JSON.parse(r.data) as ConversationFeedEvent)};\n }\n function write(s:ConversationState) {\n const {messages,events,...metadata}=s;\n db.prepare(\"UPDATE sdk_external_conversations SET metadata=? WHERE id=? AND owner_id=?\").run(JSON.stringify(metadata),s.definition.id,s.definition.ownerId);\n const put=db.prepare(\"INSERT INTO sdk_external_messages(chat_id,id,position,data) VALUES(?,?,?,?) ON CONFLICT(chat_id,id) DO UPDATE SET position=excluded.position,data=excluded.data WHERE data<>excluded.data OR position<>excluded.position\");\n messages.forEach((m,index)=>put.run(s.definition.id,m.id,index,JSON.stringify(m)));\n const event=db.prepare(\"INSERT OR IGNORE INTO sdk_external_events(chat_id,seq,data) VALUES(?,?,?)\");\n for(const e of events)event.run(s.definition.id,e.seq,JSON.stringify(e));\n db.prepare(\"DELETE FROM sdk_external_events WHERE chat_id=? AND seq<?\").run(s.definition.id,events[0]?.seq??s.cursor+1);\n }\n return {guarded:true,\n close(){db.close();},\n async create(d:ConversationDefinition){db.transaction(()=>{\n const row=db.prepare(\"SELECT owner_id,metadata FROM sdk_external_conversations WHERE id=?\").get(d.id) as {owner_id:string;metadata:string}|undefined;\n if(row){if(row.owner_id!==d.ownerId)throw Error(\"not_found\");if(JSON.stringify(JSON.parse(row.metadata).definition.execution)!==JSON.stringify(d.execution))throw Error(\"execution_conflict\");return;}\n const {messages:_m,events:_e,...s}=initialConversation(d);\n db.prepare(\"INSERT INTO sdk_external_conversations VALUES(?,?,?)\").run(d.id,d.ownerId,JSON.stringify(s));\n }).immediate();},\n async read(ownerId,id){return db.transaction(()=>read(ownerId,id))();},\n async list(ownerId){return (db.prepare(\"SELECT metadata FROM sdk_external_conversations WHERE owner_id=?\").all(ownerId) as Array<{metadata:string}>).map(r=>conversationView(JSON.parse(r.metadata))).sort((a,b)=>Number(b.pinned)-Number(a.pinned)||b.updatedAt.localeCompare(a.updatedAt));},\n async transaction<T>(ownerId:string,id:string,change:(s:ConversationState)=>T):Promise<T>{return db.transaction(()=>{\n const s=read(ownerId,id);const result=change(s);\n if(result&&typeof result===\"object\"&&\"then\" in result)throw Error(\"async_transaction_forbidden\");\n write(s);return structuredClone(result);\n }).immediate();},\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ConversationStore } from "@ykurilov/agent-chat-core/external";
|
|
2
|
+
/** Opt-in external/native projection, isolated tables; does not change ordinary ChatStorage. */
|
|
3
|
+
export declare function createSqliteConversationStore(path: string): ConversationStore & {
|
|
4
|
+
close(): void;
|
|
5
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ykurilov/agent-chat-storage-sqlite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.85.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
"@ykurilov/agent-chat-source": "./src/index.ts",
|
|
16
16
|
"types": "./dist/types/index.d.ts",
|
|
17
17
|
"import": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./external": {
|
|
20
|
+
"@ykurilov/agent-chat-source": "./src/external.ts",
|
|
21
|
+
"types": "./dist/types/external.d.ts",
|
|
22
|
+
"import": "./dist/external.js"
|
|
18
23
|
}
|
|
19
24
|
},
|
|
20
25
|
"engines": {
|
|
@@ -32,7 +37,7 @@
|
|
|
32
37
|
"dependencies": {
|
|
33
38
|
"better-sqlite3": "^13.0.3",
|
|
34
39
|
"drizzle-orm": "^0.45.3",
|
|
35
|
-
"@ykurilov/agent-chat-core": "0.
|
|
40
|
+
"@ykurilov/agent-chat-core": "0.85.0"
|
|
36
41
|
},
|
|
37
42
|
"devDependencies": {
|
|
38
43
|
"@types/better-sqlite3": "^9.6.0",
|