@sireai/optimus 0.1.1

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 (204) hide show
  1. package/.env.example +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +104 -0
  4. package/dist/cli/optimus.d.ts +2 -0
  5. package/dist/cli/optimus.js +2951 -0
  6. package/dist/cli/optimus.js.map +1 -0
  7. package/dist/cli/self-update.d.ts +49 -0
  8. package/dist/cli/self-update.js +264 -0
  9. package/dist/cli/self-update.js.map +1 -0
  10. package/dist/config/load-config.d.ts +3 -0
  11. package/dist/config/load-config.js +321 -0
  12. package/dist/config/load-config.js.map +1 -0
  13. package/dist/config/optimus-paths.d.ts +13 -0
  14. package/dist/config/optimus-paths.js +44 -0
  15. package/dist/config/optimus-paths.js.map +1 -0
  16. package/dist/index.d.ts +25 -0
  17. package/dist/index.js +27 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/integrations/jira/jira-cli.d.ts +1 -0
  20. package/dist/integrations/jira/jira-cli.js +278 -0
  21. package/dist/integrations/jira/jira-cli.js.map +1 -0
  22. package/dist/integrations/jira/jira-client.d.ts +99 -0
  23. package/dist/integrations/jira/jira-client.js +521 -0
  24. package/dist/integrations/jira/jira-client.js.map +1 -0
  25. package/dist/integrations/jira/jira-submit.d.ts +71 -0
  26. package/dist/integrations/jira/jira-submit.js +351 -0
  27. package/dist/integrations/jira/jira-submit.js.map +1 -0
  28. package/dist/problem-solving-core/codex/codex-auth-resolver.d.ts +23 -0
  29. package/dist/problem-solving-core/codex/codex-auth-resolver.js +136 -0
  30. package/dist/problem-solving-core/codex/codex-auth-resolver.js.map +1 -0
  31. package/dist/problem-solving-core/codex/codex-connectivity-checks.d.ts +6 -0
  32. package/dist/problem-solving-core/codex/codex-connectivity-checks.js +81 -0
  33. package/dist/problem-solving-core/codex/codex-connectivity-checks.js.map +1 -0
  34. package/dist/problem-solving-core/codex/codex-failure-classifier.d.ts +2 -0
  35. package/dist/problem-solving-core/codex/codex-failure-classifier.js +49 -0
  36. package/dist/problem-solving-core/codex/codex-failure-classifier.js.map +1 -0
  37. package/dist/problem-solving-core/codex/codex-global-config.d.ts +17 -0
  38. package/dist/problem-solving-core/codex/codex-global-config.js +100 -0
  39. package/dist/problem-solving-core/codex/codex-global-config.js.map +1 -0
  40. package/dist/problem-solving-core/codex/codex-preflight.d.ts +13 -0
  41. package/dist/problem-solving-core/codex/codex-preflight.js +142 -0
  42. package/dist/problem-solving-core/codex/codex-preflight.js.map +1 -0
  43. package/dist/problem-solving-core/codex/codex-provider-profile.d.ts +14 -0
  44. package/dist/problem-solving-core/codex/codex-provider-profile.js +68 -0
  45. package/dist/problem-solving-core/codex/codex-provider-profile.js.map +1 -0
  46. package/dist/problem-solving-core/codex/codex-required-env.d.ts +3 -0
  47. package/dist/problem-solving-core/codex/codex-required-env.js +21 -0
  48. package/dist/problem-solving-core/codex/codex-required-env.js.map +1 -0
  49. package/dist/problem-solving-core/codex/codex-runner.d.ts +37 -0
  50. package/dist/problem-solving-core/codex/codex-runner.js +926 -0
  51. package/dist/problem-solving-core/codex/codex-runner.js.map +1 -0
  52. package/dist/problem-solving-core/codex/evolution-skill-guard.d.ts +36 -0
  53. package/dist/problem-solving-core/codex/evolution-skill-guard.js +143 -0
  54. package/dist/problem-solving-core/codex/evolution-skill-guard.js.map +1 -0
  55. package/dist/problem-solving-core/codex/repo-memory-service.d.ts +24 -0
  56. package/dist/problem-solving-core/codex/repo-memory-service.js +114 -0
  57. package/dist/problem-solving-core/codex/repo-memory-service.js.map +1 -0
  58. package/dist/problem-solving-core/codex/skill-sync-service.d.ts +35 -0
  59. package/dist/problem-solving-core/codex/skill-sync-service.js +280 -0
  60. package/dist/problem-solving-core/codex/skill-sync-service.js.map +1 -0
  61. package/dist/task-environment/cancellation/task-abort-registry.d.ts +17 -0
  62. package/dist/task-environment/cancellation/task-abort-registry.js +51 -0
  63. package/dist/task-environment/cancellation/task-abort-registry.js.map +1 -0
  64. package/dist/task-environment/cancellation/task-cancellation-service.d.ts +25 -0
  65. package/dist/task-environment/cancellation/task-cancellation-service.js +54 -0
  66. package/dist/task-environment/cancellation/task-cancellation-service.js.map +1 -0
  67. package/dist/task-environment/cancellation/task-cleanup-service.d.ts +22 -0
  68. package/dist/task-environment/cancellation/task-cleanup-service.js +67 -0
  69. package/dist/task-environment/cancellation/task-cleanup-service.js.map +1 -0
  70. package/dist/task-environment/delivery/commit-message/bugfix-commit-message-template.d.ts +13 -0
  71. package/dist/task-environment/delivery/commit-message/bugfix-commit-message-template.js +83 -0
  72. package/dist/task-environment/delivery/commit-message/bugfix-commit-message-template.js.map +1 -0
  73. package/dist/task-environment/delivery/commit-message/commit-message-builder.d.ts +6 -0
  74. package/dist/task-environment/delivery/commit-message/commit-message-builder.js +15 -0
  75. package/dist/task-environment/delivery/commit-message/commit-message-builder.js.map +1 -0
  76. package/dist/task-environment/delivery/commit-message/commit-message-template-types.d.ts +16 -0
  77. package/dist/task-environment/delivery/commit-message/commit-message-template-types.js +2 -0
  78. package/dist/task-environment/delivery/commit-message/commit-message-template-types.js.map +1 -0
  79. package/dist/task-environment/delivery/feishu-analysis-doc-service.d.ts +50 -0
  80. package/dist/task-environment/delivery/feishu-analysis-doc-service.js +454 -0
  81. package/dist/task-environment/delivery/feishu-analysis-doc-service.js.map +1 -0
  82. package/dist/task-environment/delivery/feishu-card-renderer.d.ts +38 -0
  83. package/dist/task-environment/delivery/feishu-card-renderer.js +449 -0
  84. package/dist/task-environment/delivery/feishu-card-renderer.js.map +1 -0
  85. package/dist/task-environment/delivery/feishu-content/feishu-content-renderer.d.ts +34 -0
  86. package/dist/task-environment/delivery/feishu-content/feishu-content-renderer.js +201 -0
  87. package/dist/task-environment/delivery/feishu-content/feishu-content-renderer.js.map +1 -0
  88. package/dist/task-environment/delivery/feishu-content/feishu-copy-config.d.ts +27 -0
  89. package/dist/task-environment/delivery/feishu-content/feishu-copy-config.js +74 -0
  90. package/dist/task-environment/delivery/feishu-content/feishu-copy-config.js.map +1 -0
  91. package/dist/task-environment/delivery/feishu-notifier.d.ts +45 -0
  92. package/dist/task-environment/delivery/feishu-notifier.js +250 -0
  93. package/dist/task-environment/delivery/feishu-notifier.js.map +1 -0
  94. package/dist/task-environment/delivery/feishu-templates/analysis-message-template.d.ts +6 -0
  95. package/dist/task-environment/delivery/feishu-templates/analysis-message-template.js +39 -0
  96. package/dist/task-environment/delivery/feishu-templates/analysis-message-template.js.map +1 -0
  97. package/dist/task-environment/delivery/feishu-templates/bugfix-message-template.d.ts +6 -0
  98. package/dist/task-environment/delivery/feishu-templates/bugfix-message-template.js +40 -0
  99. package/dist/task-environment/delivery/feishu-templates/bugfix-message-template.js.map +1 -0
  100. package/dist/task-environment/delivery/feishu-templates/default-message-template.d.ts +6 -0
  101. package/dist/task-environment/delivery/feishu-templates/default-message-template.js +33 -0
  102. package/dist/task-environment/delivery/feishu-templates/default-message-template.js.map +1 -0
  103. package/dist/task-environment/delivery/feishu-templates/patch-message-template.d.ts +6 -0
  104. package/dist/task-environment/delivery/feishu-templates/patch-message-template.js +40 -0
  105. package/dist/task-environment/delivery/feishu-templates/patch-message-template.js.map +1 -0
  106. package/dist/task-environment/delivery/feishu-templates/template-registry.d.ts +2 -0
  107. package/dist/task-environment/delivery/feishu-templates/template-registry.js +11 -0
  108. package/dist/task-environment/delivery/feishu-templates/template-registry.js.map +1 -0
  109. package/dist/task-environment/delivery/feishu-templates/template-types.d.ts +20 -0
  110. package/dist/task-environment/delivery/feishu-templates/template-types.js +2 -0
  111. package/dist/task-environment/delivery/feishu-templates/template-types.js.map +1 -0
  112. package/dist/task-environment/delivery/task-delivery-dispatcher.d.ts +14 -0
  113. package/dist/task-environment/delivery/task-delivery-dispatcher.js +109 -0
  114. package/dist/task-environment/delivery/task-delivery-dispatcher.js.map +1 -0
  115. package/dist/task-environment/delivery/task-delivery-service.d.ts +33 -0
  116. package/dist/task-environment/delivery/task-delivery-service.js +432 -0
  117. package/dist/task-environment/delivery/task-delivery-service.js.map +1 -0
  118. package/dist/task-environment/delivery/task-publication-service.d.ts +97 -0
  119. package/dist/task-environment/delivery/task-publication-service.js +1369 -0
  120. package/dist/task-environment/delivery/task-publication-service.js.map +1 -0
  121. package/dist/task-environment/execution-addresses.d.ts +40 -0
  122. package/dist/task-environment/execution-addresses.js +63 -0
  123. package/dist/task-environment/execution-addresses.js.map +1 -0
  124. package/dist/task-environment/intake/cli-file-intake.d.ts +12 -0
  125. package/dist/task-environment/intake/cli-file-intake.js +56 -0
  126. package/dist/task-environment/intake/cli-file-intake.js.map +1 -0
  127. package/dist/task-environment/intake/manual-problem-intake.d.ts +3 -0
  128. package/dist/task-environment/intake/manual-problem-intake.js +57 -0
  129. package/dist/task-environment/intake/manual-problem-intake.js.map +1 -0
  130. package/dist/task-environment/intake/polling-problem-intake.d.ts +14 -0
  131. package/dist/task-environment/intake/polling-problem-intake.js +232 -0
  132. package/dist/task-environment/intake/polling-problem-intake.js.map +1 -0
  133. package/dist/task-environment/observability/logger.d.ts +76 -0
  134. package/dist/task-environment/observability/logger.js +604 -0
  135. package/dist/task-environment/observability/logger.js.map +1 -0
  136. package/dist/task-environment/observability/runtime-panel.d.ts +82 -0
  137. package/dist/task-environment/observability/runtime-panel.js +1008 -0
  138. package/dist/task-environment/observability/runtime-panel.js.map +1 -0
  139. package/dist/task-environment/observability/sound-notifier.d.ts +18 -0
  140. package/dist/task-environment/observability/sound-notifier.js +71 -0
  141. package/dist/task-environment/observability/sound-notifier.js.map +1 -0
  142. package/dist/task-environment/orchestration/execution-context-assembler.d.ts +41 -0
  143. package/dist/task-environment/orchestration/execution-context-assembler.js +464 -0
  144. package/dist/task-environment/orchestration/execution-context-assembler.js.map +1 -0
  145. package/dist/task-environment/orchestration/git-change-classifier.d.ts +19 -0
  146. package/dist/task-environment/orchestration/git-change-classifier.js +106 -0
  147. package/dist/task-environment/orchestration/git-change-classifier.js.map +1 -0
  148. package/dist/task-environment/orchestration/harness-registry.d.ts +27 -0
  149. package/dist/task-environment/orchestration/harness-registry.js +116 -0
  150. package/dist/task-environment/orchestration/harness-registry.js.map +1 -0
  151. package/dist/task-environment/orchestration/harness-resolver.d.ts +8 -0
  152. package/dist/task-environment/orchestration/harness-resolver.js +39 -0
  153. package/dist/task-environment/orchestration/harness-resolver.js.map +1 -0
  154. package/dist/task-environment/orchestration/task-orchestrator.d.ts +45 -0
  155. package/dist/task-environment/orchestration/task-orchestrator.js +1122 -0
  156. package/dist/task-environment/orchestration/task-orchestrator.js.map +1 -0
  157. package/dist/task-environment/orchestration/task-package-assembler.d.ts +4 -0
  158. package/dist/task-environment/orchestration/task-package-assembler.js +10 -0
  159. package/dist/task-environment/orchestration/task-package-assembler.js.map +1 -0
  160. package/dist/task-environment/orchestration/triage-agent.d.ts +54 -0
  161. package/dist/task-environment/orchestration/triage-agent.js +636 -0
  162. package/dist/task-environment/orchestration/triage-agent.js.map +1 -0
  163. package/dist/task-environment/orchestration/triage-runner.d.ts +65 -0
  164. package/dist/task-environment/orchestration/triage-runner.js +655 -0
  165. package/dist/task-environment/orchestration/triage-runner.js.map +1 -0
  166. package/dist/task-environment/publication-target.d.ts +12 -0
  167. package/dist/task-environment/publication-target.js +174 -0
  168. package/dist/task-environment/publication-target.js.map +1 -0
  169. package/dist/task-environment/runtime/blocking-event-queue.d.ts +7 -0
  170. package/dist/task-environment/runtime/blocking-event-queue.js +27 -0
  171. package/dist/task-environment/runtime/blocking-event-queue.js.map +1 -0
  172. package/dist/task-environment/runtime/optimus-runtime.d.ts +69 -0
  173. package/dist/task-environment/runtime/optimus-runtime.js +751 -0
  174. package/dist/task-environment/runtime/optimus-runtime.js.map +1 -0
  175. package/dist/task-environment/storage/sqlite-event-store.d.ts +52 -0
  176. package/dist/task-environment/storage/sqlite-event-store.js +288 -0
  177. package/dist/task-environment/storage/sqlite-event-store.js.map +1 -0
  178. package/dist/task-environment/storage/sqlite-task-store.d.ts +122 -0
  179. package/dist/task-environment/storage/sqlite-task-store.js +1182 -0
  180. package/dist/task-environment/storage/sqlite-task-store.js.map +1 -0
  181. package/dist/types.d.ts +629 -0
  182. package/dist/types.js +2 -0
  183. package/dist/types.js.map +1 -0
  184. package/embedded-skills/shared/repo-inspection/SKILL.md +9 -0
  185. package/embedded-skills/shared/repo-inspection/skill.json +5 -0
  186. package/embedded-skills/task/bugfix/android-debug-protocol/SKILL.md +10 -0
  187. package/embedded-skills/task/bugfix/android-debug-protocol/skill.json +6 -0
  188. package/harness/AGENTS.md +30 -0
  189. package/harness/CHECKLIST.md +44 -0
  190. package/harness/CONSTRAINTS.md +60 -0
  191. package/harness/FRAMEWORK.md +28 -0
  192. package/harness/GOAL.md +28 -0
  193. package/harness/HANDOFF.md +45 -0
  194. package/harness/TASK_PLAN.md +79 -0
  195. package/optimus.config.template.json +34 -0
  196. package/package.json +109 -0
  197. package/task-harnesses/bugfix/ACCEPT.md +47 -0
  198. package/task-harnesses/bugfix/CONSTRAINTS.md +46 -0
  199. package/task-harnesses/bugfix/CONTEXT.md +29 -0
  200. package/task-harnesses/bugfix/EVOLUTION.md +82 -0
  201. package/task-harnesses/bugfix/ROLE.md +29 -0
  202. package/task-harnesses/bugfix/STANDARD.md +250 -0
  203. package/task-harnesses/bugfix/manifest.json +13 -0
  204. package/task-harnesses/registry.json +8 -0
