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,678 +0,0 @@
1
- import { api, flash, streamEvents, getUrlParams, updateUrlParams } from "./utils.js";
2
- import { publish, subscribe } from "./bus.js";
3
- import { saveToCache, loadFromCache } from "./cache.js";
4
- import { CONSTANTS } from "./constants.js";
5
- const logRunIdInput = document.getElementById("log-run-id");
6
- const logTailInput = document.getElementById("log-tail");
7
- const toggleLogStreamButton = document.getElementById("toggle-log-stream");
8
- const showTimestampToggle = document.getElementById("log-show-timestamp");
9
- const showRunToggle = document.getElementById("log-show-run");
10
- const showSummaryToggle = document.getElementById("log-show-summary");
11
- const jumpBottomButton = document.getElementById("log-jump-bottom");
12
- const loadOlderButton = document.getElementById("log-load-older");
13
- let stopLogStream = null;
14
- let lastKnownRunId = null;
15
- let rawLogLines = [];
16
- let autoScrollEnabled = true;
17
- let renderedStartIndex = 0;
18
- let renderedEndIndex = 0;
19
- let isViewingTail = true;
20
- let renderState = null;
21
- let logContexts = [];
22
- let logContextState = { inPromptBlock: false, inDiffBlock: false };
23
- const DOC_CHAT_META_RE = /doc-chat id=[a-f0-9]+ (result=|exit_code=)/i;
24
- const LINE_PATTERNS = {
25
- runStart: /^=== run \d+ start/,
26
- runEnd: /^=== run \d+ end/,
27
- thinking: /^thinking$/i,
28
- thinkingContent: /^\*\*.+\*\*$/,
29
- thinkingMultiline: /^I'm (preparing|planning|considering|reviewing|analyzing|checking|looking|reading|searching)/i,
30
- execStart: /^exec$/i,
31
- execCommand: /^\/bin\/(zsh|bash|sh)\s+-[a-z]+\s+['"]?.+in\s+\//i,
32
- toolLine: /^tool:\s*/i,
33
- exitSimple: /^exit\s+\d+$/i,
34
- errorSimple: /^error:\s+/i,
35
- applyPatch: /^apply_patch\(/i,
36
- fileUpdate: /^file update:?$/i,
37
- fileModified: /^M\s+[\w./]/,
38
- diffGitHeader: /^diff --git /,
39
- diffFileHeader: /^(---|\+\+\+)\s+[ab]\//,
40
- diffIndex: /^index [a-f0-9]+\.\.[a-f0-9]+/,
41
- diffHunk: /^@@\s+-\d+,?\d*\s+\+\d+,?\d*\s+@@/,
42
- promptMarker: /^<(SPEC|WORK_DOCS|TODO|PROGRESS|OPINIONS|TARGET_DOC|RECENT_RUN|SYSTEM|USER|ASSISTANT)>$/,
43
- promptMarkerEnd: /^<\/(SPEC|WORK_DOCS|TODO|PROGRESS|OPINIONS|TARGET_DOC|RECENT_RUN|SYSTEM|USER|ASSISTANT)>$/,
44
- mcpStartup: /^mcp startup:/i,
45
- tokensUsed: /^tokens used/i,
46
- agentOutput: /^Agent:\s*/i,
47
- success: /succeeded in \d+ms/i,
48
- exitCode: /exited \d+ in \d+ms/i,
49
- testOutput: /^(={3,}\s*(test session|.*passed|.*failed)|PASSED|FAILED|ERROR)/i,
50
- pythonTraceback: /^(Traceback \(most recent|File ".*", line \d+|.*Error:)/i,
51
- markdownList: /^- (\[[ x]\]\s)?[A-Z]/,
52
- };
53
- let lastClassificationType = null;
54
- function classifyLine(line, context = { inPromptBlock: false, inDiffBlock: false }) {
55
- const stripped = line
56
- .replace(/^\[[^\]]*]\s*/, "")
57
- .replace(/^(run=\d+\s*)?(stdout|stderr):\s*/, "")
58
- .replace(/^doc-chat id=[a-f0-9]+ stdout:\s*/i, "")
59
- .trim();
60
- if (LINE_PATTERNS.runStart.test(stripped))
61
- return { type: "run-start", priority: 1, resetDiff: true };
62
- if (LINE_PATTERNS.runEnd.test(stripped))
63
- return { type: "run-end", priority: 1, resetDiff: true };
64
- if (LINE_PATTERNS.agentOutput.test(stripped))
65
- return { type: "agent-output", priority: 1 };
66
- if (LINE_PATTERNS.thinking.test(stripped))
67
- return { type: "thinking-label", priority: 2 };
68
- if (LINE_PATTERNS.thinkingContent.test(stripped))
69
- return { type: "thinking", priority: 2 };
70
- if (LINE_PATTERNS.thinkingMultiline.test(stripped))
71
- return { type: "thinking", priority: 2 };
72
- if (LINE_PATTERNS.execStart.test(stripped))
73
- return { type: "exec-label", priority: 3 };
74
- if (LINE_PATTERNS.execCommand.test(stripped))
75
- return { type: "exec-command", priority: 3 };
76
- if (LINE_PATTERNS.toolLine.test(stripped))
77
- return { type: "exec-command", priority: 3 };
78
- if (LINE_PATTERNS.exitSimple.test(stripped))
79
- return { type: "exit-code", priority: 3 };
80
- if (LINE_PATTERNS.errorSimple.test(stripped))
81
- return { type: "error-output", priority: 2 };
82
- if (LINE_PATTERNS.applyPatch.test(stripped))
83
- return { type: "exec-command", priority: 3 };
84
- if (LINE_PATTERNS.fileUpdate.test(stripped))
85
- return { type: "file-update-label", priority: 3, startDiff: true };
86
- if (LINE_PATTERNS.fileModified.test(stripped))
87
- return { type: "file-modified", priority: 3 };
88
- if (LINE_PATTERNS.testOutput.test(stripped))
89
- return { type: "test-output", priority: 3 };
90
- if (LINE_PATTERNS.pythonTraceback.test(stripped))
91
- return { type: "error-output", priority: 2 };
92
- if (LINE_PATTERNS.diffGitHeader.test(stripped))
93
- return { type: "diff-header", priority: 4, startDiff: true };
94
- if (LINE_PATTERNS.diffFileHeader.test(stripped))
95
- return { type: "diff-header", priority: 4 };
96
- if (LINE_PATTERNS.diffIndex.test(stripped))
97
- return { type: "diff-header", priority: 4 };
98
- if (LINE_PATTERNS.diffHunk.test(stripped))
99
- return { type: "diff-hunk", priority: 4 };
100
- if (context.inDiffBlock) {
101
- if (/^\+[^+]/.test(stripped) && !LINE_PATTERNS.markdownList.test(stripped))
102
- return { type: "diff-add", priority: 4 };
103
- if (/^-[^-]/.test(stripped) && !LINE_PATTERNS.markdownList.test(stripped))
104
- return { type: "diff-del", priority: 4 };
105
- }
106
- if (LINE_PATTERNS.promptMarker.test(stripped))
107
- return { type: "prompt-marker", priority: 5 };
108
- if (LINE_PATTERNS.promptMarkerEnd.test(stripped))
109
- return { type: "prompt-marker-end", priority: 5 };
110
- if (LINE_PATTERNS.mcpStartup.test(stripped))
111
- return { type: "system", priority: 6 };
112
- if (LINE_PATTERNS.tokensUsed.test(stripped))
113
- return { type: "tokens", priority: 6 };
114
- if (LINE_PATTERNS.success.test(stripped))
115
- return { type: "success", priority: 3 };
116
- if (LINE_PATTERNS.exitCode.test(stripped))
117
- return { type: "exit-code", priority: 3 };
118
- if (context.inPromptBlock)
119
- return { type: "prompt-context", priority: 5 };
120
- const classification = { type: "output", priority: 4 };
121
- if (lastClassificationType === "exec-label" && stripped.length > 0) {
122
- classification.type = "exec-command";
123
- classification.priority = 3;
124
- }
125
- return classification;
126
- }
127
- function setLastClassificationType(type) {
128
- lastClassificationType = type;
129
- }
130
- function isSummaryMode() {
131
- return !showSummaryToggle || showSummaryToggle.checked;
132
- }
133
- function processLine(line) {
134
- let next = line;
135
- next = next.replace(/^=== run (\d+)\s+chat(\s|$)/, "=== run $1$2");
136
- if (showTimestampToggle && !showTimestampToggle.checked) {
137
- next = next.replace(/^\[[^\]]*]\s*/, "");
138
- }
139
- if (showRunToggle && !showRunToggle.checked) {
140
- if (next.startsWith("[")) {
141
- next = next.replace(/^(\[[^\]]+]\s*)run=\d+\s*/, "$1");
142
- }
143
- else {
144
- next = next.replace(/^run=\d+\s*/, "");
145
- }
146
- }
147
- next = next.replace(/^(\[[^\]]+]\s*)?(run=\d+\s*)?chat:\s*/, "$1$2");
148
- next = next.replace(/^(\[[^\]]+]\s*)?(run=\d+\s*)?(stdout|stderr):\s*/, "$1$2");
149
- next = next.replace(/^(\[[^\]]+]\s*)?(run=\d+\s*)?doc-chat id=[a-f0-9]+ stdout:\s*/i, "$1$2");
150
- return next.trimEnd();
151
- }
152
- function shouldOmitLine(line) {
153
- if (showRunToggle && !showRunToggle.checked && DOC_CHAT_META_RE.test(line)) {
154
- return true;
155
- }
156
- return false;
157
- }
158
- function resetRenderState() {
159
- renderState = {
160
- inPromptBlock: false,
161
- promptBlockDetails: null,
162
- promptBlockContent: null,
163
- promptBlockType: null,
164
- promptLineCount: 0,
165
- inDiffBlock: false,
166
- };
167
- }
168
- function resetLogContexts() {
169
- logContexts = [];
170
- logContextState = { inPromptBlock: false, inDiffBlock: false };
171
- }
172
- function updateLogContextForLine(line) {
173
- logContexts.push({ ...logContextState });
174
- const classification = classifyLine(line, logContextState);
175
- if (classification.startDiff) {
176
- logContextState.inDiffBlock = true;
177
- }
178
- if (classification.resetDiff) {
179
- logContextState.inDiffBlock = false;
180
- }
181
- if (classification.type === "prompt-marker") {
182
- logContextState.inPromptBlock = true;
183
- logContextState.inDiffBlock = false;
184
- }
185
- else if (classification.type === "prompt-marker-end") {
186
- logContextState.inPromptBlock = false;
187
- }
188
- }
189
- function rebuildLogContexts() {
190
- resetLogContexts();
191
- rawLogLines.forEach((line) => updateLogContextForLine(line));
192
- }
193
- function finalizePromptBlock() {
194
- if (!renderState || !renderState.promptBlockDetails)
195
- return;
196
- const countEl = renderState.promptBlockDetails.querySelector(".log-context-count");
197
- if (countEl) {
198
- countEl.textContent = `(${renderState.promptLineCount} lines)`;
199
- }
200
- }
201
- function startPromptBlock(output, label) {
202
- if (!renderState)
203
- return;
204
- renderState.promptBlockType = label;
205
- renderState.promptBlockDetails = document.createElement("details");
206
- renderState.promptBlockDetails.className = "log-context-block";
207
- const summary = document.createElement("summary");
208
- summary.className = "log-context-summary";
209
- summary.innerHTML = `<span class="log-context-icon">▶</span> ${label} <span class="log-context-count"></span>`;
210
- renderState.promptBlockDetails.appendChild(summary);
211
- renderState.promptBlockContent = document.createElement("div");
212
- renderState.promptBlockContent.className = "log-context-content";
213
- renderState.promptBlockDetails.appendChild(renderState.promptBlockContent);
214
- renderState.promptLineCount = 0;
215
- output.appendChild(renderState.promptBlockDetails);
216
- }
217
- function appendRenderedLine(line, output) {
218
- if (!renderState)
219
- resetRenderState();
220
- if (shouldOmitLine(line))
221
- return;
222
- const processed = processLine(line).trimEnd();
223
- const classification = classifyLine(line, renderState);
224
- if (classification.startDiff) {
225
- renderState.inDiffBlock = true;
226
- }
227
- if (classification.resetDiff) {
228
- renderState.inDiffBlock = false;
229
- }
230
- if (classification.type === "prompt-marker") {
231
- renderState.inPromptBlock = true;
232
- renderState.inDiffBlock = false;
233
- const match = processed.match(/<(\w+)>/);
234
- const blockLabel = match ? match[1] : "CONTEXT";
235
- startPromptBlock(output, blockLabel);
236
- return;
237
- }
238
- if (classification.type === "prompt-marker-end") {
239
- finalizePromptBlock();
240
- if (renderState) {
241
- renderState.promptBlockDetails = null;
242
- renderState.promptBlockContent = null;
243
- renderState.promptBlockType = null;
244
- renderState.promptLineCount = 0;
245
- renderState.inPromptBlock = false;
246
- }
247
- return;
248
- }
249
- if (renderState &&
250
- renderState.promptBlockContent &&
251
- renderState.inPromptBlock &&
252
- (classification.type === "prompt-context" || classification.type === "output")) {
253
- const div = document.createElement("div");
254
- div.textContent = processed;
255
- div.className = "log-line log-prompt-context";
256
- renderState.promptBlockContent.appendChild(div);
257
- renderState.promptLineCount++;
258
- return;
259
- }
260
- const isBlank = processed.trim() === "";
261
- const div = document.createElement("div");
262
- div.textContent = processed;
263
- if (isBlank) {
264
- div.className = "log-line log-blank";
265
- }
266
- else {
267
- div.className = `log-line log-${classification.type}`;
268
- div.dataset.logType = classification.type;
269
- div.dataset.priority = String(classification.priority);
270
- }
271
- if (classification.type === "thinking-label" || classification.type === "thinking") {
272
- div.dataset.icon = "💭";
273
- }
274
- else if (classification.type === "exec-label" || classification.type === "exec-command") {
275
- div.dataset.icon = "⚡";
276
- }
277
- else if (classification.type === "file-update-label" ||
278
- classification.type === "file-modified") {
279
- div.dataset.icon = "📝";
280
- }
281
- else if (classification.type === "agent-output") {
282
- div.dataset.icon = "✨";
283
- }
284
- else if (classification.type === "run-start" || classification.type === "run-end") {
285
- div.dataset.icon = "🔄";
286
- }
287
- else if (classification.type === "success") {
288
- div.dataset.icon = "✓";
289
- }
290
- else if (classification.type === "tokens") {
291
- div.dataset.icon = "📊";
292
- }
293
- output.appendChild(div);
294
- }
295
- function trimLogBuffer() {
296
- const maxLines = CONSTANTS.UI.MAX_LOG_LINES_IN_MEMORY;
297
- if (!maxLines || rawLogLines.length <= maxLines)
298
- return;
299
- const overflow = rawLogLines.length - maxLines;
300
- rawLogLines = rawLogLines.slice(overflow);
301
- if (logContexts.length > overflow) {
302
- logContexts = logContexts.slice(overflow);
303
- }
304
- else {
305
- logContexts = [];
306
- }
307
- renderedStartIndex = Math.max(0, renderedStartIndex - overflow);
308
- renderedEndIndex = Math.max(0, renderedEndIndex - overflow);
309
- }
310
- function updateLoadOlderButton() {
311
- if (!loadOlderButton)
312
- return;
313
- if (renderedStartIndex > 0) {
314
- loadOlderButton.classList.remove("hidden");
315
- }
316
- else {
317
- loadOlderButton.classList.add("hidden");
318
- }
319
- }
320
- function applyLogUrlState() {
321
- const params = getUrlParams();
322
- const runId = params.get("run");
323
- const tail = params.get("tail");
324
- const summary = params.get("summary");
325
- if (runId !== null && logRunIdInput) {
326
- logRunIdInput.value = runId;
327
- }
328
- if (tail !== null && logTailInput) {
329
- logTailInput.value = tail;
330
- }
331
- if (summary !== null && showSummaryToggle) {
332
- showSummaryToggle.checked = !(summary === "0" || summary.toLowerCase() === "false");
333
- }
334
- if (runId) {
335
- isViewingTail = false;
336
- }
337
- }
338
- function syncLogUrlState() {
339
- const runId = logRunIdInput?.value?.trim() || "";
340
- const tail = logTailInput?.value?.trim() || "";
341
- updateUrlParams({
342
- run: runId || null,
343
- tail: runId ? null : tail || null,
344
- summary: showSummaryToggle?.checked ? null : "0",
345
- });
346
- }
347
- function renderLogWindow({ startIndex = null, followTail = true } = {}) {
348
- lastClassificationType = null;
349
- const output = document.getElementById("log-output");
350
- if (!output)
351
- return;
352
- if (rawLogLines.length === 0) {
353
- output.innerHTML = "";
354
- output.textContent = "(empty log)";
355
- output.dataset.isPlaceholder = "true";
356
- renderedStartIndex = 0;
357
- renderedEndIndex = 0;
358
- isViewingTail = true;
359
- updateLoadOlderButton();
360
- return;
361
- }
362
- const endIndex = rawLogLines.length;
363
- let windowStart = startIndex;
364
- if (followTail || windowStart === null) {
365
- windowStart = Math.max(0, endIndex - CONSTANTS.UI.MAX_LOG_LINES_IN_DOM);
366
- }
367
- const windowEnd = Math.min(endIndex, windowStart + CONSTANTS.UI.MAX_LOG_LINES_IN_DOM);
368
- output.innerHTML = "";
369
- delete output.dataset.isPlaceholder;
370
- resetRenderState();
371
- const startContext = logContexts[windowStart];
372
- if (startContext && renderState) {
373
- renderState.inPromptBlock = startContext.inPromptBlock;
374
- renderState.inDiffBlock = startContext.inDiffBlock;
375
- if (renderState.inPromptBlock) {
376
- startPromptBlock(output, "CONTEXT (continued)");
377
- }
378
- }
379
- const showSummary = isSummaryMode();
380
- for (let i = windowStart; i < windowEnd; i += 1) {
381
- const line = rawLogLines[i];
382
- const classification = classifyLine(line, renderState || { inPromptBlock: false, inDiffBlock: false });
383
- setLastClassificationType(classification.type);
384
- if (showSummary && classification.priority > 2) {
385
- if (renderState) {
386
- if (classification.startDiff) {
387
- renderState.inDiffBlock = true;
388
- }
389
- if (classification.resetDiff) {
390
- renderState.inDiffBlock = false;
391
- }
392
- if (classification.type === "prompt-marker-end") {
393
- renderState.inPromptBlock = false;
394
- renderState.inDiffBlock = false;
395
- }
396
- else if (classification.type === "prompt-marker") {
397
- renderState.inPromptBlock = true;
398
- renderState.inDiffBlock = false;
399
- }
400
- }
401
- continue;
402
- }
403
- appendRenderedLine(line, output);
404
- }
405
- finalizePromptBlock();
406
- renderedStartIndex = windowStart;
407
- renderedEndIndex = windowEnd;
408
- isViewingTail = followTail && windowEnd === endIndex;
409
- updateLoadOlderButton();
410
- if (isViewingTail) {
411
- scrollLogsToBottom(true);
412
- }
413
- }
414
- function appendLogLine(line) {
415
- const output = document.getElementById("log-output");
416
- if (!output)
417
- return;
418
- if (output.dataset.isPlaceholder === "true") {
419
- output.innerHTML = "";
420
- delete output.dataset.isPlaceholder;
421
- rawLogLines = [];
422
- resetRenderState();
423
- resetLogContexts();
424
- renderedStartIndex = 0;
425
- renderedEndIndex = 0;
426
- isViewingTail = true;
427
- }
428
- rawLogLines.push(line);
429
- updateLogContextForLine(line);
430
- trimLogBuffer();
431
- if (!isViewingTail) {
432
- publish("logs:line", line);
433
- updateLoadOlderButton();
434
- return;
435
- }
436
- const classification = classifyLine(line, renderState || { inPromptBlock: false, inDiffBlock: false });
437
- const showSummary = isSummaryMode();
438
- setLastClassificationType(classification.type);
439
- if (showSummary && classification.priority > 2) {
440
- if (renderState) {
441
- if (classification.type === "prompt-marker-end") {
442
- renderState.inPromptBlock = false;
443
- renderState.inDiffBlock = false;
444
- }
445
- else if (classification.type === "prompt-marker") {
446
- renderState.inPromptBlock = true;
447
- renderState.inDiffBlock = false;
448
- }
449
- }
450
- renderedEndIndex = rawLogLines.length;
451
- if (output.childElementCount > CONSTANTS.UI.MAX_LOG_LINES_IN_DOM) {
452
- output.firstElementChild?.remove();
453
- }
454
- renderedStartIndex = Math.max(0, renderedEndIndex - output.childElementCount);
455
- updateLoadOlderButton();
456
- publish("logs:line", line);
457
- return;
458
- }
459
- appendRenderedLine(line, output);
460
- renderedEndIndex = rawLogLines.length;
461
- if (output.childElementCount > CONSTANTS.UI.MAX_LOG_LINES_IN_DOM) {
462
- output.firstElementChild?.remove();
463
- }
464
- renderedStartIndex = Math.max(0, renderedEndIndex - output.childElementCount);
465
- updateLoadOlderButton();
466
- publish("logs:line", line);
467
- scrollLogsToBottom();
468
- }
469
- function scrollLogsToBottom(force = false) {
470
- const output = document.getElementById("log-output");
471
- if (!output)
472
- return;
473
- if (!autoScrollEnabled && !force)
474
- return;
475
- requestAnimationFrame(() => {
476
- output.scrollTop = output.scrollHeight;
477
- });
478
- }
479
- function updateJumpButtonVisibility() {
480
- const output = document.getElementById("log-output");
481
- if (!output || !jumpBottomButton)
482
- return;
483
- const isNearBottom = output.scrollHeight - output.scrollTop - output.clientHeight < 100;
484
- if (isNearBottom) {
485
- jumpBottomButton.classList.add("hidden");
486
- autoScrollEnabled = true;
487
- }
488
- else {
489
- jumpBottomButton.classList.remove("hidden");
490
- autoScrollEnabled = false;
491
- }
492
- }
493
- function setLogStreamButton(active) {
494
- if (toggleLogStreamButton) {
495
- toggleLogStreamButton.textContent = active ? "Stop stream" : "Start stream";
496
- }
497
- }
498
- async function loadLogs() {
499
- syncLogUrlState();
500
- const runId = logRunIdInput?.value || "";
501
- const tail = logTailInput?.value || "200";
502
- const params = new URLSearchParams();
503
- if (runId) {
504
- params.set("run_id", runId);
505
- }
506
- else if (tail) {
507
- params.set("tail", tail);
508
- }
509
- const path = params.toString()
510
- ? `/api/logs?${params.toString()}`
511
- : "/api/logs";
512
- try {
513
- const data = await api(path);
514
- const text = typeof data === "string" ? data : data.log || "";
515
- const output = document.getElementById("log-output");
516
- if (!output)
517
- return;
518
- if (text) {
519
- rawLogLines = text.split("\n");
520
- trimLogBuffer();
521
- rebuildLogContexts();
522
- delete output.dataset.isPlaceholder;
523
- isViewingTail = true;
524
- renderLogs();
525
- if (!runId) {
526
- const lines = rawLogLines.slice(-200);
527
- saveToCache("logs:tail", lines.join("\n"));
528
- }
529
- }
530
- else {
531
- output.textContent = "(empty log)";
532
- output.dataset.isPlaceholder = "true";
533
- rawLogLines = [];
534
- resetRenderState();
535
- resetLogContexts();
536
- renderedStartIndex = 0;
537
- renderedEndIndex = 0;
538
- isViewingTail = true;
539
- updateLoadOlderButton();
540
- if (!runId) {
541
- saveToCache("logs:tail", "");
542
- }
543
- }
544
- flash("Logs loaded", "success");
545
- publish("logs:loaded", { runId, tail, text });
546
- }
547
- catch (err) {
548
- flash(err.message || "Failed to load logs", "error");
549
- }
550
- }
551
- function stopLogStreaming() {
552
- if (stopLogStream) {
553
- stopLogStream();
554
- stopLogStream = null;
555
- }
556
- setLogStreamButton(false);
557
- publish("logs:streaming", false);
558
- }
559
- function startLogStreaming() {
560
- if (stopLogStream)
561
- return;
562
- const output = document.getElementById("log-output");
563
- if (!output)
564
- return;
565
- output.textContent = "(listening...)";
566
- output.dataset.isPlaceholder = "true";
567
- rawLogLines = [];
568
- resetRenderState();
569
- resetLogContexts();
570
- renderedStartIndex = 0;
571
- renderedEndIndex = 0;
572
- isViewingTail = true;
573
- updateLoadOlderButton();
574
- stopLogStream = streamEvents("/api/logs/stream", {
575
- onMessage: (data) => {
576
- appendLogLine(data || "");
577
- },
578
- onError: (err) => {
579
- flash(err.message || "Stream error", "error");
580
- stopLogStreaming();
581
- },
582
- onFinish: () => {
583
- stopLogStream = null;
584
- setLogStreamButton(false);
585
- publish("logs:streaming", false);
586
- },
587
- });
588
- setLogStreamButton(true);
589
- publish("logs:streaming", true);
590
- flash("Streaming logs…", "info");
591
- }
592
- function syncRunIdPlaceholder(state) {
593
- lastKnownRunId = state?.last_run_id ?? null;
594
- if (logRunIdInput) {
595
- logRunIdInput.placeholder = lastKnownRunId
596
- ? `latest (${lastKnownRunId})`
597
- : "latest";
598
- }
599
- }
600
- function renderLogs() {
601
- renderLogWindow({ followTail: isViewingTail });
602
- }
603
- export function initLogs() {
604
- applyLogUrlState();
605
- const loadLogsButton = document.getElementById("load-logs");
606
- if (loadLogsButton) {
607
- loadLogsButton.addEventListener("click", loadLogs);
608
- }
609
- if (toggleLogStreamButton) {
610
- toggleLogStreamButton.addEventListener("click", () => {
611
- if (stopLogStream) {
612
- stopLogStreaming();
613
- }
614
- else {
615
- startLogStreaming();
616
- }
617
- });
618
- }
619
- subscribe("state:update", syncRunIdPlaceholder);
620
- subscribe("tab:change", (tab) => {
621
- if (tab !== "logs" && stopLogStream) {
622
- stopLogStreaming();
623
- }
624
- });
625
- if (showTimestampToggle) {
626
- showTimestampToggle.addEventListener("change", renderLogs);
627
- }
628
- if (showRunToggle) {
629
- showRunToggle.addEventListener("change", renderLogs);
630
- }
631
- if (showSummaryToggle) {
632
- showSummaryToggle.addEventListener("change", () => {
633
- syncLogUrlState();
634
- renderLogs();
635
- });
636
- }
637
- if (jumpBottomButton) {
638
- jumpBottomButton.addEventListener("click", () => {
639
- if (!isViewingTail) {
640
- isViewingTail = true;
641
- renderLogs();
642
- }
643
- autoScrollEnabled = true;
644
- scrollLogsToBottom(true);
645
- jumpBottomButton.classList.add("hidden");
646
- });
647
- }
648
- if (loadOlderButton) {
649
- loadOlderButton.addEventListener("click", () => {
650
- if (renderedStartIndex <= 0)
651
- return;
652
- const nextStart = Math.max(0, renderedStartIndex - CONSTANTS.UI.LOG_PAGE_SIZE);
653
- isViewingTail = false;
654
- autoScrollEnabled = false;
655
- renderLogWindow({ startIndex: nextStart, followTail: false });
656
- });
657
- }
658
- const output = document.getElementById("log-output");
659
- if (output) {
660
- output.addEventListener("scroll", updateJumpButtonVisibility);
661
- }
662
- const cachedLogs = loadFromCache("logs:tail");
663
- if (cachedLogs) {
664
- const output = document.getElementById("log-output");
665
- if (output) {
666
- rawLogLines = cachedLogs.split("\n");
667
- if (rawLogLines.length > 0) {
668
- trimLogBuffer();
669
- rebuildLogContexts();
670
- delete output.dataset.isPlaceholder;
671
- isViewingTail = true;
672
- renderLogs();
673
- scrollLogsToBottom(true);
674
- }
675
- }
676
- }
677
- loadLogs();
678
- }