@ykurilov/agent-chat-server 0.84.0 → 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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # @ykurilov/agent-chat-server
2
2
 
3
+ ## 0.85.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1f7a921: Общий нативный чат «Одуванчик» получил Markdown, шапку со статусом исполнителя, карточки разрешений на своём месте в ленте, отметки доставки и одновременные отправку и остановку. Swift value-типы создаются публичными конструкторами. Сервер сохраняет время и позицию первого появления запроса и необязательный фактический ответ внешнего исполнителя; клиент не теряет submitted decision. Если внешний выбор неизвестен, карточка его не угадывает.
8
+
9
+ Replay сохраняет остановленный ответ, одинаковые статусы не дублируют журнал. Занятость имеет машинный code external_conversation_busy с прежним HTTP409/kind busy.
10
+
11
+ ### Как обновить приложение
12
+
13
+ 1. Обновите установленные npm-пакеты SDK одной версией и локальный Swift checkout. Прежние ChatService и приложения без /external изменений не требуют. Новых env и SQL-миграций нет.
14
+ 2. В адаптере внешнего исполнителя передавайте permission_resolved.decision только при известном фактическом resolution: behavior, selectedActionId?, updatedInput?, у deny message?. Проекция и Swift сохраняют его в resolution; decision остаётся намерением SDK. При пропущенном выборе поле отсутствует.
15
+ 3. Используйте AgentChatView(client:responderName:) либо экспортированные части AgentChatOduvanchik вокруг AgentChatScreenModel. Не копируйте сетевое поведение в скин. Для смешанной ленты доступен ChatSnapshot.timeline; старым записям неизвестный порядок не приписывается.
16
+ 4. Проверьте обе темы, Markdown в потоке, Stop с набранным текстом, выбор в SDK и извне, отсутствие выбора после офлайна и восстановление кэша 0.84. Полный контракт и имена компонентов - docs/external-native.md, раздел 0.85.
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies [1f7a921]
21
+ - @ykurilov/agent-chat-core@0.85.0
22
+
3
23
  ## 0.84.0
4
24
 
5
25
  ### Minor Changes
package/dist/external.js CHANGED
@@ -114,7 +114,7 @@ data: ${JSON.stringify(event)}
114
114
  const kind = error instanceof Error ? error.message : "internal";
115
115
  if (kind === "not_found") return json({ error: { kind } }, 404);
116
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);
117
+ if (["busy", "message_id_conflict", "command_id_conflict", "permission_not_pending", "executor_offline", "executor_not_supported"].includes(kind)) return json({ error: { kind, ...kind === "busy" && { code: "external_conversation_busy" } } }, 409);
118
118
  if (kind.startsWith("invalid_") || error instanceof SyntaxError || error instanceof URIError) return json({ error: { kind: "invalid" } }, 400);
119
119
  options.logError?.(error);
120
120
  return json({ error: { kind: "internal" } }, 500);
@@ -1 +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":[]}
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,...(kind===\"busy\"&&{code:\"external_conversation_busy\"})}},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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ykurilov/agent-chat-server",
3
- "version": "0.84.0",
3
+ "version": "0.85.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "ai": "7.0.122",
53
- "@ykurilov/agent-chat-core": "0.84.0"
53
+ "@ykurilov/agent-chat-core": "0.85.0"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "hono": "^4.13.10"