@geminixiang/mikan 1.0.2 → 1.2.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.
Files changed (97) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/dist/adapters/discord/context.js +1 -1
  3. package/dist/adapters/discord/context.js.map +1 -1
  4. package/dist/adapters/messages.d.ts +2 -0
  5. package/dist/adapters/messages.d.ts.map +1 -1
  6. package/dist/adapters/messages.js +6 -0
  7. package/dist/adapters/messages.js.map +1 -1
  8. package/dist/adapters/progressive-renderer.d.ts.map +1 -1
  9. package/dist/adapters/progressive-renderer.js +132 -147
  10. package/dist/adapters/progressive-renderer.js.map +1 -1
  11. package/dist/adapters/slack/bot.d.ts +3 -0
  12. package/dist/adapters/slack/bot.d.ts.map +1 -1
  13. package/dist/adapters/slack/bot.js +95 -34
  14. package/dist/adapters/slack/bot.js.map +1 -1
  15. package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
  16. package/dist/adapters/slack/response-lifecycle.js +1 -3
  17. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  18. package/dist/adapters/telegram/context.js +1 -1
  19. package/dist/adapters/telegram/context.js.map +1 -1
  20. package/dist/adapters/types.d.ts +1 -2
  21. package/dist/adapters/types.d.ts.map +1 -1
  22. package/dist/adapters/types.js.map +1 -1
  23. package/dist/env-manifest.js +1 -1
  24. package/dist/env-manifest.js.map +1 -1
  25. package/dist/events/scheduler.js +1 -1
  26. package/dist/events/scheduler.js.map +1 -1
  27. package/dist/harness/execution-env.d.ts.map +1 -1
  28. package/dist/harness/execution-env.js +82 -5
  29. package/dist/harness/execution-env.js.map +1 -1
  30. package/dist/harness/jev.d.ts +12 -11
  31. package/dist/harness/jev.d.ts.map +1 -1
  32. package/dist/harness/jev.js +67 -70
  33. package/dist/harness/jev.js.map +1 -1
  34. package/dist/harness/models.d.ts.map +1 -1
  35. package/dist/harness/models.js +36 -7
  36. package/dist/harness/models.js.map +1 -1
  37. package/dist/harness/presenter.d.ts.map +1 -1
  38. package/dist/harness/presenter.js +22 -47
  39. package/dist/harness/presenter.js.map +1 -1
  40. package/dist/harness/prompt.d.ts +1 -1
  41. package/dist/harness/prompt.d.ts.map +1 -1
  42. package/dist/harness/prompt.js +12 -15
  43. package/dist/harness/prompt.js.map +1 -1
  44. package/dist/harness/runner.d.ts.map +1 -1
  45. package/dist/harness/runner.js +10 -3
  46. package/dist/harness/runner.js.map +1 -1
  47. package/dist/harness/session.d.ts +2 -0
  48. package/dist/harness/session.d.ts.map +1 -1
  49. package/dist/harness/session.js +49 -8
  50. package/dist/harness/session.js.map +1 -1
  51. package/dist/harness/tools/index.d.ts +2 -1
  52. package/dist/harness/tools/index.d.ts.map +1 -1
  53. package/dist/harness/tools/index.js +2 -2
  54. package/dist/harness/tools/index.js.map +1 -1
  55. package/dist/harness/tools/jev-browser.d.ts +2 -1
  56. package/dist/harness/tools/jev-browser.d.ts.map +1 -1
  57. package/dist/harness/tools/jev-browser.js +18 -50
  58. package/dist/harness/tools/jev-browser.js.map +1 -1
  59. package/dist/harness/tools/jev.d.ts.map +1 -1
  60. package/dist/harness/tools/jev.js +2 -2
  61. package/dist/harness/tools/jev.js.map +1 -1
  62. package/dist/harness/tools/react.js +1 -1
  63. package/dist/harness/tools/react.js.map +1 -1
  64. package/dist/harness/tools/task.d.ts +4 -0
  65. package/dist/harness/tools/task.d.ts.map +1 -1
  66. package/dist/harness/tools/task.js +12 -0
  67. package/dist/harness/tools/task.js.map +1 -1
  68. package/dist/harness/types.d.ts +2 -0
  69. package/dist/harness/types.d.ts.map +1 -1
  70. package/dist/harness/types.js.map +1 -1
  71. package/dist/main.js +8 -2
  72. package/dist/main.js.map +1 -1
  73. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  74. package/dist/runtime/conversation-runtime.js +15 -0
  75. package/dist/runtime/conversation-runtime.js.map +1 -1
  76. package/dist/runtime/session-lifecycle.d.ts +1 -0
  77. package/dist/runtime/session-lifecycle.d.ts.map +1 -1
  78. package/dist/runtime/session-lifecycle.js +17 -1
  79. package/dist/runtime/session-lifecycle.js.map +1 -1
  80. package/dist/runtime/types.d.ts +1 -0
  81. package/dist/runtime/types.d.ts.map +1 -1
  82. package/dist/runtime/types.js.map +1 -1
  83. package/dist/sessions/session-store.d.ts +6 -1
  84. package/dist/sessions/session-store.d.ts.map +1 -1
  85. package/dist/sessions/session-store.js +103 -4
  86. package/dist/sessions/session-store.js.map +1 -1
  87. package/dist/sessions/types.d.ts +2 -0
  88. package/dist/sessions/types.d.ts.map +1 -1
  89. package/dist/sessions/types.js.map +1 -1
  90. package/dist/settings/index.d.ts +6 -0
  91. package/dist/settings/index.d.ts.map +1 -1
  92. package/dist/settings/index.js +5 -0
  93. package/dist/settings/index.js.map +1 -1
  94. package/dist/types.d.ts +7 -6
  95. package/dist/types.d.ts.map +1 -1
  96. package/dist/types.js.map +1 -1
  97. package/package.json +7 -8
