codex-autorunner 0.1.2__py3-none-any.whl → 1.0.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.
- codex_autorunner/__main__.py +4 -0
- codex_autorunner/agents/opencode/client.py +68 -35
- codex_autorunner/agents/opencode/logging.py +21 -5
- codex_autorunner/agents/opencode/run_prompt.py +1 -0
- codex_autorunner/agents/opencode/runtime.py +118 -30
- codex_autorunner/agents/opencode/supervisor.py +36 -48
- codex_autorunner/agents/registry.py +136 -8
- codex_autorunner/api.py +25 -0
- codex_autorunner/bootstrap.py +16 -35
- codex_autorunner/cli.py +157 -139
- codex_autorunner/core/about_car.py +44 -32
- codex_autorunner/core/adapter_utils.py +21 -0
- codex_autorunner/core/app_server_logging.py +7 -3
- codex_autorunner/core/app_server_prompts.py +27 -260
- codex_autorunner/core/app_server_threads.py +15 -26
- codex_autorunner/core/codex_runner.py +6 -0
- codex_autorunner/core/config.py +390 -100
- codex_autorunner/core/docs.py +10 -2
- codex_autorunner/core/drafts.py +82 -0
- codex_autorunner/core/engine.py +278 -262
- codex_autorunner/core/flows/__init__.py +25 -0
- codex_autorunner/core/flows/controller.py +178 -0
- codex_autorunner/core/flows/definition.py +82 -0
- codex_autorunner/core/flows/models.py +75 -0
- codex_autorunner/core/flows/runtime.py +351 -0
- codex_autorunner/core/flows/store.py +485 -0
- codex_autorunner/core/flows/transition.py +133 -0
- codex_autorunner/core/flows/worker_process.py +242 -0
- codex_autorunner/core/hub.py +15 -9
- codex_autorunner/core/locks.py +4 -0
- codex_autorunner/core/prompt.py +15 -7
- codex_autorunner/core/redaction.py +29 -0
- codex_autorunner/core/review_context.py +5 -8
- codex_autorunner/core/run_index.py +6 -0
- codex_autorunner/core/runner_process.py +5 -2
- codex_autorunner/core/state.py +0 -88
- codex_autorunner/core/static_assets.py +55 -0
- codex_autorunner/core/supervisor_utils.py +67 -0
- codex_autorunner/core/update.py +20 -11
- codex_autorunner/core/update_runner.py +2 -0
- codex_autorunner/core/utils.py +29 -2
- codex_autorunner/discovery.py +2 -4
- codex_autorunner/flows/ticket_flow/__init__.py +3 -0
- codex_autorunner/flows/ticket_flow/definition.py +91 -0
- codex_autorunner/integrations/agents/__init__.py +27 -0
- codex_autorunner/integrations/agents/agent_backend.py +142 -0
- codex_autorunner/integrations/agents/codex_backend.py +307 -0
- codex_autorunner/integrations/agents/opencode_backend.py +325 -0
- codex_autorunner/integrations/agents/run_event.py +71 -0
- codex_autorunner/integrations/app_server/client.py +576 -92
- codex_autorunner/integrations/app_server/supervisor.py +59 -33
- codex_autorunner/integrations/telegram/adapter.py +141 -167
- codex_autorunner/integrations/telegram/api_schemas.py +120 -0
- codex_autorunner/integrations/telegram/config.py +175 -0
- codex_autorunner/integrations/telegram/constants.py +16 -1
- codex_autorunner/integrations/telegram/dispatch.py +17 -0
- codex_autorunner/integrations/telegram/doctor.py +47 -0
- codex_autorunner/integrations/telegram/handlers/callbacks.py +0 -4
- codex_autorunner/integrations/telegram/handlers/commands/__init__.py +2 -0
- codex_autorunner/integrations/telegram/handlers/commands/execution.py +53 -57
- codex_autorunner/integrations/telegram/handlers/commands/files.py +2 -6
- codex_autorunner/integrations/telegram/handlers/commands/flows.py +227 -0
- codex_autorunner/integrations/telegram/handlers/commands/formatting.py +1 -1
- codex_autorunner/integrations/telegram/handlers/commands/github.py +41 -582
- codex_autorunner/integrations/telegram/handlers/commands/workspace.py +8 -8
- codex_autorunner/integrations/telegram/handlers/commands_runtime.py +133 -475
- codex_autorunner/integrations/telegram/handlers/commands_spec.py +11 -4
- codex_autorunner/integrations/telegram/handlers/messages.py +120 -9
- codex_autorunner/integrations/telegram/helpers.py +88 -16
- codex_autorunner/integrations/telegram/outbox.py +208 -37
- codex_autorunner/integrations/telegram/progress_stream.py +3 -10
- codex_autorunner/integrations/telegram/service.py +214 -40
- codex_autorunner/integrations/telegram/state.py +100 -2
- codex_autorunner/integrations/telegram/ticket_flow_bridge.py +322 -0
- codex_autorunner/integrations/telegram/transport.py +36 -3
- codex_autorunner/integrations/telegram/trigger_mode.py +53 -0
- codex_autorunner/manifest.py +2 -0
- codex_autorunner/plugin_api.py +22 -0
- codex_autorunner/routes/__init__.py +23 -14
- codex_autorunner/routes/analytics.py +239 -0
- codex_autorunner/routes/base.py +81 -109
- codex_autorunner/routes/file_chat.py +836 -0
- codex_autorunner/routes/flows.py +980 -0
- codex_autorunner/routes/messages.py +459 -0
- codex_autorunner/routes/system.py +6 -1
- codex_autorunner/routes/usage.py +87 -0
- codex_autorunner/routes/workspace.py +271 -0
- codex_autorunner/server.py +2 -1
- codex_autorunner/static/agentControls.js +1 -0
- codex_autorunner/static/agentEvents.js +248 -0
- codex_autorunner/static/app.js +25 -22
- codex_autorunner/static/autoRefresh.js +29 -1
- codex_autorunner/static/bootstrap.js +1 -0
- codex_autorunner/static/bus.js +1 -0
- codex_autorunner/static/cache.js +1 -0
- codex_autorunner/static/constants.js +20 -4
- codex_autorunner/static/dashboard.js +162 -196
- codex_autorunner/static/diffRenderer.js +37 -0
- codex_autorunner/static/docChatCore.js +324 -0
- codex_autorunner/static/docChatStorage.js +65 -0
- codex_autorunner/static/docChatVoice.js +65 -0
- codex_autorunner/static/docEditor.js +133 -0
- codex_autorunner/static/env.js +1 -0
- codex_autorunner/static/eventSummarizer.js +166 -0
- codex_autorunner/static/fileChat.js +182 -0
- codex_autorunner/static/health.js +155 -0
- codex_autorunner/static/hub.js +41 -118
- codex_autorunner/static/index.html +787 -858
- codex_autorunner/static/liveUpdates.js +1 -0
- codex_autorunner/static/loader.js +1 -0
- codex_autorunner/static/messages.js +470 -0
- codex_autorunner/static/mobileCompact.js +2 -1
- codex_autorunner/static/settings.js +24 -211
- codex_autorunner/static/styles.css +7567 -3865
- codex_autorunner/static/tabs.js +28 -5
- codex_autorunner/static/terminal.js +14 -0
- codex_autorunner/static/terminalManager.js +34 -59
- codex_autorunner/static/ticketChatActions.js +333 -0
- codex_autorunner/static/ticketChatEvents.js +16 -0
- codex_autorunner/static/ticketChatStorage.js +16 -0
- codex_autorunner/static/ticketChatStream.js +264 -0
- codex_autorunner/static/ticketEditor.js +750 -0
- codex_autorunner/static/ticketVoice.js +9 -0
- codex_autorunner/static/tickets.js +1315 -0
- codex_autorunner/static/utils.js +32 -3
- codex_autorunner/static/voice.js +1 -0
- codex_autorunner/static/workspace.js +672 -0
- codex_autorunner/static/workspaceApi.js +53 -0
- codex_autorunner/static/workspaceFileBrowser.js +504 -0
- codex_autorunner/tickets/__init__.py +20 -0
- codex_autorunner/tickets/agent_pool.py +377 -0
- codex_autorunner/tickets/files.py +85 -0
- codex_autorunner/tickets/frontmatter.py +55 -0
- codex_autorunner/tickets/lint.py +102 -0
- codex_autorunner/tickets/models.py +95 -0
- codex_autorunner/tickets/outbox.py +232 -0
- codex_autorunner/tickets/replies.py +179 -0
- codex_autorunner/tickets/runner.py +823 -0
- codex_autorunner/tickets/spec_ingest.py +77 -0
- codex_autorunner/web/app.py +269 -91
- codex_autorunner/web/middleware.py +3 -4
- codex_autorunner/web/schemas.py +89 -109
- codex_autorunner/web/static_assets.py +1 -44
- codex_autorunner/workspace/__init__.py +40 -0
- codex_autorunner/workspace/paths.py +319 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/METADATA +18 -21
- codex_autorunner-1.0.0.dist-info/RECORD +251 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/WHEEL +1 -1
- codex_autorunner/agents/execution/policy.py +0 -292
- codex_autorunner/agents/factory.py +0 -52
- codex_autorunner/agents/orchestrator.py +0 -358
- codex_autorunner/core/doc_chat.py +0 -1446
- codex_autorunner/core/snapshot.py +0 -580
- codex_autorunner/integrations/github/chatops.py +0 -268
- codex_autorunner/integrations/github/pr_flow.py +0 -1314
- codex_autorunner/routes/docs.py +0 -381
- codex_autorunner/routes/github.py +0 -327
- codex_autorunner/routes/runs.py +0 -250
- codex_autorunner/spec_ingest.py +0 -812
- codex_autorunner/static/docChatActions.js +0 -287
- codex_autorunner/static/docChatEvents.js +0 -300
- codex_autorunner/static/docChatRender.js +0 -205
- codex_autorunner/static/docChatStream.js +0 -361
- codex_autorunner/static/docs.js +0 -20
- codex_autorunner/static/docsClipboard.js +0 -69
- codex_autorunner/static/docsCrud.js +0 -257
- codex_autorunner/static/docsDocUpdates.js +0 -62
- codex_autorunner/static/docsDrafts.js +0 -16
- codex_autorunner/static/docsElements.js +0 -69
- codex_autorunner/static/docsInit.js +0 -285
- codex_autorunner/static/docsParse.js +0 -160
- codex_autorunner/static/docsSnapshot.js +0 -87
- codex_autorunner/static/docsSpecIngest.js +0 -263
- codex_autorunner/static/docsState.js +0 -127
- codex_autorunner/static/docsThreadRegistry.js +0 -44
- codex_autorunner/static/docsUi.js +0 -153
- codex_autorunner/static/docsVoice.js +0 -56
- codex_autorunner/static/github.js +0 -504
- codex_autorunner/static/logs.js +0 -678
- codex_autorunner/static/review.js +0 -157
- codex_autorunner/static/runs.js +0 -418
- codex_autorunner/static/snapshot.js +0 -124
- codex_autorunner/static/state.js +0 -94
- codex_autorunner/static/todoPreview.js +0 -27
- codex_autorunner/workspace.py +0 -16
- codex_autorunner-0.1.2.dist-info/RECORD +0 -222
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/entry_points.txt +0 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/top_level.txt +0 -0
codex_autorunner/static/tabs.js
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
1
2
|
import { publish } from "./bus.js";
|
|
2
|
-
import { getUrlParams, updateUrlParams } from "./utils.js";
|
|
3
|
+
import { escapeHtml, getUrlParams, updateUrlParams } from "./utils.js";
|
|
3
4
|
const tabs = [];
|
|
4
|
-
export function registerTab(id, label) {
|
|
5
|
-
tabs.push({ id, label });
|
|
5
|
+
export function registerTab(id, label, opts = {}) {
|
|
6
|
+
tabs.push({ id, label, hidden: Boolean(opts.hidden) });
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
+
let setActivePanelFn = null;
|
|
9
|
+
let pendingActivate = null;
|
|
10
|
+
export function activateTab(id) {
|
|
11
|
+
if (setActivePanelFn) {
|
|
12
|
+
setActivePanelFn(id);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
pendingActivate = id;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function initTabs(defaultTab = "analytics") {
|
|
8
19
|
const container = document.querySelector(".tabs");
|
|
9
20
|
if (!container)
|
|
10
21
|
return;
|
|
@@ -17,11 +28,18 @@ export function initTabs(defaultTab = "dashboard") {
|
|
|
17
28
|
updateUrlParams({ tab: id });
|
|
18
29
|
publish("tab:change", id);
|
|
19
30
|
};
|
|
31
|
+
setActivePanelFn = setActivePanel;
|
|
20
32
|
tabs.forEach(tab => {
|
|
33
|
+
if (tab.hidden) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
21
36
|
const btn = document.createElement("button");
|
|
22
37
|
btn.className = "tab";
|
|
23
38
|
btn.dataset.target = tab.id;
|
|
24
|
-
btn.
|
|
39
|
+
btn.innerHTML = `
|
|
40
|
+
<span class="tab-label">${escapeHtml(tab.label)}</span>
|
|
41
|
+
<span class="badge hidden" id="tab-badge-${tab.id}"></span>
|
|
42
|
+
`;
|
|
25
43
|
btn.addEventListener("click", () => setActivePanel(tab.id));
|
|
26
44
|
container.appendChild(btn);
|
|
27
45
|
});
|
|
@@ -38,4 +56,9 @@ export function initTabs(defaultTab = "dashboard") {
|
|
|
38
56
|
else if (tabs.length > 0) {
|
|
39
57
|
setActivePanel(tabs[0].id);
|
|
40
58
|
}
|
|
59
|
+
if (pendingActivate && tabs.some((t) => t.id === pendingActivate)) {
|
|
60
|
+
const id = pendingActivate;
|
|
61
|
+
pendingActivate = null;
|
|
62
|
+
setActivePanel(id);
|
|
63
|
+
}
|
|
41
64
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
1
2
|
import { TerminalManager } from "./terminalManager.js";
|
|
2
3
|
let terminalManager = null;
|
|
3
4
|
export function getTerminalManager() {
|
|
@@ -5,8 +6,21 @@ export function getTerminalManager() {
|
|
|
5
6
|
}
|
|
6
7
|
export function initTerminal() {
|
|
7
8
|
if (terminalManager) {
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
if (typeof terminalManager.fit === "function") {
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
terminalManager.fit();
|
|
13
|
+
}
|
|
8
14
|
return;
|
|
9
15
|
}
|
|
10
16
|
terminalManager = new TerminalManager();
|
|
11
17
|
terminalManager.init();
|
|
18
|
+
// Ensure terminal is resized to fit container after initialization
|
|
19
|
+
if (terminalManager) {
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
if (typeof terminalManager.fit === "function") {
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
|
+
terminalManager.fit();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
12
26
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
1
2
|
import { api, flash, buildWsUrl, getAuthToken, isMobileViewport } from "./utils.js";
|
|
2
3
|
import { getSelectedAgent, getSelectedModel, getSelectedReasoning, initAgentControls, } from "./agentControls.js";
|
|
3
4
|
function base64UrlEncode(value) {
|
|
@@ -44,21 +45,7 @@ const XTERM_COLOR_MODE_PALETTE_16 = 0x01000000;
|
|
|
44
45
|
const XTERM_COLOR_MODE_PALETTE_256 = 0x02000000;
|
|
45
46
|
const XTERM_COLOR_MODE_RGB = 0x03000000;
|
|
46
47
|
const CAR_CONTEXT_HOOK_ID = "car_context";
|
|
47
|
-
const
|
|
48
|
-
const CAR_CONTEXT_KEYWORDS = [
|
|
49
|
-
"car",
|
|
50
|
-
"codex",
|
|
51
|
-
"todo",
|
|
52
|
-
"progress",
|
|
53
|
-
"opinions",
|
|
54
|
-
"spec",
|
|
55
|
-
"summary",
|
|
56
|
-
"autorunner",
|
|
57
|
-
"work docs",
|
|
58
|
-
];
|
|
59
|
-
const GITHUB_LINK_RE = /https?:\/\/github\.com\/[^/\s]+\/[^/\s]+\/(?:issues|pull)\/\d+(?:[/?#][^\s]*)?/i;
|
|
60
|
-
const CAR_CONTEXT_HINT_TEXT = "Context: read .codex-autorunner/ABOUT_CAR.md for repo-specific rules.";
|
|
61
|
-
const CAR_CONTEXT_HINT = wrapInjectedContext(CAR_CONTEXT_HINT_TEXT);
|
|
48
|
+
const CAR_CONTEXT_HINT = wrapInjectedContext(CONSTANTS.PROMPTS.CAR_CONTEXT_HINT);
|
|
62
49
|
const VOICE_TRANSCRIPT_DISCLAIMER_TEXT = CONSTANTS.PROMPTS?.VOICE_TRANSCRIPT_DISCLAIMER ||
|
|
63
50
|
"Note: transcribed from user voice. If confusing or possibly inaccurate and you cannot infer the intention please clarify before proceeding.";
|
|
64
51
|
const INJECTED_CONTEXT_TAG_RE = /<injected context>/i;
|
|
@@ -360,7 +347,6 @@ export class TerminalManager {
|
|
|
360
347
|
this.lastAltScrollbackSize = 0;
|
|
361
348
|
this.transcriptResetForConnect = false;
|
|
362
349
|
this._registerTextInputHook(this._buildCarContextHook());
|
|
363
|
-
this._registerTextInputHook(this._buildGithubContextHook());
|
|
364
350
|
// Bind methods that are used as callbacks
|
|
365
351
|
this._handleResize = this._handleResize.bind(this);
|
|
366
352
|
this._handleVoiceHotkeyDown = this._handleVoiceHotkeyDown.bind(this);
|
|
@@ -444,6 +430,20 @@ export class TerminalManager {
|
|
|
444
430
|
this.connect({ mode: "attach" });
|
|
445
431
|
}
|
|
446
432
|
}
|
|
433
|
+
/**
|
|
434
|
+
* Force resize terminal to fit container
|
|
435
|
+
*/
|
|
436
|
+
fit() {
|
|
437
|
+
if (this.fitAddon && this.term) {
|
|
438
|
+
try {
|
|
439
|
+
this.fitAddon.fit();
|
|
440
|
+
this._handleResize(); // Send resize to server
|
|
441
|
+
}
|
|
442
|
+
catch (e) {
|
|
443
|
+
// ignore
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
447
|
/**
|
|
448
448
|
* Set terminal status message
|
|
449
449
|
*/
|
|
@@ -621,12 +621,12 @@ export class TerminalManager {
|
|
|
621
621
|
if (manager._hasTextInputHookFired(CAR_CONTEXT_HOOK_ID))
|
|
622
622
|
return null;
|
|
623
623
|
const lowered = text.toLowerCase();
|
|
624
|
-
const hit =
|
|
624
|
+
const hit = CONSTANTS.KEYWORDS.CAR_CONTEXT.some((kw) => lowered.includes(kw));
|
|
625
625
|
if (!hit)
|
|
626
626
|
return null;
|
|
627
627
|
if (lowered.includes("about_car.md"))
|
|
628
628
|
return null;
|
|
629
|
-
if (text.includes(
|
|
629
|
+
if (text.includes(CONSTANTS.PROMPTS.CAR_CONTEXT_HINT) ||
|
|
630
630
|
text.includes(CAR_CONTEXT_HINT)) {
|
|
631
631
|
return null;
|
|
632
632
|
}
|
|
@@ -637,43 +637,8 @@ export class TerminalManager {
|
|
|
637
637
|
},
|
|
638
638
|
};
|
|
639
639
|
}
|
|
640
|
-
_buildGithubContextHook() {
|
|
641
|
-
return {
|
|
642
|
-
id: GITHUB_CONTEXT_HOOK_ID,
|
|
643
|
-
apply: async ({ text }) => {
|
|
644
|
-
if (!text || !text.trim())
|
|
645
|
-
return null;
|
|
646
|
-
const match = text.match(GITHUB_LINK_RE);
|
|
647
|
-
if (!match)
|
|
648
|
-
return null;
|
|
649
|
-
try {
|
|
650
|
-
const res = await api("/api/github/context", {
|
|
651
|
-
method: "POST",
|
|
652
|
-
body: { url: match[0] },
|
|
653
|
-
});
|
|
654
|
-
if (!res || typeof res !== "object")
|
|
655
|
-
return null;
|
|
656
|
-
const injection = wrapInjectedContextIfNeeded(res.hint);
|
|
657
|
-
const separator = text.endsWith("\n") ? "\n" : "\n\n";
|
|
658
|
-
return { text: `${text}${separator}${injection}` };
|
|
659
|
-
}
|
|
660
|
-
catch (_err) {
|
|
661
|
-
return null;
|
|
662
|
-
}
|
|
663
|
-
},
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
640
|
async _loadTerminalIdleTimeout() {
|
|
667
|
-
|
|
668
|
-
const data = await api(CONSTANTS.API.STATE_ENDPOINT);
|
|
669
|
-
if (data &&
|
|
670
|
-
typeof data === "object" && data !== null && "terminal_idle_timeout_seconds" in data) {
|
|
671
|
-
this.terminalIdleTimeoutSeconds = data.terminal_idle_timeout_seconds;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
catch (_err) {
|
|
675
|
-
// ignore
|
|
676
|
-
}
|
|
641
|
+
// State endpoint removed - terminal idle timeout no longer loaded from /api/state
|
|
677
642
|
}
|
|
678
643
|
_getSessionStorageKey() {
|
|
679
644
|
return `${SESSION_STORAGE_PREFIX}${this._getRepoStorageKey()}`;
|
|
@@ -1880,7 +1845,8 @@ export class TerminalManager {
|
|
|
1880
1845
|
* Ensure xterm terminal is initialized
|
|
1881
1846
|
*/
|
|
1882
1847
|
_ensureTerminal() {
|
|
1883
|
-
|
|
1848
|
+
const win = window;
|
|
1849
|
+
if (!win.Terminal || !win.FitAddon) {
|
|
1884
1850
|
this._setStatus("xterm assets missing; reload or check /static/vendor");
|
|
1885
1851
|
flash("xterm assets missing; reload the page", "error");
|
|
1886
1852
|
return false;
|
|
@@ -1899,7 +1865,7 @@ export class TerminalManager {
|
|
|
1899
1865
|
const container = document.getElementById("terminal-container");
|
|
1900
1866
|
if (!container)
|
|
1901
1867
|
return false;
|
|
1902
|
-
this.term = new
|
|
1868
|
+
this.term = new win.Terminal({
|
|
1903
1869
|
convertEol: true,
|
|
1904
1870
|
fontFamily: '"JetBrains Mono", "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace',
|
|
1905
1871
|
fontSize: this._getFontSize(),
|
|
@@ -1911,7 +1877,7 @@ export class TerminalManager {
|
|
|
1911
1877
|
scrollback: this.transcriptMaxLines,
|
|
1912
1878
|
theme: CONSTANTS.THEME.XTERM,
|
|
1913
1879
|
});
|
|
1914
|
-
this.fitAddon = new
|
|
1880
|
+
this.fitAddon = new win.FitAddon.FitAddon();
|
|
1915
1881
|
this.term.loadAddon(this.fitAddon);
|
|
1916
1882
|
this.term.open(container);
|
|
1917
1883
|
this.term.write('Press "New" or "Resume" to launch Codex TUI...\r\n');
|
|
@@ -1920,6 +1886,13 @@ export class TerminalManager {
|
|
|
1920
1886
|
this.term.onScroll(() => this._updateJumpBottomVisibility());
|
|
1921
1887
|
this.term.onRender(() => this._scheduleMobileViewRender());
|
|
1922
1888
|
this._updateJumpBottomVisibility();
|
|
1889
|
+
// Initial fit
|
|
1890
|
+
try {
|
|
1891
|
+
this.fitAddon.fit();
|
|
1892
|
+
}
|
|
1893
|
+
catch (e) {
|
|
1894
|
+
// ignore fit errors when not visible
|
|
1895
|
+
}
|
|
1923
1896
|
if (!this.inputDisposable) {
|
|
1924
1897
|
this.inputDisposable = this.term.onData((data) => {
|
|
1925
1898
|
if (!this.socket || this.socket.readyState !== WebSocket.OPEN)
|
|
@@ -2004,7 +1977,8 @@ export class TerminalManager {
|
|
|
2004
1977
|
if (!viewport)
|
|
2005
1978
|
return;
|
|
2006
1979
|
const getLineHeight = () => {
|
|
2007
|
-
const
|
|
1980
|
+
const core = this.term?._core;
|
|
1981
|
+
const dims = core?._renderService?.dimensions;
|
|
2008
1982
|
if (dims && Number.isFinite(dims.actualCellHeight) && dims.actualCellHeight > 0) {
|
|
2009
1983
|
return dims.actualCellHeight;
|
|
2010
1984
|
}
|
|
@@ -3100,7 +3074,8 @@ export class TerminalManager {
|
|
|
3100
3074
|
method: "POST",
|
|
3101
3075
|
body: formData,
|
|
3102
3076
|
});
|
|
3103
|
-
const
|
|
3077
|
+
const p = response;
|
|
3078
|
+
const imagePath = p.path || p.abs_path;
|
|
3104
3079
|
if (!imagePath) {
|
|
3105
3080
|
throw new Error("Upload returned no path");
|
|
3106
3081
|
}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
2
|
+
/**
|
|
3
|
+
* Ticket Chat Actions - handles sending messages, applying/discarding patches
|
|
4
|
+
*/
|
|
5
|
+
import { api, flash, splitMarkdownFrontmatter } from "./utils.js";
|
|
6
|
+
import { performTicketChatRequest } from "./ticketChatStream.js";
|
|
7
|
+
import { renderTicketMessages } from "./ticketChatEvents.js";
|
|
8
|
+
import { publish } from "./bus.js";
|
|
9
|
+
import { createDocChat } from "./docChatCore.js";
|
|
10
|
+
import { saveTicketChatHistory } from "./ticketChatStorage.js";
|
|
11
|
+
import { renderDiff } from "./diffRenderer.js";
|
|
12
|
+
// Limits for events display
|
|
13
|
+
export const TICKET_CHAT_EVENT_LIMIT = 8;
|
|
14
|
+
export const TICKET_CHAT_EVENT_MAX = 50;
|
|
15
|
+
export const ticketChat = createDocChat({
|
|
16
|
+
idPrefix: "ticket-chat",
|
|
17
|
+
storage: { keyPrefix: "car-ticket-chat-", maxMessages: 50, version: 1 },
|
|
18
|
+
limits: { eventVisible: TICKET_CHAT_EVENT_LIMIT, eventMax: TICKET_CHAT_EVENT_MAX },
|
|
19
|
+
styling: {
|
|
20
|
+
eventClass: "ticket-chat-event",
|
|
21
|
+
eventTitleClass: "ticket-chat-event-title",
|
|
22
|
+
eventSummaryClass: "ticket-chat-event-summary",
|
|
23
|
+
eventDetailClass: "ticket-chat-event-detail",
|
|
24
|
+
eventMetaClass: "ticket-chat-event-meta",
|
|
25
|
+
eventsEmptyClass: "ticket-chat-events-empty",
|
|
26
|
+
eventsWaitingClass: "ticket-chat-events-waiting",
|
|
27
|
+
eventsHiddenClass: "hidden",
|
|
28
|
+
messagesClass: "ticket-chat-message",
|
|
29
|
+
messageRoleClass: "ticket-chat-message-role",
|
|
30
|
+
messageContentClass: "ticket-chat-message-content",
|
|
31
|
+
messageMetaClass: "ticket-chat-message-meta",
|
|
32
|
+
messageUserClass: "user",
|
|
33
|
+
messageAssistantClass: "assistant",
|
|
34
|
+
messageAssistantThinkingClass: "thinking",
|
|
35
|
+
messageAssistantFinalClass: "final",
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
// Extend state with ticket-specific fields
|
|
39
|
+
export const ticketChatState = Object.assign(ticketChat.state, {
|
|
40
|
+
ticketIndex: null,
|
|
41
|
+
draft: null,
|
|
42
|
+
});
|
|
43
|
+
export function getTicketChatElements() {
|
|
44
|
+
const base = ticketChat.elements;
|
|
45
|
+
return {
|
|
46
|
+
input: base.input,
|
|
47
|
+
sendBtn: base.sendBtn,
|
|
48
|
+
voiceBtn: base.voiceBtn,
|
|
49
|
+
cancelBtn: base.cancelBtn,
|
|
50
|
+
newThreadBtn: base.newThreadBtn,
|
|
51
|
+
statusEl: base.statusEl,
|
|
52
|
+
streamEl: base.streamEl,
|
|
53
|
+
eventsMain: base.eventsMain,
|
|
54
|
+
eventsList: base.eventsList,
|
|
55
|
+
eventsCount: base.eventsCount,
|
|
56
|
+
eventsToggle: base.eventsToggle,
|
|
57
|
+
messagesEl: base.messagesEl,
|
|
58
|
+
// Content area elements - mutually exclusive with patch preview
|
|
59
|
+
contentTextarea: document.getElementById("ticket-editor-content"),
|
|
60
|
+
contentToolbar: document.getElementById("ticket-editor-toolbar"),
|
|
61
|
+
// Patch preview elements - mutually exclusive with content area
|
|
62
|
+
patchMain: document.getElementById("ticket-patch-main"),
|
|
63
|
+
patchBody: document.getElementById("ticket-patch-body"),
|
|
64
|
+
patchStatus: document.getElementById("ticket-patch-status"),
|
|
65
|
+
applyBtn: document.getElementById("ticket-patch-apply"),
|
|
66
|
+
discardBtn: document.getElementById("ticket-patch-discard"),
|
|
67
|
+
agentSelect: document.getElementById("ticket-chat-agent-select"),
|
|
68
|
+
modelSelect: document.getElementById("ticket-chat-model-select"),
|
|
69
|
+
reasoningSelect: document.getElementById("ticket-chat-reasoning-select"),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export function resetTicketChatState() {
|
|
73
|
+
ticketChatState.status = "idle";
|
|
74
|
+
ticketChatState.error = "";
|
|
75
|
+
ticketChatState.streamText = "";
|
|
76
|
+
ticketChatState.statusText = "";
|
|
77
|
+
ticketChatState.controller = null;
|
|
78
|
+
// Note: events are cleared at the start of each new request, not here
|
|
79
|
+
// Messages persist across requests within the same ticket
|
|
80
|
+
}
|
|
81
|
+
export async function startNewTicketChatThread() {
|
|
82
|
+
if (ticketChatState.ticketIndex == null)
|
|
83
|
+
return;
|
|
84
|
+
const confirmed = window.confirm("Start a new conversation thread for this ticket?");
|
|
85
|
+
if (!confirmed)
|
|
86
|
+
return;
|
|
87
|
+
try {
|
|
88
|
+
const key = `ticket_chat.${ticketChatState.ticketIndex}`;
|
|
89
|
+
await api(`/api/app-server/threads/reset`, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
body: { key },
|
|
92
|
+
});
|
|
93
|
+
// Clear local message history
|
|
94
|
+
ticketChatState.messages = [];
|
|
95
|
+
saveTicketChatHistory(ticketChatState.ticketIndex, []);
|
|
96
|
+
clearTicketEvents();
|
|
97
|
+
flash("New thread started");
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
flash(`Failed to reset thread: ${err.message}`, "error");
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
renderTicketChat();
|
|
104
|
+
renderTicketMessages();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Clear events at the start of a new request.
|
|
109
|
+
* Events are transient (thinking/tool calls) and reset each turn.
|
|
110
|
+
*/
|
|
111
|
+
export function clearTicketEvents() {
|
|
112
|
+
ticketChat.clearEvents();
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Add a user message to the chat history.
|
|
116
|
+
*/
|
|
117
|
+
export function addUserMessage(content) {
|
|
118
|
+
ticketChat.addUserMessage(content);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Add an assistant message to the chat history.
|
|
122
|
+
* Prevents duplicates by checking if the same content was just added.
|
|
123
|
+
*/
|
|
124
|
+
export function addAssistantMessage(content, isFinal = true) {
|
|
125
|
+
ticketChat.addAssistantMessage(content, isFinal);
|
|
126
|
+
}
|
|
127
|
+
export function setTicketIndex(index) {
|
|
128
|
+
const changed = ticketChatState.ticketIndex !== index;
|
|
129
|
+
ticketChatState.ticketIndex = index;
|
|
130
|
+
ticketChatState.draft = null;
|
|
131
|
+
resetTicketChatState();
|
|
132
|
+
// Clear chat history when switching tickets
|
|
133
|
+
if (changed) {
|
|
134
|
+
ticketChat.setTarget(index != null ? String(index) : null);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export function renderTicketChat() {
|
|
138
|
+
const els = getTicketChatElements();
|
|
139
|
+
// Shared chat render (status, events, messages)
|
|
140
|
+
ticketChat.render();
|
|
141
|
+
// MUTUALLY EXCLUSIVE: Show either the content editor OR the patch preview, never both.
|
|
142
|
+
// This prevents confusion about which view is the "current" state.
|
|
143
|
+
const hasDraft = !!ticketChatState.draft;
|
|
144
|
+
// Hide content area when showing patch preview
|
|
145
|
+
if (els.contentTextarea) {
|
|
146
|
+
els.contentTextarea.classList.toggle("hidden", hasDraft);
|
|
147
|
+
}
|
|
148
|
+
if (els.contentToolbar) {
|
|
149
|
+
els.contentToolbar.classList.toggle("hidden", hasDraft);
|
|
150
|
+
}
|
|
151
|
+
// Show patch preview only when there's a draft
|
|
152
|
+
if (els.patchMain) {
|
|
153
|
+
els.patchMain.classList.toggle("hidden", !hasDraft);
|
|
154
|
+
if (hasDraft) {
|
|
155
|
+
if (els.patchBody) {
|
|
156
|
+
renderDiff(ticketChatState.draft.patch || "(no changes)", els.patchBody);
|
|
157
|
+
}
|
|
158
|
+
if (els.patchStatus) {
|
|
159
|
+
els.patchStatus.textContent = ticketChatState.draft.agentMessage || "";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
export async function sendTicketChat() {
|
|
165
|
+
const els = getTicketChatElements();
|
|
166
|
+
const message = (els.input?.value || "").trim();
|
|
167
|
+
if (!message) {
|
|
168
|
+
ticketChatState.error = "Enter a message to send.";
|
|
169
|
+
renderTicketChat();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (ticketChatState.status === "running") {
|
|
173
|
+
ticketChatState.error = "Ticket chat already running.";
|
|
174
|
+
renderTicketChat();
|
|
175
|
+
flash("Ticket chat already running", "error");
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (ticketChatState.ticketIndex == null) {
|
|
179
|
+
ticketChatState.error = "No ticket selected.";
|
|
180
|
+
renderTicketChat();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
resetTicketChatState();
|
|
184
|
+
ticketChatState.status = "running";
|
|
185
|
+
ticketChatState.statusText = "queued";
|
|
186
|
+
ticketChatState.controller = new AbortController();
|
|
187
|
+
renderTicketChat();
|
|
188
|
+
if (els.input) {
|
|
189
|
+
els.input.value = "";
|
|
190
|
+
}
|
|
191
|
+
const agent = els.agentSelect?.value || "codex";
|
|
192
|
+
const model = els.modelSelect?.value || undefined;
|
|
193
|
+
const reasoning = els.reasoningSelect?.value || undefined;
|
|
194
|
+
try {
|
|
195
|
+
await performTicketChatRequest(ticketChatState.ticketIndex, message, ticketChatState.controller.signal, {
|
|
196
|
+
agent,
|
|
197
|
+
model,
|
|
198
|
+
reasoning,
|
|
199
|
+
});
|
|
200
|
+
// Try to load any pending draft
|
|
201
|
+
await loadTicketPending(ticketChatState.ticketIndex, true);
|
|
202
|
+
if (ticketChatState.status === "running") {
|
|
203
|
+
ticketChatState.status = "done";
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
const error = err;
|
|
208
|
+
if (error.name === "AbortError") {
|
|
209
|
+
ticketChatState.status = "interrupted";
|
|
210
|
+
ticketChatState.error = "";
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
ticketChatState.status = "error";
|
|
214
|
+
ticketChatState.error = error.message || "Ticket chat failed";
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
finally {
|
|
218
|
+
ticketChatState.controller = null;
|
|
219
|
+
renderTicketChat();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
export async function cancelTicketChat() {
|
|
223
|
+
if (ticketChatState.status !== "running")
|
|
224
|
+
return;
|
|
225
|
+
// Abort the request
|
|
226
|
+
if (ticketChatState.controller) {
|
|
227
|
+
ticketChatState.controller.abort();
|
|
228
|
+
}
|
|
229
|
+
// Send interrupt to server
|
|
230
|
+
if (ticketChatState.ticketIndex != null) {
|
|
231
|
+
try {
|
|
232
|
+
await api(`/api/tickets/${ticketChatState.ticketIndex}/chat/interrupt`, {
|
|
233
|
+
method: "POST",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
// Ignore interrupt errors
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
ticketChatState.status = "interrupted";
|
|
241
|
+
ticketChatState.error = "";
|
|
242
|
+
ticketChatState.statusText = "";
|
|
243
|
+
ticketChatState.controller = null;
|
|
244
|
+
renderTicketChat();
|
|
245
|
+
}
|
|
246
|
+
export async function applyTicketPatch() {
|
|
247
|
+
if (ticketChatState.ticketIndex == null) {
|
|
248
|
+
flash("No ticket selected", "error");
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (!ticketChatState.draft) {
|
|
252
|
+
flash("No draft to apply", "error");
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
const res = await api(`/api/tickets/${ticketChatState.ticketIndex}/chat/apply`, { method: "POST" });
|
|
257
|
+
ticketChatState.draft = null;
|
|
258
|
+
flash("Draft applied");
|
|
259
|
+
// Notify that tickets changed
|
|
260
|
+
publish("tickets:updated", {});
|
|
261
|
+
// Update the editor textarea if content is returned
|
|
262
|
+
if (res.content) {
|
|
263
|
+
const textarea = document.getElementById("ticket-editor-content");
|
|
264
|
+
if (textarea) {
|
|
265
|
+
const [fmYaml, body] = splitMarkdownFrontmatter(res.content);
|
|
266
|
+
if (fmYaml !== null) {
|
|
267
|
+
textarea.value = body.trimStart();
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
textarea.value = res.content.trimStart();
|
|
271
|
+
}
|
|
272
|
+
// Trigger input event to update undo stack and autosave
|
|
273
|
+
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (err) {
|
|
278
|
+
const error = err;
|
|
279
|
+
flash(error.message || "Failed to apply draft", "error");
|
|
280
|
+
}
|
|
281
|
+
finally {
|
|
282
|
+
renderTicketChat();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
export async function discardTicketPatch() {
|
|
286
|
+
if (ticketChatState.ticketIndex == null) {
|
|
287
|
+
flash("No ticket selected", "error");
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
await api(`/api/tickets/${ticketChatState.ticketIndex}/chat/discard`, { method: "POST" });
|
|
292
|
+
ticketChatState.draft = null;
|
|
293
|
+
flash("Draft discarded");
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
const error = err;
|
|
297
|
+
flash(error.message || "Failed to discard draft", "error");
|
|
298
|
+
}
|
|
299
|
+
finally {
|
|
300
|
+
renderTicketChat();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
export async function loadTicketPending(index, silent = false) {
|
|
304
|
+
try {
|
|
305
|
+
const res = await api(`/api/tickets/${index}/chat/pending`, { method: "GET" });
|
|
306
|
+
ticketChatState.draft = {
|
|
307
|
+
patch: res.patch || "",
|
|
308
|
+
content: res.content || "",
|
|
309
|
+
agentMessage: res.agent_message || "",
|
|
310
|
+
createdAt: res.created_at || "",
|
|
311
|
+
baseHash: res.base_hash || "",
|
|
312
|
+
};
|
|
313
|
+
if (!silent) {
|
|
314
|
+
flash("Loaded pending draft");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
catch (err) {
|
|
318
|
+
const error = err;
|
|
319
|
+
const message = error?.message || "";
|
|
320
|
+
if (message.includes("No pending")) {
|
|
321
|
+
ticketChatState.draft = null;
|
|
322
|
+
if (!silent) {
|
|
323
|
+
flash("No pending draft");
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else if (!silent) {
|
|
327
|
+
flash(message || "Failed to load pending draft", "error");
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
finally {
|
|
331
|
+
renderTicketChat();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
2
|
+
import { ticketChat } from "./ticketChatActions.js";
|
|
3
|
+
// This module now delegates to docChatCore for rendering and event parsing.
|
|
4
|
+
export function applyTicketEvent(payload) {
|
|
5
|
+
ticketChat.applyAppEvent(payload);
|
|
6
|
+
}
|
|
7
|
+
export function renderTicketEvents() {
|
|
8
|
+
ticketChat.renderEvents();
|
|
9
|
+
}
|
|
10
|
+
export function renderTicketMessages() {
|
|
11
|
+
ticketChat.renderMessages();
|
|
12
|
+
}
|
|
13
|
+
export function initTicketChatEvents() {
|
|
14
|
+
// Toggle already wired in docChatCore constructor.
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
2
|
+
import { clearChatHistory, loadChatHistory, saveChatHistory, } from "./docChatStorage.js";
|
|
3
|
+
const STORAGE_CONFIG = {
|
|
4
|
+
keyPrefix: "car-ticket-chat-",
|
|
5
|
+
maxMessages: 50,
|
|
6
|
+
version: 1,
|
|
7
|
+
};
|
|
8
|
+
export function saveTicketChatHistory(ticketIndex, messages) {
|
|
9
|
+
saveChatHistory(STORAGE_CONFIG, String(ticketIndex), messages);
|
|
10
|
+
}
|
|
11
|
+
export function loadTicketChatHistory(ticketIndex) {
|
|
12
|
+
return loadChatHistory(STORAGE_CONFIG, String(ticketIndex));
|
|
13
|
+
}
|
|
14
|
+
export function clearTicketChatHistory(ticketIndex) {
|
|
15
|
+
clearChatHistory(STORAGE_CONFIG, String(ticketIndex));
|
|
16
|
+
}
|