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,160 +0,0 @@
1
- export function parseChatPayload(payload) {
2
- if (!payload)
3
- return { response: "", content: "", patch: "", drafts: {}, updated: [], createdAt: "", baseHash: "", agentMessage: "" };
4
- if (typeof payload === "string")
5
- return { response: payload, content: "", patch: "", drafts: {}, updated: [], createdAt: "", baseHash: "", agentMessage: "" };
6
- const p = payload;
7
- if (p.status && p.status !== "ok") {
8
- if (p.status === "interrupted") {
9
- return {
10
- interrupted: true,
11
- detail: p.detail || "Doc chat interrupted",
12
- response: "", content: "", patch: "", drafts: {}, updated: [], createdAt: "", baseHash: "", agentMessage: ""
13
- };
14
- }
15
- return { error: p.detail || "Doc chat failed", response: "", content: "", patch: "", drafts: {}, updated: [], createdAt: "", baseHash: "", agentMessage: "" };
16
- }
17
- return {
18
- response: p.response ||
19
- p.message ||
20
- p.agent_message ||
21
- p.agentMessage ||
22
- p.content ||
23
- "",
24
- content: p.content || "",
25
- patch: p.patch || "",
26
- drafts: normalizeDraftMap(p.drafts || p.draft),
27
- updated: Array.isArray(p.updated)
28
- ? p.updated.filter((entry) => typeof entry === "string")
29
- : [],
30
- createdAt: p.created_at || p.createdAt || "",
31
- baseHash: p.base_hash || p.baseHash || "",
32
- agentMessage: p.agent_message || p.agentMessage || "",
33
- };
34
- }
35
- export function parseSpecIngestPayload(payload) {
36
- if (!payload || typeof payload !== "object") {
37
- return { error: "Spec ingest failed", todo: "", progress: "", opinions: "", spec: "", summary: "", patch: "", agentMessage: "" };
38
- }
39
- const p = payload;
40
- if (p.status && p.status !== "ok") {
41
- if (p.status === "interrupted") {
42
- return {
43
- interrupted: true,
44
- todo: p.todo || "",
45
- progress: p.progress || "",
46
- opinions: p.opinions || "",
47
- spec: p.spec || "",
48
- summary: p.summary || "",
49
- patch: p.patch || "",
50
- agentMessage: p.agent_message || p.agentMessage || "",
51
- };
52
- }
53
- return { error: p.detail || "Spec ingest failed", todo: "", progress: "", opinions: "", spec: "", summary: "", patch: "", agentMessage: "" };
54
- }
55
- return {
56
- todo: p.todo || "",
57
- progress: p.progress || "",
58
- opinions: p.opinions || "",
59
- spec: p.spec || "",
60
- summary: p.summary || "",
61
- patch: p.patch || "",
62
- agentMessage: p.agent_message || p.agentMessage || "",
63
- };
64
- }
65
- export function parseMaybeJson(raw) {
66
- try {
67
- return JSON.parse(raw);
68
- }
69
- catch (err) {
70
- if (typeof raw === "string" && raw.includes("\n")) {
71
- try {
72
- return JSON.parse(raw.replace(/\n/g, "\\n"));
73
- }
74
- catch (_retryErr) {
75
- // fall through
76
- }
77
- }
78
- if (typeof raw === "string" && raw.includes("\\n")) {
79
- try {
80
- return JSON.parse(raw.replace(/\\n/g, "\\\\n"));
81
- }
82
- catch (_retryErr) {
83
- // fall through
84
- }
85
- }
86
- return raw;
87
- }
88
- }
89
- export function recoverDraftMap(raw) {
90
- if (typeof raw !== "string" || !raw.includes("\"drafts\""))
91
- return null;
92
- const candidates = [
93
- raw,
94
- raw.replace(/\n/g, "\\n"),
95
- raw.replace(/\\n/g, "\n"),
96
- raw.replace(/\\n/g, "\\\\n"),
97
- ];
98
- for (const candidate of candidates) {
99
- try {
100
- const parsed = JSON.parse(candidate);
101
- const drafts = normalizeDraftMap(parsed.drafts || parsed.draft);
102
- if (Object.keys(drafts).length)
103
- return drafts;
104
- }
105
- catch (_err) {
106
- // try next candidate
107
- }
108
- }
109
- return null;
110
- }
111
- export function recoverPatchFromRaw(raw) {
112
- if (typeof raw !== "string" || !raw.includes("\"patch\""))
113
- return null;
114
- const match = raw.match(/"patch"\s*:\s*"([\s\S]*?)"(?:,|\})/);
115
- if (!match)
116
- return null;
117
- return match[1]
118
- .replace(/\\\\n/g, "\n")
119
- .replace(/\\"/g, "\"")
120
- .replace(/\\\\/g, "\\");
121
- }
122
- export function normalizeDraftPayload(payload) {
123
- if (!payload || typeof payload !== "object")
124
- return null;
125
- const p = payload;
126
- const content = typeof p.content === "string" ? p.content : "";
127
- const patch = typeof p.patch === "string" ? p.patch : "";
128
- if (!content && !patch)
129
- return null;
130
- return {
131
- content,
132
- patch,
133
- agentMessage: typeof p.agent_message === "string"
134
- ? p.agent_message
135
- : typeof p.agentMessage === "string"
136
- ? p.agentMessage
137
- : "",
138
- createdAt: typeof p.created_at === "string"
139
- ? p.created_at
140
- : typeof p.createdAt === "string"
141
- ? p.createdAt
142
- : "",
143
- baseHash: typeof p.base_hash === "string"
144
- ? p.base_hash
145
- : typeof p.baseHash === "string"
146
- ? p.baseHash
147
- : "",
148
- };
149
- }
150
- export function normalizeDraftMap(raw) {
151
- if (!raw || typeof raw !== "object")
152
- return {};
153
- const drafts = {};
154
- Object.entries(raw).forEach(([kind, entry]) => {
155
- const normalized = normalizeDraftPayload(entry);
156
- if (normalized)
157
- drafts[kind] = normalized;
158
- });
159
- return drafts;
160
- }
@@ -1,87 +0,0 @@
1
- import { api, flash } from "./utils.js";
2
- import { snapshotUI } from "./docsElements.js";
3
- import { docsState, getActiveDoc } from "./docsState.js";
4
- import { getDocTextarea, updateCopyButton, getDocCopyText } from "./docsUi.js";
5
- export function setSnapshotBusy(on) {
6
- docsState.snapshotBusy = on;
7
- const disabled = !!on;
8
- for (const btn of [
9
- snapshotUI.generate,
10
- snapshotUI.update,
11
- snapshotUI.regenerate,
12
- snapshotUI.refresh,
13
- ]) {
14
- if (btn)
15
- btn.disabled = disabled;
16
- }
17
- updateCopyButton(snapshotUI.copy, getDocCopyText("snapshot"), disabled);
18
- const statusEl = document.getElementById("doc-status");
19
- if (statusEl && getActiveDoc() === "snapshot") {
20
- statusEl.textContent = on ? "Working…" : "Viewing SNAPSHOT";
21
- }
22
- }
23
- export function renderSnapshotButtons() {
24
- if (snapshotUI.generate)
25
- snapshotUI.generate.classList.toggle("hidden", false);
26
- if (snapshotUI.update)
27
- snapshotUI.update.classList.toggle("hidden", true);
28
- if (snapshotUI.regenerate)
29
- snapshotUI.regenerate.classList.toggle("hidden", true);
30
- updateCopyButton(snapshotUI.copy, getDocCopyText("snapshot"), docsState.snapshotBusy);
31
- }
32
- export async function loadSnapshot({ notify = false } = {}) {
33
- if (docsState.snapshotBusy)
34
- return;
35
- try {
36
- setSnapshotBusy(true);
37
- const data = await api("/api/snapshot");
38
- docsState.snapshotCache = {
39
- exists: !!data?.exists,
40
- content: data?.content || "",
41
- state: data?.state || {},
42
- };
43
- if (getActiveDoc() === "snapshot") {
44
- const textarea = getDocTextarea();
45
- if (textarea)
46
- textarea.value = docsState.snapshotCache.content || "";
47
- }
48
- renderSnapshotButtons();
49
- if (notify)
50
- flash(docsState.snapshotCache.exists ? "Snapshot loaded" : "No snapshot yet");
51
- }
52
- catch (err) {
53
- flash(err?.message || "Failed to load snapshot");
54
- }
55
- finally {
56
- setSnapshotBusy(false);
57
- }
58
- }
59
- export async function runSnapshot() {
60
- if (docsState.snapshotBusy)
61
- return;
62
- try {
63
- setSnapshotBusy(true);
64
- const data = await api("/api/snapshot", {
65
- method: "POST",
66
- body: {},
67
- });
68
- docsState.snapshotCache = {
69
- exists: true,
70
- content: data?.content || "",
71
- state: data?.state || {},
72
- };
73
- if (getActiveDoc() === "snapshot") {
74
- const textarea = getDocTextarea();
75
- if (textarea)
76
- textarea.value = docsState.snapshotCache.content || "";
77
- }
78
- renderSnapshotButtons();
79
- flash("Snapshot generated");
80
- }
81
- catch (err) {
82
- flash(err?.message || "Snapshot generation failed");
83
- }
84
- finally {
85
- setSnapshotBusy(false);
86
- }
87
- }
@@ -1,263 +0,0 @@
1
- import { api, confirmModal, flash } from "./utils.js";
2
- import { applySpecIngestDocs } from "./docsDocUpdates.js";
3
- import { parseSpecIngestPayload } from "./docsParse.js";
4
- import { getActiveDoc, docsState } from "./docsState.js";
5
- import { specIngestUI } from "./docsElements.js";
6
- import { renderDiffHtml, updateDocVisibility, autoResizeTextarea } from "./docsUi.js";
7
- import { getSelectedAgent, getSelectedModel, getSelectedReasoning, } from "./agentControls.js";
8
- export function renderSpecIngestPatch() {
9
- if (!specIngestUI.patchMain)
10
- return;
11
- const isSpec = getActiveDoc() === "spec";
12
- const hasPatch = !!(docsState.specIngestState.patch || "").trim();
13
- if (specIngestUI.continueBtn)
14
- specIngestUI.continueBtn.disabled = docsState.specIngestState.busy;
15
- if (specIngestUI.cancelBtn) {
16
- specIngestUI.cancelBtn.disabled = !docsState.specIngestState.busy;
17
- specIngestUI.cancelBtn.classList.toggle("hidden", !docsState.specIngestState.busy);
18
- }
19
- specIngestUI.patchMain.classList.toggle("hidden", !isSpec || !hasPatch);
20
- if (!isSpec || !hasPatch) {
21
- updateDocVisibility();
22
- return;
23
- }
24
- specIngestUI.patchBody.innerHTML = renderDiffHtml(docsState.specIngestState.patch);
25
- specIngestUI.patchSummary.textContent =
26
- docsState.specIngestState.agentMessage || "Spec ingest patch ready";
27
- if (specIngestUI.patchApply)
28
- specIngestUI.patchApply.disabled =
29
- docsState.specIngestState.busy || !hasPatch;
30
- if (specIngestUI.patchDiscard)
31
- specIngestUI.patchDiscard.disabled =
32
- docsState.specIngestState.busy || !hasPatch;
33
- if (specIngestUI.patchReload)
34
- specIngestUI.patchReload.disabled = docsState.specIngestState.busy;
35
- updateDocVisibility();
36
- }
37
- async function interruptSpecIngest() {
38
- try {
39
- await api("/api/ingest-spec/interrupt", { method: "POST" });
40
- }
41
- catch (err) {
42
- flash(err.message || "Failed to interrupt spec ingest", "error");
43
- }
44
- }
45
- export async function ingestSpec() {
46
- if (docsState.specIngestState.busy)
47
- return;
48
- const needsForce = ["todo", "progress", "opinions"].some((k) => (docsState.docsCache[k] || "").trim().length > 0);
49
- if (needsForce) {
50
- const ok = await confirmModal("Overwrite TODO, PROGRESS, and OPINIONS from SPEC? Existing content will be replaced.");
51
- if (!ok)
52
- return;
53
- }
54
- const button = document.getElementById("ingest-spec");
55
- if (button)
56
- button.disabled = true;
57
- button?.classList.add("loading");
58
- if (button)
59
- button.disabled = true;
60
- docsState.specIngestState.busy = true;
61
- docsState.specIngestState.controller = new AbortController();
62
- renderSpecIngestPatch();
63
- try {
64
- const data = await api("/api/ingest-spec", {
65
- method: "POST",
66
- body: {
67
- force: needsForce,
68
- agent: getSelectedAgent(),
69
- model: getSelectedModel(),
70
- reasoning: getSelectedReasoning(),
71
- },
72
- signal: docsState.specIngestState.controller.signal,
73
- });
74
- const parsed = parseSpecIngestPayload(data);
75
- if (parsed.error)
76
- throw new Error(parsed.error);
77
- if (parsed.interrupted) {
78
- docsState.specIngestState.patch = "";
79
- docsState.specIngestState.agentMessage = parsed.agentMessage || "";
80
- applySpecIngestDocs(parsed);
81
- renderSpecIngestPatch();
82
- flash("Spec ingest interrupted");
83
- return;
84
- }
85
- docsState.specIngestState.patch = parsed.patch || "";
86
- docsState.specIngestState.agentMessage = parsed.agentMessage || "";
87
- applySpecIngestDocs(parsed);
88
- renderSpecIngestPatch();
89
- flash(parsed.patch ? "Spec ingest patch ready" : "Ingested SPEC into docs");
90
- }
91
- catch (err) {
92
- if (err.name === "AbortError") {
93
- return;
94
- }
95
- else {
96
- flash(err.message, "error");
97
- }
98
- }
99
- finally {
100
- button?.classList.remove("loading");
101
- if (button)
102
- button.disabled = false;
103
- docsState.specIngestState.busy = false;
104
- docsState.specIngestState.controller = null;
105
- renderSpecIngestPatch();
106
- }
107
- }
108
- export function cancelSpecIngest() {
109
- if (!docsState.specIngestState.busy)
110
- return;
111
- interruptSpecIngest();
112
- if (docsState.specIngestState.controller)
113
- docsState.specIngestState.controller.abort();
114
- docsState.specIngestState.busy = false;
115
- docsState.specIngestState.controller = null;
116
- if (specIngestUI.continueBtn)
117
- specIngestUI.continueBtn.disabled = false;
118
- flash("Spec ingest interrupted");
119
- renderSpecIngestPatch();
120
- }
121
- export async function continueSpecIngest() {
122
- if (docsState.specIngestState.busy)
123
- return;
124
- if (!specIngestUI.input)
125
- return;
126
- const message = (specIngestUI.input.value || "").trim();
127
- if (!message) {
128
- flash("Enter a follow-up prompt to continue", "error");
129
- return;
130
- }
131
- const needsForce = ["todo", "progress", "opinions"].some((k) => (docsState.docsCache[k] || "").trim().length > 0);
132
- docsState.specIngestState.busy = true;
133
- if (specIngestUI.continueBtn)
134
- specIngestUI.continueBtn.disabled = true;
135
- docsState.specIngestState.controller = new AbortController();
136
- renderSpecIngestPatch();
137
- try {
138
- const data = await api("/api/ingest-spec", {
139
- method: "POST",
140
- body: {
141
- force: needsForce,
142
- message,
143
- agent: getSelectedAgent(),
144
- model: getSelectedModel(),
145
- reasoning: getSelectedReasoning(),
146
- },
147
- signal: docsState.specIngestState.controller.signal,
148
- });
149
- const parsed = parseSpecIngestPayload(data);
150
- if (parsed.error)
151
- throw new Error(parsed.error);
152
- if (parsed.interrupted) {
153
- docsState.specIngestState.patch = "";
154
- docsState.specIngestState.agentMessage = parsed.agentMessage || "";
155
- applySpecIngestDocs(parsed);
156
- renderSpecIngestPatch();
157
- flash("Spec ingest interrupted");
158
- return;
159
- }
160
- docsState.specIngestState.patch = parsed.patch || "";
161
- docsState.specIngestState.agentMessage = parsed.agentMessage || "";
162
- applySpecIngestDocs(parsed);
163
- renderSpecIngestPatch();
164
- specIngestUI.input.value = "";
165
- autoResizeTextarea(specIngestUI.input);
166
- flash(parsed.patch ? "Spec ingest patch updated" : "Spec ingest updated docs");
167
- }
168
- catch (err) {
169
- if (err.name === "AbortError") {
170
- return;
171
- }
172
- else {
173
- flash(err.message, "error");
174
- }
175
- }
176
- finally {
177
- docsState.specIngestState.busy = false;
178
- if (specIngestUI.continueBtn)
179
- specIngestUI.continueBtn.disabled = false;
180
- docsState.specIngestState.controller = null;
181
- renderSpecIngestPatch();
182
- }
183
- }
184
- export async function applySpecIngestPatch() {
185
- if (!docsState.specIngestState.patch) {
186
- flash("No spec ingest patch to apply", "error");
187
- return;
188
- }
189
- docsState.specIngestState.busy = true;
190
- renderSpecIngestPatch();
191
- try {
192
- const res = await api("/api/ingest-spec/apply", { method: "POST" });
193
- const parsed = parseSpecIngestPayload(res);
194
- if (parsed.error)
195
- throw new Error(parsed.error);
196
- docsState.specIngestState.patch = "";
197
- docsState.specIngestState.agentMessage = "";
198
- applySpecIngestDocs(parsed);
199
- flash("Spec ingest patch applied");
200
- }
201
- catch (err) {
202
- flash(err.message || "Failed to apply spec ingest patch", "error");
203
- }
204
- finally {
205
- docsState.specIngestState.busy = false;
206
- renderSpecIngestPatch();
207
- }
208
- }
209
- export async function discardSpecIngestPatch() {
210
- if (!docsState.specIngestState.patch)
211
- return;
212
- docsState.specIngestState.busy = true;
213
- renderSpecIngestPatch();
214
- try {
215
- const res = await api("/api/ingest-spec/discard", { method: "POST" });
216
- const parsed = parseSpecIngestPayload(res);
217
- if (parsed.error)
218
- throw new Error(parsed.error);
219
- docsState.specIngestState.patch = "";
220
- docsState.specIngestState.agentMessage = "";
221
- applySpecIngestDocs(parsed);
222
- flash("Spec ingest patch discarded");
223
- }
224
- catch (err) {
225
- flash(err.message || "Failed to discard spec ingest patch", "error");
226
- }
227
- finally {
228
- docsState.specIngestState.busy = false;
229
- renderSpecIngestPatch();
230
- }
231
- }
232
- export async function reloadSpecIngestPatch(silent = false) {
233
- try {
234
- const res = await api("/api/ingest-spec/pending", { method: "GET" });
235
- const parsed = parseSpecIngestPayload(res);
236
- if (parsed.error)
237
- throw new Error(parsed.error);
238
- if (parsed.patch) {
239
- docsState.specIngestState.patch = parsed.patch;
240
- docsState.specIngestState.agentMessage = parsed.agentMessage || "";
241
- applySpecIngestDocs(parsed);
242
- renderSpecIngestPatch();
243
- if (!silent)
244
- flash("Loaded spec ingest patch");
245
- return;
246
- }
247
- }
248
- catch (err) {
249
- const message = err?.message || "";
250
- if (message.includes("No pending spec ingest patch")) {
251
- docsState.specIngestState.patch = "";
252
- docsState.specIngestState.agentMessage = "";
253
- renderSpecIngestPatch();
254
- return;
255
- }
256
- if (!silent) {
257
- flash(message || "Failed to load spec ingest patch", "error");
258
- }
259
- }
260
- if (!docsState.specIngestState.patch) {
261
- renderSpecIngestPatch();
262
- }
263
- }
@@ -1,127 +0,0 @@
1
- import { CONSTANTS } from "./constants.js";
2
- import { docButtons } from "./docsElements.js";
3
- export const DOC_TYPES = ["todo", "progress", "opinions", "spec", "summary"];
4
- export const CLEARABLE_DOCS = ["todo", "progress", "opinions"];
5
- export const COPYABLE_DOCS = ["spec", "summary"];
6
- export const PASTEABLE_DOCS = ["spec"];
7
- export const CHAT_HISTORY_LIMIT = 8;
8
- export const CHAT_EVENT_LIMIT = CONSTANTS.UI?.DOC_CHAT_EVENT_LIMIT || 12;
9
- export const CHAT_EVENT_MAX = Math.max(60, CHAT_EVENT_LIMIT * 8);
10
- export const chatDecoder = new TextDecoder();
11
- export const docsState = {
12
- docsCache: { todo: "", progress: "", opinions: "", spec: "", summary: "" },
13
- snapshotCache: { exists: false, content: "", state: {} },
14
- snapshotBusy: false,
15
- activeDoc: "todo",
16
- chatState: createChatState(),
17
- draftState: {
18
- data: {},
19
- preview: {},
20
- },
21
- specIngestState: {
22
- status: "idle",
23
- patch: "",
24
- agentMessage: "",
25
- error: "",
26
- busy: false,
27
- controller: null,
28
- },
29
- historyNavIndex: -1,
30
- };
31
- export const VOICE_TRANSCRIPT_DISCLAIMER_TEXT = CONSTANTS.PROMPTS?.VOICE_TRANSCRIPT_DISCLAIMER ||
32
- "Note: transcribed from user voice. If confusing or possibly inaccurate and you cannot infer the intention please clarify before proceeding.";
33
- export function createChatState() {
34
- return {
35
- history: [],
36
- status: "idle",
37
- statusText: "",
38
- error: "",
39
- streamText: "",
40
- controller: null,
41
- events: [],
42
- eventsExpanded: false,
43
- eventController: null,
44
- eventTurnId: null,
45
- eventThreadId: null,
46
- eventAgent: null,
47
- eventItemIndex: {},
48
- eventError: "",
49
- };
50
- }
51
- export function getChatState() {
52
- return docsState.chatState;
53
- }
54
- export function getActiveDoc() {
55
- return docsState.activeDoc;
56
- }
57
- export function setActiveDoc(kind) {
58
- docsState.activeDoc = kind;
59
- }
60
- export function getHistoryNavIndex() {
61
- return docsState.historyNavIndex;
62
- }
63
- export function setHistoryNavIndex(value) {
64
- docsState.historyNavIndex = value;
65
- }
66
- export function setDraft(kind, draft) {
67
- if (!DOC_TYPES.includes(kind))
68
- return;
69
- if (!draft) {
70
- delete docsState.draftState.data[kind];
71
- delete docsState.draftState.preview[kind];
72
- }
73
- else {
74
- docsState.draftState.data[kind] = draft;
75
- }
76
- updateDocDraftIndicators();
77
- }
78
- export function getDraft(kind) {
79
- return docsState.draftState.data[kind] || null;
80
- }
81
- export function hasDraft(kind) {
82
- return !!getDraft(kind);
83
- }
84
- export function isDraftPreview(kind) {
85
- return !!docsState.draftState.preview[kind];
86
- }
87
- export function setDraftPreview(kind, value) {
88
- if (!DOC_TYPES.includes(kind))
89
- return;
90
- if (value) {
91
- docsState.draftState.preview[kind] = true;
92
- }
93
- else {
94
- delete docsState.draftState.preview[kind];
95
- }
96
- updateDocDraftIndicators();
97
- }
98
- export function updateDocDraftIndicators() {
99
- docButtons.forEach((btn) => {
100
- const htmlBtn = btn;
101
- const kind = htmlBtn.dataset.doc;
102
- if (!DOC_TYPES.includes(kind))
103
- return;
104
- htmlBtn.classList.toggle("has-draft", hasDraft(kind));
105
- htmlBtn.classList.toggle("previewing", hasDraft(kind) && isDraftPreview(kind));
106
- });
107
- }
108
- export function resetChatEvents(state, { preserve = false } = {}) {
109
- if (state.eventController) {
110
- state.eventController.abort();
111
- }
112
- state.eventController = null;
113
- state.eventTurnId = null;
114
- state.eventThreadId = null;
115
- state.eventAgent = null;
116
- state.eventItemIndex = {};
117
- state.eventError = "";
118
- if (!preserve) {
119
- state.events = [];
120
- state.eventsExpanded = false;
121
- }
122
- }
123
- export function getDocChatViewing() {
124
- if (!DOC_TYPES.includes(docsState.activeDoc))
125
- return "todo";
126
- return docsState.activeDoc;
127
- }
@@ -1,44 +0,0 @@
1
- import { api, flash, resolvePath } from "./utils.js";
2
- import { threadRegistryUI } from "./docsElements.js";
3
- export function renderThreadRegistryBanner(notice) {
4
- if (!threadRegistryUI.banner)
5
- return;
6
- const active = notice && notice.status === "corrupt";
7
- threadRegistryUI.banner.classList.toggle("hidden", !active);
8
- if (!active)
9
- return;
10
- const backupPath = notice && typeof notice.backup_path === "string" ? notice.backup_path : "";
11
- if (threadRegistryUI.detail) {
12
- threadRegistryUI.detail.textContent = backupPath
13
- ? `Backup: ${backupPath}`
14
- : "Backup unavailable";
15
- threadRegistryUI.detail.title = backupPath || "";
16
- }
17
- if (threadRegistryUI.download) {
18
- threadRegistryUI.download.classList.toggle("hidden", !backupPath);
19
- }
20
- }
21
- export async function loadThreadRegistryStatus() {
22
- if (!threadRegistryUI.banner)
23
- return;
24
- try {
25
- const data = await api("/api/app-server/threads");
26
- renderThreadRegistryBanner(data?.corruption);
27
- }
28
- catch (err) {
29
- console.error("Failed to load thread registry status", err);
30
- }
31
- }
32
- export async function resetThreadRegistry() {
33
- try {
34
- await api("/api/app-server/threads/reset-all", { method: "POST" });
35
- renderThreadRegistryBanner(null);
36
- flash("Conversations reset");
37
- }
38
- catch (err) {
39
- flash(err.message || "Failed to reset conversations", "error");
40
- }
41
- }
42
- export function downloadThreadRegistryBackup() {
43
- window.location.href = resolvePath("/api/app-server/threads/backup");
44
- }