package/CHANGELOG.md CHANGED
@@ -11,6 +11,79 @@ change in a minor release.
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [1.2.0] - 2026-10-09
15
+
16
+ ### Added
17
+
18
+ - Jev can run on any service that speaks TypeSafe's System One protocol, such as an LLM gateway.
19
+ Set `jev` in `settings.json` to a provider and model, and declare that model in `models.json`
20
+ with `"type": "classifier"` and `"api": "typesafe-system-one"`; mikan sends it to
21
+ `<baseUrl>/systemone` with the provider's key. Without the setting Jev stays on OpenRouter.
22
+
23
+ ### Changed
24
+
25
+ - `jev_browser` writes text for form fields with the conversation's chat model instead of
26
+ `openai/gpt-4o-mini` on OpenRouter, so it no longer needs `OPENROUTER_API_KEY`.
27
+ - Jev decisions go through pi-ai's classifier support instead of `@geminixiang/jev`, which is
28
+ removed. The model, `OPENROUTER_API_KEY`, timeout, and retries stay the same; requests go to
29
+ OpenRouter's System One endpoint, a text state is sent as `{ "text": … }`, score answers no
30
+ longer carry per-level probabilities, and cost is the token count at pi-ai's catalog price.
31
+ - A periodic event run always posts its answer. The `[SILENT]` reply that deleted the message
32
+ is removed, along with the prompt rules that told the agent to use it or to react with eyes
33
+ instead of reporting nothing new, because a job that kept answering `[SILENT]` could stall for
34
+ days without anyone noticing. An event whose own text still asks for `[SILENT]` now posts that
35
+ text; edit the event to say what it should report instead.
36
+
37
+ ### Fixed
38
+
39
+ - A DM task resumes after a restart at most twice in a row. Before, a task whose step crashed
40
+ the daemon was resumed on every restart for up to 24 hours, crashing it again each time; now
41
+ the third interruption stops the task and says so once in its thread.
42
+ - A scheduled event's reply no longer ends with two signatures. The system prompt told the agent
43
+ that event messages start with an `[EVENT:<file>:…]` prefix, which events stopped carrying in
44
+ May, so the agent signed the reply as `[event: unknown]` and mikan added the real signature.
45
+ An event run is now told its event file in the Event Trigger Mode instructions, and a message
46
+ that starts with a typed `[EVENT:` marker is no longer attributed to an event.
47
+
48
+ ## [1.1.0] - 2026-10-08
49
+
50
+ ### Added
51
+
52
+ - A DM task survives a restart. A deploy no longer waits up to five minutes for running
53
+ tasks, and a task interrupted by a deploy or crash in the last 24 hours resumes in its
54
+ thread with a notice. A stopped task stays stopped.
55
+ - A finished task's answer is written to the DM conversation that started it, so a later
56
+ question in the DM can be answered from the result.
57
+
58
+ ### Changed
59
+
60
+ - Pi packages are upgraded to 1.1.0. Retryable "server busy" provider errors are retried, the
61
+ first system prompt leads the model context so tool changes keep the prompt cache, and
62
+ each tool result records how long it ran. The container sandbox implements Pi's new file
63
+ reader in one `docker exec` per read, as before.
64
+ - Embedders: `ConversationResponder` no longer has `appendResponseDelta` or
65
+ `finishResponse`. `replaceResponse` always receives the whole main message, with
66
+ `{ final: true }` on the final write, and a responder sets `showsPartialAnswer` to receive
67
+ the answer while it is written. A responder that implemented the removed methods stops
68
+ receiving streamed text until it handles `replaceResponse` this way.
69
+
70
+ ### Fixed
71
+
72
+ - A task run is no longer offered `task_status` and `start_task`, which made one of three
73
+ test tasks report its own status instead of doing the work.
74
+ - A reply's message no longer flickers between its text and the tool checklist. Text written
75
+ before a tool call stays under the checklist until the next answer replaces it, the
76
+ message is updated at most once a second, and the final answer is written once instead of
77
+ three times. A Slack thread reply that has to change earlier text stops streaming and is
78
+ edited in place instead of deleted and posted again.
79
+ - The `...` that marks a reply still being written is removed when the reply ends, also when
80
+ the final edit is first rejected.
81
+ - A signature the model wrote at the end of its last line is replaced, so a reply no longer
82
+ ends with two `Triggered by` lines.
83
+ - A reply no longer streams with its first words missing. The first text of an answer, and
84
+ sometimes its last, never reached the chat while it streamed, so the message jumped when
85
+ the final version replaced it, and a Slack thread reply was deleted and posted again.
86
+
14
87
  ## [1.0.2] - 2026-10-08
15
88
 
16
89
  ### Changed
