codex-autorunner 0.1.2__py3-none-any.whl → 1.1.0__py3-none-any.whl

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 (276) hide show
  1. codex_autorunner/__init__.py +12 -1
  2. codex_autorunner/__main__.py +4 -0
  3. codex_autorunner/agents/codex/harness.py +1 -1
  4. codex_autorunner/agents/opencode/client.py +68 -35
  5. codex_autorunner/agents/opencode/constants.py +3 -0
  6. codex_autorunner/agents/opencode/harness.py +6 -1
  7. codex_autorunner/agents/opencode/logging.py +21 -5
  8. codex_autorunner/agents/opencode/run_prompt.py +1 -0
  9. codex_autorunner/agents/opencode/runtime.py +176 -47
  10. codex_autorunner/agents/opencode/supervisor.py +36 -48
  11. codex_autorunner/agents/registry.py +155 -8
  12. codex_autorunner/api.py +25 -0
  13. codex_autorunner/bootstrap.py +22 -37
  14. codex_autorunner/cli.py +5 -1156
  15. codex_autorunner/codex_cli.py +20 -84
  16. codex_autorunner/core/__init__.py +4 -0
  17. codex_autorunner/core/about_car.py +49 -32
  18. codex_autorunner/core/adapter_utils.py +21 -0
  19. codex_autorunner/core/app_server_ids.py +59 -0
  20. codex_autorunner/core/app_server_logging.py +7 -3
  21. codex_autorunner/core/app_server_prompts.py +27 -260
  22. codex_autorunner/core/app_server_threads.py +26 -28
  23. codex_autorunner/core/app_server_utils.py +165 -0
  24. codex_autorunner/core/archive.py +349 -0
  25. codex_autorunner/core/codex_runner.py +12 -2
  26. codex_autorunner/core/config.py +587 -103
  27. codex_autorunner/core/docs.py +10 -2
  28. codex_autorunner/core/drafts.py +136 -0
  29. codex_autorunner/core/engine.py +1531 -866
  30. codex_autorunner/core/exceptions.py +4 -0
  31. codex_autorunner/core/flows/__init__.py +25 -0
  32. codex_autorunner/core/flows/controller.py +202 -0
  33. codex_autorunner/core/flows/definition.py +82 -0
  34. codex_autorunner/core/flows/models.py +88 -0
  35. codex_autorunner/core/flows/reasons.py +52 -0
  36. codex_autorunner/core/flows/reconciler.py +131 -0
  37. codex_autorunner/core/flows/runtime.py +382 -0
  38. codex_autorunner/core/flows/store.py +568 -0
  39. codex_autorunner/core/flows/transition.py +138 -0
  40. codex_autorunner/core/flows/ux_helpers.py +257 -0
  41. codex_autorunner/core/flows/worker_process.py +242 -0
  42. codex_autorunner/core/git_utils.py +62 -0
  43. codex_autorunner/core/hub.py +136 -16
  44. codex_autorunner/core/locks.py +4 -0
  45. codex_autorunner/core/notifications.py +14 -2
  46. codex_autorunner/core/ports/__init__.py +28 -0
  47. codex_autorunner/core/ports/agent_backend.py +150 -0
  48. codex_autorunner/core/ports/backend_orchestrator.py +41 -0
  49. codex_autorunner/core/ports/run_event.py +91 -0
  50. codex_autorunner/core/prompt.py +15 -7
  51. codex_autorunner/core/redaction.py +29 -0
  52. codex_autorunner/core/review_context.py +5 -8
  53. codex_autorunner/core/run_index.py +6 -0
  54. codex_autorunner/core/runner_process.py +5 -2
  55. codex_autorunner/core/state.py +0 -88
  56. codex_autorunner/core/state_roots.py +57 -0
  57. codex_autorunner/core/supervisor_protocol.py +15 -0
  58. codex_autorunner/core/supervisor_utils.py +67 -0
  59. codex_autorunner/core/text_delta_coalescer.py +54 -0
  60. codex_autorunner/core/ticket_linter_cli.py +201 -0
  61. codex_autorunner/core/ticket_manager_cli.py +432 -0
  62. codex_autorunner/core/update.py +24 -16
  63. codex_autorunner/core/update_paths.py +28 -0
  64. codex_autorunner/core/update_runner.py +2 -0
  65. codex_autorunner/core/usage.py +164 -12
  66. codex_autorunner/core/utils.py +120 -11
  67. codex_autorunner/discovery.py +2 -4
  68. codex_autorunner/flows/review/__init__.py +17 -0
  69. codex_autorunner/{core/review.py → flows/review/service.py} +15 -10
  70. codex_autorunner/flows/ticket_flow/__init__.py +3 -0
  71. codex_autorunner/flows/ticket_flow/definition.py +98 -0
  72. codex_autorunner/integrations/agents/__init__.py +17 -0
  73. codex_autorunner/integrations/agents/backend_orchestrator.py +284 -0
  74. codex_autorunner/integrations/agents/codex_adapter.py +90 -0
  75. codex_autorunner/integrations/agents/codex_backend.py +448 -0
  76. codex_autorunner/integrations/agents/opencode_adapter.py +108 -0
  77. codex_autorunner/integrations/agents/opencode_backend.py +598 -0
  78. codex_autorunner/integrations/agents/runner.py +91 -0
  79. codex_autorunner/integrations/agents/wiring.py +271 -0
  80. codex_autorunner/integrations/app_server/client.py +583 -152
  81. codex_autorunner/integrations/app_server/env.py +2 -107
  82. codex_autorunner/{core/app_server_events.py → integrations/app_server/event_buffer.py} +15 -8
  83. codex_autorunner/integrations/app_server/supervisor.py +59 -33
  84. codex_autorunner/integrations/telegram/adapter.py +204 -165
  85. codex_autorunner/integrations/telegram/api_schemas.py +120 -0
  86. codex_autorunner/integrations/telegram/config.py +221 -0
  87. codex_autorunner/integrations/telegram/constants.py +17 -2
  88. codex_autorunner/integrations/telegram/dispatch.py +17 -0
  89. codex_autorunner/integrations/telegram/doctor.py +47 -0
  90. codex_autorunner/integrations/telegram/handlers/callbacks.py +7 -4
  91. codex_autorunner/integrations/telegram/handlers/commands/__init__.py +2 -0
  92. codex_autorunner/integrations/telegram/handlers/commands/execution.py +53 -57
  93. codex_autorunner/integrations/telegram/handlers/commands/files.py +2 -6
  94. codex_autorunner/integrations/telegram/handlers/commands/flows.py +1364 -0
  95. codex_autorunner/integrations/telegram/handlers/commands/formatting.py +1 -1
  96. codex_autorunner/integrations/telegram/handlers/commands/github.py +41 -582
  97. codex_autorunner/integrations/telegram/handlers/commands/workspace.py +8 -8
  98. codex_autorunner/integrations/telegram/handlers/commands_runtime.py +137 -478
  99. codex_autorunner/integrations/telegram/handlers/commands_spec.py +17 -4
  100. codex_autorunner/integrations/telegram/handlers/messages.py +121 -9
  101. codex_autorunner/integrations/telegram/handlers/selections.py +61 -1
  102. codex_autorunner/integrations/telegram/helpers.py +111 -16
  103. codex_autorunner/integrations/telegram/outbox.py +208 -37
  104. codex_autorunner/integrations/telegram/progress_stream.py +3 -10
  105. codex_autorunner/integrations/telegram/service.py +221 -42
  106. codex_autorunner/integrations/telegram/state.py +100 -2
  107. codex_autorunner/integrations/telegram/ticket_flow_bridge.py +611 -0
  108. codex_autorunner/integrations/telegram/transport.py +39 -4
  109. codex_autorunner/integrations/telegram/trigger_mode.py +53 -0
  110. codex_autorunner/manifest.py +2 -0
  111. codex_autorunner/plugin_api.py +22 -0
  112. codex_autorunner/routes/__init__.py +37 -67
  113. codex_autorunner/routes/agents.py +2 -137
  114. codex_autorunner/routes/analytics.py +3 -0
  115. codex_autorunner/routes/app_server.py +2 -131
  116. codex_autorunner/routes/base.py +2 -624
  117. codex_autorunner/routes/file_chat.py +7 -0
  118. codex_autorunner/routes/flows.py +7 -0
  119. codex_autorunner/routes/messages.py +7 -0
  120. codex_autorunner/routes/repos.py +2 -196
  121. codex_autorunner/routes/review.py +2 -147
  122. codex_autorunner/routes/sessions.py +2 -175
  123. codex_autorunner/routes/settings.py +2 -168
  124. codex_autorunner/routes/shared.py +2 -275
  125. codex_autorunner/routes/system.py +4 -188
  126. codex_autorunner/routes/usage.py +3 -0
  127. codex_autorunner/routes/voice.py +2 -119
  128. codex_autorunner/routes/workspace.py +3 -0
  129. codex_autorunner/server.py +3 -2
  130. codex_autorunner/static/agentControls.js +41 -11
  131. codex_autorunner/static/agentEvents.js +248 -0
  132. codex_autorunner/static/app.js +35 -24
  133. codex_autorunner/static/archive.js +826 -0
  134. codex_autorunner/static/archiveApi.js +37 -0
  135. codex_autorunner/static/autoRefresh.js +36 -8
  136. codex_autorunner/static/bootstrap.js +1 -0
  137. codex_autorunner/static/bus.js +1 -0
  138. codex_autorunner/static/cache.js +1 -0
  139. codex_autorunner/static/constants.js +20 -4
  140. codex_autorunner/static/dashboard.js +344 -325
  141. codex_autorunner/static/diffRenderer.js +37 -0
  142. codex_autorunner/static/docChatCore.js +324 -0
  143. codex_autorunner/static/docChatStorage.js +65 -0
  144. codex_autorunner/static/docChatVoice.js +65 -0
  145. codex_autorunner/static/docEditor.js +133 -0
  146. codex_autorunner/static/env.js +1 -0
  147. codex_autorunner/static/eventSummarizer.js +166 -0
  148. codex_autorunner/static/fileChat.js +182 -0
  149. codex_autorunner/static/health.js +155 -0
  150. codex_autorunner/static/hub.js +126 -185
  151. codex_autorunner/static/index.html +839 -863
  152. codex_autorunner/static/liveUpdates.js +1 -0
  153. codex_autorunner/static/loader.js +1 -0
  154. codex_autorunner/static/messages.js +873 -0
  155. codex_autorunner/static/mobileCompact.js +2 -1
  156. codex_autorunner/static/preserve.js +17 -0
  157. codex_autorunner/static/settings.js +149 -217
  158. codex_autorunner/static/smartRefresh.js +52 -0
  159. codex_autorunner/static/styles.css +8850 -3876
  160. codex_autorunner/static/tabs.js +175 -11
  161. codex_autorunner/static/terminal.js +32 -0
  162. codex_autorunner/static/terminalManager.js +34 -59
  163. codex_autorunner/static/ticketChatActions.js +333 -0
  164. codex_autorunner/static/ticketChatEvents.js +16 -0
  165. codex_autorunner/static/ticketChatStorage.js +16 -0
  166. codex_autorunner/static/ticketChatStream.js +264 -0
  167. codex_autorunner/static/ticketEditor.js +844 -0
  168. codex_autorunner/static/ticketVoice.js +9 -0
  169. codex_autorunner/static/tickets.js +1988 -0
  170. codex_autorunner/static/utils.js +43 -3
  171. codex_autorunner/static/voice.js +1 -0
  172. codex_autorunner/static/workspace.js +765 -0
  173. codex_autorunner/static/workspaceApi.js +53 -0
  174. codex_autorunner/static/workspaceFileBrowser.js +504 -0
  175. codex_autorunner/surfaces/__init__.py +5 -0
  176. codex_autorunner/surfaces/cli/__init__.py +6 -0
  177. codex_autorunner/surfaces/cli/cli.py +1224 -0
  178. codex_autorunner/surfaces/cli/codex_cli.py +20 -0
  179. codex_autorunner/surfaces/telegram/__init__.py +3 -0
  180. codex_autorunner/surfaces/web/__init__.py +1 -0
  181. codex_autorunner/surfaces/web/app.py +2019 -0
  182. codex_autorunner/surfaces/web/hub_jobs.py +192 -0
  183. codex_autorunner/surfaces/web/middleware.py +587 -0
  184. codex_autorunner/surfaces/web/pty_session.py +370 -0
  185. codex_autorunner/surfaces/web/review.py +6 -0
  186. codex_autorunner/surfaces/web/routes/__init__.py +78 -0
  187. codex_autorunner/surfaces/web/routes/agents.py +138 -0
  188. codex_autorunner/surfaces/web/routes/analytics.py +277 -0
  189. codex_autorunner/surfaces/web/routes/app_server.py +132 -0
  190. codex_autorunner/surfaces/web/routes/archive.py +357 -0
  191. codex_autorunner/surfaces/web/routes/base.py +615 -0
  192. codex_autorunner/surfaces/web/routes/file_chat.py +836 -0
  193. codex_autorunner/surfaces/web/routes/flows.py +1164 -0
  194. codex_autorunner/surfaces/web/routes/messages.py +459 -0
  195. codex_autorunner/surfaces/web/routes/repos.py +197 -0
  196. codex_autorunner/surfaces/web/routes/review.py +148 -0
  197. codex_autorunner/surfaces/web/routes/sessions.py +176 -0
  198. codex_autorunner/surfaces/web/routes/settings.py +169 -0
  199. codex_autorunner/surfaces/web/routes/shared.py +280 -0
  200. codex_autorunner/surfaces/web/routes/system.py +196 -0
  201. codex_autorunner/surfaces/web/routes/usage.py +89 -0
  202. codex_autorunner/surfaces/web/routes/voice.py +120 -0
  203. codex_autorunner/surfaces/web/routes/workspace.py +271 -0
  204. codex_autorunner/surfaces/web/runner_manager.py +25 -0
  205. codex_autorunner/surfaces/web/schemas.py +417 -0
  206. codex_autorunner/surfaces/web/static_assets.py +490 -0
  207. codex_autorunner/surfaces/web/static_refresh.py +86 -0
  208. codex_autorunner/surfaces/web/terminal_sessions.py +78 -0
  209. codex_autorunner/tickets/__init__.py +27 -0
  210. codex_autorunner/tickets/agent_pool.py +399 -0
  211. codex_autorunner/tickets/files.py +89 -0
  212. codex_autorunner/tickets/frontmatter.py +55 -0
  213. codex_autorunner/tickets/lint.py +102 -0
  214. codex_autorunner/tickets/models.py +97 -0
  215. codex_autorunner/tickets/outbox.py +244 -0
  216. codex_autorunner/tickets/replies.py +179 -0
  217. codex_autorunner/tickets/runner.py +881 -0
  218. codex_autorunner/tickets/spec_ingest.py +77 -0
  219. codex_autorunner/web/__init__.py +5 -1
  220. codex_autorunner/web/app.py +2 -1771
  221. codex_autorunner/web/hub_jobs.py +2 -191
  222. codex_autorunner/web/middleware.py +2 -587
  223. codex_autorunner/web/pty_session.py +2 -369
  224. codex_autorunner/web/runner_manager.py +2 -24
  225. codex_autorunner/web/schemas.py +2 -396
  226. codex_autorunner/web/static_assets.py +4 -484
  227. codex_autorunner/web/static_refresh.py +2 -85
  228. codex_autorunner/web/terminal_sessions.py +2 -77
  229. codex_autorunner/workspace/__init__.py +40 -0
  230. codex_autorunner/workspace/paths.py +335 -0
  231. codex_autorunner-1.1.0.dist-info/METADATA +154 -0
  232. codex_autorunner-1.1.0.dist-info/RECORD +308 -0
  233. {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.1.0.dist-info}/WHEEL +1 -1
  234. codex_autorunner/agents/execution/policy.py +0 -292
  235. codex_autorunner/agents/factory.py +0 -52
  236. codex_autorunner/agents/orchestrator.py +0 -358
  237. codex_autorunner/core/doc_chat.py +0 -1446
  238. codex_autorunner/core/snapshot.py +0 -580
  239. codex_autorunner/integrations/github/chatops.py +0 -268
  240. codex_autorunner/integrations/github/pr_flow.py +0 -1314
  241. codex_autorunner/routes/docs.py +0 -381
  242. codex_autorunner/routes/github.py +0 -327
  243. codex_autorunner/routes/runs.py +0 -250
  244. codex_autorunner/spec_ingest.py +0 -812
  245. codex_autorunner/static/docChatActions.js +0 -287
  246. codex_autorunner/static/docChatEvents.js +0 -300
  247. codex_autorunner/static/docChatRender.js +0 -205
  248. codex_autorunner/static/docChatStream.js +0 -361
  249. codex_autorunner/static/docs.js +0 -20
  250. codex_autorunner/static/docsClipboard.js +0 -69
  251. codex_autorunner/static/docsCrud.js +0 -257
  252. codex_autorunner/static/docsDocUpdates.js +0 -62
  253. codex_autorunner/static/docsDrafts.js +0 -16
  254. codex_autorunner/static/docsElements.js +0 -69
  255. codex_autorunner/static/docsInit.js +0 -285
  256. codex_autorunner/static/docsParse.js +0 -160
  257. codex_autorunner/static/docsSnapshot.js +0 -87
  258. codex_autorunner/static/docsSpecIngest.js +0 -263
  259. codex_autorunner/static/docsState.js +0 -127
  260. codex_autorunner/static/docsThreadRegistry.js +0 -44
  261. codex_autorunner/static/docsUi.js +0 -153
  262. codex_autorunner/static/docsVoice.js +0 -56
  263. codex_autorunner/static/github.js +0 -504
  264. codex_autorunner/static/logs.js +0 -678
  265. codex_autorunner/static/review.js +0 -157
  266. codex_autorunner/static/runs.js +0 -418
  267. codex_autorunner/static/snapshot.js +0 -124
  268. codex_autorunner/static/state.js +0 -94
  269. codex_autorunner/static/todoPreview.js +0 -27
  270. codex_autorunner/workspace.py +0 -16
  271. codex_autorunner-0.1.2.dist-info/METADATA +0 -249
  272. codex_autorunner-0.1.2.dist-info/RECORD +0 -222
  273. /codex_autorunner/{routes → surfaces/web/routes}/terminal_images.py +0 -0
  274. {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.1.0.dist-info}/entry_points.txt +0 -0
  275. {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.1.0.dist-info}/licenses/LICENSE +0 -0
  276. {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.1.0.dist-info}/top_level.txt +0 -0
