@ccjr1120/memory-one 0.1.8 → 0.1.10

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.
@@ -37,7 +37,8 @@ const messages = {
37
37
  cancelled: "已取消。",
38
38
  notRunning: "Memory One 未运行。",
39
39
  stopped: "Memory One 已停止。",
40
- updateFailed: "Memory One 更新失败。",
40
+ updateFailed: "Memory One 更新失败,已保留当前版本。",
41
+ updateWaiting: (version) => `新版本 ${version} 尚未在 npm 全部同步,请稍后重试。`,
41
42
  updated: "Memory One 已更新。",
42
43
  status: (pid, value) => `Memory One 正在运行(PID ${pid}):${value}`,
43
44
  opening: "正在打开 Memory One。",
@@ -54,7 +55,8 @@ const messages = {
54
55
  cancelled: "Cancelled.",
55
56
  notRunning: "Memory One is not running.",
56
57
  stopped: "Memory One stopped.",
57
- updateFailed: "Memory One update failed.",
58
+ updateFailed: "Memory One update failed; the current version was preserved.",
59
+ updateWaiting: (version) => `Version ${version} is not fully available from npm yet. Try again later.`,
58
60
  updated: "Memory One updated.",
59
61
  status: (pid, value) => `Memory One is running (PID ${pid}): ${value}`,
60
62
  opening: "Opening Memory One.",
@@ -189,16 +191,23 @@ async function stop() {
189
191
  }
190
192
 
191
193
  async function update() {
192
- const wasRunning = Boolean(currentPid());
193
- if (wasRunning) await stop();
194
194
  const npmCommand = platform() === "win32" ? "npm.cmd" : "npm";
195
- const result = spawnSync(npmCommand, ["install", "--global", "@ccjr1120/memory-one@latest"], { stdio: "inherit" });
196
- if (result.status !== 0) {
197
- if (wasRunning) await start();
198
- throw new Error(copy().updateFailed);
195
+ const versionResult = spawnSync(npmCommand, ["view", "@ccjr1120/memory-one@latest", "version", "--json"], { encoding: "utf8" });
196
+ if (versionResult.status !== 0) throw new Error(copy().updateFailed);
197
+ let latestVersion = "";
198
+ try { latestVersion = String(JSON.parse(versionResult.stdout || '""')).trim(); } catch {}
199
+ if (!latestVersion) throw new Error(copy().updateFailed);
200
+ const packageResult = spawnSync(npmCommand, ["view", `@ccjr1120/memory-one@${latestVersion}`, "dist.tarball", "--json"], { encoding: "utf8" });
201
+ if (packageResult.status !== 0 || !String(packageResult.stdout).trim()) throw new Error(copy().updateWaiting(latestVersion));
202
+
203
+ const wasRunning = Boolean(currentPid());
204
+ const result = spawnSync(npmCommand, ["install", "--global", `@ccjr1120/memory-one@${latestVersion}`], { stdio: "inherit" });
205
+ if (result.status !== 0) throw new Error(copy().updateFailed);
206
+ if (wasRunning) {
207
+ await stop();
208
+ await start();
199
209
  }
200
210
  console.log(copy().updated);
201
- if (wasRunning) await start();
202
211
  }
203
212
 
204
213
  function status() {
package/dist/server.js CHANGED
@@ -23,10 +23,10 @@ const exposeMemory = (memory) => { if (!memory || typeof memory !== "object" ||
23
23
  const exposeMemories = (memories) => memories.map(exposeMemory);
24
24
  const toStorageInput = (input) => { const { scope, ...rest } = input; return { ...rest, scope: storageScope, project: typeof scope === "string" && scope !== "global" ? scope : null }; };
25
25
  const toStoragePatch = (patch) => { const { scope, ...rest } = patch; return "scope" in patch ? { ...rest, project: typeof scope === "string" && scope !== "global" ? scope : null } : rest; };
26
- const agentSystemPrompt = "你是 Memory One 的记忆管家。你通过 MCP 工具管理用户的长期记忆,支持记忆的搜索、读取、保存、更新、删除和反馈。请先理解用户意图,涉及记忆事实时优先调用工具,不要编造记忆。每轮都检查用户是否表达了纠正、偏好、决定、项目约定、个人事实或其他未来有用的信息:有则在最终回复前主动调用 memory_store;如果是在修正已有记忆,先读取并调用 memory_update。只有明显临时、一次性的内容才不保存。删除前必须确认目标唯一;回复使用中文,简洁但可以使用 Markdown。";
26
+ const agentSystemPrompt = "你是 Memory One 的记忆管家,首要职责是结合已读取的相关记忆直接回答用户的问题。你可以主动搜索和读取记忆来提高回答准确性,但不得因为普通对话、提问、纠正回答、顺带提到的偏好或项目细节而新增、更新或删除记忆。只有当用户明确要求‘记住/保存’某项内容、明确要求修改某条记忆,或明确要求‘忘记/删除’某条记忆时,才调用 memory_store、memory_update memory_delete。执行更新或删除前先确认目标唯一,不要编造记忆。回复使用中文,简洁但可以使用 Markdown。";
27
27
  const toolLabels = { memory_get_context: "读取相关上下文", memory_search: "搜索记忆", memory_get: "读取记忆", memory_list: "列出记忆", memory_store: "保存记忆", memory_update: "更新记忆", memory_delete: "删除记忆", memory_feedback: "记录反馈" };
28
28
  const isMemoryOverviewRequest = (message) => /(?:有哪些|所有记忆|全部记忆|列出(?:全部)?|查看(?:全部)?|浏览全部|总结(?:下)?(?:我的)?记忆|总结我的特点|概括我的特点|我的画像|我的偏好和特点|我的记忆(?:有什么)?特点|记忆特点)/.test(message);
29
- function sseEvent(type, payload) { return `event: ${type}\ndata: ${JSON.stringify(payload)}\n\n`; }
29
+ function sseEvent(type, payload, id) { return `${id === undefined ? "" : `id: ${id}\n`}event: ${type}\ndata: ${JSON.stringify(payload)}\n\n`; }
30
30
  async function readSse(response, onEvent) {
31
31
  if (!response.body)
32
32
  throw new Error("provider_empty_stream");
@@ -42,8 +42,10 @@ async function readSse(response, onEvent) {
42
42
  const lines = chunk.split(/\r?\n/);
43
43
  const event = lines.find((line) => line.startsWith("event:"))?.slice(6).trim() ?? "message";
44
44
  const data = lines.filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).join("\n");
45
- if (data)
46
- await onEvent({ event, data });
45
+ if (data && await onEvent({ event, data }) === false) {
46
+ await reader.cancel().catch(() => undefined);
47
+ return;
48
+ }
47
49
  }
48
50
  if (done)
49
51
  break;
@@ -67,7 +69,7 @@ async function openAiRound(config, messages, tools, emit) {
67
69
  const calls = new Map();
68
70
  await readSse(response, ({ data }) => {
69
71
  if (data === "[DONE]")
70
- return;
72
+ return false;
71
73
  const chunk = JSON.parse(data);
72
74
  const delta = chunk.choices?.[0]?.delta;
73
75
  if (delta?.content) {
@@ -126,6 +128,8 @@ async function anthropicRound(config, messages, tools, emit) {
126
128
  const calls = [];
127
129
  let current = null;
128
130
  await readSse(response, ({ event, data }) => {
131
+ if (event === "message_stop")
132
+ return false;
129
133
  const item = JSON.parse(data);
130
134
  if (event === "content_block_start" && item.content_block?.type === "tool_use") {
131
135
  current = { id: item.content_block.id, name: item.content_block.name, arguments: "" };
@@ -415,7 +419,7 @@ const publicDir = join(fileURLToPath(new URL(".", import.meta.url)), "../public"
415
419
  const packageJsonPath = join(fileURLToPath(new URL(".", import.meta.url)), "../package.json");
416
420
  const packageVersion = JSON.parse(readFileSync(packageJsonPath, "utf8")).version;
417
421
  app.register(fastifyStatic, { root: publicDir, prefix: "/" });
418
- for (const frontendRoute of ["/", "/timeline", "/archive", "/preferences", "/scopes", "/tags", "/settings", "/mcp-service"]) {
422
+ for (const frontendRoute of ["/", "/timeline", "/preferences", "/scopes", "/tags", "/settings", "/mcp-service"]) {
419
423
  app.get(frontendRoute, async (_, reply) => reply.sendFile("index.html"));
420
424
  }
421
425
  app.get("/api/memories", async (request) => { const q = request.query; return store.list(q.scope ?? "user", q.project ?? null, Number(q.limit ?? 50)); });
@@ -465,68 +469,257 @@ app.delete("/api/mcp/keys/:id", async (request, reply) => {
465
469
  const { id } = request.params;
466
470
  return { revoked: store.revokeMcpKey(id) };
467
471
  });
468
- /* Agent API removed. */
469
- /*
472
+ function emitExecutionEvent(executionId, type, data, reply) {
473
+ const event = store.appendAgentExecutionEvent(executionId, type, data);
474
+ if (reply && !reply.raw.destroyed)
475
+ reply.raw.write(sseEvent(type, data, event.id));
476
+ return event;
477
+ }
478
+ app.get("/api/agent/config", async () => store.getAgentConfig());
479
+ app.put("/api/agent/config", async (request, reply) => {
480
+ const body = request.body ?? {};
481
+ return reply.send(store.saveAgentConfig(body));
482
+ });
483
+ app.get("/api/agent/messages", async () => store.listAgentMessages());
484
+ app.post("/api/agent/messages", async (request, reply) => {
485
+ const body = request.body;
486
+ if (!body?.id || !body.role || typeof body.content !== "string")
487
+ return reply.code(422).send({ detail: "message_required" });
488
+ return store.saveAgentMessage({ id: body.id, role: body.role, content: body.content, toolCalls: body.toolCalls });
489
+ });
490
+ app.post("/api/agent/executions", async (request, reply) => {
491
+ const body = request.body ?? {};
492
+ if (!body.message?.trim())
493
+ return reply.code(422).send({ detail: "message_required" });
494
+ const userMessageId = randomUUID();
495
+ const assistantMessageId = randomUUID();
496
+ const history = (body.history ?? []).slice(-12);
497
+ store.saveAgentMessage({ id: userMessageId, role: "user", content: body.message });
498
+ store.saveAgentMessage({ id: assistantMessageId, role: "assistant", content: "", toolCalls: [] });
499
+ const execution = store.createAgentExecution({ messageIds: [userMessageId, assistantMessageId] });
500
+ const turn = agentTurnQueue.then(async () => {
501
+ let content = "";
502
+ let toolCalls = [];
503
+ try {
504
+ await runAgent({ ...body, history }, (event) => {
505
+ if (event.type === "delta")
506
+ content += event.text;
507
+ if (event.type === "tool")
508
+ toolCalls = [...toolCalls, event.tool];
509
+ if (event.type === "done")
510
+ toolCalls = event.toolCalls;
511
+ store.saveAgentMessage({ id: assistantMessageId, role: "assistant", content, toolCalls });
512
+ });
513
+ store.updateAgentExecution(execution.id, { status: "completed" });
514
+ }
515
+ catch (error) {
516
+ const errorMessage = error instanceof Error ? error.message : "agent_request_failed";
517
+ store.updateAgentExecution(execution.id, { status: "failed", error: errorMessage });
518
+ emitExecutionEvent(execution.id, "status", { status: "failed", error: errorMessage });
519
+ }
520
+ });
521
+ agentTurnQueue = turn.catch(() => undefined);
522
+ return reply.code(202).send(store.getAgentExecution(execution.id));
523
+ });
524
+ app.get("/api/agent/executions/:executionId/events", async (request, reply) => {
525
+ const { executionId } = request.params;
526
+ if (!store.getAgentExecution(executionId))
527
+ return reply.code(404).send({ detail: "execution_not_found" });
528
+ const header = request.headers["last-event-id"];
529
+ const query = request.query;
530
+ const after = Number(header ?? query.after ?? 0) || 0;
531
+ reply.hijack();
532
+ reply.raw.writeHead(200, { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-cache, no-transform", connection: "keep-alive", "x-accel-buffering": "no" });
533
+ let closed = false;
534
+ request.raw.on("close", () => { closed = true; });
535
+ const send = () => { for (const event of store.listAgentExecutionEvents(executionId, after))
536
+ if (!closed)
537
+ reply.raw.write(sseEvent(event.type, event.data, event.id)); };
538
+ send();
539
+ const timer = setInterval(() => { if (closed) {
540
+ clearInterval(timer);
541
+ return;
542
+ } send(); }, 250);
543
+ request.raw.on("close", () => { clearInterval(timer); if (!reply.raw.destroyed)
544
+ reply.raw.end(); });
545
+ });
546
+ app.get("/api/agent/executions/:executionId", async (request, reply) => {
547
+ const { executionId } = request.params;
548
+ const execution = store.getAgentExecution(executionId);
549
+ return execution ? execution : reply.code(404).send({ detail: "execution_not_found" });
550
+ });
551
+ app.post("/api/agent/executions/:executionId/messages", async (request, reply) => {
552
+ const { executionId } = request.params;
553
+ const execution = store.getAgentExecution(executionId);
554
+ const body = request.body ?? {};
555
+ if (!execution)
556
+ return reply.code(404).send({ detail: "execution_not_found" });
557
+ if (execution.status !== "completed" && execution.status !== "failed" && execution.status !== "cancelled")
558
+ return reply.code(409).send({ detail: "execution_in_progress" });
559
+ if (!body.message?.trim())
560
+ return reply.code(422).send({ detail: "message_required" });
561
+ const userMessageId = randomUUID();
562
+ const assistantMessageId = randomUUID();
563
+ store.saveAgentMessage({ id: userMessageId, role: "user", content: body.message });
564
+ store.saveAgentMessage({ id: assistantMessageId, role: "assistant", content: "", toolCalls: [] });
565
+ store.updateAgentExecution(executionId, { status: "running", messageIds: [...execution.messageIds, userMessageId, assistantMessageId], error: null });
566
+ const history = [...execution.messages, { id: userMessageId, role: "user", content: body.message }].slice(-12).map((message) => ({ role: message.role, content: message.content }));
567
+ const turn = agentTurnQueue.then(async () => {
568
+ let content = "";
569
+ let toolCalls = [];
570
+ try {
571
+ await runAgent({ ...body, history }, (event) => { if (event.type === "delta")
572
+ content += event.text; if (event.type === "tool")
573
+ toolCalls = [...toolCalls, event.tool]; if (event.type === "done")
574
+ toolCalls = event.toolCalls; store.saveAgentMessage({ id: assistantMessageId, role: "assistant", content, toolCalls }); });
575
+ store.updateAgentExecution(executionId, { status: "completed" });
576
+ }
577
+ catch (error) {
578
+ store.updateAgentExecution(executionId, { status: "failed", error: error instanceof Error ? error.message : "agent_request_failed" });
579
+ }
580
+ });
581
+ agentTurnQueue = turn.catch(() => undefined);
582
+ return reply.code(202).send(store.getAgentExecution(executionId));
583
+ });
584
+ app.post("/api/agent/executions/:executionId/cancel", async (request, reply) => {
585
+ const { executionId } = request.params;
586
+ const execution = store.getAgentExecution(executionId);
587
+ if (!execution)
588
+ return reply.code(404).send({ detail: "execution_not_found" });
589
+ if (execution.status === "running") {
590
+ store.updateAgentExecution(executionId, { status: "cancelled" });
591
+ emitExecutionEvent(executionId, "status", { status: "cancelled" });
592
+ }
593
+ return store.getAgentExecution(executionId);
594
+ });
595
+ app.post("/api/agent/executions/:executionId/retry", async (request, reply) => {
596
+ const { executionId } = request.params;
597
+ const execution = store.getAgentExecution(executionId);
598
+ if (!execution)
599
+ return reply.code(404).send({ detail: "execution_not_found" });
600
+ if (execution.status === "running")
601
+ return reply.code(409).send({ detail: "execution_in_progress" });
602
+ const lastUser = [...execution.messages].reverse().find((message) => message.role === "user");
603
+ if (!lastUser)
604
+ return reply.code(422).send({ detail: "message_required" });
605
+ const body = request.body ?? {};
606
+ const messageIds = [...execution.messageIds, randomUUID(), randomUUID()];
607
+ const userMessageId = messageIds.at(-2);
608
+ const assistantMessageId = messageIds.at(-1);
609
+ store.saveAgentMessage({ id: userMessageId, role: "user", content: lastUser.content });
610
+ store.saveAgentMessage({ id: assistantMessageId, role: "assistant", content: "", toolCalls: [] });
611
+ store.updateAgentExecution(executionId, { status: "running", messageIds, error: null });
612
+ const history = [...execution.messages.filter((message) => message.id !== lastUser.id), { role: "user", content: lastUser.content }].slice(-12).map((message) => ({ role: message.role, content: message.content }));
613
+ const turn = agentTurnQueue.then(async () => {
614
+ let content = "";
615
+ let toolCalls = [];
616
+ try {
617
+ await runAgent({ ...body, message: lastUser.content, history }, (event) => { if (event.type === "delta")
618
+ content += event.text; if (event.type === "tool")
619
+ toolCalls = [...toolCalls, event.tool]; if (event.type === "done")
620
+ toolCalls = event.toolCalls; store.saveAgentMessage({ id: assistantMessageId, role: "assistant", content, toolCalls }); });
621
+ store.updateAgentExecution(executionId, { status: "completed" });
622
+ }
623
+ catch (error) {
624
+ store.updateAgentExecution(executionId, { status: "failed", error: error instanceof Error ? error.message : "agent_request_failed" });
625
+ }
626
+ });
627
+ agentTurnQueue = turn.catch(() => undefined);
628
+ return reply.code(202).send(store.getAgentExecution(executionId));
629
+ });
630
+ app.post("/api/agent/chat", async (request, reply) => {
631
+ try {
632
+ let text = "";
633
+ const toolCalls = [];
634
+ await runAgent(request.body ?? {}, (event) => { if (event.type === "delta")
635
+ text += event.text; if (event.type === "done")
636
+ toolCalls.push(...event.toolCalls); });
637
+ return { reply: text, toolCalls };
638
+ }
639
+ catch (error) {
640
+ request.log.error(error);
641
+ return reply.code(500).send({ detail: error instanceof Error ? error.message : "agent_request_failed" });
642
+ }
643
+ });
470
644
  app.post("/api/agent/models", async (request, reply) => {
471
- const body = (request.body as { provider?: string; base_url?: string | null; api_key?: string | null } | undefined) ?? {};
472
- const provider = (body.provider || "").toLowerCase();
473
- if (!["openai", "openai-compatible", "anthropic", "local"].includes(provider)) return reply.code(422).send({ detail: "unsupported_provider" });
474
- const base = (body.base_url?.trim() || "").replace(/\/$/, "");
475
- if (!base) return reply.code(422).send({ detail: "base_url_required" });
476
- const headers: Record<string, string> = { accept: "application/json" };
477
- if (provider === "anthropic") {
478
- headers["anthropic-version"] = "2023-06-01";
479
- if (body.api_key?.trim()) headers["x-api-key"] = body.api_key.trim();
480
- } else if (body.api_key?.trim()) {
481
- headers.authorization = `Bearer ${body.api_key.trim()}`;
482
- }
483
- try {
484
- const response = await fetch(`${base}/models`, { headers });
485
- const text = await response.text();
486
- let payload: any = null;
487
- try { payload = text ? JSON.parse(text) : null; } catch { payload = null; }
488
- if (!response.ok) return reply.code(response.status >= 400 && response.status < 600 ? response.status : 502).send({ detail: payload?.error?.message || payload?.message || text || "model_list_failed" });
489
- const entries: unknown[] = Array.isArray(payload?.data) ? payload.data : Array.isArray(payload) ? payload : [];
490
- const models = [...new Set(entries.map((item: unknown) => typeof item === "string" ? item : (item as { id?: unknown })?.id).filter((item): item is string => typeof item === "string" && item.trim().length > 0))];
491
- return { models };
492
- } catch (error) {
493
- request.log.error(error);
494
- return reply.code(502).send({ detail: "model_list_unreachable" });
495
- }
645
+ const body = request.body ?? {};
646
+ const provider = (body.provider || "").toLowerCase();
647
+ if (!["openai", "openai-compatible", "anthropic", "local"].includes(provider))
648
+ return reply.code(422).send({ detail: "unsupported_provider" });
649
+ const base = (body.base_url?.trim() || "").replace(/\/$/, "");
650
+ if (!base)
651
+ return reply.code(422).send({ detail: "base_url_required" });
652
+ const headers = { accept: "application/json" };
653
+ if (provider === "anthropic") {
654
+ headers["anthropic-version"] = "2023-06-01";
655
+ if (body.api_key?.trim())
656
+ headers["x-api-key"] = body.api_key.trim();
657
+ }
658
+ else if (body.api_key?.trim()) {
659
+ headers.authorization = `Bearer ${body.api_key.trim()}`;
660
+ }
661
+ try {
662
+ const response = await fetch(`${base}/models`, { headers });
663
+ const text = await response.text();
664
+ let payload = null;
665
+ try {
666
+ payload = text ? JSON.parse(text) : null;
667
+ }
668
+ catch {
669
+ payload = null;
670
+ }
671
+ if (!response.ok)
672
+ return reply.code(response.status >= 400 && response.status < 600 ? response.status : 502).send({ detail: payload?.error?.message || payload?.message || text || "model_list_failed" });
673
+ const entries = Array.isArray(payload?.data) ? payload.data : Array.isArray(payload) ? payload : [];
674
+ const models = [...new Set(entries.map((item) => typeof item === "string" ? item : item?.id).filter((item) => typeof item === "string" && item.trim().length > 0))];
675
+ return { models };
676
+ }
677
+ catch (error) {
678
+ request.log.error(error);
679
+ return reply.code(502).send({ detail: "model_list_unreachable" });
680
+ }
496
681
  });
497
- */
498
- /*
499
682
  app.post("/api/agent/stream", async (request, reply) => {
500
- const body = ((request.body as AgentChatRequest | undefined) ?? {});
501
- const userMessageId = body.user_message_id;
502
- const assistantMessageId = body.assistant_message_id;
503
- if (userMessageId && body.message) store.saveAgentMessage({ id: userMessageId, role: "user", content: body.message });
504
- if (assistantMessageId) store.saveAgentMessage({ id: assistantMessageId, role: "assistant", content: "", toolCalls: [] });
505
- let assistantContent = "";
506
- let assistantTools: AgentToolCall[] = [];
507
- reply.hijack();
508
- reply.raw.writeHead(200, { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-cache, no-transform", connection: "keep-alive", "x-accel-buffering": "no" });
509
- const turn = agentTurnQueue.then(async () => {
510
- const allMessages = store.listAgentMessages(1000).filter((item) => item.content);
511
- const currentIndex = userMessageId ? allMessages.findIndex((item) => item.id === userMessageId) : allMessages.length - 1;
512
- const storedHistory = allMessages.slice(0, Math.max(0, currentIndex)).map((item) => ({ role: item.role, content: item.content })).slice(-12);
513
- await runAgent({ ...body, history: storedHistory }, (event) => {
514
- if (event.type === "delta") assistantContent += event.text;
515
- if (event.type === "tool") assistantTools = [...assistantTools, event.tool];
516
- if (event.type === "done") assistantTools = event.toolCalls;
517
- if (assistantMessageId) store.saveAgentMessage({ id: assistantMessageId, role: "assistant", content: assistantContent, toolCalls: assistantTools });
518
- if (!reply.raw.destroyed) reply.raw.write(sseEvent(event.type, event.type === "delta" ? { text: event.text } : event.type === "tool" ? event.tool : { toolCalls: event.toolCalls }));
683
+ const body = (request.body ?? {});
684
+ const userMessageId = body.user_message_id;
685
+ const assistantMessageId = body.assistant_message_id;
686
+ if (userMessageId && body.message)
687
+ store.saveAgentMessage({ id: userMessageId, role: "user", content: body.message });
688
+ if (assistantMessageId)
689
+ store.saveAgentMessage({ id: assistantMessageId, role: "assistant", content: "", toolCalls: [] });
690
+ let assistantContent = "";
691
+ let assistantTools = [];
692
+ reply.hijack();
693
+ reply.raw.writeHead(200, { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-cache, no-transform", connection: "keep-alive", "x-accel-buffering": "no" });
694
+ const turn = agentTurnQueue.then(async () => {
695
+ const allMessages = store.listAgentMessages(1000).filter((item) => item.content);
696
+ const currentIndex = userMessageId ? allMessages.findIndex((item) => item.id === userMessageId) : allMessages.length - 1;
697
+ const storedHistory = allMessages.slice(0, Math.max(0, currentIndex)).map((item) => ({ role: item.role, content: item.content })).slice(-12);
698
+ await runAgent({ ...body, history: storedHistory }, (event) => {
699
+ if (event.type === "delta")
700
+ assistantContent += event.text;
701
+ if (event.type === "tool")
702
+ assistantTools = [...assistantTools, event.tool];
703
+ if (event.type === "done")
704
+ assistantTools = event.toolCalls;
705
+ if (assistantMessageId)
706
+ store.saveAgentMessage({ id: assistantMessageId, role: "assistant", content: assistantContent, toolCalls: assistantTools });
707
+ if (!reply.raw.destroyed)
708
+ reply.raw.write(sseEvent(event.type, event.type === "delta" ? { text: event.text } : event.type === "tool" ? event.tool : { toolCalls: event.toolCalls }));
709
+ });
519
710
  });
520
- });
521
- agentTurnQueue = turn.catch(() => undefined);
522
- try {
523
- await turn;
524
- } catch (error) {
525
- request.log.error(error);
526
- reply.raw.write(sseEvent("error", { detail: error instanceof Error ? error.message : "agent_request_failed" }));
527
- } finally { reply.raw.end(); }
711
+ agentTurnQueue = turn.catch(() => undefined);
712
+ try {
713
+ await turn;
714
+ }
715
+ catch (error) {
716
+ request.log.error(error);
717
+ reply.raw.write(sseEvent("error", { detail: error instanceof Error ? error.message : "agent_request_failed" }));
718
+ }
719
+ finally {
720
+ reply.raw.end();
721
+ }
528
722
  });
529
- */
530
723
  app.get("/api/integrations/codex", async () => getCodexIntegration());
531
724
  app.post("/api/integrations/codex/install", async () => installCodexIntegration());
532
725
  app.get("/api/integrations/codex/mcp", async (request) => {