@@ -47,7 +47,7 @@ export function createDiscordAdapters(event, bot) {
47
47
  formatContinuation: formatDiscordContinuation,
48
48
  errorPrefix: "*Error:* ",
49
49
  workingIndicator: " ...",
50
- supportsDeltas: true,
50
+ showsPartialAnswer: true,
51
51
  typing: {
52
52
  send: () => bot.sendTyping(channelId),
53
53
  intervalMs: 8000,
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/discord/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ConversationContext } from \"../../types.js\";\nimport { createConversationMessage } from \"../../office/index.js\";\nimport { resolveChatSessionKey } from \"../../sessions/session-key.js\";\nimport { createProgressiveRenderer, formatMarkdownToolResult } from \"../progressive-renderer.js\";\nimport { DISCORD_V2_TEXT_LIMIT } from \"./components.js\";\nimport { formatDiscordMarkdown } from \"./format.js\";\nimport type { DiscordEvent, DiscordResponseBot } from \"./types.js\";\n\nconst MAX_LENGTH = DISCORD_V2_TEXT_LIMIT - 100;\n\nconst formatDiscordContinuation = (partNum: number): string => `*(continued ${partNum})*`;\n\nfunction isDiscordMessageReference(id: string | undefined): id is string {\n return typeof id === \"string\" && id !== \"\" && !id.startsWith(\"event:\");\n}\n\nexport function createDiscordAdapters(\n event: DiscordEvent,\n bot: DiscordResponseBot,\n): ConversationContext {\n const conversationId = event.address.conversationId;\n const channelId = conversationId;\n const threadTargetId = isDiscordMessageReference(event.thread_ts) ? event.thread_ts : undefined;\n const replyTargetId = isDiscordMessageReference(event.ts) ? event.ts : undefined;\n\n const message = createConversationMessage({\n platform: \"discord\",\n conversationId,\n address: event.address,\n id: event.ts,\n sessionKey:\n event.sessionKey ??\n resolveChatSessionKey({\n conversationId,\n conversationKind: event.conversationKind,\n messageId: event.ts,\n persistentTopLevel: true,\n threadTs: event.thread_ts,\n }),\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: event.userName,\n text: event.text,\n attachments: event.attachments,\n threadTs: event.thread_ts,\n });\n\n const platform = bot.getMessagingInfo();\n\n function postFirst(text: string): Promise<string> {\n if (threadTargetId) return bot.postInThread(channelId, threadTargetId, text);\n if (replyTargetId) return bot.postReply(channelId, replyTargetId, text);\n return bot.postMessage(channelId, text);\n }\n\n const { responder } = createProgressiveRenderer({\n label: \"Discord\",\n maxLength: MAX_LENGTH,\n formatContinuation: formatDiscordContinuation,\n errorPrefix: \"*Error:* \",\n workingIndicator: \" ...\",\n supportsDeltas: true,\n typing: {\n send: () => bot.sendTyping(channelId),\n intervalMs: 8000,\n stopOnSend: true,\n },\n prepareSource: (text) => formatDiscordMarkdown(text),\n formatToolResult: formatMarkdownToolResult,\n responseErrorContext: (responseId) => ({\n platform: \"discord\",\n conversationId,\n channelId,\n messageId: message.id,\n sessionKey: message.sessionKey,\n responseMessageId: responseId,\n threadTs: threadTargetId,\n replyTargetId,\n conversationKind: message.conversationKind,\n }),\n post: postFirst,\n update: (id, text) => bot.updateMessageRaw(channelId, id, text),\n postExtra: async (text, responseId) => {\n if (threadTargetId) return bot.postInThread(channelId, threadTargetId, text);\n if (replyTargetId) return bot.postReply(channelId, replyTargetId, text);\n if (responseId !== null) return bot.postReply(channelId, responseId, text);\n return bot.postMessage(channelId, text);\n },\n delete: (id) => bot.deleteMessageRaw(channelId, id),\n logBotResponse: (text, id) =>\n bot.logBotResponse(channelId, text, id, undefined, {\n answer: { replyTo: message.id, sessionKey: message.sessionKey },\n }),\n uploadFile: (filePath, title) => bot.uploadFile(channelId, filePath, title),\n react: replyTargetId ? (emoji) => bot.addReaction(channelId, replyTargetId, emoji) : undefined,\n });\n\n return { address: message.address, message, responder, platform };\n}\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/discord/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ConversationContext } from \"../../types.js\";\nimport { createConversationMessage } from \"../../office/index.js\";\nimport { resolveChatSessionKey } from \"../../sessions/session-key.js\";\nimport { createProgressiveRenderer, formatMarkdownToolResult } from \"../progressive-renderer.js\";\nimport { DISCORD_V2_TEXT_LIMIT } from \"./components.js\";\nimport { formatDiscordMarkdown } from \"./format.js\";\nimport type { DiscordEvent, DiscordResponseBot } from \"./types.js\";\n\nconst MAX_LENGTH = DISCORD_V2_TEXT_LIMIT - 100;\n\nconst formatDiscordContinuation = (partNum: number): string => `*(continued ${partNum})*`;\n\nfunction isDiscordMessageReference(id: string | undefined): id is string {\n return typeof id === \"string\" && id !== \"\" && !id.startsWith(\"event:\");\n}\n\nexport function createDiscordAdapters(\n event: DiscordEvent,\n bot: DiscordResponseBot,\n): ConversationContext {\n const conversationId = event.address.conversationId;\n const channelId = conversationId;\n const threadTargetId = isDiscordMessageReference(event.thread_ts) ? event.thread_ts : undefined;\n const replyTargetId = isDiscordMessageReference(event.ts) ? event.ts : undefined;\n\n const message = createConversationMessage({\n platform: \"discord\",\n conversationId,\n address: event.address,\n id: event.ts,\n sessionKey:\n event.sessionKey ??\n resolveChatSessionKey({\n conversationId,\n conversationKind: event.conversationKind,\n messageId: event.ts,\n persistentTopLevel: true,\n threadTs: event.thread_ts,\n }),\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: event.userName,\n text: event.text,\n attachments: event.attachments,\n threadTs: event.thread_ts,\n });\n\n const platform = bot.getMessagingInfo();\n\n function postFirst(text: string): Promise<string> {\n if (threadTargetId) return bot.postInThread(channelId, threadTargetId, text);\n if (replyTargetId) return bot.postReply(channelId, replyTargetId, text);\n return bot.postMessage(channelId, text);\n }\n\n const { responder } = createProgressiveRenderer({\n label: \"Discord\",\n maxLength: MAX_LENGTH,\n formatContinuation: formatDiscordContinuation,\n errorPrefix: \"*Error:* \",\n workingIndicator: \" ...\",\n showsPartialAnswer: true,\n typing: {\n send: () => bot.sendTyping(channelId),\n intervalMs: 8000,\n stopOnSend: true,\n },\n prepareSource: (text) => formatDiscordMarkdown(text),\n formatToolResult: formatMarkdownToolResult,\n responseErrorContext: (responseId) => ({\n platform: \"discord\",\n conversationId,\n channelId,\n messageId: message.id,\n sessionKey: message.sessionKey,\n responseMessageId: responseId,\n threadTs: threadTargetId,\n replyTargetId,\n conversationKind: message.conversationKind,\n }),\n post: postFirst,\n update: (id, text) => bot.updateMessageRaw(channelId, id, text),\n postExtra: async (text, responseId) => {\n if (threadTargetId) return bot.postInThread(channelId, threadTargetId, text);\n if (replyTargetId) return bot.postReply(channelId, replyTargetId, text);\n if (responseId !== null) return bot.postReply(channelId, responseId, text);\n return bot.postMessage(channelId, text);\n },\n delete: (id) => bot.deleteMessageRaw(channelId, id),\n logBotResponse: (text, id) =>\n bot.logBotResponse(channelId, text, id, undefined, {\n answer: { replyTo: message.id, sessionKey: message.sessionKey },\n }),\n uploadFile: (filePath, title) => bot.uploadFile(channelId, filePath, title),\n react: replyTargetId ? (emoji) => bot.addReaction(channelId, replyTargetId, emoji) : undefined,\n });\n\n return { address: message.address, message, responder, platform };\n}\n"]}
@@ -5,6 +5,8 @@ export declare function formatNothingRunning(source: PlatformSource): string;
5
5
  export declare function formatStopping(source: PlatformSource): string;