@@ -1,205 +0,0 @@
1
- import { flash, statusPill, isMobileViewport } from "./utils.js";
2
- import { chatUI } from "./docsElements.js";
3
- import { CHAT_HISTORY_LIMIT, getActiveDoc, getChatState, getDraft, isDraftPreview, setHistoryNavIndex, } from "./docsState.js";
4
- import { autoResizeTextarea, formatDraftTimestamp, renderDiffHtml, updateDocControls, updateDocVisibility, } from "./docsUi.js";
5
- import { renderChatEvents } from "./docChatEvents.js";
6
- export function updatePatchPreviewFromDraft(draft) {
7
- if (!chatUI.patchBody || !draft || !draft.patch)
8
- return;
9
- chatUI.patchBody.innerHTML = renderDiffHtml(draft.patch);
10
- }
11
- export function renderChat() {
12
- const state = getChatState();
13
- const latest = state.history[0];
14
- const isRunning = state.status === "running";
15
- const hasError = !!state.error;
16
- const pillState = isRunning
17
- ? "running"
18
- : state.status === "error"
19
- ? "error"
20
- : state.status === "interrupted"
21
- ? "interrupted"
22
- : "idle";
23
- if (chatUI.status) {
24
- statusPill(chatUI.status, pillState);
25
- }
26
- if (chatUI.send) {
27
- chatUI.send.disabled = isRunning;
28
- }
29
- if (chatUI.input) {
30
- chatUI.input.disabled = isRunning;
31
- }
32
- if (chatUI.cancel) {
33
- chatUI.cancel.classList.toggle("hidden", !isRunning);
34
- }
35
- if (chatUI.voiceBtn) {
36
- chatUI.voiceBtn.disabled =
37
- isRunning && !chatUI.voiceBtn.classList.contains("voice-retry");
38
- chatUI.voiceBtn.classList.toggle("disabled", chatUI.voiceBtn.disabled);
39
- if (typeof chatUI.voiceBtn.setAttribute === "function") {
40
- chatUI.voiceBtn.setAttribute("aria-disabled", chatUI.voiceBtn.disabled ? "true" : "false");
41
- }
42
- }
43
- if (chatUI.newThread) {
44
- chatUI.newThread.disabled = isRunning;
45
- chatUI.newThread.classList.toggle("disabled", isRunning);
46
- }
47
- if (chatUI.hint) {
48
- if (isRunning) {
49
- const statusText = state.statusText || "processing";
50
- chatUI.hint.textContent = statusText;
51
- chatUI.hint.classList.add("loading");
52
- }
53
- else {
54
- const sendHint = isMobileViewport()
55
- ? "Tap Send to send · Enter for newline"
56
- : "Cmd+Enter / Ctrl+Enter to send · Enter for newline";
57
- chatUI.hint.textContent = sendHint;
58
- chatUI.hint.classList.remove("loading");
59
- }
60
- }
61
- if (hasError) {
62
- chatUI.error.textContent = state.error;
63
- chatUI.error.classList.remove("hidden");
64
- }
65
- else {
66
- chatUI.error.textContent = "";
67
- chatUI.error.classList.add("hidden");
68
- }
69
- const activeDoc = getActiveDoc();
70
- const latestDrafts = latest?.drafts;
71
- const draft = getDraft(activeDoc) || (latestDrafts?.[activeDoc] || null);
72
- const hasPatch = !!(draft && (draft.patch || "").trim());
73
- const previewing = hasPatch && isDraftPreview(activeDoc);
74
- if (chatUI.patchMain) {
75
- chatUI.patchMain.classList.toggle("hidden", !hasPatch);
76
- chatUI.patchMain.classList.toggle("previewing", previewing);
77
- chatUI.patchBody.innerHTML = hasPatch
78
- ? renderDiffHtml(draft.patch)
79
- : "(no draft)";
80
- if (hasPatch) {
81
- chatUI.patchSummary.textContent =
82
- draft?.agentMessage ||
83
- latest?.response ||
84
- state.error ||
85
- "Draft ready";
86
- }
87
- else {
88
- chatUI.patchSummary.textContent = "";
89
- }
90
- if (chatUI.patchMeta) {
91
- const metaParts = [];
92
- if (hasPatch && draft?.createdAt) {
93
- metaParts.push(`drafted ${formatDraftTimestamp(draft.createdAt)}`);
94
- }
95
- if (hasPatch && draft?.baseHash) {
96
- metaParts.push(`base ${draft.baseHash.slice(0, 7)}`);
97
- }
98
- chatUI.patchMeta.textContent = metaParts.join(" · ");
99
- }
100
- if (chatUI.patchApply)
101
- chatUI.patchApply.disabled = isRunning || !hasPatch;
102
- if (chatUI.patchDiscard)
103
- chatUI.patchDiscard.disabled = isRunning || !hasPatch;
104
- if (chatUI.patchReload)
105
- chatUI.patchReload.disabled = isRunning;
106
- if (chatUI.patchPreview) {
107
- chatUI.patchPreview.disabled = isRunning || !hasPatch;
108
- chatUI.patchPreview.textContent = previewing
109
- ? "Hide preview"
110
- : "Preview draft";
111
- chatUI.patchPreview.classList.toggle("active", previewing);
112
- chatUI.patchPreview.setAttribute("aria-pressed", previewing ? "true" : "false");
113
- }
114
- }
115
- updateDocVisibility();
116
- updateDocControls(activeDoc);
117
- renderChatEvents(state);
118
- renderChatHistory(state);
119
- }
120
- export function renderChatHistory(state) {
121
- if (!chatUI.history)
122
- return;
123
- const count = state.history.length;
124
- chatUI.historyCount.textContent = String(count);
125
- chatUI.history.innerHTML = "";
126
- if (count === 0) {
127
- const empty = document.createElement("div");
128
- empty.className = "doc-chat-empty";
129
- empty.textContent = "No messages yet.";
130
- chatUI.history.appendChild(empty);
131
- return;
132
- }
133
- state.history.slice(0, CHAT_HISTORY_LIMIT).forEach((entry) => {
134
- const wrapper = document.createElement("div");
135
- wrapper.className = `doc-chat-entry ${entry.status}`;
136
- const header = document.createElement("div");
137
- header.className = "doc-chat-entry-header";
138
- const promptRow = document.createElement("div");
139
- promptRow.className = "prompt-row";
140
- const prompt = document.createElement("div");
141
- prompt.className = "prompt";
142
- prompt.textContent = entry.prompt || "(no prompt)";
143
- prompt.title = entry.prompt;
144
- const copyBtn = document.createElement("button");
145
- copyBtn.className = "copy-prompt-btn";
146
- copyBtn.title = "Copy to input";
147
- copyBtn.innerHTML = "↑";
148
- copyBtn.addEventListener("click", (e) => {
149
- e.stopPropagation();
150
- if (chatUI.input)
151
- chatUI.input.value = entry.prompt;
152
- autoResizeTextarea(chatUI.input);
153
- chatUI.input?.focus();
154
- setHistoryNavIndex(-1);
155
- flash("Prompt restored to input");
156
- });
157
- promptRow.appendChild(prompt);
158
- promptRow.appendChild(copyBtn);
159
- const meta = document.createElement("div");
160
- meta.className = "meta";
161
- const dot = document.createElement("span");
162
- dot.className = "status-dot";
163
- const stamp = document.createElement("span");
164
- stamp.textContent = entry.time
165
- ? new Date(entry.time).toLocaleTimeString([], {
166
- hour: "2-digit",
167
- minute: "2-digit",
168
- })
169
- : entry.status;
170
- meta.appendChild(dot);
171
- meta.appendChild(stamp);
172
- header.appendChild(promptRow);
173
- header.appendChild(meta);
174
- const response = document.createElement("div");
175
- response.className = "doc-chat-entry-response";
176
- const isLatest = entry === state.history[0];
177
- const runningText = (isLatest && state.streamText) ||
178
- entry.response ||
179
- (isLatest && state.statusText) ||
180
- "queued";
181
- const responseText = entry.error ||
182
- (entry.status === "running" ? runningText : entry.response || "(no response)");
183
- response.textContent = responseText;
184
- response.classList.toggle("streaming", entry.status === "running" && !!(state.streamText || entry.response));
185
- wrapper.appendChild(header);
186
- wrapper.appendChild(response);
187
- const tags = [];
188
- if (entry.viewing) {
189
- tags.push(`Viewing: ${entry.viewing.toUpperCase()}`);
190
- }
191
- else if (entry.targets && entry.targets.length) {
192
- tags.push(`Targets: ${entry.targets.map((k) => k.toUpperCase()).join(", ")}`);
193
- }
194
- if (entry.updated && entry.updated.length) {
195
- tags.push(`Drafts: ${entry.updated.map((k) => k.toUpperCase()).join(", ")}`);
196
- }
197
- if (tags.length) {
198
- const tagLine = document.createElement("div");
199
- tagLine.className = "doc-chat-entry-tags";
200
- tagLine.textContent = tags.join(" · ");
201
- wrapper.appendChild(tagLine);
202
- }
203
- chatUI.history.appendChild(wrapper);
204
- });
205
- }
@@ -1,361 +0,0 @@
1
- import { resolvePath, getAuthToken } from "./utils.js";
2
- import { chatDecoder, getActiveDoc, getChatState, resetChatEvents, } from "./docsState.js";
3
- import { parseChatPayload, parseMaybeJson, recoverDraftMap, recoverPatchFromRaw, } from "./docsParse.js";
4
- import { applyDraftUpdates } from "./docsDrafts.js";
5
- import { renderChat, updatePatchPreviewFromDraft } from "./docChatRender.js";
6
- import { applyAppServerEvent, extractOutputDelta, renderChatEvents } from "./docChatEvents.js";
7
- import { getSelectedAgent, getSelectedModel, getSelectedReasoning, } from "./agentControls.js";
8
- export async function performDocChatRequest(entry, state) {
9
- const endpoint = resolvePath("/api/docs/chat");
10
- const headers = {
11
- "Content-Type": "application/json",
12
- };
13
- const token = getAuthToken();
14
- if (token) {
15
- headers.Authorization = `Bearer ${token}`;
16
- }
17
- const payload = { message: entry.prompt, stream: true };
18
- payload.agent = entry.agent || getSelectedAgent();
19
- const selectedModel = entry.model || getSelectedModel(payload.agent);
20
- const selectedReasoning = entry.reasoning || getSelectedReasoning(payload.agent);
21
- if (selectedModel) {
22
- payload.model = selectedModel;
23
- }
24
- if (selectedReasoning) {
25
- payload.reasoning = selectedReasoning;
26
- }
27
- if (entry.viewing) {
28
- payload.context_doc = entry.viewing;
29
- }
30
- const res = await fetch(endpoint, {
31
- method: "POST",
32
- headers,
33
- body: JSON.stringify(payload),
34
- signal: state.controller.signal,
35
- });
36
- if (!res.ok) {
37
- const text = await res.text();
38
- let detail = text;
39
- try {
40
- const parsed = JSON.parse(text);
41
- const parsedObj = parsed;
42
- detail = parsedObj.detail || parsedObj.error || text;
43
- }
44
- catch (err) {
45
- // ignore parse errors
46
- }
47
- throw new Error(detail || `Request failed (${res.status})`);
48
- }
49
- const contentType = res.headers.get("content-type") || "";
50
- if (contentType.includes("text/event-stream")) {
51
- await readChatStream(res, state, entry);
52
- if (entry.status !== "error" &&
53
- entry.status !== "done" &&
54
- entry.status !== "interrupted") {
55
- entry.status = "done";
56
- }
57
- }
58
- else {
59
- const responsePayload = contentType.includes("application/json")
60
- ? await res.json()
61
- : await res.text();
62
- applyChatResult(responsePayload, state, entry);
63
- }
64
- }
65
- export async function startDocChatEventStream(payload) {
66
- const threadId = payload?.thread_id || payload?.threadId;
67
- const turnId = payload?.turn_id || payload?.turnId;
68
- const agent = payload?.agent || getSelectedAgent();
69
- if (!threadId || !turnId)
70
- return;
71
- const state = getChatState();
72
- if (state.eventTurnId === turnId && state.eventThreadId === threadId) {
73
- return;
74
- }
75
- resetChatEvents(state);
76
- state.eventTurnId = turnId;
77
- state.eventThreadId = threadId;
78
- state.eventAgent = agent;
79
- state.eventController = new AbortController();
80
- renderChatEvents(state);
81
- const endpoint = resolvePath(`/api/agents/${encodeURIComponent(agent)}/turns/${encodeURIComponent(turnId)}/events`);
82
- const url = `${endpoint}?thread_id=${encodeURIComponent(threadId)}`;
83
- const headers = {};
84
- const token = getAuthToken();
85
- if (token)
86
- headers.Authorization = `Bearer ${token}`;
87
- try {
88
- const res = await fetch(url, {
89
- method: "GET",
90
- headers,
91
- signal: state.eventController.signal,
92
- });
93
- if (!res.ok) {
94
- const text = await res.text();
95
- throw new Error(text || `Event stream failed (${res.status})`);
96
- }
97
- const contentType = res.headers.get("content-type") || "";
98
- if (!contentType.includes("text/event-stream")) {
99
- throw new Error("Event stream unavailable");
100
- }
101
- await readAppServerEventStream(res, state);
102
- }
103
- catch (err) {
104
- const error = err;
105
- if (error.name === "AbortError")
106
- return;
107
- state.eventError = error.message || "Failed to stream app-server events";
108
- renderChatEvents(state);
109
- }
110
- }
111
- export async function readAppServerEventStream(res, state) {
112
- if (!res.body)
113
- throw new Error("Streaming not supported in this browser");
114
- const reader = res.body.getReader();
115
- let buffer = "";
116
- let escapedNewlines = false;
117
- for (;;) {
118
- const { value, done } = await reader.read();
119
- if (done)
120
- break;
121
- const decoded = chatDecoder.decode(value, { stream: true });
122
- if (!escapedNewlines) {
123
- const combined = buffer + decoded;
124
- if (!combined.includes("\n") && combined.includes("\\n")) {
125
- escapedNewlines = true;
126
- buffer = buffer.replace(/\\n(?=event:|data:|\\n)/g, "\n");
127
- }
128
- }
129
- buffer += escapedNewlines
130
- ? decoded.replace(/\\n(?=event:|data:|\\n)/g, "\n")
131
- : decoded;
132
- const chunks = buffer.split("\n\n");
133
- buffer = chunks.pop() || "";
134
- for (const chunk of chunks) {
135
- if (!chunk.trim())
136
- continue;
137
- let event = "message";
138
- const dataLines = [];
139
- chunk.split("\n").forEach((line) => {
140
- if (line.startsWith("event:")) {
141
- event = line.slice(6).trim();
142
- }
143
- else if (line.startsWith("data:")) {
144
- dataLines.push(line.slice(5).trimStart());
145
- }
146
- else if (line.trim()) {
147
- dataLines.push(line);
148
- }
149
- });
150
- if (dataLines.length === 0)
151
- continue;
152
- const data = dataLines.join("\n");
153
- await handleAppServerStreamEvent(event || "message", data, state);
154
- }
155
- }
156
- }
157
- async function handleAppServerStreamEvent(_event, rawData, state) {
158
- if (!rawData)
159
- return;
160
- const parsed = parseMaybeJson(rawData);
161
- applyAppServerEvent(state, parsed);
162
- const delta = extractOutputDelta(parsed);
163
- if (delta) {
164
- const entry = state.history[0];
165
- if (entry && entry.status === "running") {
166
- entry.response = (entry.response || "") + delta;
167
- state.streamText = entry.response;
168
- renderChat();
169
- }
170
- }
171
- renderChatEvents(state);
172
- }
173
- export async function readChatStream(res, state, entry) {
174
- if (!res.body)
175
- throw new Error("Streaming not supported in this browser");
176
- const reader = res.body.getReader();
177
- let buffer = "";
178
- let escapedNewlines = false;
179
- for (;;) {
180
- const { value, done } = await reader.read();
181
- if (done)
182
- break;
183
- const decoded = chatDecoder.decode(value, { stream: true });
184
- if (!escapedNewlines) {
185
- const combined = buffer + decoded;
186
- if (!combined.includes("\n") && combined.includes("\\n")) {
187
- escapedNewlines = true;
188
- buffer = buffer.replace(/\\n(?=event:|data:|\\n)/g, "\n");
189
- }
190
- }
191
- buffer += escapedNewlines
192
- ? decoded.replace(/\\n(?=event:|data:|\\n)/g, "\n")
193
- : decoded;
194
- const chunks = buffer.split("\n\n");
195
- buffer = chunks.pop() || "";
196
- for (const chunk of chunks) {
197
- if (!chunk.trim())
198
- continue;
199
- let event = "message";
200
- const dataLines = [];
201
- chunk.split("\n").forEach((line) => {
202
- if (line.startsWith("event:")) {
203
- event = line.slice(6).trim();
204
- }
205
- else if (line.startsWith("data:")) {
206
- dataLines.push(line.slice(5).trimStart());
207
- }
208
- else if (line.trim()) {
209
- dataLines.push(line);
210
- }
211
- });
212
- const data = dataLines.join("\n");
213
- const sanitizedData = data.includes("\n")
214
- ? data.replace(/\n/g, "\\n")
215
- : data;
216
- await handleStreamEvent(event || "message", sanitizedData, state, entry);
217
- }
218
- }
219
- }
220
- export async function handleStreamEvent(event, rawData, state, entry) {
221
- const parsed = parseMaybeJson(rawData);
222
- if (event === "turn") {
223
- void startDocChatEventStream(parsed);
224
- return;
225
- }
226
- if (event === "status") {
227
- state.statusText =
228
- typeof parsed === "string" ? parsed : parsed.status || "";
229
- renderChat();
230
- return;
231
- }
232
- if (event === "token") {
233
- const token = typeof parsed === "string"
234
- ? parsed
235
- : parsed.token || parsed.text || rawData || "";
236
- entry.response = (entry.response || "") + token;
237
- state.streamText = entry.response || "";
238
- if (!state.statusText || state.statusText === "queued") {
239
- state.statusText = "responding";
240
- }
241
- renderChat();
242
- return;
243
- }
244
- if (event === "update") {
245
- const payload = parseChatPayload(parsed);
246
- const fallbackPatch = recoverPatchFromRaw(rawData);
247
- if (fallbackPatch) {
248
- updatePatchPreviewFromDraft({
249
- patch: fallbackPatch,
250
- content: "",
251
- agentMessage: "",
252
- createdAt: "",
253
- baseHash: "",
254
- });
255
- }
256
- if (payload.response) {
257
- entry.response = payload.response;
258
- }
259
- state.streamText = entry.response;
260
- let updated = (payload.updated && payload.updated.length
261
- ? payload.updated
262
- : Object.keys(payload.drafts || {})) || [];
263
- if (!updated.length) {
264
- const recoveredDrafts = recoverDraftMap(rawData);
265
- if (recoveredDrafts) {
266
- updated = Object.keys(recoveredDrafts);
267
- entry.updated = updated;
268
- entry.drafts = recoveredDrafts;
269
- applyDraftUpdates(recoveredDrafts);
270
- updatePatchPreviewFromDraft(recoveredDrafts[getActiveDoc()]);
271
- entry.status = "done";
272
- renderChat();
273
- return;
274
- }
275
- const recoveredPatch = recoverPatchFromRaw(rawData);
276
- if (recoveredPatch) {
277
- const recoveredDraft = {
278
- patch: recoveredPatch,
279
- content: "",
280
- agentMessage: "",
281
- createdAt: "",
282
- baseHash: "",
283
- };
284
- entry.updated = [getActiveDoc()];
285
- entry.drafts = { [getActiveDoc()]: recoveredDraft };
286
- applyDraftUpdates(entry.drafts);
287
- updatePatchPreviewFromDraft(recoveredDraft);
288
- entry.status = "done";
289
- renderChat();
290
- return;
291
- }
292
- }
293
- if (updated.length) {
294
- entry.updated = updated;
295
- entry.drafts = payload.drafts || {};
296
- applyDraftUpdates(payload.drafts);
297
- updatePatchPreviewFromDraft(payload.drafts?.[getActiveDoc()]);
298
- entry.status = "done";
299
- }
300
- renderChat();
301
- return;
302
- }
303
- if (event === "error") {
304
- const message = (parsed && parsed.detail) ||
305
- (parsed && parsed.error) ||
306
- rawData ||
307
- "Doc chat failed";
308
- entry.status = "error";
309
- entry.error = String(message);
310
- state.error = String(message);
311
- state.status = "error";
312
- renderChat();
313
- resetChatEvents(state, { preserve: true });
314
- throw new Error(String(message));
315
- }
316
- if (event === "interrupted") {
317
- const message = (parsed && parsed.detail) || rawData || "Doc chat interrupted";
318
- entry.status = "interrupted";
319
- entry.error = String(message);
320
- state.error = "";
321
- state.status = "interrupted";
322
- state.streamText = entry.response || "";
323
- resetChatEvents(state, { preserve: true });
324
- renderChat();
325
- return;
326
- }
327
- if (event === "done" || event === "finish") {
328
- entry.status = "done";
329
- resetChatEvents(state, { preserve: true });
330
- return;
331
- }
332
- }
333
- export function applyChatResult(payload, state, entry) {
334
- const parsed = parseChatPayload(payload);
335
- if (parsed.interrupted) {
336
- entry.status = "interrupted";
337
- entry.error = parsed.detail || "Doc chat interrupted";
338
- state.status = "interrupted";
339
- state.error = "";
340
- return;
341
- }
342
- if (parsed.error) {
343
- entry.status = "error";
344
- entry.error = parsed.error;
345
- state.error = parsed.error;
346
- state.status = "error";
347
- renderChat();
348
- return;
349
- }
350
- entry.status = "done";
351
- entry.response = parsed.response || "(no response)";
352
- state.streamText = parsed.response || "";
353
- const updated = (parsed.updated && parsed.updated.length
354
- ? parsed.updated
355
- : Object.keys(parsed.drafts || {})) || [];
356
- if (updated.length) {
357
- entry.updated = updated;
358
- entry.drafts = parsed.drafts || {};
359
- applyDraftUpdates(parsed.drafts);
360
- }
361
- }
@@ -1,20 +0,0 @@
1
- import { initDocs } from "./docsInit.js";
2
- import { applyDocUpdateFromChat } from "./docsDocUpdates.js";
3
- import { applyPatch, discardPatch, reloadPatch } from "./docChatActions.js";
4
- import { getChatState } from "./docsState.js";
5
- import { handleStreamEvent, performDocChatRequest, applyChatResult } from "./docChatStream.js";
6
- import { renderChat } from "./docChatRender.js";
7
- import { setDoc } from "./docsCrud.js";
8
- export { initDocs };
9
- export const __docChatTest = {
10
- applyChatResult,
11
- applyDocUpdateFromChat,
12
- applyPatch,
13
- reloadPatch,
14
- discardPatch,
15
- getChatState,
16
- handleStreamEvent,
17
- performDocChatRequest,
18
- renderChat,
19
- setDoc,
20
- };
@@ -1,69 +0,0 @@
1
- import { flash } from "./utils.js";
2
- import { PASTEABLE_DOCS, getActiveDoc, hasDraft, isDraftPreview } from "./docsState.js";
3
- import { getDocTextarea, getDocCopyText, updateDocControls } from "./docsUi.js";
4
- export async function copyDocToClipboard(kind = getActiveDoc()) {
5
- const text = getDocCopyText(kind);
6
- if (!text.trim())
7
- return;
8
- try {
9
- if (navigator.clipboard?.writeText) {
10
- await navigator.clipboard.writeText(text);
11
- flash("Copied to clipboard");
12
- return;
13
- }
14
- }
15
- catch {
16
- // fall through
17
- }
18
- let temp = null;
19
- try {
20
- temp = document.createElement("textarea");
21
- temp.value = text;
22
- temp.setAttribute("readonly", "");
23
- temp.style.position = "fixed";
24
- temp.style.top = "-9999px";
25
- temp.style.opacity = "0";
26
- document.body.appendChild(temp);
27
- temp.select();
28
- const ok = document.execCommand("copy");
29
- flash(ok ? "Copied to clipboard" : "Copy failed");
30
- }
31
- catch {
32
- flash("Copy failed");
33
- }
34
- finally {
35
- if (temp && temp.parentNode) {
36
- temp.parentNode.removeChild(temp);
37
- }
38
- }
39
- }
40
- export async function pasteSpecFromClipboard() {
41
- const activeDoc = getActiveDoc();
42
- if (!PASTEABLE_DOCS.includes(activeDoc))
43
- return;
44
- if (hasDraft(activeDoc) && isDraftPreview(activeDoc)) {
45
- flash("Exit draft preview before pasting.", "error");
46
- return;
47
- }
48
- const textarea = getDocTextarea();
49
- if (!textarea)
50
- return;
51
- try {
52
- if (!navigator.clipboard?.readText) {
53
- flash("Paste not supported in this browser", "error");
54
- return;
55
- }
56
- const text = await navigator.clipboard.readText();
57
- if (!text) {
58
- flash("Clipboard is empty", "error");
59
- return;
60
- }
61
- textarea.value = text;
62
- textarea.focus();
63
- updateDocControls("spec");
64
- flash("SPEC replaced from clipboard");
65
- }
66
- catch {
67
- flash("Paste failed", "error");
68
- }
69
- }