@ykurilov/agent-chat-server 0.83.3 → 0.84.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 +20 -0
- package/dist/external.js +155 -0
- package/dist/external.js.map +1 -0
- package/dist/types/external.d.ts +18 -0
- package/package.json +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @ykurilov/agent-chat-server
|
|
2
2
|
|
|
3
|
+
## 0.84.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5e90be0: Добавлен opt-in первый срез бесед с внешним исполнителем: текст сохраняется до отправки, недоступный исполнитель получает очередь после восстановления, ответ и разрешения приходят через сохранённый журнал. Исходный clientMessageId проверяется при повторе. Новые публичные входы core/external, server/external и storage-sqlite/external не меняют прежний ChatService.
|
|
8
|
+
|
|
9
|
+
В checkout SDK добавлены локальные SPM-продукты AgentChatClient и AgentChatOduvanchik для iOS 17/macOS 14: история, поток, отдельные разрешения, «Стоп», переподключение и очередь на устройстве. Канонический YKDesign подключается отдельно по локальному пути. Переносимого публичного SPM URL пока нет.
|
|
10
|
+
|
|
11
|
+
### Как обновить приложение
|
|
12
|
+
|
|
13
|
+
1. Старые приложения не требуют изменений. Для внешнего исполнителя в серверном composition root подключите новые /external входы, создайте SQLite conversation store, сервис и fetch handler. Существующее ChatStorage не заменяйте.
|
|
14
|
+
2. При старте provision разрешённых бесед и service.start; при завершении await service.close до store.close. Адаптер обязан подтверждать async sink после записи, поддерживать reconcile и отдельный stopTurn. Полный договор - docs/external-native.md.
|
|
15
|
+
3. В Xcode подключите локальный SDK package и канонический YKDesign, передайте endpoint, accountID, cacheURL и credential closure в AgentChatClient. Авторизация, список разрешённых привязок и политика кеша остаются приложению.
|
|
16
|
+
4. Проверьте недоступного исполнителя, повтор одного clientMessageId, переподключение, разрешение и остановку. Не включайте kind:model: встроенный нативный модельный исполнитель отложен. Перед production подтвердите на Мосте поиск исходного ID при неопределённой отправке; без доказательства SDK не повторяет внешнее действие.
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [5e90be0]
|
|
21
|
+
- @ykurilov/agent-chat-core@0.84.0
|
|
22
|
+
|
|
3
23
|
## 0.83.3
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/dist/external.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// src/external.ts
|
|
2
|
+
import { createRequestOriginCheck } from "@ykurilov/agent-chat-core";
|
|
3
|
+
function createExternalChatHandler(options) {
|
|
4
|
+
const base = (options.basePath ?? "/api/conversations").replace(/\/$/, "");
|
|
5
|
+
const originCheck = createRequestOriginCheck({ allowedOrigins: options.allowedOrigins });
|
|
6
|
+
const service = options.service;
|
|
7
|
+
const json = (value, status = 200) => Response.json(value, { status, headers: { "cache-control": "no-store" } });
|
|
8
|
+
return {
|
|
9
|
+
async fetch(request) {
|
|
10
|
+
try {
|
|
11
|
+
const url = new URL(request.url);
|
|
12
|
+
if (url.pathname !== base && !url.pathname.startsWith(base + "/")) return json({ error: { kind: "not_found" } }, 404);
|
|
13
|
+
if (request.method !== "GET" && request.method !== "HEAD" && !originCheck(request)) return json({ error: { kind: "forbidden_origin" } }, 403);
|
|
14
|
+
const user = await options.resolveUser(request);
|
|
15
|
+
if (!user) return json({ error: { kind: "unauthorized" } }, 401);
|
|
16
|
+
const parts = url.pathname.slice(base.length).split("/").filter(Boolean).map(decodeURIComponent);
|
|
17
|
+
if (parts.length === 0 && request.method === "GET") return json({ version: 1, conversations: await service.list(user.id) });
|
|
18
|
+
const id = parts[0];
|
|
19
|
+
if (!id) return json({ error: { kind: "not_found" } }, 404);
|
|
20
|
+
await service.snapshot(user.id, id);
|
|
21
|
+
if (parts.length === 1 && request.method === "GET") return json(await service.snapshot(user.id, id));
|
|
22
|
+
if (parts[1] === "events" && parts.length === 2 && request.method === "GET") {
|
|
23
|
+
const raw = url.searchParams.get("after") ?? request.headers.get("last-event-id") ?? "0";
|
|
24
|
+
if (!/^\d+$/.test(raw)) throw Error("invalid_cursor");
|
|
25
|
+
const after = Number(raw);
|
|
26
|
+
const page = await service.events(user.id, id, after);
|
|
27
|
+
if (page.reset) return json({ version: 1, reset: true }, 409);
|
|
28
|
+
if (!request.headers.get("accept")?.includes("text/event-stream")) return json(page);
|
|
29
|
+
let dispose = () => {
|
|
30
|
+
};
|
|
31
|
+
let cursor = after, closed = false, timer;
|
|
32
|
+
const encoder = new TextEncoder();
|
|
33
|
+
let lastPing = Date.now();
|
|
34
|
+
const stream = new ReadableStream({
|
|
35
|
+
start(controller) {
|
|
36
|
+
const finish = () => {
|
|
37
|
+
if (closed) return;
|
|
38
|
+
closed = true;
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
request.signal.removeEventListener("abort", abort);
|
|
41
|
+
controller.close();
|
|
42
|
+
};
|
|
43
|
+
const abort = () => finish();
|
|
44
|
+
request.signal.addEventListener("abort", abort, { once: true });
|
|
45
|
+
dispose = () => request.signal.removeEventListener("abort", abort);
|
|
46
|
+
const poll = async () => {
|
|
47
|
+
if (closed) return;
|
|
48
|
+
try {
|
|
49
|
+
if ((controller.desiredSize ?? 1) <= 0) {
|
|
50
|
+
timer = setTimeout(() => void poll(), options.pollMs ?? 200);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const next = await service.events(user.id, id, cursor);
|
|
54
|
+
if (closed) return;
|
|
55
|
+
if (next.reset) {
|
|
56
|
+
controller.enqueue(encoder.encode('event: reset\ndata: {"version":1,"reset":true}\n\n'));
|
|
57
|
+
finish();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
for (const event of next.events) {
|
|
61
|
+
if ((controller.desiredSize ?? 1) <= 0) break;
|
|
62
|
+
controller.enqueue(encoder.encode(`id: ${event.seq}
|
|
63
|
+
event: change
|
|
64
|
+
data: ${JSON.stringify(event)}
|
|
65
|
+
|
|
66
|
+
`));
|
|
67
|
+
cursor = event.seq;
|
|
68
|
+
}
|
|
69
|
+
if (Date.now() - lastPing >= (options.heartbeatMs ?? 1e4)) {
|
|
70
|
+
controller.enqueue(encoder.encode(": heartbeat\n\n"));
|
|
71
|
+
lastPing = Date.now();
|
|
72
|
+
}
|
|
73
|
+
timer = setTimeout(() => void poll(), options.pollMs ?? 200);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
options.logError?.(error);
|
|
76
|
+
finish();
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
if (request.signal.aborted) finish();
|
|
80
|
+
else void poll();
|
|
81
|
+
},
|
|
82
|
+
cancel() {
|
|
83
|
+
closed = true;
|
|
84
|
+
clearTimeout(timer);
|
|
85
|
+
dispose();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return new Response(stream, { headers: { "content-type": "text/event-stream", "cache-control": "no-cache, no-transform", "x-accel-buffering": "no" } });
|
|
89
|
+
}
|
|
90
|
+
if (request.method === "POST") {
|
|
91
|
+
const bytes = await readBody(request);
|
|
92
|
+
const body = JSON.parse(bytes);
|
|
93
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) throw Error("invalid_body");
|
|
94
|
+
if (parts[1] === "messages" && parts.length === 2) {
|
|
95
|
+
if (typeof body.clientMessageId !== "string" || typeof body.text !== "string") throw Error("invalid_message");
|
|
96
|
+
const receipt = await service.send(user.id, id, { clientMessageId: body.clientMessageId, text: body.text });
|
|
97
|
+
return json({ version: 1, ...receipt }, 202);
|
|
98
|
+
}
|
|
99
|
+
if (parts[1] === "stop" && parts.length === 2) {
|
|
100
|
+
if (typeof body.commandId !== "string") throw Error("invalid_command");
|
|
101
|
+
return json({ version: 1, ...await service.stop(user.id, id, body.commandId) }, 202);
|
|
102
|
+
}
|
|
103
|
+
if (parts[1] === "permissions" && parts[2] && parts.length === 3) {
|
|
104
|
+
if (typeof body.commandId !== "string" || !body.decision || typeof body.decision !== "object" || Array.isArray(body.decision)) throw Error("invalid_permission_answer");
|
|
105
|
+
const decision = body.decision;
|
|
106
|
+
if (decision.behavior !== void 0 && decision.behavior !== "allow" && decision.behavior !== "deny") throw Error("invalid_permission_answer");
|
|
107
|
+
if (decision.actionId !== void 0 && typeof decision.actionId !== "string") throw Error("invalid_permission_answer");
|
|
108
|
+
if (decision.answers !== void 0 && (!decision.answers || typeof decision.answers !== "object" || Array.isArray(decision.answers))) throw Error("invalid_permission_answer");
|
|
109
|
+
return json({ version: 1, ...await service.answerPermission(user.id, id, parts[2], body.commandId, decision) }, 202);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return json({ error: { kind: "not_found" } }, 404);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
const kind = error instanceof Error ? error.message : "internal";
|
|
115
|
+
if (kind === "not_found") return json({ error: { kind } }, 404);
|
|
116
|
+
if (kind === "body_too_large") return json({ error: { kind } }, 413);
|
|
117
|
+
if (["busy", "message_id_conflict", "command_id_conflict", "permission_not_pending", "executor_offline", "executor_not_supported"].includes(kind)) return json({ error: { kind } }, 409);
|
|
118
|
+
if (kind.startsWith("invalid_") || error instanceof SyntaxError || error instanceof URIError) return json({ error: { kind: "invalid" } }, 400);
|
|
119
|
+
options.logError?.(error);
|
|
120
|
+
return json({ error: { kind: "internal" } }, 500);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
async function readBody(request) {
|
|
126
|
+
if (!request.body) return "{}";
|
|
127
|
+
const reader = request.body.getReader();
|
|
128
|
+
let size = 0;
|
|
129
|
+
const chunks = [];
|
|
130
|
+
try {
|
|
131
|
+
while (true) {
|
|
132
|
+
const { done, value } = await reader.read();
|
|
133
|
+
if (done) break;
|
|
134
|
+
size += value.length;
|
|
135
|
+
if (size > 128e3) {
|
|
136
|
+
await reader.cancel();
|
|
137
|
+
throw Error("body_too_large");
|
|
138
|
+
}
|
|
139
|
+
chunks.push(value);
|
|
140
|
+
}
|
|
141
|
+
} finally {
|
|
142
|
+
reader.releaseLock();
|
|
143
|
+
}
|
|
144
|
+
const result = new Uint8Array(size);
|
|
145
|
+
let offset = 0;
|
|
146
|
+
for (const c of chunks) {
|
|
147
|
+
result.set(c, offset);
|
|
148
|
+
offset += c.length;
|
|
149
|
+
}
|
|
150
|
+
return new TextDecoder().decode(result);
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
createExternalChatHandler
|
|
154
|
+
};
|
|
155
|
+
//# sourceMappingURL=external.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/external.ts"],"sourcesContent":["import type { ExternalConversationService, PermissionDecision } from \"@ykurilov/agent-chat-core/external\";\nimport { createRequestOriginCheck } from \"@ykurilov/agent-chat-core\";\n\nexport interface ExternalChatHandlerOptions {\n service:ExternalConversationService;\n basePath?:string;\n resolveUser:(request:Request)=>{id:string}|null|Promise<{id:string}|null>;\n allowedOrigins?:string[];\n pollMs?:number; heartbeatMs?:number;\n logError?:(error:unknown)=>void;\n}\n/** Version 1 native protocol. Standard fetch handler: mount with Hono's app.all(..., c => handler.fetch(c.req.raw)). */\nexport function createExternalChatHandler(options:ExternalChatHandlerOptions) {\n const base=(options.basePath??\"/api/conversations\").replace(/\\/$/,\"\");\n const originCheck=createRequestOriginCheck({allowedOrigins:options.allowedOrigins});\n const service=options.service;\n const json=(value:unknown,status=200)=>Response.json(value,{status,headers:{\"cache-control\":\"no-store\"}});\n return {\n async fetch(request:Request):Promise<Response> {\n try {\n const url=new URL(request.url);\n if(url.pathname!==base&&!url.pathname.startsWith(base+\"/\"))return json({error:{kind:\"not_found\"}},404);\n if(request.method!==\"GET\"&&request.method!==\"HEAD\"&&!originCheck(request))return json({error:{kind:\"forbidden_origin\"}},403);\n const user=await options.resolveUser(request);if(!user)return json({error:{kind:\"unauthorized\"}},401);\n const parts=url.pathname.slice(base.length).split(\"/\").filter(Boolean).map(decodeURIComponent);\n if(parts.length===0&&request.method===\"GET\")return json({version:1,conversations:await service.list(user.id)});\n const id=parts[0];if(!id)return json({error:{kind:\"not_found\"}},404);\n // Always authorize before parsing request bodies or creating a stream.\n await service.snapshot(user.id,id);\n if(parts.length===1&&request.method===\"GET\")return json(await service.snapshot(user.id,id));\n if(parts[1]===\"events\"&&parts.length===2&&request.method===\"GET\"){\n const raw=url.searchParams.get(\"after\")??request.headers.get(\"last-event-id\")??\"0\";\n if(!/^\\d+$/.test(raw))throw Error(\"invalid_cursor\");\n const after=Number(raw);\n const page=await service.events(user.id,id,after);\n if(page.reset)return json({version:1,reset:true},409);\n if(!request.headers.get(\"accept\")?.includes(\"text/event-stream\"))return json(page);\n let dispose=()=>{};\n let cursor=after,closed=false,timer:ReturnType<typeof setTimeout>|undefined;\n const encoder=new TextEncoder();let lastPing=Date.now();\n const stream=new ReadableStream<Uint8Array>({\n start(controller){\n const finish=()=>{if(closed)return;closed=true;clearTimeout(timer);request.signal.removeEventListener(\"abort\",abort);controller.close();};\n const abort=()=>finish();\n request.signal.addEventListener(\"abort\",abort,{once:true});\n dispose=()=>request.signal.removeEventListener(\"abort\",abort);\n const poll=async()=>{\n if(closed)return;\n try {\n if((controller.desiredSize??1)<=0){timer=setTimeout(()=>void poll(),options.pollMs??200);return;}\n const next=await service.events(user.id,id,cursor);\n if(closed)return;\n if(next.reset){controller.enqueue(encoder.encode('event: reset\\ndata: {\"version\":1,\"reset\":true}\\n\\n'));finish();return;}\n for(const event of next.events){if((controller.desiredSize??1)<=0)break;controller.enqueue(encoder.encode(`id: ${event.seq}\\nevent: change\\ndata: ${JSON.stringify(event)}\\n\\n`));cursor=event.seq;}\n if(Date.now()-lastPing>=(options.heartbeatMs??10_000)){controller.enqueue(encoder.encode(\": heartbeat\\n\\n\"));lastPing=Date.now();}\n timer=setTimeout(()=>void poll(),options.pollMs??200);\n }catch(error){options.logError?.(error);finish();}\n };\n if(request.signal.aborted)finish();else void poll();\n },\n cancel(){closed=true;clearTimeout(timer);dispose();},\n });\n return new Response(stream,{headers:{\"content-type\":\"text/event-stream\",\"cache-control\":\"no-cache, no-transform\",\"x-accel-buffering\":\"no\"}});\n }\n if(request.method===\"POST\"){\n const bytes=await readBody(request);const body=JSON.parse(bytes) as Record<string,unknown>;\n if(!body||typeof body!==\"object\"||Array.isArray(body))throw Error(\"invalid_body\");\n if(parts[1]===\"messages\"&&parts.length===2){\n if(typeof body.clientMessageId!==\"string\"||typeof body.text!==\"string\")throw Error(\"invalid_message\");\n const receipt=await service.send(user.id,id,{clientMessageId:body.clientMessageId,text:body.text});return json({version:1,...receipt},202);\n }\n if(parts[1]===\"stop\"&&parts.length===2){if(typeof body.commandId!==\"string\")throw Error(\"invalid_command\");return json({version:1,...await service.stop(user.id,id,body.commandId)},202);}\n if(parts[1]===\"permissions\"&&parts[2]&&parts.length===3){\n if(typeof body.commandId!==\"string\"||!body.decision||typeof body.decision!==\"object\"||Array.isArray(body.decision))throw Error(\"invalid_permission_answer\");\n const decision=body.decision as PermissionDecision;\n if(decision.behavior!==undefined&&decision.behavior!==\"allow\"&&decision.behavior!==\"deny\")throw Error(\"invalid_permission_answer\");\n if(decision.actionId!==undefined&&typeof decision.actionId!==\"string\")throw Error(\"invalid_permission_answer\");\n if(decision.answers!==undefined&&(!decision.answers||typeof decision.answers!==\"object\"||Array.isArray(decision.answers)))throw Error(\"invalid_permission_answer\");\n return json({version:1,...await service.answerPermission(user.id,id,parts[2],body.commandId,decision)},202);\n }\n }\n return json({error:{kind:\"not_found\"}},404);\n }catch(error){\n const kind=error instanceof Error?error.message:\"internal\";\n if(kind===\"not_found\")return json({error:{kind}},404);\n if(kind===\"body_too_large\")return json({error:{kind}},413);\n if([\"busy\",\"message_id_conflict\",\"command_id_conflict\",\"permission_not_pending\",\"executor_offline\",\"executor_not_supported\"].includes(kind))return json({error:{kind}},409);\n if(kind.startsWith(\"invalid_\")||error instanceof SyntaxError||error instanceof URIError)return json({error:{kind:\"invalid\"}},400);\n options.logError?.(error);return json({error:{kind:\"internal\"}},500);\n }\n },\n };\n}\nasync function readBody(request:Request):Promise<string> {\n if(!request.body)return \"{}\";\n const reader=request.body.getReader();let size=0;const chunks:Uint8Array[]=[];\n try{while(true){const {done,value}=await reader.read();if(done)break;size+=value.length;if(size>128_000){await reader.cancel();throw Error(\"body_too_large\");}chunks.push(value);}}finally{reader.releaseLock();}\n const result=new Uint8Array(size);let offset=0;for(const c of chunks){result.set(c,offset);offset+=c.length;}return new TextDecoder().decode(result);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ExternalConversationService } from "@ykurilov/agent-chat-core/external";
|
|
2
|
+
export interface ExternalChatHandlerOptions {
|
|
3
|
+
service: ExternalConversationService;
|
|
4
|
+
basePath?: string;
|
|
5
|
+
resolveUser: (request: Request) => {
|
|
6
|
+
id: string;
|
|
7
|
+
} | null | Promise<{
|
|
8
|
+
id: string;
|
|
9
|
+
} | null>;
|
|
10
|
+
allowedOrigins?: string[];
|
|
11
|
+
pollMs?: number;
|
|
12
|
+
heartbeatMs?: number;
|
|
13
|
+
logError?: (error: unknown) => void;
|
|
14
|
+
}
|
|
15
|
+
/** Version 1 native protocol. Standard fetch handler: mount with Hono's app.all(..., c => handler.fetch(c.req.raw)). */
|
|
16
|
+
export declare function createExternalChatHandler(options: ExternalChatHandlerOptions): {
|
|
17
|
+
fetch(request: Request): Promise<Response>;
|
|
18
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ykurilov/agent-chat-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.84.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -29,6 +29,11 @@
|
|
|
29
29
|
"@ykurilov/agent-chat-source": "./src/node.ts",
|
|
30
30
|
"types": "./dist/types/node.d.ts",
|
|
31
31
|
"import": "./dist/node.js"
|
|
32
|
+
},
|
|
33
|
+
"./external": {
|
|
34
|
+
"@ykurilov/agent-chat-source": "./src/external.ts",
|
|
35
|
+
"types": "./dist/types/external.d.ts",
|
|
36
|
+
"import": "./dist/external.js"
|
|
32
37
|
}
|
|
33
38
|
},
|
|
34
39
|
"engines": {
|
|
@@ -45,7 +50,7 @@
|
|
|
45
50
|
},
|
|
46
51
|
"dependencies": {
|
|
47
52
|
"ai": "7.0.122",
|
|
48
|
-
"@ykurilov/agent-chat-core": "0.
|
|
53
|
+
"@ykurilov/agent-chat-core": "0.84.0"
|
|
49
54
|
},
|
|
50
55
|
"peerDependencies": {
|
|
51
56
|
"hono": "^4.13.10"
|