6
6
  export declare function formatStopped(source: PlatformSource): string;
7
7
  export declare function formatRestarting(source: PlatformSource): string;
8
+ export declare function formatResumingTask(source: PlatformSource): string;
9
+ export declare function formatTaskResumesExhausted(source: PlatformSource): string;
8
10
  export declare function formatAlreadyWorking(source: PlatformSource, stopCommand: string, options?: {
9
11
  scope?: "thread";
10
12
  }): string;
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/adapters/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/D,eAAO,MAAM,YAAY,UAAU,CAAC;AAEpC,KAAK,cAAc,GAAG,YAAY,GAAG,aAAa,GAAG,MAAM,CAAC;AAoB5D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEnE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAE7D;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAE5D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAK/D;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,QAAQ,CAAA;CAAE,GAC7B,MAAM,CAMR;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAErF"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/adapters/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/D,eAAO,MAAM,YAAY,UAAU,CAAC;AAEpC,KAAK,cAAc,GAAG,YAAY,GAAG,aAAa,GAAG,MAAM,CAAC;AAoB5D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEnE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAE7D;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAE5D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAK/D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAKjE;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAKzE;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,QAAQ,CAAA;CAAE,GAC7B,MAAM,CAMR;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAErF"}
@@ -27,6 +27,12 @@ export function formatStopped(source) {
27
27
  export function formatRestarting(source) {
28
28
  return formatItalic(resolvePlatformName(source), "Restarting for an update; this reply was interrupted. Please send your message again.");
29
29
  }
30
+ export function formatResumingTask(source) {
31
+ return formatItalic(resolvePlatformName(source), "Restarted for an update; continuing this task.");
32
+ }
33
+ export function formatTaskResumesExhausted(source) {
34
+ return formatItalic(resolvePlatformName(source), "Interrupted again after two automatic restarts; not continuing this task. Ask me to continue when you are ready.");
35
+ }
30
36
  export function formatAlreadyWorking(source, stopCommand, options) {
31
37
  const platformName = resolvePlatformName(source);
32
38
  const command = formatCode(platformName, stopCommand);
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/adapters/messages.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AAIpC,SAAS,mBAAmB,CAAC,MAAsB;IACjD,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IAC9C,IAAI,kBAAkB,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC;IACxE,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,SAAS,sBAAsB,CAAC,YAAoB;IAClD,OAAO,YAAY,KAAK,UAAU,CAAC;AACrC,CAAC;AAED,SAAS,YAAY,CAAC,YAAoB,EAAE,IAAY;IACtD,OAAO,sBAAsB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC;AACnE,CAAC;AAED,SAAS,UAAU,CAAC,YAAoB,EAAE,IAAY;IACpD,OAAO,sBAAsB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAsB;IACzD,OAAO,YAAY,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,OAAO,YAAY,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAsB;IAClD,OAAO,YAAY,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAsB;IACrD,OAAO,YAAY,CACjB,mBAAmB,CAAC,MAAM,CAAC,EAC3B,uFAAuF,CACxF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,MAAsB,EACtB,WAAmB,EACnB,OAA8B;IAE9B,MAAM,YAAY,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,UAAU,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IACtD,MAAM,MAAM,GACV,OAAO,EAAE,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC,kBAAkB,CAAC;IACvF,OAAO,YAAY,CAAC,YAAY,EAAE,GAAG,MAAM,SAAS,OAAO,aAAa,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAsB,EAAE,UAAkB;IAC3E,OAAO,YAAY,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,oBAAoB,UAAU,GAAG,CAAC,CAAC;AACtF,CAAC","sourcesContent":["import type { MessagingBot, MessagingInfo } from \"../types.js\";\n\nexport const PRODUCT_NAME = \"mikan\";\n\ntype PlatformSource = MessagingBot | MessagingInfo | string;\n\nfunction resolvePlatformName(source: PlatformSource): string {\n if (typeof source === \"string\") return source;\n if (\"getMessagingInfo\" in source) return source.getMessagingInfo().name;\n return source.name;\n}\n\nfunction supportsHtmlFormatting(platformName: string): boolean {\n return platformName === \"telegram\";\n}\n\nfunction formatItalic(platformName: string, text: string): string {\n return supportsHtmlFormatting(platformName) ? text : `_${text}_`;\n}\n\nfunction formatCode(platformName: string, text: string): string {\n return supportsHtmlFormatting(platformName) ? `<code>${text}</code>` : `\\`${text}\\``;\n}\n\nexport function formatNothingRunning(source: PlatformSource): string {\n return formatItalic(resolvePlatformName(source), \"Nothing running.\");\n}\n\nexport function formatStopping(source: PlatformSource): string {\n return formatItalic(resolvePlatformName(source), \"Stopping…\");\n}\n\nexport function formatStopped(source: PlatformSource): string {\n return formatItalic(resolvePlatformName(source), \"Stopped.\");\n}\n\nexport function formatRestarting(source: PlatformSource): string {\n return formatItalic(\n resolvePlatformName(source),\n \"Restarting for an update; this reply was interrupted. Please send your message again.\",\n );\n}\n\nexport function formatAlreadyWorking(\n source: PlatformSource,\n stopCommand: string,\n options?: { scope?: \"thread\" },\n): string {\n const platformName = resolvePlatformName(source);\n const command = formatCode(platformName, stopCommand);\n const prefix =\n options?.scope === \"thread\" ? \"Already working in this thread.\" : \"Already working.\";\n return formatItalic(platformName, `${prefix} Send ${command} to cancel.`);\n}\n\nexport function formatForceStopped(source: PlatformSource, actorLabel: string): string {\n return formatItalic(resolvePlatformName(source), `Force stopped by ${actorLabel}.`);\n}\n"]}
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/adapters/messages.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AAIpC,SAAS,mBAAmB,CAAC,MAAsB;IACjD,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IAC9C,IAAI,kBAAkB,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC;IACxE,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,SAAS,sBAAsB,CAAC,YAAoB;IAClD,OAAO,YAAY,KAAK,UAAU,CAAC;AACrC,CAAC;AAED,SAAS,YAAY,CAAC,YAAoB,EAAE,IAAY;IACtD,OAAO,sBAAsB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC;AACnE,CAAC;AAED,SAAS,UAAU,CAAC,YAAoB,EAAE,IAAY;IACpD,OAAO,sBAAsB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAsB;IACzD,OAAO,YAAY,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,OAAO,YAAY,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAsB;IAClD,OAAO,YAAY,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAsB;IACrD,OAAO,YAAY,CACjB,mBAAmB,CAAC,MAAM,CAAC,EAC3B,uFAAuF,CACxF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAsB;IACvD,OAAO,YAAY,CACjB,mBAAmB,CAAC,MAAM,CAAC,EAC3B,gDAAgD,CACjD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,MAAsB;IAC/D,OAAO,YAAY,CACjB,mBAAmB,CAAC,MAAM,CAAC,EAC3B,kHAAkH,CACnH,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,MAAsB,EACtB,WAAmB,EACnB,OAA8B;IAE9B,MAAM,YAAY,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,UAAU,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IACtD,MAAM,MAAM,GACV,OAAO,EAAE,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC,kBAAkB,CAAC;IACvF,OAAO,YAAY,CAAC,YAAY,EAAE,GAAG,MAAM,SAAS,OAAO,aAAa,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAsB,EAAE,UAAkB;IAC3E,OAAO,YAAY,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,oBAAoB,UAAU,GAAG,CAAC,CAAC;AACtF,CAAC","sourcesContent":["import type { MessagingBot, MessagingInfo } from \"../types.js\";\n\nexport const PRODUCT_NAME = \"mikan\";\n\ntype PlatformSource = MessagingBot | MessagingInfo | string;\n\nfunction resolvePlatformName(source: PlatformSource): string {\n if (typeof source === \"string\") return source;\n if (\"getMessagingInfo\" in source) return source.getMessagingInfo().name;\n return source.name;\n}\n\nfunction supportsHtmlFormatting(platformName: string): boolean {\n return platformName === \"telegram\";\n}\n\nfunction formatItalic(platformName: string, text: string): string {\n return supportsHtmlFormatting(platformName) ? text : `_${text}_`;\n}\n\nfunction formatCode(platformName: string, text: string): string {\n return supportsHtmlFormatting(platformName) ? `<code>${text}</code>` : `\\`${text}\\``;\n}\n\nexport function formatNothingRunning(source: PlatformSource): string {\n return formatItalic(resolvePlatformName(source), \"Nothing running.\");\n}\n\nexport function formatStopping(source: PlatformSource): string {\n return formatItalic(resolvePlatformName(source), \"Stopping…\");\n}\n\nexport function formatStopped(source: PlatformSource): string {\n return formatItalic(resolvePlatformName(source), \"Stopped.\");\n}\n\nexport function formatRestarting(source: PlatformSource): string {\n return formatItalic(\n resolvePlatformName(source),\n \"Restarting for an update; this reply was interrupted. Please send your message again.\",\n );\n}\n\nexport function formatResumingTask(source: PlatformSource): string {\n return formatItalic(\n resolvePlatformName(source),\n \"Restarted for an update; continuing this task.\",\n );\n}\n\nexport function formatTaskResumesExhausted(source: PlatformSource): string {\n return formatItalic(\n resolvePlatformName(source),\n \"Interrupted again after two automatic restarts; not continuing this task. Ask me to continue when you are ready.\",\n );\n}\n\nexport function formatAlreadyWorking(\n source: PlatformSource,\n stopCommand: string,\n options?: { scope?: \"thread\" },\n): string {\n const platformName = resolvePlatformName(source);\n const command = formatCode(platformName, stopCommand);\n const prefix =\n options?.scope === \"thread\" ? \"Already working in this thread.\" : \"Already working.\";\n return formatItalic(platformName, `${prefix} Send ${command} to cancel.`);\n}\n\nexport function formatForceStopped(source: PlatformSource, actorLabel: string): string {\n return formatItalic(resolvePlatformName(source), `Force stopped by ${actorLabel}.`);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"progressive-renderer.d.ts","sourceRoot":"","sources":["../../src/adapters/progressive-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAA4B,MAAM,aAAa,CAAC;AAGnG,OAAO,KAAK,EAA8B,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAI1F,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CASvE;AAsgBD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,2BAA2B,GAAG;IAChF,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAEA"}
1
+ {"version":3,"file":"progressive-renderer.d.ts","sourceRoot":"","sources":["../../src/adapters/progressive-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EAGtB,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAA8B,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAI1F,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CASvE;AAgeD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,2BAA2B,GAAG;IAChF,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAEA"}
@@ -27,13 +27,13 @@ class ProgressiveRenderer {
27
27
  streamActive: false,
28
28
  streamUnavailable: false,
29
29
  streamedSource: "",
30
- pendingChars: 0,
31
- lastFlushAt: 0,
32
- resetDelta: false,
33
30
  typingInterval: null,
34
31
  typingFailureWarned: false,
35
32
  extraIds: [],
36
33
  continuationIds: [],
34
+ shown: null,
35
+ lastWriteAt: 0,
36
+ flushTimer: null,
37
37
  };
38
38
  this.sanitize = platform.sanitize ?? ((text) => text);
39
39
  this.reportResponseError = platform.responseErrorContext
@@ -45,15 +45,10 @@ class ProgressiveRenderer {
45
45
  createResponder() {
46
46
  return {
47
47
  respond: (text) => this.respond(text),
48
- appendResponseDelta: this.platform.supportsDeltas || this.platform.stream
49
- ? (delta) => this.appendDelta(delta)
50
- : undefined,
51
- finishResponse: this.platform.supportsDeltas || this.platform.stream
52
- ? (finalText) => this.finishResponse(finalText)
53
- : undefined,
48
+ showsPartialAnswer: Boolean(this.platform.showsPartialAnswer || this.platform.stream),
54
49
  replaceResponse: (text, options) => this.replaceResponse(text, options),
55
50
  replaceSubagentProgress: this.platform.formatSubagentProgress
56
- ? (progress, finalText) => this.replaceSubagentProgress(progress, finalText)
51
+ ? (progress, finalText, options) => this.replaceSubagentProgress(progress, finalText, options)
57
52
  : undefined,
58
53
  respondDiagnostic: (text, options) => this.respondDiagnostic(text, options),
59
54
  respondToolResult: (result) => this.respondToolResult(result),
@@ -116,19 +111,6 @@ class ProgressiveRenderer {
116
111
  this.state.streamedSource = "";
117
112
  await this.platform.stream.stop(streamId);
118
113
  }
119
- async abandonNativeStream() {
120
- if (!this.platform.stream || this.state.responseId === null)
121
- return;
122
- const streamId = this.state.responseId;
123
- this.state.streamActive = false;
124
- this.state.streamedSource = "";
125
- await this.platform.stream.stop(streamId).catch(() => undefined);
126
- await this.platform.delete?.(streamId).catch((error) => {
127
- log.logWarning("Could not delete an abandoned stream message", errorMessage(error));
128
- });
129
- if (this.state.responseId === streamId)
130
- this.state.responseId = null;
131
- }
132
114
  async renderRaw(text, operation, options, canonicalText = text) {
133
115
  try {
134
116
  await this.postSplit(text);
@@ -150,77 +132,37 @@ class ProgressiveRenderer {
150
132
  return fallback.text;
151
133
  }
152
134
  }
153
- async renderDelta(text) {
154
- if (this.state.working && !text.trim())
155
- return text;
156
- const prepared = this.platform.prepareSource?.(text, this.state.working) ?? text;
157
- const stream = this.platform.stream;
158
- const display = this.provisional(prepared, this.state.working);
159
- if (!stream || this.state.streamUnavailable) {
160
- await this.renderRaw(display, "render", undefined, prepared);
161
- return prepared;
162
- }
163
- if (this.state.responseId !== null && !this.state.streamActive) {
164
- await this.renderRaw(display, "render", undefined, prepared);
165
- return prepared;
166
- }
167
- if (this.state.responseId !== null &&
168
- this.state.streamActive &&
169
- !prepared.startsWith(this.state.streamedSource)) {
170
- await this.abandonNativeStream();
171
- await this.renderRaw(display, "render", undefined, prepared);
172
- return prepared;
173
- }
174
- try {
175
- if (this.state.responseId !== null) {
176
- const delta = prepared.slice(this.state.streamedSource.length);
177
- if (delta && delta.length >= (stream.minDeltaChars ?? 0)) {
178
- await stream.append(this.state.responseId, delta);
179
- this.state.streamedSource = prepared;
180
- }
181
- return prepared;
182
- }
183
- this.state.responseId = await stream.start(prepared);
184
- this.state.streamActive = true;
185
- this.state.streamedSource = prepared;
186
- return prepared;
187
- }
188
- catch (err) {
189
- this.state.streamUnavailable = true;
190
- log.logWarning("Native response streaming unavailable; falling back to message updates", errorMessage(err));
191
- await this.abandonNativeStream();
192
- await this.renderRaw(display, "render", undefined, prepared);
193
- return prepared;
194
- }
195
- }
196
- async renderFinal(text) {
135
+ async renderFinal(text, options) {
197
136
  const stream = this.platform.stream;
198
137
  if (stream && this.state.streamActive && this.state.responseId !== null) {
199
- let streamed = false;
138
+ const streamId = this.state.responseId;
139
+ const extendsStream = text.startsWith(this.state.streamedSource);
140
+ this.state.streamActive = false;
200
141
  try {
201
- if (text.startsWith(this.state.streamedSource)) {
142
+ if (extendsStream) {
202
143
  const delta = text.slice(this.state.streamedSource.length);
203
144
  if (delta)
204
- await stream.append(this.state.responseId, delta);
205
- await stream.stop(this.state.responseId);
206
- streamed = true;
145
+ await stream.append(streamId, delta);
207
146
  }
147
+ await stream.stop(streamId);
208
148
  }
209
149
  catch (err) {
210
150
  this.state.streamUnavailable = true;
211
151
  log.logWarning("Native response streaming unavailable; falling back to message updates", errorMessage(err));
152
+ await stream.stop(streamId).catch(() => undefined);
153
+ return this.renderRaw(text, "replace", options);
212
154
  }
213
- this.state.streamActive = false;
214
- this.state.streamedSource = "";
215
- if (streamed) {
216
- if (this.platform.needsCanonicalRender?.(text))
217
- await this.renderRaw(text, "render");
218
- return text;
155
+ finally {
156
+ this.state.streamedSource = "";
219
157
  }
220
- await this.abandonNativeStream();
158
+ if (extendsStream && !this.platform.needsCanonicalRender?.(text))
159
+ return text;
160
+ return this.renderRaw(text, "replace", options);
221
161
  }
162
+ if (text === this.state.shown)
163
+ return text;
222
164
  if (this.state.responseId !== null || text)
223
- return this.renderRaw(text, "render");
165
+ return this.renderRaw(text, "replace", options);
224
166
  return text;
225
167
  }
226
168
  async run(label, operation, work, extra) {
@@ -235,43 +177,18 @@ class ProgressiveRenderer {
235
177
  }
236
178
  catch { }
237
179
  }
238
- if (label === "replaceResponse" || label === "finishResponse")
180
+ if (label === "finalResponse")
239
181
  throw err;
240
182
  });
241
183
  this.queueTail = handled.catch(() => undefined);
242
184
  return handled;
243
185
  }
244
- async appendDelta(delta) {
245
- await this.run("appendResponseDelta", "respond", async () => {
246
- if (!delta)
247
- return;
248
- if (this.state.resetDelta) {
249
- this.state.source = "";
250
- this.state.pendingChars = 0;
251
- this.state.resetDelta = false;
252
- }
253
- const sanitized = this.sanitize(delta);
254
- this.state.source += sanitized;
255
- this.state.pendingChars += sanitized.length;
256
- const elapsed = this.now() - this.state.lastFlushAt;
257
- if (this.state.lastFlushAt === 0 ||
258
- (elapsed >= this.flushIntervalMs && this.state.pendingChars > 0)) {
259
- this.state.pendingChars = 0;
260
- try {
261
- this.state.source = await this.renderDelta(this.state.source);
262
- }
263
- finally {
264
- this.state.lastFlushAt = this.now();
265
- }
266
- }
267
- }, () => ({ textLength: delta.length, accumulatedLength: this.state.source.length }));
268
- }
269
186
  async respond(text) {
270
187
  await this.run("respond", "respond", async () => {
271
188
  const sanitized = this.sanitize(text);
272
189
  this.state.source = this.state.source ? `${this.state.source}\n${sanitized}` : sanitized;
273
- this.state.pendingChars = 0;
274
- this.state.source = await this.renderDelta(this.state.source);
190
+ this.cancelFlush();
191
+ await this.renderView();
275
192
  if (this.state.responseId !== null && this.platform.logIntermediateResponses) {
276
193
  this.platform.logBotResponse?.(text, this.state.responseId);
277
194
  }
@@ -281,37 +198,109 @@ class ProgressiveRenderer {
281
198
  accumulatedLength: this.state.source.length,
282
199
  }));
283
200
  }
284
- async finishResponse(finalText) {
285
- await this.run("finishResponse", "set_working", async () => {
286
- if (finalText !== undefined)
287
- this.state.source = this.sanitize(finalText);
288
- this.state.resetDelta = false;
289
- this.state.pendingChars = 0;
290
- this.stopTyping();
291
- this.state.working = false;
292
- this.state.source = await this.renderFinal(this.state.source);
293
- if (this.state.responseId !== null) {
294
- this.platform.logBotResponse?.(this.state.source, this.state.responseId);
295
- }
296
- await this.platform.onFinish?.(this.state.source, this.state.responseId);
297
- }, () => ({ finalTextLength: finalText?.length }));
298
- }
299
201
  async replaceResponse(text, options) {
300
- await this.run("replaceResponse", "replace_response", async () => {
202
+ await this.run(options?.final ? "finalResponse" : "replaceResponse", "replace_response", async () => {
301
203
  this.state.source = this.sanitize(text);
302
- this.state.pendingChars = 0;
303
- this.state.resetDelta = true;
204
+ if (options?.final)
205
+ await this.finish(options);
206
+ else
207
+ await this.show(options);
208
+ }, () => ({
209
+ textLength: text.length,
210
+ hadExistingResponse: Boolean(this.state.responseId),
211
+ final: options?.final === true,
212
+ }));
213
+ }
214
+ async show(options) {
215
+ if (this.state.working && !this.state.source.trim())
216
+ return;
217
+ const wait = this.state.lastWriteAt + this.flushIntervalMs - this.now();
218
+ if (this.state.working && wait > 0) {
219
+ this.scheduleFlush(wait);
220
+ return;
221
+ }
222
+ await this.renderView(options);
223
+ }
224
+ async finish(options) {
225
+ this.stopTyping();
226
+ this.cancelFlush();
227
+ this.state.working = false;
228
+ this.state.source = await this.renderFinal(this.state.source, options);
229
+ this.state.shown = this.state.source;
230
+ this.state.lastWriteAt = this.now();
231
+ if (this.state.responseId !== null) {
232
+ this.platform.logBotResponse?.(this.state.source, this.state.responseId);
233
+ }
234
+ await this.platform.onFinish?.(this.state.source, this.state.responseId);
235
+ }
236
+ scheduleFlush(delayMs) {
237
+ if (this.state.flushTimer !== null)
238
+ return;
239
+ this.state.flushTimer = setTimeout(() => {
240
+ this.state.flushTimer = null;
241
+ void this.run("flushResponse", "replace_response", () => this.renderView(), () => ({ deferred: true }));
242
+ }, delayMs);
243
+ this.state.flushTimer.unref?.();
244
+ }
245
+ cancelFlush() {
246
+ if (this.state.flushTimer === null)
247
+ return;
248
+ clearTimeout(this.state.flushTimer);
249
+ this.state.flushTimer = null;
250
+ }
251
+ async renderView(options) {
252
+ if (this.state.working && !this.state.source.trim())
253
+ return;
254
+ const prepared = this.platform.prepareSource?.(this.state.source, this.state.working) ?? this.state.source;
255
+ const display = this.provisional(prepared, this.state.working);
256
+ if (display === this.state.shown)
257
+ return;
258
+ try {
259
+ await this.writeView(prepared, display, options);
260
+ }
261
+ finally {
262
+ this.state.lastWriteAt = this.now();
263
+ }
264
+ this.state.shown = display;
265
+ }
266
+ async writeView(prepared, display, options) {
267
+ const stream = this.platform.stream;
268
+ const canStream = stream !== undefined &&
269
+ this.state.working &&
270
+ !this.state.streamUnavailable &&
271
+ (this.state.responseId === null ||
272
+ (this.state.streamActive && prepared.startsWith(this.state.streamedSource)));
273
+ try {
274
+ if (canStream && this.state.responseId === null) {
275
+ this.state.responseId = await stream.start(prepared);
276
+ this.state.streamActive = true;
277
+ this.state.streamedSource = prepared;
278
+ }
279
+ else if (canStream && this.state.responseId !== null) {
280
+ const delta = prepared.slice(this.state.streamedSource.length);
281
+ if (delta)
282
+ await stream.append(this.state.responseId, delta);
283
+ this.state.streamedSource = prepared;
284
+ }
285
+ else {
286
+ if (this.state.streamActive)
287
+ await this.stopNativeStream();
288
+ await this.renderRaw(display, "render", options, prepared);
289
+ }
290
+ }
291
+ catch (err) {
292
+ if (!canStream)
293
+ throw err;
294
+ this.state.streamUnavailable = true;
295
+ log.logWarning("Native response streaming unavailable; falling back to message updates", errorMessage(err));
304
296
  if (this.state.streamActive)
305
- await this.stopNativeStream();
306
- if (this.state.working && !this.state.source.trim())
307
- return;
308
- const prepared = this.platform.prepareSource?.(this.state.source, this.state.working) ?? this.state.source;
309
- this.state.source = await this.renderRaw(this.provisional(prepared, this.state.working), "replace", options, prepared);
310
- }, () => ({ textLength: text.length, hadExistingResponse: Boolean(this.state.responseId) }));
297
+ await this.stopNativeStream().catch(() => undefined);
298
+ await this.renderRaw(display, "render", options, prepared);
299
+ }
311
300
  }
312
- async replaceSubagentProgress(progress, finalText) {
301
+ async replaceSubagentProgress(progress, finalText, options) {
313
302
  const dashboard = this.platform.formatSubagentProgress(progress);
314
- await this.responder.replaceResponse(finalText ? `${dashboard}\n\n${finalText}` : dashboard);
303
+ await this.responder.replaceResponse(finalText ? `${dashboard}\n\n${finalText}` : dashboard, options);
315
304
  }
316
305
  async respondDiagnostic(text, options = {}) {
317
306
  await this.run("respondDiagnostic", "respond_diagnostic", async () => {
@@ -353,22 +342,16 @@ class ProgressiveRenderer {
353
342
  async setWorking(working) {
354
343
  await this.run("setWorking", "set_working", async () => {
355
344
  this.state.working = working;
356
- if (!working)
345
+ if (!working) {
357
346
  this.stopTyping();
358
- await this.platform.onWorkingChanged?.(working, this.state.responseId);
359
- if (this.state.responseId === null ||
360
- (!this.platform.workingIndicator && !this.platform.stream)) {
361
- return;
347
+ this.cancelFlush();
362
348
  }
363
- if (working && !this.state.source.trim())
349
+ await this.platform.onWorkingChanged?.(working, this.state.responseId);
350
+ if (this.state.responseId === null)
364
351
  return;
365
- if (this.state.streamActive && !working) {
352
+ if (!working && this.state.streamActive)
366
353
  await this.stopNativeStream();
367
- return;
368
- }
369
- if (this.state.responseId !== null) {
370
- this.state.source = await this.renderRaw(this.provisional(this.state.source, working), "render", undefined, this.state.source);
371
- }
354
+ await this.renderView();
372
355
  }, () => ({ working }));
373
356
  }
374
357
  async uploadFile(filePath, title) {
@@ -384,6 +367,8 @@ class ProgressiveRenderer {
384
367
  async deleteResponse() {
385
368
  await this.run("deleteResponse", "respond", async () => {
386
369
  this.stopTyping();
370
+ this.cancelFlush();
371
+ this.state.shown = null;
387
372
  if (this.state.streamActive)
388
373
  await this.stopNativeStream().catch(() => undefined);
389
374
  for (const id of [...this.state.extraIds, ...this.state.continuationIds]) {
@@ -405,7 +390,7 @@ class ProgressiveRenderer {
405
390
  this.state.streamUnavailable = false;
406
391
  this.state.streamedSource = "";
407
392
  this.state.streamActive = false;
408
- this.state.resetDelta = false;
393
+ this.state.lastWriteAt = 0;
409
394
  this.state.working = true;
410
395
  }, () => ({}));
411
396
  }