@gajae-code/coding-agent 0.3.0 → 0.3.2
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 +32 -0
- package/README.md +1 -1
- package/dist/types/async/job-manager.d.ts +7 -0
- package/dist/types/cli/args.d.ts +3 -1
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/commands/launch.d.ts +6 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/model-profile-activation.d.ts +30 -0
- package/dist/types/config/model-profiles.d.ts +19 -0
- package/dist/types/config/model-registry.d.ts +8 -0
- package/dist/types/config/model-resolver.d.ts +1 -1
- package/dist/types/config/models-config-schema.d.ts +47 -0
- package/dist/types/config/settings-schema.d.ts +14 -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/ultragoal-runtime.d.ts +2 -1
- 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/main.d.ts +10 -1
- 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/custom-provider-wizard.d.ts +10 -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/model-selector.d.ts +6 -1
- package/dist/types/modes/components/provider-onboarding-selector.d.ts +1 -1
- 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 +9 -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 +2 -0
- package/dist/types/sdk.d.ts +3 -1
- 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/executor.d.ts +1 -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/hindsight-recall.d.ts +0 -2
- package/dist/types/tools/hindsight-reflect.d.ts +0 -2
- package/dist/types/tools/hindsight-retain.d.ts +0 -2
- package/dist/types/tools/index.d.ts +6 -4
- 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 +19 -2
- package/src/commands/deep-interview.ts +1 -0
- package/src/commands/harness.ts +289 -19
- package/src/commands/launch.ts +10 -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/model-profile-activation.ts +157 -0
- package/src/config/model-profiles.ts +155 -0
- package/src/config/model-registry.ts +19 -0
- package/src/config/model-resolver.ts +3 -2
- package/src/config/models-config-schema.ts +36 -0
- package/src/config/settings-schema.ts +16 -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 +39 -3
- package/src/defaults/gjc/skills/ultragoal/ai-slop-cleaner.md +61 -0
- package/src/defaults/gjc-defaults.ts +7 -0
- 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 +231 -38
- 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 +8 -10
- 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 +88 -6
- 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/custom-provider-wizard.ts +318 -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/model-selector.ts +108 -18
- package/src/modes/components/provider-onboarding-selector.ts +6 -1
- 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 +86 -1
- 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 +2 -0
- package/src/prompts/memories/consolidation.md +1 -1
- package/src/prompts/memories/read-path.md +6 -7
- package/src/prompts/memories/unavailable.md +2 -2
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/irc.md +1 -1
- package/src/prompts/tools/read.md +2 -2
- package/src/prompts/tools/recall.md +1 -0
- package/src/prompts/tools/reflect.md +1 -0
- package/src/prompts/tools/retain.md +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 +5 -1
- 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 +9 -1
- package/src/task/executor.ts +31 -5
- package/src/task/id.ts +33 -0
- package/src/task/index.ts +259 -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/hindsight-recall.ts +0 -2
- package/src/tools/hindsight-reflect.ts +0 -2
- package/src/tools/hindsight-retain.ts +0 -2
- package/src/tools/index.ts +6 -18
- package/src/tools/path-utils.ts +3 -2
- package/src/tools/read.ts +4 -3
- 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
|
+
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
Text,
|
|
13
13
|
type TUI,
|
|
14
14
|
} from "@gajae-code/tui";
|
|
15
|
+
import type { ModelProfileDefinition } from "../../config/model-profiles";
|
|
15
16
|
import type { GjcModelAssignmentTargetId, ModelRegistry } from "../../config/model-registry";
|
|
16
17
|
import { GJC_MODEL_ASSIGNMENT_TARGET_IDS, GJC_MODEL_ASSIGNMENT_TARGETS } from "../../config/model-registry";
|
|
17
18
|
import {
|
|
@@ -74,6 +75,12 @@ interface CanonicalModelItem {
|
|
|
74
75
|
explicitThinkingLevel?: boolean;
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
interface ProfileItem {
|
|
79
|
+
kind: "profile";
|
|
80
|
+
name: string;
|
|
81
|
+
profile: ModelProfileDefinition;
|
|
82
|
+
}
|
|
83
|
+
|
|
77
84
|
type ScopedModelItem = ScopedModelSelection;
|
|
78
85
|
|
|
79
86
|
interface RoleAssignment {
|
|
@@ -102,6 +109,11 @@ export type ModelSelectorSelection =
|
|
|
102
109
|
selector: string;
|
|
103
110
|
preset: ModelAssignmentPreset;
|
|
104
111
|
assignments: Record<GjcModelAssignmentTargetId, ThinkingLevel>;
|
|
112
|
+
}
|
|
113
|
+
| {
|
|
114
|
+
kind: "profile";
|
|
115
|
+
profileName: string;
|
|
116
|
+
setDefault: boolean;
|
|
105
117
|
};
|
|
106
118
|
|
|
107
119
|
interface PendingThinkingChoice {
|
|
@@ -110,7 +122,7 @@ interface PendingThinkingChoice {
|
|
|
110
122
|
levels: ThinkingLevel[];
|
|
111
123
|
}
|
|
112
124
|
|
|
113
|
-
type RoleSelectCallback = (selection: ModelSelectorSelection) => void
|
|
125
|
+
type RoleSelectCallback = (selection: ModelSelectorSelection) => void | Promise<void>;
|
|
114
126
|
type CancelCallback = () => void;
|
|
115
127
|
|
|
116
128
|
interface ProviderTabState {
|
|
@@ -161,6 +173,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
161
173
|
#filteredModels: ModelItem[] = [];
|
|
162
174
|
#canonicalModels: CanonicalModelItem[] = [];
|
|
163
175
|
#filteredCanonicalModels: CanonicalModelItem[] = [];
|
|
176
|
+
#profileItems: ProfileItem[] = [];
|
|
164
177
|
#selectedIndex: number = 0;
|
|
165
178
|
#roles = {} as Record<string, RoleAssignment | undefined>;
|
|
166
179
|
#settings = null as unknown as Settings;
|
|
@@ -228,7 +241,11 @@ export class ModelSelectorComponent extends Container {
|
|
|
228
241
|
this.#searchInput.onSubmit = () => {
|
|
229
242
|
const selectedItem = this.#getSelectedItem();
|
|
230
243
|
if (selectedItem) {
|
|
231
|
-
|
|
244
|
+
if (selectedItem.kind === "profile") {
|
|
245
|
+
this.#beginProfileActionMenu(selectedItem);
|
|
246
|
+
} else {
|
|
247
|
+
this.#beginActionMenuOrSelect(selectedItem);
|
|
248
|
+
}
|
|
232
249
|
}
|
|
233
250
|
};
|
|
234
251
|
this.addChild(this.#searchInput);
|
|
@@ -465,11 +482,18 @@ export class ModelSelectorComponent extends Container {
|
|
|
465
482
|
|
|
466
483
|
this.#sortModels(models);
|
|
467
484
|
this.#sortCanonicalModels(canonicalModels);
|
|
485
|
+
const profiles = this.#modelRegistry.getModelProfiles?.() ?? new Map();
|
|
486
|
+
const profileItems = this.#temporaryOnly
|
|
487
|
+
? []
|
|
488
|
+
: [...profiles.values()]
|
|
489
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
490
|
+
.map(profile => ({ kind: "profile" as const, name: profile.name, profile }));
|
|
468
491
|
|
|
469
492
|
this.#allModels = models;
|
|
470
493
|
this.#filteredModels = models;
|
|
471
494
|
this.#canonicalModels = canonicalModels;
|
|
472
495
|
this.#filteredCanonicalModels = canonicalModels;
|
|
496
|
+
this.#profileItems = profileItems;
|
|
473
497
|
this.#selectedIndex = Math.min(this.#selectedIndex, Math.max(0, models.length - 1));
|
|
474
498
|
}
|
|
475
499
|
|
|
@@ -664,20 +688,40 @@ export class ModelSelectorComponent extends Container {
|
|
|
664
688
|
}
|
|
665
689
|
}
|
|
666
690
|
|
|
691
|
+
#getVisibleProfiles(): ProfileItem[] {
|
|
692
|
+
return !this.#temporaryOnly && !this.#isCanonicalTab() && this.#getActiveTabId() === ALL_TAB
|
|
693
|
+
? this.#profileItems
|
|
694
|
+
: [];
|
|
695
|
+
}
|
|
696
|
+
|
|
667
697
|
#updateList(): void {
|
|
668
698
|
this.#listContainer.clear();
|
|
669
699
|
const isCanonicalTab = this.#isCanonicalTab();
|
|
700
|
+
const visibleProfiles = this.#getVisibleProfiles();
|
|
701
|
+
const modelSelectedIndex = Math.max(0, this.#selectedIndex - visibleProfiles.length);
|
|
670
702
|
const visibleItems = isCanonicalTab ? this.#filteredCanonicalModels : this.#filteredModels;
|
|
671
703
|
|
|
672
704
|
const maxVisible = 10;
|
|
673
705
|
const startIndex = Math.max(
|
|
674
706
|
0,
|
|
675
|
-
Math.min(
|
|
707
|
+
Math.min(modelSelectedIndex - Math.floor(maxVisible / 2), visibleItems.length - maxVisible),
|
|
676
708
|
);
|
|
677
709
|
const endIndex = Math.min(startIndex + maxVisible, visibleItems.length);
|
|
678
710
|
|
|
679
711
|
const showProvider = this.#getActiveTabId() === ALL_TAB;
|
|
680
712
|
|
|
713
|
+
if (visibleProfiles.length > 0) {
|
|
714
|
+
this.#listContainer.addChild(new Text(theme.fg("muted", "Profiles"), 0, 0));
|
|
715
|
+
for (let i = 0; i < visibleProfiles.length; i++) {
|
|
716
|
+
const profile = visibleProfiles[i];
|
|
717
|
+
if (!profile) continue;
|
|
718
|
+
const isSelected = i === this.#selectedIndex;
|
|
719
|
+
const prefix = isSelected ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
|
|
720
|
+
const label = isSelected ? theme.fg("accent", profile.name) : profile.name;
|
|
721
|
+
this.#listContainer.addChild(new Text(`${prefix}${label}`, 0, 0));
|
|
722
|
+
}
|
|
723
|
+
this.#listContainer.addChild(new Spacer(1));
|
|
724
|
+
}
|
|
681
725
|
// Show visible slice of filtered models
|
|
682
726
|
for (let i = startIndex; i < endIndex; i++) {
|
|
683
727
|
const item = visibleItems[i];
|
|
@@ -685,7 +729,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
685
729
|
const canonicalItem = isCanonicalTab ? (item as CanonicalModelItem) : undefined;
|
|
686
730
|
const providerItem = isCanonicalTab ? undefined : (item as ModelItem);
|
|
687
731
|
|
|
688
|
-
const isSelected = i === this.#selectedIndex;
|
|
732
|
+
const isSelected = i + visibleProfiles.length === this.#selectedIndex;
|
|
689
733
|
|
|
690
734
|
// Build role badges (inverted: color as background, black text)
|
|
691
735
|
const roleBadgeTokens: string[] = [];
|
|
@@ -742,7 +786,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
742
786
|
for (const line of errorLines) {
|
|
743
787
|
this.#listContainer.addChild(new Text(theme.fg("error", line), 0, 0));
|
|
744
788
|
}
|
|
745
|
-
} else if (visibleItems.length === 0) {
|
|
789
|
+
} else if (visibleItems.length === 0 && visibleProfiles.length === 0) {
|
|
746
790
|
const statusMessage = this.#getProviderEmptyStateMessage();
|
|
747
791
|
this.#listContainer.addChild(
|
|
748
792
|
new Text(
|
|
@@ -751,8 +795,13 @@ export class ModelSelectorComponent extends Container {
|
|
|
751
795
|
0,
|
|
752
796
|
),
|
|
753
797
|
);
|
|
798
|
+
} else if (this.#selectedIndex < visibleProfiles.length) {
|
|
799
|
+
const selectedProfile = visibleProfiles[this.#selectedIndex];
|
|
800
|
+
if (selectedProfile && this.#pendingActionItem) {
|
|
801
|
+
this.#renderProfileActionMenu(selectedProfile);
|
|
802
|
+
}
|
|
754
803
|
} else {
|
|
755
|
-
const selected = visibleItems[
|
|
804
|
+
const selected = visibleItems[modelSelectedIndex];
|
|
756
805
|
if (!selected) {
|
|
757
806
|
return;
|
|
758
807
|
}
|
|
@@ -806,6 +855,20 @@ export class ModelSelectorComponent extends Container {
|
|
|
806
855
|
}
|
|
807
856
|
}
|
|
808
857
|
|
|
858
|
+
#renderProfileActionMenu(profile: ProfileItem): void {
|
|
859
|
+
this.#listContainer.addChild(new Spacer(1));
|
|
860
|
+
this.#listContainer.addChild(new Text(theme.fg("muted", ` Action for profile: ${profile.name}`), 0, 0));
|
|
861
|
+
this.#listContainer.addChild(new Spacer(1));
|
|
862
|
+
const labels = ["Apply for this session", "Set as default"];
|
|
863
|
+
for (let i = 0; i < labels.length; i++) {
|
|
864
|
+
const label = labels[i] ?? "";
|
|
865
|
+
const prefix = i === this.#selectedActionIndex ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
|
|
866
|
+
this.#listContainer.addChild(
|
|
867
|
+
new Text(`${prefix}${i === this.#selectedActionIndex ? theme.fg("accent", label) : label}`, 0, 0),
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
809
872
|
#getCurrentRoleThinkingLevel(role: string): ThinkingLevel {
|
|
810
873
|
return this.#roles[role]?.thinkingLevel ?? ThinkingLevel.Inherit;
|
|
811
874
|
}
|
|
@@ -813,10 +876,11 @@ export class ModelSelectorComponent extends Container {
|
|
|
813
876
|
return GJC_MODEL_ASSIGNMENT_TARGET_IDS.length + (supportsOpenAICodexPreset(model) ? 1 : 0);
|
|
814
877
|
}
|
|
815
878
|
|
|
816
|
-
#getSelectedItem(): ModelItem | CanonicalModelItem | undefined {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
879
|
+
#getSelectedItem(): ModelItem | CanonicalModelItem | ProfileItem | undefined {
|
|
880
|
+
const visibleProfiles = this.#getVisibleProfiles();
|
|
881
|
+
if (this.#selectedIndex < visibleProfiles.length) return visibleProfiles[this.#selectedIndex];
|
|
882
|
+
const modelIndex = this.#selectedIndex - visibleProfiles.length;
|
|
883
|
+
return this.#isCanonicalTab() ? this.#filteredCanonicalModels[modelIndex] : this.#filteredModels[modelIndex];
|
|
820
884
|
}
|
|
821
885
|
|
|
822
886
|
handleInput(keyData: string): void {
|
|
@@ -836,7 +900,9 @@ export class ModelSelectorComponent extends Container {
|
|
|
836
900
|
|
|
837
901
|
// Up arrow - navigate list (wrap to bottom when at top)
|
|
838
902
|
if (matchesKey(keyData, "up")) {
|
|
839
|
-
const itemCount =
|
|
903
|
+
const itemCount =
|
|
904
|
+
this.#getVisibleProfiles().length +
|
|
905
|
+
(this.#isCanonicalTab() ? this.#filteredCanonicalModels.length : this.#filteredModels.length);
|
|
840
906
|
if (itemCount === 0) return;
|
|
841
907
|
this.#selectedIndex = this.#selectedIndex === 0 ? itemCount - 1 : this.#selectedIndex - 1;
|
|
842
908
|
this.#updateList();
|
|
@@ -845,7 +911,9 @@ export class ModelSelectorComponent extends Container {
|
|
|
845
911
|
|
|
846
912
|
// Down arrow - navigate list (wrap to top when at bottom)
|
|
847
913
|
if (matchesKey(keyData, "down")) {
|
|
848
|
-
const itemCount =
|
|
914
|
+
const itemCount =
|
|
915
|
+
this.#getVisibleProfiles().length +
|
|
916
|
+
(this.#isCanonicalTab() ? this.#filteredCanonicalModels.length : this.#filteredModels.length);
|
|
849
917
|
if (itemCount === 0) return;
|
|
850
918
|
this.#selectedIndex = this.#selectedIndex === itemCount - 1 ? 0 : this.#selectedIndex + 1;
|
|
851
919
|
this.#updateList();
|
|
@@ -857,7 +925,11 @@ export class ModelSelectorComponent extends Container {
|
|
|
857
925
|
if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
858
926
|
const selectedItem = this.#getSelectedItem();
|
|
859
927
|
if (selectedItem) {
|
|
860
|
-
|
|
928
|
+
if (selectedItem.kind === "profile") {
|
|
929
|
+
this.#beginProfileActionMenu(selectedItem);
|
|
930
|
+
} else {
|
|
931
|
+
this.#beginActionMenuOrSelect(selectedItem);
|
|
932
|
+
}
|
|
861
933
|
}
|
|
862
934
|
return;
|
|
863
935
|
}
|
|
@@ -872,6 +944,12 @@ export class ModelSelectorComponent extends Container {
|
|
|
872
944
|
this.#searchInput.handleInput(keyData);
|
|
873
945
|
this.#filterModels(this.#searchInput.getValue());
|
|
874
946
|
}
|
|
947
|
+
#beginProfileActionMenu(profile: ProfileItem): void {
|
|
948
|
+
this.#pendingActionItem = profile as unknown as ModelItem;
|
|
949
|
+
this.#selectedActionIndex = 0;
|
|
950
|
+
this.#updateList();
|
|
951
|
+
}
|
|
952
|
+
|
|
875
953
|
#beginActionMenuOrSelect(item: ModelItem | CanonicalModelItem): void {
|
|
876
954
|
if (this.#temporaryOnly) {
|
|
877
955
|
this.#handleSelect(item, null);
|
|
@@ -885,7 +963,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
885
963
|
#handleActionMenuInput(keyData: string): void {
|
|
886
964
|
const item = this.#pendingActionItem;
|
|
887
965
|
if (!item) return;
|
|
888
|
-
const actionCount = this.#getActionCount(item.model);
|
|
966
|
+
const actionCount = (item as unknown as ProfileItem).kind === "profile" ? 2 : this.#getActionCount(item.model);
|
|
889
967
|
if (matchesKey(keyData, "up")) {
|
|
890
968
|
this.#selectedActionIndex = this.#selectedActionIndex === 0 ? actionCount - 1 : this.#selectedActionIndex - 1;
|
|
891
969
|
this.#updateList();
|
|
@@ -898,11 +976,20 @@ export class ModelSelectorComponent extends Container {
|
|
|
898
976
|
}
|
|
899
977
|
if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
900
978
|
this.#pendingActionItem = undefined;
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
this.#
|
|
979
|
+
if ((item as unknown as ProfileItem).kind === "profile") {
|
|
980
|
+
const profile = item as unknown as ProfileItem;
|
|
981
|
+
this.#onSelectCallback({
|
|
982
|
+
kind: "profile",
|
|
983
|
+
profileName: profile.name,
|
|
984
|
+
setDefault: this.#selectedActionIndex === 1,
|
|
985
|
+
});
|
|
904
986
|
} else {
|
|
905
|
-
this.#
|
|
987
|
+
const role = GJC_MODEL_ASSIGNMENT_TARGET_IDS[this.#selectedActionIndex];
|
|
988
|
+
if (role) {
|
|
989
|
+
this.#handleSelect(item, role);
|
|
990
|
+
} else {
|
|
991
|
+
this.#handlePresetSelect(item, OPENAI_CODE_PROFILE_PRESET);
|
|
992
|
+
}
|
|
906
993
|
}
|
|
907
994
|
return;
|
|
908
995
|
}
|
|
@@ -1004,6 +1091,9 @@ export class ModelSelectorComponent extends Container {
|
|
|
1004
1091
|
getSearchInput(): Input {
|
|
1005
1092
|
return this.#searchInput;
|
|
1006
1093
|
}
|
|
1094
|
+
async __testSelectProfile(profileName: string, setDefault: boolean): Promise<void> {
|
|
1095
|
+
await this.#onSelectCallback({ kind: "profile", profileName, setDefault });
|
|
1096
|
+
}
|
|
1007
1097
|
}
|
|
1008
1098
|
|
|
1009
1099
|
function requiresExplicitThinkingChoice(model: Model): boolean {
|
|
@@ -4,7 +4,7 @@ import { matchesSelectCancel } from "../../modes/utils/keybinding-matchers";
|
|
|
4
4
|
import { formatModelOnboardingGuidance } from "../../setup/model-onboarding-guidance";
|
|
5
5
|
import { DynamicBorder } from "./dynamic-border";
|
|
6
6
|
|
|
7
|
-
export type ProviderOnboardingAction = "oauth-login" | "api-guide";
|
|
7
|
+
export type ProviderOnboardingAction = "custom-provider-wizard" | "oauth-login" | "api-guide";
|
|
8
8
|
|
|
9
9
|
interface ProviderOnboardingOption {
|
|
10
10
|
label: string;
|
|
@@ -13,6 +13,11 @@ interface ProviderOnboardingOption {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const PROVIDER_ONBOARDING_OPTIONS: ProviderOnboardingOption[] = [
|
|
16
|
+
{
|
|
17
|
+
label: "Add custom provider",
|
|
18
|
+
description: "Configure an OpenAI- or Anthropic-compatible API provider interactively.",
|
|
19
|
+
action: "custom-provider-wizard",
|
|
20
|
+
},
|
|
16
21
|
{
|
|
17
22
|
label: "Login with OAuth/subscription",
|
|
18
23
|
description: "Open the interactive OAuth provider selector.",
|
|
@@ -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`;
|