@@ -0,0 +1,1008 @@
1
+ import process from "node:process";
2
+ import { join } from "node:path";
3
+ import { resolveDefaultRuntimeRootDir } from "../../config/optimus-paths.js";
4
+ const LOGO_LINES = [
5
+ ' _____________',
6
+ ' /_/_/_/_/_/_/\\',
7
+ ' /_/_/_/_/_/_/ \\\\',
8
+ ' /_/_/____/_/ /\\ \\\\',
9
+ ' /_/_/ /___/_/ / /\\ \\\\',
10
+ ' /_/_/ /_____/ /_/ /\\ \\\\',
11
+ ' \\\\_\\/_____/\\___\\/ / /',
12
+ ' \\\\_\\____/ /_/_/ / /',
13
+ ' \\\\_\\__/ /_/_/ / /',
14
+ ' \\\\___\\/____\\/ /',
15
+ ' \\\\_____________/',
16
+ ];
17
+ const ANSI = {
18
+ reset: "\u001B[0m",
19
+ dim: "\u001B[2m",
20
+ bold: "\u001B[1m",
21
+ red: "\u001B[31m",
22
+ green: "\u001B[32m",
23
+ yellow: "\u001B[33m",
24
+ blue: "\u001B[34m",
25
+ magenta: "\u001B[35m",
26
+ cyan: "\u001B[36m",
27
+ gray: "\u001B[90m",
28
+ orange: "\u001B[38;5;208m",
29
+ orangeSoft: "\u001B[38;5;215m"
30
+ };
31
+ const ANSI_PATTERN = /\u001B\[[0-9;]*m/g;
32
+ import { OptimusLogger } from "./logger.js";
33
+ // RuntimePanel is a terminal-only projection of runtime activity.
34
+ // Persisted runtime logs and trace logs remain unchanged; the panel only renders a compact live view.
35
+ export class RuntimePanel {
36
+ runtimeRootDir;
37
+ state;
38
+ activeTasks = new Map();
39
+ unsubscribers = [];
40
+ maxRecentEvents = 30;
41
+ maxAlerts = 6;
42
+ handleResize = () => {
43
+ if (!this.started) {
44
+ return;
45
+ }
46
+ this.renderedLineCount = 0;
47
+ process.stdout.write("\u001B[2J\u001B[H");
48
+ this.dirty = true;
49
+ this.render();
50
+ };
51
+ renderedLineCount = 0;
52
+ started = false;
53
+ dirty = true;
54
+ runtimeStartedAtMs = Date.now();
55
+ stdinDataHandler = (chunk) => {
56
+ // Swallow terminal input while the panel owns the screen so trackpad or arrow-key escape sequences do not leak into the UI.
57
+ const value = typeof chunk === "string" ? chunk : chunk.toString("utf8");
58
+ if (value.includes("")) {
59
+ process.kill(process.pid, "SIGINT");
60
+ }
61
+ };
62
+ stdinRawModeBeforeStart;
63
+ constructor(runtimeRootDir = resolveDefaultRuntimeRootDir()) {
64
+ this.runtimeRootDir = runtimeRootDir;
65
+ this.state = {
66
+ runtime: {
67
+ running: false,
68
+ queueDepth: 0,
69
+ activeWorkers: 0,
70
+ activeRuns: 0,
71
+ stalledRuns: 0,
72
+ triageStatus: "booting",
73
+ uptimeMs: 0,
74
+ databasePath: join(runtimeRootDir, "optimus.db")
75
+ },
76
+ activeTasks: [],
77
+ recentEvents: [],
78
+ alerts: []
79
+ };
80
+ }
81
+ // The panel is only useful in an interactive terminal.
82
+ canRender() {
83
+ return Boolean(process.stdout.isTTY);
84
+ }
85
+ // start() switches to the terminal alternate screen so the live panel does not pollute shell history.
86
+ start() {
87
+ if (this.started || !this.canRender()) {
88
+ return;
89
+ }
90
+ this.started = true;
91
+ this.renderedLineCount = 0;
92
+ this.stdinRawModeBeforeStart = process.stdin.isTTY ? process.stdin.isRaw : undefined;
93
+ if (process.stdin.isTTY) {
94
+ process.stdin.setRawMode?.(true);
95
+ }
96
+ process.stdin.resume();
97
+ process.stdin.on("data", this.stdinDataHandler);
98
+ process.stdout.write("[?1049h[?25l");
99
+ this.unsubscribers.push(OptimusLogger.subscribe((record) => {
100
+ this.record(record);
101
+ }));
102
+ OptimusLogger.setConsoleSink(() => {
103
+ // The panel owns the terminal while runtime is active.
104
+ });
105
+ process.stdout.on("resize", this.handleResize);
106
+ this.render();
107
+ }
108
+ // stop() restores the previous shell screen and default logger console behavior.
109
+ stop() {
110
+ if (!this.started) {
111
+ return;
112
+ }
113
+ for (const unsubscribe of this.unsubscribers.splice(0)) {
114
+ unsubscribe();
115
+ }
116
+ process.stdout.off("resize", this.handleResize);
117
+ process.stdin.off("data", this.stdinDataHandler);
118
+ if (process.stdin.isTTY) {
119
+ process.stdin.setRawMode?.(this.stdinRawModeBeforeStart ?? false);
120
+ }
121
+ process.stdin.pause();
122
+ OptimusLogger.setConsoleSink(undefined);
123
+ process.stdout.write("[?25h[?1049l");
124
+ this.stdinRawModeBeforeStart = undefined;
125
+ this.renderedLineCount = 0;
126
+ this.started = false;
127
+ }
128
+ // record() projects logger events into a readable operator snapshot.
129
+ record(record) {
130
+ this.state.runtime.lastEventAt = record.timestampShort;
131
+ if (record.event === "runtime.started") {
132
+ this.runtimeStartedAtMs = Date.parse(record.timestampIso) || Date.now();
133
+ this.state.runtime.running = true;
134
+ }
135
+ if (record.event === "runtime.stopped") {
136
+ this.state.runtime.running = false;
137
+ }
138
+ if (record.event === "runtime.health") {
139
+ this.projectRuntimeHealth(record.context);
140
+ }
141
+ if (record.event === "optimus.triage.starting") {
142
+ this.state.runtime.triageStatus = "starting";
143
+ }
144
+ if (record.event === "optimus.triage.ready") {
145
+ this.state.runtime.triageStatus = "ready";
146
+ }
147
+ if (record.event === "optimus.triage.start_failed") {
148
+ this.state.runtime.triageStatus = "failed";
149
+ }
150
+ this.state.runtime.uptimeMs = Math.max(0, (Date.parse(record.timestampIso) || Date.now()) - this.runtimeStartedAtMs);
151
+ this.projectTask(record);
152
+ this.pushPanelEvent(this.toPanelEvent(record));
153
+ this.dirty = true;
154
+ this.render();
155
+ }
156
+ projectRuntimeHealth(context) {
157
+ this.state.runtime.queueDepth = this.toNumber(context.queueDepth);
158
+ this.state.runtime.activeWorkers = this.toNumber(context.activeWorkers);
159
+ this.state.runtime.activeRuns = this.toNumber(context.activeRuns);
160
+ this.state.runtime.stalledRuns = this.toNumber(context.stalledRuns);
161
+ }
162
+ // Active tasks stay on screen until they reach a terminal state.
163
+ projectTask(record) {
164
+ const taskId = this.toString(record.context.taskId);
165
+ if (!taskId) {
166
+ return;
167
+ }
168
+ if (["task.completed", "task.failed", "task.canceled", "task.timed_out", "task.needs_human"].includes(record.event)) {
169
+ this.activeTasks.delete(taskId);
170
+ this.syncActiveTasks();
171
+ return;
172
+ }
173
+ if (!["task.received", "task.dispatched", "task.execution.started", "optimus.runner.stream_event", "optimus.runner.result_parsed", "task.fallback", "repo.execution_mode_resolved"].includes(record.event)) {
174
+ return;
175
+ }
176
+ const timestampMs = Date.parse(record.timestampIso) || Date.now();
177
+ const snapshot = this.activeTasks.get(taskId) ?? this.createTaskSnapshot(record, timestampMs);
178
+ const runId = this.toString(record.context.runId);
179
+ const taskType = this.toString(record.context.taskType);
180
+ const repo = this.toString(record.context.repo);
181
+ const health = this.resolveTaskHealth(record, snapshot.health);
182
+ if (runId) {
183
+ snapshot.runId = runId;
184
+ }
185
+ if (taskType) {
186
+ snapshot.taskType = taskType;
187
+ }
188
+ if (repo) {
189
+ snapshot.repo = repo;
190
+ }
191
+ if (health) {
192
+ snapshot.health = health;
193
+ }
194
+ snapshot.status = this.resolveTaskStatus(record, snapshot.status);
195
+ snapshot.phase = this.resolveTaskPhase(record, snapshot.phase);
196
+ snapshot.lastHeadline = this.buildTaskHeadline(record);
197
+ const currentAction = this.describeCurrentAction(record);
198
+ if (currentAction) {
199
+ snapshot.currentAction = currentAction;
200
+ }
201
+ snapshot.lastUpdatedAtMs = timestampMs;
202
+ if (typeof record.context.artifactCount === "number" && record.context.artifactCount > 0) {
203
+ snapshot.artifacts = [`artifacts:${record.context.artifactCount}`];
204
+ }
205
+ this.activeTasks.set(taskId, snapshot);
206
+ this.syncActiveTasks();
207
+ }
208
+ createTaskSnapshot(record, timestampMs) {
209
+ const runId = this.toString(record.context.runId);
210
+ const repo = this.toString(record.context.repo);
211
+ const currentAction = this.describeCurrentAction(record);
212
+ return {
213
+ taskId: this.toString(record.context.taskId) ?? "unknown-task",
214
+ ...(runId ? { runId } : {}),
215
+ taskType: this.toString(record.context.taskType) ?? "unknown",
216
+ status: "queued",
217
+ phase: "queued",
218
+ ...(repo ? { repo } : {}),
219
+ lastHeadline: this.buildTaskHeadline(record),
220
+ ...(currentAction ? { currentAction } : {}),
221
+ artifacts: [],
222
+ startedAtMs: timestampMs,
223
+ lastUpdatedAtMs: timestampMs
224
+ };
225
+ }
226
+ syncActiveTasks() {
227
+ const now = Date.now();
228
+ this.state.activeTasks = Array.from(this.activeTasks.values())
229
+ .map((snapshot) => this.toPublicSnapshot(snapshot, now))
230
+ .sort((left, right) => right.elapsedMs - left.elapsedMs);
231
+ }
232
+ toPublicSnapshot(snapshot, now) {
233
+ const projected = {
234
+ taskId: snapshot.taskId,
235
+ taskType: snapshot.taskType,
236
+ status: snapshot.status,
237
+ phase: snapshot.phase,
238
+ lastHeadline: snapshot.lastHeadline,
239
+ ...(snapshot.currentAction ? { currentAction: snapshot.currentAction } : {}),
240
+ elapsedMs: Math.max(0, now - snapshot.startedAtMs),
241
+ idleMs: Math.max(0, now - snapshot.lastUpdatedAtMs),
242
+ artifacts: snapshot.artifacts
243
+ };
244
+ if (snapshot.runId) {
245
+ projected.runId = snapshot.runId;
246
+ }
247
+ if (snapshot.health) {
248
+ projected.health = snapshot.health;
249
+ }
250
+ if (snapshot.repo) {
251
+ projected.repo = snapshot.repo;
252
+ }
253
+ return projected;
254
+ }
255
+ resolveTaskStatus(record, fallback) {
256
+ const explicit = this.toString(record.context.status);
257
+ if (this.isTaskStatus(explicit)) {
258
+ return explicit;
259
+ }
260
+ if (record.event === "task.received") {
261
+ return "queued";
262
+ }
263
+ if (["task.dispatched", "task.execution.started", "optimus.runner.stream_event", "optimus.runner.result_parsed"].includes(record.event)) {
264
+ return "running";
265
+ }
266
+ return fallback;
267
+ }
268
+ resolveTaskPhase(record, fallback) {
269
+ const runStatus = this.toString(record.context.runStatus);
270
+ if (this.isTaskRunStatus(runStatus)) {
271
+ return runStatus;
272
+ }
273
+ const eventType = this.toString(record.context.eventType);
274
+ if (this.isTaskRunStatus(eventType)) {
275
+ return eventType;
276
+ }
277
+ if (this.isTaskStatus(eventType)) {
278
+ return eventType;
279
+ }
280
+ if (record.event === "task.received") {
281
+ return "queued";
282
+ }
283
+ if (record.event === "task.execution.started") {
284
+ return "sdk_starting";
285
+ }
286
+ return fallback;
287
+ }
288
+ resolveTaskHealth(record, fallback) {
289
+ const explicit = this.toString(record.context.health);
290
+ if (this.isTaskRunHealth(explicit)) {
291
+ return explicit;
292
+ }
293
+ if (record.event === "optimus.runner.stream_event") {
294
+ return "healthy_running";
295
+ }
296
+ return fallback;
297
+ }
298
+ buildTaskHeadline(record) {
299
+ if (record.event === "optimus.runner.stream_event") {
300
+ return this.toString(record.context.headline) ?? "Execution update.";
301
+ }
302
+ if (record.event === "task.execution.started") {
303
+ return "Execution started.";
304
+ }
305
+ if (record.event === "task.dispatched") {
306
+ return "Worker dispatched task.";
307
+ }
308
+ if (record.event === "task.fallback") {
309
+ return `Fallback used: ${this.toString(record.context.reason) ?? "unknown"}.`;
310
+ }
311
+ if (record.event === "repo.execution_mode_resolved") {
312
+ const mode = this.toString(record.context.addressesResolvedExecutionMode) ?? "unknown";
313
+ return `Workspace prepared (${mode}).`;
314
+ }
315
+ return this.buildEventHeadline(record);
316
+ }
317
+ toPanelEvent(record) {
318
+ const detail = this.buildEventDetail(record);
319
+ const taskId = this.toString(record.context.taskId);
320
+ const runId = this.toString(record.context.runId);
321
+ const level = record.level === "debug" ? "info" : record.level;
322
+ return {
323
+ at: record.timestampShort,
324
+ scope: this.resolveScope(record.event),
325
+ level,
326
+ headline: this.buildEventHeadline(record),
327
+ ...(detail ? { detail } : {}),
328
+ ...(taskId ? { taskId } : {}),
329
+ ...(runId ? { runId } : {})
330
+ };
331
+ }
332
+ pushPanelEvent(event) {
333
+ if (this.shouldSuppressRecentEvent(event)) {
334
+ return;
335
+ }
336
+ const prioritizedEvents = this.prioritizeRecentEvents([...this.state.recentEvents, event]);
337
+ this.state.recentEvents = prioritizedEvents.slice(-this.maxRecentEvents);
338
+ if (event.level === "warn" || event.level === "error" || event.scope === "alert") {
339
+ this.state.alerts = [event, ...this.state.alerts].slice(0, this.maxAlerts);
340
+ }
341
+ }
342
+ prioritizeRecentEvents(events) {
343
+ // Keep arrival order so events render top-to-bottom in the same sequence they happened.
344
+ return events;
345
+ }
346
+ shouldSuppressRecentEvent(event) {
347
+ if (event.scope === "triage" && event.headline === "Optimus Triage Session Restored") {
348
+ return true;
349
+ }
350
+ if (event.scope === "runner" && event.level === "info" && !event.taskId) {
351
+ return true;
352
+ }
353
+ return false;
354
+ }
355
+ resolveScope(event) {
356
+ if (event.startsWith("runtime.")) {
357
+ return "runtime";
358
+ }
359
+ if (event.startsWith("optimus.triage.") || event.startsWith("triage.")) {
360
+ return "triage";
361
+ }
362
+ if (event.startsWith("optimus.runner.")) {
363
+ return "runner";
364
+ }
365
+ if (event.startsWith("repo.") || event.startsWith("skills.")) {
366
+ return "repo";
367
+ }
368
+ if (event.includes("failed") || event.includes("timed_out") || event.includes("rejected")) {
369
+ return "alert";
370
+ }
371
+ return "task";
372
+ }
373
+ buildEventHeadline(record) {
374
+ switch (record.event) {
375
+ case "runtime.started":
376
+ return "Runtime started.";
377
+ case "runtime.ready":
378
+ return "Runtime ready.";
379
+ case "runtime.health":
380
+ return `Health refresh: queue ${this.toNumber(record.context.queueDepth)}, workers ${this.toNumber(record.context.activeWorkers)}, runs ${this.toNumber(record.context.activeRuns)}.`;
381
+ case "optimus.triage.starting":
382
+ return "Triage resident worker starting.";
383
+ case "optimus.triage.ready":
384
+ return "Triage resident worker ready.";
385
+ case "optimus.triage.start_failed":
386
+ return `Triage startup failed: ${this.toString(record.context.reason) ?? "unknown"}.`;
387
+ case "triage.accepted":
388
+ return `Accepted as ${this.toString(record.context.taskType) ?? "unknown"}.`;
389
+ case "triage.rejected":
390
+ return `Rejected: ${this.toString(record.context.reason) ?? "unknown"}.`;
391
+ case "task.received":
392
+ return `Task queued for ${this.toString(record.context.taskType) ?? "unknown"}.`;
393
+ case "task.dispatched":
394
+ return "Task dispatched to worker.";
395
+ case "task.execution.started":
396
+ return "Optimus execution started.";
397
+ case "task.publication.review_submit.started":
398
+ return "Code review submission started.";
399
+ case "task.publication.review_submit.completed":
400
+ return `Code review submission ${this.toString(record.context.publicationStatus) === "submitted" ? "completed" : "finished"}.`;
401
+ case "task.publication.review_submit.failed":
402
+ return `Code review submission failed: ${this.toString(record.context.reason) ?? "unknown"}.`;
403
+ case "optimus.runner.stream_event":
404
+ return this.toString(record.context.headline) ?? "Execution update.";
405
+ case "optimus.runner.result_parsed":
406
+ return `Execution result parsed: ${this.toString(record.context.status) ?? "unknown"}.`;
407
+ case "optimus.runner.turn_completed":
408
+ return "Execution turn completed.";
409
+ case "task.completed":
410
+ return "Task completed.";
411
+ case "task.cancel_requested":
412
+ return "Cancellation requested.";
413
+ case "task.cancel_forced":
414
+ return "Forced cancellation applied.";
415
+ case "task.canceled":
416
+ return "Task canceled.";
417
+ case "task.cancel_cleanup_completed":
418
+ return "Cancellation cleanup completed.";
419
+ case "task.cancel_cleanup_failed":
420
+ return `Cancellation cleanup failed: ${this.toString(record.context.reason) ?? "unknown"}.`;
421
+ case "task.failed":
422
+ return `Task failed: ${this.toString(record.context.reason) ?? "unknown"}.`;
423
+ case "task.timed_out":
424
+ return `Task timed out: ${this.toString(record.context.timeoutKind) ?? "unknown"}.`;
425
+ default:
426
+ return this.humanizeEventName(record.event);
427
+ }
428
+ }
429
+ buildEventDetail(record) {
430
+ if (record.event === "optimus.runner.stream_event") {
431
+ const itemType = this.toString(record.context.itemType);
432
+ const detail = this.toString(record.context.detail);
433
+ if (detail && itemType) {
434
+ return `${itemType}: ${detail}`;
435
+ }
436
+ if (detail) {
437
+ return detail;
438
+ }
439
+ return itemType ? `item=${itemType}` : undefined;
440
+ }
441
+ if (["task.failed", "triage.rejected", "optimus.triage.start_failed", "task.cancel_cleanup_failed", "task.publication.review_submit.failed"].includes(record.event)) {
442
+ return this.toString(record.context.reason) ?? this.toString(record.context.summary);
443
+ }
444
+ if (record.event === "task.publication.review_submit.started") {
445
+ const strategy = this.toString(record.context.publicationStrategy);
446
+ const topic = this.toString(record.context.topic);
447
+ return [strategy ? `strategy ${strategy}` : undefined, topic ? `topic ${topic}` : undefined].filter(Boolean).join(", ");
448
+ }
449
+ if (record.event === "task.publication.review_submit.completed") {
450
+ const status = this.toString(record.context.publicationStatus);
451
+ const reviewUrl = this.toString(record.context.reviewUrl);
452
+ const topic = this.toString(record.context.topic);
453
+ return [
454
+ status ? `status ${status}` : undefined,
455
+ topic ? `topic ${topic}` : undefined,
456
+ reviewUrl ? `review ${reviewUrl}` : undefined
457
+ ].filter(Boolean).join(", ");
458
+ }
459
+ if (["task.cancel_requested", "task.cancel_forced"].includes(record.event)) {
460
+ const source = this.toString(record.context.cancelSource);
461
+ const abortedRunCount = this.toNumber(record.context.abortedRunCount);
462
+ const parts = [];
463
+ if (source) {
464
+ parts.push(`source ${source}`);
465
+ }
466
+ if (abortedRunCount > 0) {
467
+ parts.push(`aborted runs ${abortedRunCount}`);
468
+ }
469
+ if (record.context.force === true) {
470
+ parts.push("force");
471
+ }
472
+ return parts.length > 0 ? parts.join(", ") : undefined;
473
+ }
474
+ if (record.event === "task.cancel_cleanup_completed") {
475
+ const removed = record.context.workspaceRemoved === true ? "workspace removed" : "workspace kept";
476
+ const artifacts = this.toNumber(record.context.artifactCount);
477
+ const duration = this.formatDuration(this.toNumber(record.context.durationMs));
478
+ const source = this.toString(record.context.cancelSource);
479
+ return [source ? `source ${source}` : undefined, removed, `artifacts ${artifacts}`, `time ${duration}`].filter(Boolean).join(", ");
480
+ }
481
+ return undefined;
482
+ }
483
+ // render() redraws only the panel area below the startup banner.
484
+ render(includeFooter = false) {
485
+ if (!this.started || !this.canRender()) {
486
+ return;
487
+ }
488
+ this.syncActiveTasks();
489
+ if (!includeFooter && !this.dirty) {
490
+ return;
491
+ }
492
+ const width = this.getTerminalWidth();
493
+ const lines = this.buildPanelLines(includeFooter, width).map((line) => this.fitLine(line, width));
494
+ const output = `${lines.join("\n")}\n`;
495
+ if (this.renderedLineCount > 0) {
496
+ process.stdout.write(`\u001B[${this.renderedLineCount}A\u001B[0J`);
497
+ }
498
+ process.stdout.write(output);
499
+ this.renderedLineCount = lines.length + 1;
500
+ this.dirty = false;
501
+ }
502
+ buildPanelLines(includeFooter, width) {
503
+ const focusedTask = this.state.activeTasks[0];
504
+ const recentEventLines = this.state.recentEvents.length === 0
505
+ ? [" none"]
506
+ : this.state.recentEvents.map((event) => this.buildRecentEventLine(event));
507
+ const lines = [
508
+ ...this.buildHeaderLines(width),
509
+ "",
510
+ ...this.buildStatusBlockLines(focusedTask),
511
+ "",
512
+ ...this.buildRecentEventBlockLines(recentEventLines)
513
+ ];
514
+ if (includeFooter) {
515
+ lines.push('', `${ANSI.bold}RUNTIME${ANSI.reset}`, ' panel stopped');
516
+ }
517
+ return lines;
518
+ }
519
+ buildStatusBlockLines(focusedTask) {
520
+ const values = [
521
+ this.colorPresence(`runtime ${this.state.runtime.running ? 'running' : 'stopped'}`, this.state.runtime.running),
522
+ this.colorPresence(`triage ${this.state.runtime.triageStatus}`, this.state.runtime.triageStatus === 'ready'),
523
+ this.colorPresence(`queue ${this.state.runtime.queueDepth}`, this.state.runtime.queueDepth > 0),
524
+ this.colorPresence(`workers ${this.state.runtime.activeWorkers}`, this.state.runtime.activeWorkers > 0),
525
+ this.colorPresence(`runs ${this.state.runtime.activeRuns}`, this.state.runtime.activeRuns > 0),
526
+ `uptime ${this.formatDuration(this.state.runtime.uptimeMs)}`
527
+ ];
528
+ if (focusedTask) {
529
+ values.push(this.colorPresence(`focus ${focusedTask.taskId} ${focusedTask.taskType}`, true));
530
+ values.push(this.colorPresence(focusedTask.currentAction ?? 'processing', true));
531
+ }
532
+ else {
533
+ values.push(this.colorPresence('focus none', false));
534
+ }
535
+ return [
536
+ `${ANSI.bold}STATUS${ANSI.reset}`,
537
+ ...this.wrapStatusValues(values, this.getTerminalWidth())
538
+ ];
539
+ }
540
+ wrapStatusValues(values, width) {
541
+ const indent = ' ';
542
+ const separator = `${ANSI.gray} · ${ANSI.reset}`;
543
+ const maxWidth = Math.max(24, width - 2);
544
+ const lines = [];
545
+ let currentLine = indent;
546
+ for (const value of values) {
547
+ const candidate = currentLine === indent ? `${indent}${value}` : `${currentLine}${separator}${value}`;
548
+ if (this.visibleWidth(candidate) <= maxWidth) {
549
+ currentLine = candidate;
550
+ continue;
551
+ }
552
+ if (currentLine !== indent) {
553
+ lines.push(currentLine);
554
+ }
555
+ const singleLine = `${indent}${value}`;
556
+ currentLine = this.visibleWidth(singleLine) <= maxWidth
557
+ ? singleLine
558
+ : `${indent}${this.truncateAnsiAware(value, Math.max(8, maxWidth - this.visibleWidth(indent)), true)}`;
559
+ }
560
+ if (currentLine !== indent) {
561
+ lines.push(currentLine);
562
+ }
563
+ return lines.length > 0 ? lines : [indent];
564
+ }
565
+ buildRecentEventBlockLines(eventLines) {
566
+ return [
567
+ `${ANSI.bold}RECENT EVENTS${ANSI.reset}`,
568
+ ...eventLines
569
+ ];
570
+ }
571
+ buildRecentEventLine(event) {
572
+ const owner = event.taskId ? ` ${this.shortenId(event.taskId, "task#")}` : "";
573
+ const level = event.level === "info" ? "" : `/${event.level}`;
574
+ const scopeLabel = `[${event.scope}${level}]`;
575
+ const detail = event.detail ? ` ${ANSI.gray}·${ANSI.reset} ${this.formatRecentEventDetail(event.detail)}` : "";
576
+ return ` ${ANSI.gray}${event.at}${ANSI.reset} ${scopeLabel}${owner} ${event.headline}${detail}`;
577
+ }
578
+ formatRecentEventDetail(detail) {
579
+ const separatorIndex = detail.indexOf(": ");
580
+ if (separatorIndex <= 0) {
581
+ return detail;
582
+ }
583
+ const itemType = detail.slice(0, separatorIndex);
584
+ const payload = detail.slice(separatorIndex + 2);
585
+ switch (itemType) {
586
+ case "command_execution":
587
+ return `${ANSI.yellow}Command${ANSI.reset} ${payload}`;
588
+ case "reasoning":
589
+ return `${ANSI.blue}Thinking${ANSI.reset} ${payload}`;
590
+ case "agent_message":
591
+ return `${ANSI.green}Message${ANSI.reset} ${payload}`;
592
+ case "mcp_tool_call":
593
+ return `${ANSI.magenta}Tool${ANSI.reset} ${payload}`;
594
+ case "web_search":
595
+ return `${ANSI.yellow}Search${ANSI.reset} ${payload}`;
596
+ case "todo_list":
597
+ return `${ANSI.blue}Plan${ANSI.reset} ${payload}`;
598
+ case "file_change":
599
+ return `${ANSI.magenta}Edit${ANSI.reset} ${payload}`;
600
+ default:
601
+ return `${this.humanizeItemType(itemType)} ${payload}`;
602
+ }
603
+ }
604
+ buildHeaderLines(_width) {
605
+ const brand = `${ANSI.bold}${ANSI.orange}OPTIMUS${ANSI.reset}`;
606
+ return [
607
+ '',
608
+ ...this.buildLogoLines(),
609
+ '',
610
+ ` ${brand}`
611
+ ];
612
+ }
613
+ buildLogoLines() {
614
+ return LOGO_LINES.map((line, index) => ` ${this.colorLogoLine(line, index, LOGO_LINES.length)}`);
615
+ }
616
+ colorLogoLine(line, index, totalLines) {
617
+ const accent = index === Math.floor(totalLines / 2) ? ANSI.bold : '';
618
+ const color = index < 2 ? ANSI.orangeSoft : ANSI.orange;
619
+ return `${accent}${color}${line}${ANSI.reset}`;
620
+ }
621
+ colorRuntimeState(state) {
622
+ return state === "running" ? `${ANSI.green}${state}${ANSI.reset}` : `${ANSI.gray}${state}${ANSI.reset}`;
623
+ }
624
+ colorTriageState(state) {
625
+ switch (state) {
626
+ case "ready":
627
+ return `${ANSI.green}${state}${ANSI.reset}`;
628
+ default:
629
+ return `${ANSI.gray}${state}${ANSI.reset}`;
630
+ }
631
+ }
632
+ colorPresence(value, active) {
633
+ return active ? `${ANSI.green}${value}${ANSI.reset}` : `${ANSI.gray}${value}${ANSI.reset}`;
634
+ }
635
+ colorStrong(value) {
636
+ return `${ANSI.bold}${value}${ANSI.reset}`;
637
+ }
638
+ colorTaskType(taskType) {
639
+ return `${ANSI.bold}${taskType}${ANSI.reset}`;
640
+ }
641
+ colorTaskStatus(status) {
642
+ switch (status) {
643
+ case "completed":
644
+ return `${ANSI.green}${status}${ANSI.reset}`;
645
+ case "failed":
646
+ case "canceled":
647
+ return `${ANSI.red}${status}${ANSI.reset}`;
648
+ case "patch_generated":
649
+ case "validating":
650
+ return `${ANSI.magenta}${status}${ANSI.reset}`;
651
+ case "running":
652
+ case "preparing":
653
+ return `${ANSI.yellow}${status}${ANSI.reset}`;
654
+ default:
655
+ return `${ANSI.gray}${status}${ANSI.reset}`;
656
+ }
657
+ }
658
+ colorHealth(health) {
659
+ switch (health) {
660
+ case "healthy_running":
661
+ case "completed":
662
+ case "recovered":
663
+ return `${ANSI.green}${health}${ANSI.reset}`;
664
+ case "slow_running":
665
+ case "waiting_start":
666
+ case "cancel_requested":
667
+ return `${ANSI.yellow}${health}${ANSI.reset}`;
668
+ case "suspected_stall":
669
+ case "failed":
670
+ case "timed_out":
671
+ case "canceled":
672
+ return `${ANSI.red}${health}${ANSI.reset}`;
673
+ default:
674
+ return `${ANSI.gray}${health}${ANSI.reset}`;
675
+ }
676
+ }
677
+ colorValidationState(state) {
678
+ switch (state) {
679
+ case "completed":
680
+ return `${ANSI.green}${state}${ANSI.reset}`;
681
+ case "in_progress":
682
+ case "active":
683
+ return `${ANSI.yellow}${state}${ANSI.reset}`;
684
+ case "pending":
685
+ return `${ANSI.magenta}${state}${ANSI.reset}`;
686
+ default:
687
+ return `${ANSI.gray}${state}${ANSI.reset}`;
688
+ }
689
+ }
690
+ colorPhase(phase) {
691
+ const label = this.describePhase(phase);
692
+ if (label.includes("running")) {
693
+ return `${ANSI.blue}${label}${ANSI.reset}`;
694
+ }
695
+ if (label.includes("verify") || label.includes("patch")) {
696
+ return `${ANSI.magenta}${label}${ANSI.reset}`;
697
+ }
698
+ if (label.includes("preparing") || label.includes("starting")) {
699
+ return `${ANSI.yellow}${label}${ANSI.reset}`;
700
+ }
701
+ if (label.includes("cancel")) {
702
+ return `${ANSI.red}${label}${ANSI.reset}`;
703
+ }
704
+ return `${ANSI.gray}${label}${ANSI.reset}`;
705
+ }
706
+ colorAction(action) {
707
+ if (action.includes("planning") || action.includes("plan")) {
708
+ return `${ANSI.blue}${action}${ANSI.reset}`;
709
+ }
710
+ if (action.includes("running command") || action.includes("calling tool") || action.includes("searching")) {
711
+ return `${ANSI.yellow}${action}${ANSI.reset}`;
712
+ }
713
+ if (action.includes("editing") || action.includes("fallback")) {
714
+ return `${ANSI.magenta}${action}${ANSI.reset}`;
715
+ }
716
+ if (action.includes("finalizing")) {
717
+ return `${ANSI.green}${action}${ANSI.reset}`;
718
+ }
719
+ return `${ANSI.gray}${action}${ANSI.reset}`;
720
+ }
721
+ colorScope(scope) {
722
+ switch (scope) {
723
+ case "runtime":
724
+ return `${ANSI.bold}${scope}${ANSI.reset}`;
725
+ case "triage":
726
+ return `${ANSI.yellow}${scope}${ANSI.reset}`;
727
+ case "runner":
728
+ return `${ANSI.blue}${scope}${ANSI.reset}`;
729
+ case "alert":
730
+ return `${ANSI.red}${scope}${ANSI.reset}`;
731
+ case "repo":
732
+ return `${ANSI.magenta}${scope}${ANSI.reset}`;
733
+ default:
734
+ return `${ANSI.gray}${scope}${ANSI.reset}`;
735
+ }
736
+ }
737
+ colorLevel(level) {
738
+ switch (level) {
739
+ case "error":
740
+ return `${ANSI.red}${level}${ANSI.reset}`;
741
+ case "warn":
742
+ return `${ANSI.yellow}${level}${ANSI.reset}`;
743
+ default:
744
+ return `${ANSI.gray}${level}${ANSI.reset}`;
745
+ }
746
+ }
747
+ colorAlertHeadline(headline) {
748
+ return `${ANSI.bold}${headline}${ANSI.reset}`;
749
+ }
750
+ colorIdle(idleMs) {
751
+ const value = this.formatDuration(idleMs);
752
+ if (idleMs >= 300000) {
753
+ return `${ANSI.red}${value}${ANSI.reset}`;
754
+ }
755
+ if (idleMs >= 60000) {
756
+ return `${ANSI.yellow}${value}${ANSI.reset}`;
757
+ }
758
+ return `${ANSI.gray}${value}${ANSI.reset}`;
759
+ }
760
+ colorCount(value, color) {
761
+ return `${ANSI[color]}${value}${ANSI.reset}`;
762
+ }
763
+ describeCurrentAction(record) {
764
+ if (record.event === "task.execution.started") {
765
+ return "starting execution";
766
+ }
767
+ if (record.event === "task.dispatched") {
768
+ return "waiting for agent progress";
769
+ }
770
+ if (record.event === "repo.execution_mode_resolved") {
771
+ return "preparing workspace";
772
+ }
773
+ if (record.event === "task.fallback") {
774
+ return "using result fallback";
775
+ }
776
+ if (record.event !== "optimus.runner.stream_event") {
777
+ return undefined;
778
+ }
779
+ const itemType = this.toString(record.context.itemType);
780
+ switch (itemType) {
781
+ case "reasoning":
782
+ return "planning";
783
+ case "command_execution":
784
+ return "running command";
785
+ case "mcp_tool_call":
786
+ return "calling tool";
787
+ case "web_search":
788
+ return "searching";
789
+ case "file_change":
790
+ return "editing files";
791
+ case "agent_message":
792
+ return "finalizing result";
793
+ case "todo_list":
794
+ return "updating plan";
795
+ default:
796
+ return "processing";
797
+ }
798
+ }
799
+ resolvePipelineStage(focusedTask) {
800
+ if (!focusedTask) {
801
+ return "triage";
802
+ }
803
+ const action = (focusedTask.currentAction ?? "").toLowerCase();
804
+ const phase = String(focusedTask.phase).toLowerCase();
805
+ const status = focusedTask.status.toLowerCase();
806
+ if (status === "queued") {
807
+ return "triage";
808
+ }
809
+ if (action.includes("preparing") || phase.includes("starting") || phase.includes("preparing")) {
810
+ return "prepare";
811
+ }
812
+ if (action.includes("editing") || phase.includes("patch") || status === "patch_generated") {
813
+ return "patch";
814
+ }
815
+ if (phase.includes("validat") || status === "validating") {
816
+ return "validate";
817
+ }
818
+ if (action.includes("review")) {
819
+ return "review";
820
+ }
821
+ if (action.includes("final") || status === "completed") {
822
+ return "finalize";
823
+ }
824
+ return "inspect";
825
+ }
826
+ colorPipelineStage(stage, active) {
827
+ const label = active ? `[${stage.toUpperCase()}]` : stage.toUpperCase();
828
+ if (!active) {
829
+ return `${ANSI.gray}${label}${ANSI.reset}`;
830
+ }
831
+ switch (stage) {
832
+ case "triage":
833
+ return `${ANSI.yellow}${label}${ANSI.reset}`;
834
+ case "prepare":
835
+ return `${ANSI.cyan}${label}${ANSI.reset}`;
836
+ case "inspect":
837
+ return `${ANSI.blue}${label}${ANSI.reset}`;
838
+ case "patch":
839
+ return `${ANSI.magenta}${label}${ANSI.reset}`;
840
+ case "validate":
841
+ return `${ANSI.green}${label}${ANSI.reset}`;
842
+ case "review":
843
+ return `${ANSI.yellow}${label}${ANSI.reset}`;
844
+ case "finalize":
845
+ return `${ANSI.green}${label}${ANSI.reset}`;
846
+ default:
847
+ return `${ANSI.gray}${label}${ANSI.reset}`;
848
+ }
849
+ }
850
+ describeValidationState(task) {
851
+ const phase = String(task.phase).toLowerCase();
852
+ const headline = task.lastHeadline.toLowerCase();
853
+ if (task.status === "completed") {
854
+ return "completed";
855
+ }
856
+ if (phase.includes("validat")) {
857
+ return "in_progress";
858
+ }
859
+ if (headline.includes("compile") || headline.includes("test") || headline.includes("verify")) {
860
+ return "active";
861
+ }
862
+ if (task.status === "patch_generated") {
863
+ return "pending";
864
+ }
865
+ return "not_started";
866
+ }
867
+ describePhase(phase) {
868
+ switch (phase) {
869
+ case "created":
870
+ case "leased":
871
+ case "bootstrapping":
872
+ return "preparing";
873
+ case "sdk_starting":
874
+ return "optimus_starting";
875
+ case "sdk_running":
876
+ case "sdk_streaming":
877
+ return "optimus_running";
878
+ case "validating":
879
+ return "verifying";
880
+ case "patch_generated":
881
+ return "patch_ready";
882
+ case "cancel_requested":
883
+ return "canceling";
884
+ default:
885
+ return phase;
886
+ }
887
+ }
888
+ humanizeItemType(itemType) {
889
+ return itemType
890
+ .replace(/_/g, " ")
891
+ .replace(/\b\w/g, (char) => char.toUpperCase());
892
+ }
893
+ humanizeEventName(event) {
894
+ return event
895
+ .replace(/[._]/g, " ")
896
+ .replace(/\b\w/g, (char) => char.toUpperCase());
897
+ }
898
+ formatDuration(valueMs) {
899
+ const totalSeconds = Math.max(0, Math.floor(valueMs / 1000));
900
+ const hours = Math.floor(totalSeconds / 3600);
901
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
902
+ const seconds = totalSeconds % 60;
903
+ if (hours > 0) {
904
+ return `${hours}h${String(minutes).padStart(2, "0")}m${String(seconds).padStart(2, "0")}s`;
905
+ }
906
+ return `${String(minutes).padStart(2, "0")}m${String(seconds).padStart(2, "0")}s`;
907
+ }
908
+ fitLine(value, width) {
909
+ const availableWidth = Math.max(20, width - 1);
910
+ if (this.visibleWidth(value) <= availableWidth) {
911
+ return value;
912
+ }
913
+ return this.truncateAnsiAware(value, Math.max(4, availableWidth - 3), true);
914
+ }
915
+ getTerminalWidth() {
916
+ return Math.max(24, process.stdout.columns ?? 120);
917
+ }
918
+ visibleWidth(value) {
919
+ let width = 0;
920
+ for (const char of value.replace(ANSI_PATTERN, "")) {
921
+ width += this.charDisplayWidth(char);
922
+ }
923
+ return width;
924
+ }
925
+ truncateAnsiAware(value, maxWidth, withEllipsis = false) {
926
+ let width = 0;
927
+ let index = 0;
928
+ let output = "";
929
+ const suffix = withEllipsis ? "..." : "";
930
+ const suffixWidth = withEllipsis ? 3 : 0;
931
+ while (index < value.length) {
932
+ if (value[index] === "\u001B") {
933
+ const ansiMatch = value.slice(index).match(/^\u001B\[[0-9;]*m/);
934
+ if (ansiMatch) {
935
+ output += ansiMatch[0];
936
+ index += ansiMatch[0].length;
937
+ continue;
938
+ }
939
+ }
940
+ const codePoint = value.codePointAt(index);
941
+ if (codePoint === undefined) {
942
+ break;
943
+ }
944
+ const char = String.fromCodePoint(codePoint);
945
+ const charWidth = this.charDisplayWidth(char);
946
+ if (width + charWidth + suffixWidth > maxWidth) {
947
+ break;
948
+ }
949
+ output += char;
950
+ width += charWidth;
951
+ index += char.length;
952
+ }
953
+ const needsReset = output.includes("\u001B[");
954
+ return `${output}${suffix}${needsReset ? ANSI.reset : ""}`;
955
+ }
956
+ charDisplayWidth(char) {
957
+ const codePoint = char.codePointAt(0);
958
+ if (codePoint === undefined) {
959
+ return 0;
960
+ }
961
+ if (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f)) {
962
+ return 0;
963
+ }
964
+ if (codePoint >= 0x1100 && (codePoint <= 0x115f ||
965
+ codePoint === 0x2329 ||
966
+ codePoint === 0x232a ||
967
+ (codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||
968
+ (codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
969
+ (codePoint >= 0xf900 && codePoint <= 0xfaff) ||
970
+ (codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
971
+ (codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
972
+ (codePoint >= 0xff00 && codePoint <= 0xff60) ||
973
+ (codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
974
+ (codePoint >= 0x1f300 && codePoint <= 0x1f64f) ||
975
+ (codePoint >= 0x1f900 && codePoint <= 0x1f9ff) ||
976
+ (codePoint >= 0x20000 && codePoint <= 0x3fffd))) {
977
+ return 2;
978
+ }
979
+ return 1;
980
+ }
981
+ shortenId(value, prefix) {
982
+ const parts = value.split("-").filter(Boolean);
983
+ return `${prefix}${parts[parts.length - 1] ?? value}`;
984
+ }
985
+ shortenPath(value) {
986
+ const home = process.env.HOME;
987
+ if (home && value.startsWith(home)) {
988
+ return `~${value.slice(home.length)}`;
989
+ }
990
+ return value;
991
+ }
992
+ toString(value) {
993
+ return typeof value === "string" && value.length > 0 ? value : undefined;
994
+ }
995
+ toNumber(value) {
996
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
997
+ }
998
+ isTaskStatus(value) {
999
+ return value === "queued" || value === "preparing" || value === "running" || value === "patch_generated" || value === "validating" || value === "completed" || value === "needs_human" || value === "failed" || value === "canceled";
1000
+ }
1001
+ isTaskRunStatus(value) {
1002
+ return value === "created" || value === "leased" || value === "bootstrapping" || value === "sdk_starting" || value === "sdk_running" || value === "sdk_streaming" || value === "validating" || value === "completed" || value === "failed" || value === "timed_out" || value === "cancel_requested" || value === "canceled" || value === "recovered";
1003
+ }
1004
+ isTaskRunHealth(value) {
1005
+ return value === "waiting_start" || value === "healthy_running" || value === "slow_running" || value === "suspected_stall" || value === "completed" || value === "failed" || value === "timed_out" || value === "cancel_requested" || value === "canceled" || value === "recovered";
1006
+ }
1007
+ }
1008
+ //# sourceMappingURL=runtime-panel.js.map