@gajae-code/coding-agent 0.3.0 → 0.3.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.
- package/CHANGELOG.md +18 -0
- package/dist/types/async/job-manager.d.ts +7 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +4 -4
- package/dist/types/debug/crash-diagnostics.d.ts +45 -0
- package/dist/types/debug/runtime-gauges.d.ts +6 -0
- package/dist/types/deep-interview/render-middleware.d.ts +1 -0
- package/dist/types/eval/py/executor.d.ts +2 -0
- package/dist/types/eval/py/kernel.d.ts +2 -0
- package/dist/types/exec/bash-executor.d.ts +10 -0
- package/dist/types/gjc-runtime/cli-write-receipt.d.ts +24 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -0
- package/dist/types/gjc-runtime/state-migrations.d.ts +9 -0
- package/dist/types/gjc-runtime/state-schema.d.ts +317 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +10 -0
- package/dist/types/gjc-runtime/workflow-command-ref.d.ts +43 -0
- package/dist/types/harness-control-plane/control-endpoint.d.ts +3 -2
- package/dist/types/hooks/skill-state.d.ts +21 -0
- package/dist/types/internal-urls/agent-protocol.d.ts +2 -2
- package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
- package/dist/types/internal-urls/registry-helpers.d.ts +8 -7
- package/dist/types/internal-urls/types.d.ts +4 -0
- package/dist/types/lsp/index.d.ts +10 -10
- package/dist/types/modes/bridge/auth.d.ts +12 -0
- package/dist/types/modes/bridge/bridge-client-bridge.d.ts +9 -0
- package/dist/types/modes/bridge/bridge-mode.d.ts +44 -0
- package/dist/types/modes/bridge/bridge-ui-context.d.ts +88 -0
- package/dist/types/modes/bridge/event-stream.d.ts +8 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -0
- package/dist/types/modes/components/jobs-overlay-model.d.ts +31 -0
- package/dist/types/modes/components/jobs-overlay.d.ts +30 -0
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/status-line.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +8 -0
- package/dist/types/modes/index.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/jobs-observer.d.ts +57 -0
- package/dist/types/modes/rpc/host-tools.d.ts +1 -16
- package/dist/types/modes/rpc/host-uris.d.ts +1 -38
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +20 -0
- package/dist/types/modes/shared/agent-wire/command-validation.d.ts +2 -0
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +24 -0
- package/dist/types/modes/shared/agent-wire/handshake.d.ts +46 -0
- package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +16 -0
- package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +17 -0
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +44 -0
- package/dist/types/modes/shared/agent-wire/responses.d.ts +4 -0
- package/dist/types/modes/shared/agent-wire/scopes.d.ts +18 -0
- package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +42 -0
- package/dist/types/modes/shared/agent-wire/ui-result.d.ts +27 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +11 -1
- package/dist/types/skill-state/workflow-state-contract.d.ts +1 -2
- package/dist/types/skill-state/workflow-state-version.d.ts +3 -0
- package/dist/types/task/id.d.ts +7 -0
- package/dist/types/task/index.d.ts +5 -0
- package/dist/types/task/receipt.d.ts +85 -0
- package/dist/types/task/spawn-gate.d.ts +38 -0
- package/dist/types/task/types.d.ts +143 -11
- package/dist/types/tools/cron.d.ts +6 -0
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/subagent.d.ts +15 -0
- package/package.json +7 -7
- package/scripts/build-binary.ts +7 -0
- package/src/async/job-manager.ts +36 -0
- package/src/cli/args.ts +9 -2
- package/src/commands/deep-interview.ts +1 -0
- package/src/commands/harness.ts +289 -19
- package/src/commands/launch.ts +2 -2
- package/src/commands/state.ts +2 -1
- package/src/commands/team.ts +22 -4
- package/src/config/keybindings.ts +6 -0
- package/src/config/settings-schema.ts +6 -3
- package/src/dap/client.ts +17 -3
- package/src/debug/crash-diagnostics.ts +223 -0
- package/src/debug/runtime-gauges.ts +20 -0
- package/src/deep-interview/render-middleware.ts +6 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ralplan/SKILL.md +31 -2
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +28 -2
- package/src/eval/py/executor.ts +21 -1
- package/src/eval/py/kernel.ts +15 -0
- package/src/exec/bash-executor.ts +41 -0
- package/src/gjc-runtime/cli-write-receipt.ts +31 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +69 -32
- package/src/gjc-runtime/ralplan-runtime.ts +213 -36
- package/src/gjc-runtime/state-migrations.ts +54 -7
- package/src/gjc-runtime/state-runtime.ts +461 -64
- package/src/gjc-runtime/state-schema.ts +192 -0
- package/src/gjc-runtime/state-writer.ts +32 -1
- package/src/gjc-runtime/team-runtime.ts +177 -105
- package/src/gjc-runtime/ultragoal-runtime.ts +114 -26
- package/src/gjc-runtime/workflow-command-ref.ts +239 -0
- package/src/gjc-runtime/workflow-manifest.generated.json +108 -4
- package/src/gjc-runtime/workflow-manifest.ts +3 -1
- package/src/harness-control-plane/control-endpoint.ts +19 -8
- package/src/harness-control-plane/owner.ts +57 -10
- package/src/harness-control-plane/state-machine.ts +2 -1
- package/src/hooks/skill-state.ts +176 -26
- package/src/internal-urls/agent-protocol.ts +68 -21
- package/src/internal-urls/artifact-protocol.ts +12 -17
- package/src/internal-urls/docs-index.generated.ts +3 -2
- package/src/internal-urls/registry-helpers.ts +19 -16
- package/src/internal-urls/types.ts +4 -0
- package/src/lsp/client.ts +18 -2
- package/src/main.ts +21 -5
- package/src/modes/bridge/auth.ts +41 -0
- package/src/modes/bridge/bridge-client-bridge.ts +47 -0
- package/src/modes/bridge/bridge-mode.ts +520 -0
- package/src/modes/bridge/bridge-ui-context.ts +200 -0
- package/src/modes/bridge/event-stream.ts +70 -0
- package/src/modes/components/custom-editor.ts +101 -0
- package/src/modes/components/hook-selector.ts +61 -18
- package/src/modes/components/jobs-overlay-model.ts +109 -0
- package/src/modes/components/jobs-overlay.ts +172 -0
- package/src/modes/components/status-line/presets.ts +7 -5
- package/src/modes/components/status-line/segments.ts +25 -0
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/status-line.ts +9 -1
- package/src/modes/controllers/extension-ui-controller.ts +39 -3
- package/src/modes/controllers/input-controller.ts +97 -9
- package/src/modes/controllers/selector-controller.ts +29 -0
- package/src/modes/index.ts +1 -0
- package/src/modes/interactive-mode.ts +27 -0
- package/src/modes/jobs-observer.ts +204 -0
- package/src/modes/rpc/host-tools.ts +1 -186
- package/src/modes/rpc/host-uris.ts +1 -235
- package/src/modes/rpc/rpc-client.ts +25 -10
- package/src/modes/rpc/rpc-mode.ts +12 -381
- package/src/modes/shared/agent-wire/command-dispatch.ts +341 -0
- package/src/modes/shared/agent-wire/command-validation.ts +131 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +108 -0
- package/src/modes/shared/agent-wire/handshake.ts +117 -0
- package/src/modes/shared/agent-wire/host-tool-bridge.ts +194 -0
- package/src/modes/shared/agent-wire/host-uri-bridge.ts +236 -0
- package/src/modes/shared/agent-wire/protocol.ts +96 -0
- package/src/modes/shared/agent-wire/responses.ts +17 -0
- package/src/modes/shared/agent-wire/scopes.ts +89 -0
- package/src/modes/shared/agent-wire/ui-request-broker.ts +150 -0
- package/src/modes/shared/agent-wire/ui-result.ts +48 -0
- package/src/modes/types.ts +1 -0
- package/src/prompts/tools/subagent.md +12 -7
- package/src/prompts/tools/task-summary.md +3 -9
- package/src/prompts/tools/task.md +5 -1
- package/src/sdk.ts +4 -0
- package/src/session/agent-session.ts +214 -38
- package/src/skill-state/deep-interview-mutation-guard.ts +23 -4
- package/src/skill-state/workflow-state-contract.ts +7 -4
- package/src/skill-state/workflow-state-version.ts +3 -0
- package/src/slash-commands/builtin-registry.ts +8 -0
- package/src/task/executor.ts +29 -5
- package/src/task/id.ts +33 -0
- package/src/task/index.ts +257 -67
- package/src/task/output-manager.ts +5 -4
- package/src/task/receipt.ts +297 -0
- package/src/task/render.ts +48 -131
- package/src/task/spawn-gate.ts +132 -0
- package/src/task/types.ts +48 -7
- package/src/tools/ask.ts +73 -33
- package/src/tools/ast-edit.ts +1 -0
- package/src/tools/ast-grep.ts +1 -0
- package/src/tools/bash.ts +1 -1
- package/src/tools/cron.ts +48 -0
- package/src/tools/find.ts +4 -1
- package/src/tools/index.ts +2 -0
- package/src/tools/path-utils.ts +3 -2
- package/src/tools/read.ts +1 -0
- package/src/tools/search.ts +1 -0
- package/src/tools/skill.ts +6 -1
- package/src/tools/subagent.ts +237 -84
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { Container, type SelectItem, SelectList } from "@gajae-code/tui";
|
|
2
|
+
import type { JobsSnapshot } from "../jobs-observer";
|
|
3
|
+
import { getSelectListTheme } from "../theme/theme";
|
|
4
|
+
import { DynamicBorder } from "./dynamic-border";
|
|
5
|
+
import {
|
|
6
|
+
buildConfirmItems,
|
|
7
|
+
buildJobDetailItems,
|
|
8
|
+
buildJobsListItems,
|
|
9
|
+
type JobRef,
|
|
10
|
+
parseJobRef,
|
|
11
|
+
} from "./jobs-overlay-model";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generic single-level selector used by the jobs overlay. The selector
|
|
15
|
+
* controller mounts a fresh instance per navigation level (list -> detail ->
|
|
16
|
+
* confirm); focus is placed on the inner SelectList, matching the existing
|
|
17
|
+
* selector components (e.g. ThemeSelectorComponent).
|
|
18
|
+
*/
|
|
19
|
+
export class JobsSelectorComponent extends Container {
|
|
20
|
+
#selectList: SelectList;
|
|
21
|
+
|
|
22
|
+
constructor(items: SelectItem[], onSelect: (item: SelectItem) => void, onCancel: () => void, maxVisible = 12) {
|
|
23
|
+
super();
|
|
24
|
+
this.addChild(new DynamicBorder());
|
|
25
|
+
this.#selectList = new SelectList(items, maxVisible, getSelectListTheme());
|
|
26
|
+
this.#selectList.onSelect = onSelect;
|
|
27
|
+
this.#selectList.onCancel = onCancel;
|
|
28
|
+
this.addChild(this.#selectList);
|
|
29
|
+
this.addChild(new DynamicBorder());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getSelectList(): SelectList {
|
|
33
|
+
return this.#selectList;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface JobsOverlayController {
|
|
38
|
+
acknowledgeFailures(): void;
|
|
39
|
+
getSnapshot(): JobsSnapshot;
|
|
40
|
+
getMonitorOutput(id: string): string;
|
|
41
|
+
cancelMonitor(id: string): boolean;
|
|
42
|
+
deleteCron(id: string): boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface JobsOverlayCallbacks {
|
|
46
|
+
close(): void;
|
|
47
|
+
requestRender(): void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type JobsOverlayView = "list" | "detail" | "confirm";
|
|
51
|
+
type JobsOverlayAction = "cancel" | "delete";
|
|
52
|
+
|
|
53
|
+
export class JobsOverlayComponent extends Container {
|
|
54
|
+
readonly #controller: JobsOverlayController;
|
|
55
|
+
readonly #callbacks: JobsOverlayCallbacks;
|
|
56
|
+
#view: JobsOverlayView = "list";
|
|
57
|
+
#ref: JobRef | undefined;
|
|
58
|
+
#action: JobsOverlayAction | undefined;
|
|
59
|
+
#selectList: SelectList | undefined;
|
|
60
|
+
|
|
61
|
+
constructor(controller: JobsOverlayController, callbacks: JobsOverlayCallbacks) {
|
|
62
|
+
super();
|
|
63
|
+
this.#controller = controller;
|
|
64
|
+
this.#callbacks = callbacks;
|
|
65
|
+
this.#controller.acknowledgeFailures();
|
|
66
|
+
this.#renderList();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getFocus(): SelectList {
|
|
70
|
+
if (!this.#selectList) throw new Error("Jobs overlay has no focusable list");
|
|
71
|
+
return this.#selectList;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
handleInput(data: string): void {
|
|
75
|
+
if (this.#view === "confirm") {
|
|
76
|
+
const key = data.toLowerCase();
|
|
77
|
+
if (key === "y") {
|
|
78
|
+
this.#confirmYes();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (key === "n") {
|
|
82
|
+
this.#renderDetail();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
this.#selectList?.handleInput(data);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#replaceList(
|
|
90
|
+
items: SelectItem[],
|
|
91
|
+
onSelect: (item: SelectItem) => void,
|
|
92
|
+
onCancel: () => void,
|
|
93
|
+
maxVisible = 12,
|
|
94
|
+
): void {
|
|
95
|
+
this.clear();
|
|
96
|
+
this.addChild(new DynamicBorder());
|
|
97
|
+
this.#selectList = new SelectList(items, maxVisible, getSelectListTheme());
|
|
98
|
+
this.#selectList.onSelect = onSelect;
|
|
99
|
+
this.#selectList.onCancel = onCancel;
|
|
100
|
+
this.addChild(this.#selectList);
|
|
101
|
+
this.addChild(new DynamicBorder());
|
|
102
|
+
this.#callbacks.requestRender();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#renderList(): void {
|
|
106
|
+
this.#view = "list";
|
|
107
|
+
this.#ref = undefined;
|
|
108
|
+
this.#action = undefined;
|
|
109
|
+
const snapshot = this.#controller.getSnapshot();
|
|
110
|
+
const built = buildJobsListItems(snapshot);
|
|
111
|
+
const items = built.length > 0 ? built : [{ value: "close", label: "No active monitor or cron jobs" }];
|
|
112
|
+
this.#replaceList(
|
|
113
|
+
items,
|
|
114
|
+
item => {
|
|
115
|
+
const ref = parseJobRef(item.value);
|
|
116
|
+
if (ref) this.#renderDetail(ref);
|
|
117
|
+
else this.#callbacks.close();
|
|
118
|
+
},
|
|
119
|
+
() => this.#callbacks.close(),
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
#renderDetail(ref = this.#ref): void {
|
|
124
|
+
if (!ref) {
|
|
125
|
+
this.#renderList();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this.#view = "detail";
|
|
129
|
+
this.#ref = ref;
|
|
130
|
+
this.#action = undefined;
|
|
131
|
+
const output = ref.kind === "monitor" ? this.#controller.getMonitorOutput(ref.id) : "";
|
|
132
|
+
const items = buildJobDetailItems(this.#controller.getSnapshot(), ref, output);
|
|
133
|
+
this.#replaceList(
|
|
134
|
+
items,
|
|
135
|
+
item => {
|
|
136
|
+
if (item.value === "action:cancel") this.#renderConfirm("cancel");
|
|
137
|
+
else if (item.value === "action:delete") this.#renderConfirm("delete");
|
|
138
|
+
else if (item.value === "back") this.#renderList();
|
|
139
|
+
},
|
|
140
|
+
() => this.#callbacks.close(),
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#renderConfirm(action: JobsOverlayAction): void {
|
|
145
|
+
if (!this.#ref) {
|
|
146
|
+
this.#renderList();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
this.#view = "confirm";
|
|
150
|
+
this.#action = action;
|
|
151
|
+
const label = action === "cancel" ? "cancel this monitor" : "delete this cron";
|
|
152
|
+
this.#replaceList(
|
|
153
|
+
buildConfirmItems(label),
|
|
154
|
+
item => {
|
|
155
|
+
if (item.value === "yes") this.#confirmYes();
|
|
156
|
+
else this.#renderDetail();
|
|
157
|
+
},
|
|
158
|
+
() => this.#renderDetail(),
|
|
159
|
+
4,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
#confirmYes(): void {
|
|
164
|
+
if (!this.#ref || !this.#action) {
|
|
165
|
+
this.#renderList();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (this.#action === "cancel") this.#controller.cancelMonitor(this.#ref.id);
|
|
169
|
+
else this.#controller.deleteCron(this.#ref.id);
|
|
170
|
+
this.#renderList();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { PresetDef, StatusLinePreset } from "./types";
|
|
|
3
3
|
export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
4
4
|
default: {
|
|
5
5
|
leftSegments: ["model", "mode", "git", "pr", "path"],
|
|
6
|
-
rightSegments: ["session_name", "token_rate", "context_pct", "cost"],
|
|
6
|
+
rightSegments: ["session_name", "jobs", "token_rate", "context_pct", "cost"],
|
|
7
7
|
separator: "slash",
|
|
8
8
|
segmentOptions: {
|
|
9
9
|
model: { showThinkingLevel: true },
|
|
@@ -14,7 +14,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
|
14
14
|
|
|
15
15
|
minimal: {
|
|
16
16
|
leftSegments: ["path", "git"],
|
|
17
|
-
rightSegments: ["session_name", "mode", "context_pct"],
|
|
17
|
+
rightSegments: ["session_name", "jobs", "mode", "context_pct"],
|
|
18
18
|
separator: "slash",
|
|
19
19
|
segmentOptions: {
|
|
20
20
|
path: { abbreviate: true, maxLength: 30 },
|
|
@@ -24,7 +24,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
|
24
24
|
|
|
25
25
|
compact: {
|
|
26
26
|
leftSegments: ["model", "mode", "git", "pr"],
|
|
27
|
-
rightSegments: ["session_name", "cost", "context_pct"],
|
|
27
|
+
rightSegments: ["session_name", "jobs", "cost", "context_pct"],
|
|
28
28
|
separator: "slash",
|
|
29
29
|
segmentOptions: {
|
|
30
30
|
model: { showThinkingLevel: false },
|
|
@@ -36,6 +36,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
|
36
36
|
leftSegments: ["gajae", "hostname", "model", "mode", "path", "git", "pr", "subagents"],
|
|
37
37
|
rightSegments: [
|
|
38
38
|
"session_name",
|
|
39
|
+
"jobs",
|
|
39
40
|
"token_in",
|
|
40
41
|
"token_out",
|
|
41
42
|
"token_rate",
|
|
@@ -59,6 +60,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
|
59
60
|
leftSegments: ["gajae", "hostname", "model", "mode", "path", "git", "pr", "session", "subagents"],
|
|
60
61
|
rightSegments: [
|
|
61
62
|
"session_name",
|
|
63
|
+
"jobs",
|
|
62
64
|
"token_in",
|
|
63
65
|
"token_out",
|
|
64
66
|
"cache_read",
|
|
@@ -82,7 +84,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
|
82
84
|
ascii: {
|
|
83
85
|
// No Nerd Font dependencies
|
|
84
86
|
leftSegments: ["model", "mode", "path", "git", "pr"],
|
|
85
|
-
rightSegments: ["session_name", "token_total", "cost", "context_pct"],
|
|
87
|
+
rightSegments: ["session_name", "jobs", "token_total", "cost", "context_pct"],
|
|
86
88
|
separator: "ascii",
|
|
87
89
|
segmentOptions: {
|
|
88
90
|
model: { showThinkingLevel: true },
|
|
@@ -94,7 +96,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
|
94
96
|
custom: {
|
|
95
97
|
// User-defined - these are just defaults that get overridden
|
|
96
98
|
leftSegments: ["model", "mode", "path", "git", "pr"],
|
|
97
|
-
rightSegments: ["session_name", "token_total", "cost", "context_pct"],
|
|
99
|
+
rightSegments: ["session_name", "jobs", "token_total", "cost", "context_pct"],
|
|
98
100
|
separator: "slash",
|
|
99
101
|
segmentOptions: {},
|
|
100
102
|
},
|
|
@@ -270,6 +270,30 @@ const subagentsSegment: StatusLineSegment = {
|
|
|
270
270
|
},
|
|
271
271
|
};
|
|
272
272
|
|
|
273
|
+
const jobsSegment: StatusLineSegment = {
|
|
274
|
+
id: "jobs",
|
|
275
|
+
render(ctx) {
|
|
276
|
+
const { jobs } = ctx;
|
|
277
|
+
const visible = jobs.activeMonitorCount > 0 || jobs.activeCronCount > 0 || jobs.worstState === "failed";
|
|
278
|
+
if (!visible) {
|
|
279
|
+
return { content: "", visible: false };
|
|
280
|
+
}
|
|
281
|
+
const parts: string[] = [];
|
|
282
|
+
if (jobs.activeMonitorCount > 0) {
|
|
283
|
+
parts.push(withIcon(theme.icon.agents, `${jobs.activeMonitorCount}`));
|
|
284
|
+
}
|
|
285
|
+
if (jobs.activeCronCount > 0) {
|
|
286
|
+
parts.push(withIcon(theme.icon.time, `${jobs.activeCronCount}`));
|
|
287
|
+
}
|
|
288
|
+
if (parts.length === 0) {
|
|
289
|
+
// Nothing active but a failure is unacknowledged — keep a drill-in marker.
|
|
290
|
+
parts.push(withIcon(theme.icon.warning, "jobs"));
|
|
291
|
+
}
|
|
292
|
+
const color: ThemeColor = jobs.worstState === "failed" ? "error" : "statusLineSubagents";
|
|
293
|
+
return { content: theme.fg(color, parts.join(" ")), visible: true };
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
|
|
273
297
|
const tokenInSegment: StatusLineSegment = {
|
|
274
298
|
id: "token_in",
|
|
275
299
|
render(ctx) {
|
|
@@ -521,6 +545,7 @@ export const SEGMENTS: Record<StatusLineSegmentId, StatusLineSegment> = {
|
|
|
521
545
|
git: gitSegment,
|
|
522
546
|
pr: prSegment,
|
|
523
547
|
subagents: subagentsSegment,
|
|
548
|
+
jobs: jobsSegment,
|
|
524
549
|
token_in: tokenInSegment,
|
|
525
550
|
token_out: tokenOutSegment,
|
|
526
551
|
token_total: tokenTotalSegment,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { StatusLinePreset, StatusLineSegmentId, StatusLineSeparatorStyle } from "../../../config/settings-schema";
|
|
2
2
|
import type { AgentSession } from "../../../session/agent-session";
|
|
3
|
+
import type { JobsSnapshot } from "../../jobs-observer";
|
|
3
4
|
import type { StatusLineSegmentOptions, StatusLineSettings } from "../status-line";
|
|
4
5
|
|
|
5
6
|
export type {
|
|
@@ -42,6 +43,7 @@ export interface SegmentContext {
|
|
|
42
43
|
contextWindow: number;
|
|
43
44
|
autoCompactEnabled: boolean;
|
|
44
45
|
subagentCount: number;
|
|
46
|
+
jobs: JobsSnapshot;
|
|
45
47
|
sessionStartTime: number;
|
|
46
48
|
git: {
|
|
47
49
|
branch: string | null;
|
|
@@ -11,6 +11,7 @@ import type { AgentSession } from "../../session/agent-session";
|
|
|
11
11
|
import { readVisibleSkillActiveState, type SkillActiveEntry } from "../../skill-state/active-state";
|
|
12
12
|
import * as git from "../../utils/git";
|
|
13
13
|
import { getSessionAccentAnsi, getSessionAccentHex } from "../../utils/session-color";
|
|
14
|
+
import { EMPTY_JOBS_SNAPSHOT, type JobsSnapshot } from "../jobs-observer";
|
|
14
15
|
import { sanitizeStatusText } from "../shared";
|
|
15
16
|
import { computeNonMessageTokens } from "../utils/context-usage";
|
|
16
17
|
import { renderSkillHudBar } from "./skill-hud/render";
|
|
@@ -153,6 +154,7 @@ export class StatusLineComponent implements Component {
|
|
|
153
154
|
#autoCompactEnabled: boolean = true;
|
|
154
155
|
#hookStatuses: Map<string, string> = new Map();
|
|
155
156
|
#subagentCount: number = 0;
|
|
157
|
+
#jobs: JobsSnapshot = EMPTY_JOBS_SNAPSHOT;
|
|
156
158
|
#sessionStartTime: number = Date.now();
|
|
157
159
|
#planModeStatus: { enabled: boolean; paused: boolean } | null = null;
|
|
158
160
|
#goalModeStatus: { enabled: boolean; paused: boolean } | null = null;
|
|
@@ -220,6 +222,10 @@ export class StatusLineComponent implements Component {
|
|
|
220
222
|
this.#subagentCount = count;
|
|
221
223
|
}
|
|
222
224
|
|
|
225
|
+
setJobs(jobs: JobsSnapshot): void {
|
|
226
|
+
this.#jobs = jobs;
|
|
227
|
+
}
|
|
228
|
+
|
|
223
229
|
setSessionStartTime(time: number): void {
|
|
224
230
|
this.#sessionStartTime = time;
|
|
225
231
|
}
|
|
@@ -612,6 +618,7 @@ export class StatusLineComponent implements Component {
|
|
|
612
618
|
contextWindow,
|
|
613
619
|
autoCompactEnabled: this.#autoCompactEnabled,
|
|
614
620
|
subagentCount: this.#subagentCount,
|
|
621
|
+
jobs: this.#jobs,
|
|
615
622
|
sessionStartTime: this.#sessionStartTime,
|
|
616
623
|
git: {
|
|
617
624
|
branch: this.#getCurrentBranch(),
|
|
@@ -687,7 +694,8 @@ export class StatusLineComponent implements Component {
|
|
|
687
694
|
}
|
|
688
695
|
}
|
|
689
696
|
|
|
690
|
-
const runningBackgroundJobs =
|
|
697
|
+
const runningBackgroundJobs =
|
|
698
|
+
this.session.getAsyncJobSnapshot()?.running.filter(job => job.metadata?.monitor !== true).length ?? 0;
|
|
691
699
|
if (runningBackgroundJobs > 0) {
|
|
692
700
|
const icon = theme.icon.agents ? `${theme.icon.agents} ` : "";
|
|
693
701
|
const label = `${formatCount("job", runningBackgroundJobs)} running`;
|
|
@@ -25,11 +25,17 @@ import type { InteractiveModeContext } from "../../modes/types";
|
|
|
25
25
|
import { setSessionTerminalTitle, setTerminalTitle } from "../../utils/title-generator";
|
|
26
26
|
|
|
27
27
|
const MAX_WIDGET_LINES = 10;
|
|
28
|
+
const HOOK_SELECTOR_MOUSE_REPORTING_ENABLE = "\x1b[?1006h\x1b[?1000h";
|
|
29
|
+
const HOOK_SELECTOR_MOUSE_REPORTING_DISABLE = "\x1b[?1000l\x1b[?1006l";
|
|
30
|
+
const HOOK_SELECTOR_CHROME_ROWS = 7;
|
|
31
|
+
const HOOK_SELECTOR_OUTLINE_ROWS = 2;
|
|
28
32
|
|
|
29
33
|
export class ExtensionUiController {
|
|
30
34
|
#extensionTerminalInputUnsubscribers = new Set<() => void>();
|
|
31
35
|
#hookWidgetsAbove = new Map<string, ExtensionUiComponent>();
|
|
32
36
|
#hookWidgetsBelow = new Map<string, ExtensionUiComponent>();
|
|
37
|
+
#hookSelectorMouseReportingEnabled = false;
|
|
38
|
+
|
|
33
39
|
constructor(private ctx: InteractiveModeContext) {}
|
|
34
40
|
|
|
35
41
|
/**
|
|
@@ -589,12 +595,20 @@ export class ExtensionUiController {
|
|
|
589
595
|
() => this.hideHookSelector(),
|
|
590
596
|
dialogOptions?.signal,
|
|
591
597
|
);
|
|
592
|
-
const maxVisible = Math.max(4, Math.min(15, this.ctx.ui.terminal.rows - 12));
|
|
593
598
|
const requestedTitleRows = dialogOptions?.scrollTitleRows;
|
|
594
|
-
const
|
|
595
|
-
const
|
|
599
|
+
const baseMaxVisible = Math.max(4, Math.min(15, this.ctx.ui.terminal.rows - 12));
|
|
600
|
+
const scrollOptionRows = Math.max(1, Math.min(baseMaxVisible, options.length));
|
|
601
|
+
const maxVisible =
|
|
602
|
+
requestedTitleRows === undefined ? baseMaxVisible : Math.min(15, Math.max(3, scrollOptionRows + 1));
|
|
603
|
+
const listChromeRows = dialogOptions?.outline === true ? HOOK_SELECTOR_OUTLINE_ROWS : 0;
|
|
604
|
+
const availableTitleRows =
|
|
605
|
+
this.ctx.ui.terminal.rows - scrollOptionRows - listChromeRows - HOOK_SELECTOR_CHROME_ROWS;
|
|
596
606
|
const scrollTitleRows =
|
|
597
607
|
requestedTitleRows === undefined ? undefined : Math.max(1, Math.min(requestedTitleRows, availableTitleRows));
|
|
608
|
+
if (scrollTitleRows !== undefined) {
|
|
609
|
+
this.#enableHookSelectorMouseReporting();
|
|
610
|
+
}
|
|
611
|
+
|
|
598
612
|
this.ctx.hookSelector = new HookSelectorComponent(
|
|
599
613
|
title,
|
|
600
614
|
options,
|
|
@@ -640,10 +654,32 @@ export class ExtensionUiController {
|
|
|
640
654
|
attachAbort();
|
|
641
655
|
return promise;
|
|
642
656
|
}
|
|
657
|
+
|
|
658
|
+
#enableHookSelectorMouseReporting(): void {
|
|
659
|
+
if (this.#hookSelectorMouseReportingEnabled) return;
|
|
660
|
+
this.#hookSelectorMouseReportingEnabled = true;
|
|
661
|
+
this.#writeTerminalControl(HOOK_SELECTOR_MOUSE_REPORTING_ENABLE);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
#disableHookSelectorMouseReporting(): void {
|
|
665
|
+
if (!this.#hookSelectorMouseReportingEnabled) return;
|
|
666
|
+
this.#hookSelectorMouseReportingEnabled = false;
|
|
667
|
+
this.#writeTerminalControl(HOOK_SELECTOR_MOUSE_REPORTING_DISABLE);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
#writeTerminalControl(sequence: string): void {
|
|
671
|
+
try {
|
|
672
|
+
this.ctx.ui.terminal.write(sequence);
|
|
673
|
+
} catch {
|
|
674
|
+
// Terminal teardown can race selector cleanup; normal shutdown restores modes.
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
643
678
|
/**
|
|
644
679
|
* Hide the hook selector.
|
|
645
680
|
*/
|
|
646
681
|
hideHookSelector(): void {
|
|
682
|
+
this.#disableHookSelectorMouseReporting();
|
|
647
683
|
this.ctx.hookSelector?.dispose();
|
|
648
684
|
this.ctx.editorContainer.clear();
|
|
649
685
|
this.ctx.editorContainer.addChild(this.ctx.editor);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
2
3
|
import { type AgentMessage, ThinkingLevel } from "@gajae-code/agent-core";
|
|
3
4
|
import type { AutocompleteProvider, SlashCommand } from "@gajae-code/tui";
|
|
4
5
|
import { $env, sanitizeText } from "@gajae-code/utils";
|
|
@@ -13,7 +14,7 @@ import { SKILL_PROMPT_MESSAGE_TYPE, type SkillPromptDetails } from "../../sessio
|
|
|
13
14
|
import { executeBuiltinSlashCommand } from "../../slash-commands/builtin-registry";
|
|
14
15
|
import { copyToClipboard, readImageFromClipboard } from "../../utils/clipboard";
|
|
15
16
|
import { getEditorCommand, openInEditor } from "../../utils/external-editor";
|
|
16
|
-
import { ensureSupportedImageInput } from "../../utils/image-loading";
|
|
17
|
+
import { ensureSupportedImageInput, ImageInputTooLargeError, loadImageInput } from "../../utils/image-loading";
|
|
17
18
|
import { resizeImage } from "../../utils/image-resize";
|
|
18
19
|
import { generateSessionTitle, setSessionTerminalTitle } from "../../utils/title-generator";
|
|
19
20
|
|
|
@@ -22,6 +23,8 @@ interface Expandable {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
const INTERACTIVE_ABORT_CLEANUP_TIMEOUT_MS = 5_000;
|
|
26
|
+
const CLIPBOARD_TEMP_IMAGE_FILE_PATTERN = /^clipboard-\d{4}-\d{2}-\d{2}-\d{6}-[A-Za-z0-9]+\.(?:png|jpe?g|gif|webp)$/i;
|
|
27
|
+
const MACOS_CLIPBOARD_TEMP_DIR_PATTERN = /^\/var\/folders\/[^/]+\/[^/]+\/T$/;
|
|
25
28
|
|
|
26
29
|
function isExpandable(obj: unknown): obj is Expandable {
|
|
27
30
|
return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
|
|
@@ -30,8 +33,17 @@ function isExpandable(obj: unknown): obj is Expandable {
|
|
|
30
33
|
export class InputController {
|
|
31
34
|
constructor(private ctx: InteractiveModeContext) {}
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
/** Set after a first Esc silently consumes a queued steer. Kept until the
|
|
37
|
+
* queued steer is either cancelled by a second Esc or drained by continuation,
|
|
38
|
+
* so abort cleanup going idle cannot turn the second Esc into an idle action. */
|
|
39
|
+
#steerConsumePending = false;
|
|
40
|
+
|
|
41
|
+
#abortInteractive(options?: { silent?: boolean }): Promise<void> {
|
|
42
|
+
return this.ctx.session.abort({
|
|
43
|
+
timeoutMs: INTERACTIVE_ABORT_CLEANUP_TIMEOUT_MS,
|
|
44
|
+
cause: "user_interrupt",
|
|
45
|
+
silent: options?.silent,
|
|
46
|
+
});
|
|
35
47
|
}
|
|
36
48
|
|
|
37
49
|
setupKeyHandlers(): void {
|
|
@@ -40,6 +52,7 @@ export class InputController {
|
|
|
40
52
|
Boolean(
|
|
41
53
|
this.ctx.loadingAnimation ||
|
|
42
54
|
this.ctx.hasActiveBtw() ||
|
|
55
|
+
(this.#steerConsumePending && this.ctx.session.hasQueuedSteering) ||
|
|
43
56
|
this.ctx.session.isStreaming ||
|
|
44
57
|
this.ctx.session.isCompacting ||
|
|
45
58
|
this.ctx.session.isGeneratingHandoff ||
|
|
@@ -54,6 +67,17 @@ export class InputController {
|
|
|
54
67
|
if (this.ctx.hasActiveBtw() && this.ctx.handleBtwEscape()) {
|
|
55
68
|
return;
|
|
56
69
|
}
|
|
70
|
+
if (this.#steerConsumePending) {
|
|
71
|
+
if (this.ctx.session.hasQueuedSteering) {
|
|
72
|
+
// Second Esc before the scheduled steer continuation drains the
|
|
73
|
+
// queue: restore/drop the queued steer and perform a real abort,
|
|
74
|
+
// even if abort cleanup already made the session look idle.
|
|
75
|
+
this.#steerConsumePending = false;
|
|
76
|
+
this.restoreQueuedMessagesToEditor({ abort: true });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.#steerConsumePending = false;
|
|
80
|
+
}
|
|
57
81
|
if (this.ctx.loadingAnimation) {
|
|
58
82
|
if (this.ctx.cancelPendingSubmission()) {
|
|
59
83
|
return;
|
|
@@ -73,7 +97,15 @@ export class InputController {
|
|
|
73
97
|
this.ctx.isPythonMode = false;
|
|
74
98
|
this.ctx.updateEditorBorderColor();
|
|
75
99
|
} else if (this.ctx.session.isStreaming) {
|
|
76
|
-
|
|
100
|
+
if (this.ctx.session.hasQueuedSteering && !this.#steerConsumePending) {
|
|
101
|
+
// First Esc with a queued steer: silently consume it and
|
|
102
|
+
// auto-continue via steer-on-interrupt instead of stalling on
|
|
103
|
+
// "Operation aborted".
|
|
104
|
+
this.#steerConsumePending = true;
|
|
105
|
+
void this.#abortInteractive({ silent: true });
|
|
106
|
+
} else {
|
|
107
|
+
void this.#abortInteractive();
|
|
108
|
+
}
|
|
77
109
|
} else if (!this.ctx.editor.getText().trim()) {
|
|
78
110
|
// Double-interrupt with empty editor triggers /tree, /branch, or nothing based on setting
|
|
79
111
|
const action = settings.get("doubleEscapeAction");
|
|
@@ -132,6 +164,13 @@ export class InputController {
|
|
|
132
164
|
this.ctx.keybindings.getKeys("app.clipboard.copyPrompt"),
|
|
133
165
|
);
|
|
134
166
|
this.ctx.editor.onCopyPrompt = () => this.handleCopyPrompt();
|
|
167
|
+
this.ctx.editor.onPasteText = text => this.handleTextPaste(text);
|
|
168
|
+
this.ctx.editor.onPastePendingInputCleared = (reason, droppedInputCount) => {
|
|
169
|
+
const reasonText = reason === "timeout" ? "timed out" : "exceeded the input queue limit";
|
|
170
|
+
this.ctx.showWarning(
|
|
171
|
+
`Paste handling ${reasonText}; discarded ${droppedInputCount} buffered input event${droppedInputCount === 1 ? "" : "s"}.`,
|
|
172
|
+
);
|
|
173
|
+
};
|
|
135
174
|
this.ctx.editor.setActionKeys("app.tools.expand", this.ctx.keybindings.getKeys("app.tools.expand"));
|
|
136
175
|
this.ctx.editor.onExpandTools = () => this.toggleToolOutputExpansion();
|
|
137
176
|
this.ctx.editor.setActionKeys("app.message.dequeue", this.ctx.keybindings.getKeys("app.message.dequeue"));
|
|
@@ -170,6 +209,9 @@ export class InputController {
|
|
|
170
209
|
for (const key of this.ctx.keybindings.getKeys("app.session.observe")) {
|
|
171
210
|
this.ctx.editor.setCustomKeyHandler(key, () => this.ctx.showSessionObserver());
|
|
172
211
|
}
|
|
212
|
+
for (const key of this.ctx.keybindings.getKeys("app.jobs.open")) {
|
|
213
|
+
this.ctx.editor.setCustomKeyHandler(key, () => this.ctx.showJobsOverlay());
|
|
214
|
+
}
|
|
173
215
|
|
|
174
216
|
this.ctx.editor.onChange = (text: string) => {
|
|
175
217
|
const wasBashMode = this.ctx.isBashMode;
|
|
@@ -602,6 +644,55 @@ export class InputController {
|
|
|
602
644
|
process.kill(0, "SIGTSTP");
|
|
603
645
|
}
|
|
604
646
|
|
|
647
|
+
handleTextPaste(text: string): boolean | Promise<boolean> {
|
|
648
|
+
const imagePath = this.#getPastedImagePathCandidate(text);
|
|
649
|
+
return imagePath ? this.#attachPastedImagePath(imagePath) : false;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
async #attachPastedImagePath(imagePath: string): Promise<boolean> {
|
|
653
|
+
try {
|
|
654
|
+
const image = await loadImageInput({
|
|
655
|
+
path: imagePath,
|
|
656
|
+
cwd: this.ctx.sessionManager.getCwd(),
|
|
657
|
+
autoResize: this.ctx.settings.get("images.autoResize"),
|
|
658
|
+
});
|
|
659
|
+
if (!image) {
|
|
660
|
+
this.ctx.showStatus("Unsupported pasted clipboard image file");
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
this.ctx.pendingImages.push({
|
|
665
|
+
type: "image",
|
|
666
|
+
data: image.data,
|
|
667
|
+
mimeType: image.mimeType,
|
|
668
|
+
});
|
|
669
|
+
this.ctx.editor.insertText(`${this.#nextImagePlaceholder()} `);
|
|
670
|
+
this.ctx.showStatus(`Attached image: ${path.basename(image.resolvedPath)}`, { dim: true });
|
|
671
|
+
this.ctx.ui.requestRender();
|
|
672
|
+
return true;
|
|
673
|
+
} catch (error) {
|
|
674
|
+
if (error instanceof ImageInputTooLargeError) {
|
|
675
|
+
this.ctx.showStatus(error.message);
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
this.ctx.showStatus("Failed to attach pasted clipboard image");
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
#getPastedImagePathCandidate(text: string): string | undefined {
|
|
684
|
+
const resolvedPath = path.resolve(text.trim());
|
|
685
|
+
const parentDir = path.dirname(resolvedPath);
|
|
686
|
+
const isClipboardTempPath =
|
|
687
|
+
(parentDir === "/tmp" || MACOS_CLIPBOARD_TEMP_DIR_PATTERN.test(parentDir)) &&
|
|
688
|
+
CLIPBOARD_TEMP_IMAGE_FILE_PATTERN.test(path.basename(resolvedPath));
|
|
689
|
+
return isClipboardTempPath ? resolvedPath : undefined;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
#nextImagePlaceholder(): string {
|
|
693
|
+
return `[image ${this.ctx.pendingImages.length}]`;
|
|
694
|
+
}
|
|
695
|
+
|
|
605
696
|
async handleImagePaste(): Promise<boolean> {
|
|
606
697
|
try {
|
|
607
698
|
const image = await readImageFromClipboard();
|
|
@@ -616,7 +707,7 @@ export class InputController {
|
|
|
616
707
|
this.ctx.showStatus(`Unsupported clipboard image format: ${image.mimeType}`);
|
|
617
708
|
return false;
|
|
618
709
|
}
|
|
619
|
-
if (settings.get("images.autoResize")) {
|
|
710
|
+
if (this.ctx.settings.get("images.autoResize")) {
|
|
620
711
|
try {
|
|
621
712
|
const resized = await resizeImage({
|
|
622
713
|
type: "image",
|
|
@@ -634,10 +725,7 @@ export class InputController {
|
|
|
634
725
|
data: imageData.data,
|
|
635
726
|
mimeType: imageData.mimeType,
|
|
636
727
|
});
|
|
637
|
-
|
|
638
|
-
const imageNum = this.ctx.pendingImages.length;
|
|
639
|
-
const placeholder = `[Image #${imageNum}]`;
|
|
640
|
-
this.ctx.editor.insertText(`${placeholder} `);
|
|
728
|
+
this.ctx.editor.insertText(`${this.#nextImagePlaceholder()} `);
|
|
641
729
|
this.ctx.ui.requestRender();
|
|
642
730
|
return true;
|
|
643
731
|
}
|
|
@@ -41,6 +41,7 @@ import { AgentDashboard } from "../components/agent-dashboard";
|
|
|
41
41
|
import { AssistantMessageComponent } from "../components/assistant-message";
|
|
42
42
|
import { ExtensionDashboard } from "../components/extensions";
|
|
43
43
|
import { HistorySearchComponent } from "../components/history-search";
|
|
44
|
+
import { JobsOverlayComponent } from "../components/jobs-overlay";
|
|
44
45
|
import { ModelSelectorComponent, type ModelSelectorSelection } from "../components/model-selector";
|
|
45
46
|
import { OAuthSelectorComponent } from "../components/oauth-selector";
|
|
46
47
|
import { PluginSelectorComponent } from "../components/plugin-selector";
|
|
@@ -55,6 +56,7 @@ import { ThemeSelectorComponent } from "../components/theme-selector";
|
|
|
55
56
|
import { ToolExecutionComponent } from "../components/tool-execution";
|
|
56
57
|
import { TreeSelectorComponent } from "../components/tree-selector";
|
|
57
58
|
import { UserMessageSelectorComponent } from "../components/user-message-selector";
|
|
59
|
+
import type { JobsObserver } from "../jobs-observer";
|
|
58
60
|
import type { SessionObserverRegistry } from "../session-observer-registry";
|
|
59
61
|
|
|
60
62
|
const CALLBACK_SERVER_PROVIDERS = new Set<string>([
|
|
@@ -1150,4 +1152,31 @@ export class SelectorController {
|
|
|
1150
1152
|
this.ctx.ui.setFocus(selector);
|
|
1151
1153
|
this.ctx.ui.requestRender();
|
|
1152
1154
|
}
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* Jobs overlay: navigate ongoing monitor + cron jobs (Monitors then Crons,
|
|
1158
|
+
* newest-first), drill into per-type detail, and cancel/delete with a y/N
|
|
1159
|
+
* confirm. Built from nested SelectLists (list -> detail -> confirm) so focus
|
|
1160
|
+
* stays on the active SelectList.
|
|
1161
|
+
*/
|
|
1162
|
+
showJobsOverlay(observer: JobsObserver): void {
|
|
1163
|
+
let overlay: JobsOverlayComponent | undefined;
|
|
1164
|
+
const close = () => {
|
|
1165
|
+
this.ctx.editorContainer.clear();
|
|
1166
|
+
this.ctx.editorContainer.addChild(this.ctx.editor);
|
|
1167
|
+
this.ctx.ui.setFocus(this.ctx.editor);
|
|
1168
|
+
this.ctx.ui.requestRender();
|
|
1169
|
+
};
|
|
1170
|
+
overlay = new JobsOverlayComponent(observer, {
|
|
1171
|
+
close,
|
|
1172
|
+
requestRender: () => {
|
|
1173
|
+
if (overlay) this.ctx.ui.setFocus(overlay.getFocus());
|
|
1174
|
+
this.ctx.ui.requestRender();
|
|
1175
|
+
},
|
|
1176
|
+
});
|
|
1177
|
+
this.ctx.editorContainer.clear();
|
|
1178
|
+
this.ctx.editorContainer.addChild(overlay);
|
|
1179
|
+
this.ctx.ui.setFocus(overlay.getFocus());
|
|
1180
|
+
this.ctx.ui.requestRender();
|
|
1181
|
+
}
|
|
1153
1182
|
}
|
package/src/modes/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { postmortem } from "@gajae-code/utils";
|
|
|
5
5
|
* Run modes for the coding agent.
|
|
6
6
|
*/
|
|
7
7
|
export { runAcpMode } from "./acp";
|
|
8
|
+
export { runBridgeMode } from "./bridge/bridge-mode";
|
|
8
9
|
export { InteractiveMode, type InteractiveModeOptions } from "./interactive-mode";
|
|
9
10
|
export { type PrintModeOptions, runPrintMode } from "./print-mode";
|
|
10
11
|
export {
|