@gajae-code/coding-agent 0.2.0 → 0.2.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 +38 -1
- package/dist/types/cli/skills-cli.d.ts +9 -0
- package/dist/types/commands/contribution-prep.d.ts +18 -0
- package/dist/types/commands/session.d.ts +24 -0
- package/dist/types/commands/skills.d.ts +26 -0
- package/dist/types/config/model-registry.d.ts +33 -4
- package/dist/types/config/models-config-schema.d.ts +52 -5
- package/dist/types/config/settings-schema.d.ts +1 -24
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +15 -0
- package/dist/types/gjc-runtime/goal-mode-request.d.ts +1 -1
- package/dist/types/gjc-runtime/launch-tmux.d.ts +12 -11
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +25 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +13 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +37 -5
- package/dist/types/gjc-runtime/tmux-common.d.ts +41 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +17 -0
- package/dist/types/goals/runtime.d.ts +3 -9
- package/dist/types/goals/state.d.ts +3 -6
- package/dist/types/goals/tools/goal-tool.d.ts +1 -69
- package/dist/types/modes/components/model-selector.d.ts +21 -1
- package/dist/types/modes/components/status-line/types.d.ts +0 -3
- package/dist/types/modes/components/status-line.d.ts +0 -3
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -12
- package/dist/types/modes/theme/defaults/index.d.ts +0 -2
- package/dist/types/modes/theme/theme.d.ts +1 -2
- package/dist/types/modes/types.d.ts +1 -7
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/session/contribution-prep.d.ts +47 -0
- package/dist/types/skill-state/active-state.d.ts +4 -0
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +6 -1
- package/dist/types/skill-state/workflow-hud.d.ts +9 -4
- package/dist/types/skill-state/workflow-state-contract.d.ts +34 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -0
- package/package.json +7 -7
- package/src/cli/args.ts +17 -2
- package/src/cli/skills-cli.ts +88 -0
- package/src/cli.ts +7 -1
- package/src/commands/contribution-prep.ts +41 -0
- package/src/commands/deep-interview.ts +6 -22
- package/src/commands/launch.ts +10 -1
- package/src/commands/ralplan.ts +10 -22
- package/src/commands/session.ts +150 -0
- package/src/commands/skills.ts +48 -0
- package/src/commands/state.ts +14 -4
- package/src/commands/team.ts +23 -3
- package/src/commit/agentic/index.ts +1 -0
- package/src/commit/pipeline.ts +1 -0
- package/src/config/model-registry.ts +269 -10
- package/src/config/models-config-schema.ts +124 -88
- package/src/config/settings-schema.ts +1 -25
- package/src/config.ts +1 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +14 -13
- package/src/defaults/gjc/skills/ralplan/SKILL.md +14 -2
- package/src/defaults/gjc/skills/team/SKILL.md +29 -7
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +23 -25
- package/src/eval/py/prelude.py +1 -1
- package/src/gjc-runtime/deep-interview-runtime.ts +279 -0
- package/src/gjc-runtime/goal-mode-request.ts +2 -19
- package/src/gjc-runtime/launch-tmux.ts +83 -43
- package/src/gjc-runtime/ralplan-runtime.ts +460 -0
- package/src/gjc-runtime/state-runtime.ts +562 -0
- package/src/gjc-runtime/team-runtime.ts +708 -52
- package/src/gjc-runtime/tmux-common.ts +119 -0
- package/src/gjc-runtime/tmux-sessions.ts +165 -0
- package/src/gjc-runtime/ultragoal-guard.ts +6 -3
- package/src/gjc-runtime/ultragoal-runtime.ts +5 -4
- package/src/goals/runtime.ts +38 -144
- package/src/goals/state.ts +36 -7
- package/src/goals/tools/goal-tool.ts +15 -172
- package/src/hooks/skill-state.ts +31 -12
- package/src/internal-urls/docs-index.generated.ts +4 -3
- package/src/main.ts +10 -1
- package/src/modes/components/model-selector.ts +109 -28
- package/src/modes/components/skill-hud/render.ts +4 -0
- package/src/modes/components/status-line/segments.ts +5 -16
- package/src/modes/components/status-line/types.ts +0 -3
- package/src/modes/components/status-line.ts +0 -6
- package/src/modes/controllers/command-controller.ts +25 -1
- package/src/modes/controllers/input-controller.ts +0 -15
- package/src/modes/controllers/selector-controller.ts +42 -2
- package/src/modes/interactive-mode.ts +18 -219
- package/src/modes/theme/defaults/dark-poimandres.json +0 -1
- package/src/modes/theme/defaults/light-poimandres.json +0 -1
- package/src/modes/theme/theme.ts +0 -6
- package/src/modes/types.ts +1 -7
- package/src/prompts/goals/goal-continuation.md +1 -4
- package/src/prompts/goals/goal-mode-active.md +3 -5
- package/src/prompts/system/system-prompt.md +5 -7
- package/src/prompts/tools/goal.md +4 -4
- package/src/sdk.ts +2 -1
- package/src/session/agent-session.ts +18 -0
- package/src/session/contribution-prep.ts +320 -0
- package/src/setup/provider-onboarding.ts +2 -0
- package/src/skill-state/active-state.ts +38 -0
- package/src/skill-state/deep-interview-mutation-guard.ts +88 -24
- package/src/skill-state/workflow-hud.ts +23 -5
- package/src/skill-state/workflow-state-contract.ts +121 -0
- package/src/slash-commands/acp-builtins.ts +11 -2
- package/src/slash-commands/builtin-registry.ts +40 -13
- package/src/task/commands.ts +1 -5
- package/src/tools/gh.ts +212 -2
- package/src/tools/index.ts +2 -5
- package/dist/types/commands/gjc-runtime-bridge.d.ts +0 -30
- package/dist/types/commands/question.d.ts +0 -7
- package/dist/types/modes/loop-limit.d.ts +0 -22
- package/src/commands/gjc-runtime-bridge.ts +0 -227
- package/src/commands/question.ts +0 -12
- package/src/modes/loop-limit.ts +0 -140
- package/src/prompts/commands/orchestrate.md +0 -49
- package/src/prompts/goals/goal-budget-limit.md +0 -16
- package/src/prompts/tools/create-goal.md +0 -3
- package/src/prompts/tools/get-goal.md +0 -3
- package/src/prompts/tools/update-goal.md +0 -3
package/src/main.ts
CHANGED
|
@@ -46,6 +46,7 @@ import type { AgentSession } from "./session/agent-session";
|
|
|
46
46
|
import type { AuthStorage } from "./session/auth-storage";
|
|
47
47
|
import { resolveResumableSession, type SessionInfo, SessionManager } from "./session/session-manager";
|
|
48
48
|
import { formatModelOnboardingGuidance } from "./setup/model-onboarding-guidance";
|
|
49
|
+
import { executeBuiltinSlashCommand } from "./slash-commands/builtin-registry";
|
|
49
50
|
import { resolvePromptInput } from "./system-prompt";
|
|
50
51
|
import type { LspStartupServerInfo } from "./tools";
|
|
51
52
|
import { getChangelogPath, getNewEntries, parseChangelog } from "./utils/changelog";
|
|
@@ -295,7 +296,14 @@ async function runInteractiveMode(
|
|
|
295
296
|
|
|
296
297
|
for (const message of initialMessages) {
|
|
297
298
|
try {
|
|
298
|
-
|
|
299
|
+
let text = message;
|
|
300
|
+
const slashResult = await executeBuiltinSlashCommand(text, {
|
|
301
|
+
ctx: mode,
|
|
302
|
+
handleBackgroundCommand: () => mode.handleBackgroundCommand(),
|
|
303
|
+
});
|
|
304
|
+
if (slashResult === true) continue;
|
|
305
|
+
if (typeof slashResult === "string") text = slashResult;
|
|
306
|
+
await session.prompt(text);
|
|
299
307
|
} catch (error: unknown) {
|
|
300
308
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
301
309
|
mode.showError(errorMessage);
|
|
@@ -710,6 +718,7 @@ export async function runRootCommand(
|
|
|
710
718
|
if (parsedArgs.mode === "rpc" || parsedArgs.mode === "rpc-ui" || parsedArgs.mode === "acp") {
|
|
711
719
|
applyRpcDefaultSettingOverrides(settingsInstance);
|
|
712
720
|
}
|
|
721
|
+
modelRegistry.applyConfiguredModelBindings(settingsInstance);
|
|
713
722
|
if (parsedArgs.noPty || parsedArgs.mode === "rpc-ui") {
|
|
714
723
|
Bun.env.PI_NO_PTY = "1";
|
|
715
724
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ThinkingLevel } from "@gajae-code/agent-core";
|
|
2
|
-
import { getSupportedEfforts, type Model, modelsAreEqual } from "@gajae-code/ai";
|
|
2
|
+
import { clampThinkingLevelForModel, getSupportedEfforts, type Model, modelsAreEqual } from "@gajae-code/ai";
|
|
3
3
|
import {
|
|
4
4
|
Container,
|
|
5
5
|
fuzzyFilter,
|
|
@@ -81,18 +81,36 @@ interface RoleAssignment {
|
|
|
81
81
|
thinkingLevel: ThinkingLevel;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
export interface ModelAssignmentPreset {
|
|
85
|
+
id: "openai-codex";
|
|
86
|
+
label: string;
|
|
87
|
+
description: string;
|
|
88
|
+
assignments: Partial<Record<GjcModelAssignmentTargetId, ThinkingLevel>>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type ModelSelectorSelection =
|
|
92
|
+
| {
|
|
93
|
+
kind: "assignment";
|
|
94
|
+
model: Model;
|
|
95
|
+
role: GjcModelAssignmentTargetId | null;
|
|
96
|
+
thinkingLevel?: ThinkingLevel;
|
|
97
|
+
selector?: string;
|
|
98
|
+
}
|
|
99
|
+
| {
|
|
100
|
+
kind: "preset";
|
|
101
|
+
model: Model;
|
|
102
|
+
selector: string;
|
|
103
|
+
preset: ModelAssignmentPreset;
|
|
104
|
+
assignments: Record<GjcModelAssignmentTargetId, ThinkingLevel>;
|
|
105
|
+
};
|
|
106
|
+
|
|
84
107
|
interface PendingThinkingChoice {
|
|
85
108
|
item: ModelItem | CanonicalModelItem;
|
|
86
109
|
role: GjcModelAssignmentTargetId | null;
|
|
87
110
|
levels: ThinkingLevel[];
|
|
88
111
|
}
|
|
89
112
|
|
|
90
|
-
type RoleSelectCallback = (
|
|
91
|
-
model: Model,
|
|
92
|
-
role: GjcModelAssignmentTargetId | null,
|
|
93
|
-
thinkingLevel?: ThinkingLevel,
|
|
94
|
-
selector?: string,
|
|
95
|
-
) => void;
|
|
113
|
+
type RoleSelectCallback = (selection: ModelSelectorSelection) => void;
|
|
96
114
|
type CancelCallback = () => void;
|
|
97
115
|
|
|
98
116
|
interface ProviderTabState {
|
|
@@ -107,6 +125,18 @@ const STATIC_PROVIDER_TABS: ProviderTabState[] = [
|
|
|
107
125
|
{ id: ALL_TAB, label: ALL_TAB },
|
|
108
126
|
{ id: CANONICAL_TAB, label: CANONICAL_TAB },
|
|
109
127
|
];
|
|
128
|
+
const OPENAI_CODE_PROFILE_PRESET: ModelAssignmentPreset = {
|
|
129
|
+
id: "openai-codex",
|
|
130
|
+
label: "Apply OpenAI Codex role preset",
|
|
131
|
+
description: "Default medium, Executor low, Architect xhigh, Planner medium, Critic high",
|
|
132
|
+
assignments: {
|
|
133
|
+
default: ThinkingLevel.Medium,
|
|
134
|
+
executor: ThinkingLevel.Low,
|
|
135
|
+
architect: ThinkingLevel.XHigh,
|
|
136
|
+
planner: ThinkingLevel.Medium,
|
|
137
|
+
critic: ThinkingLevel.High,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
110
140
|
|
|
111
141
|
function formatProviderTabLabel(providerId: string): string {
|
|
112
142
|
return providerId.replace(/[-_]+/g, " ").toUpperCase();
|
|
@@ -156,12 +186,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
156
186
|
settings: Settings,
|
|
157
187
|
modelRegistry: ModelRegistry,
|
|
158
188
|
scopedModels: ReadonlyArray<ScopedModelItem>,
|
|
159
|
-
onSelect:
|
|
160
|
-
model: Model,
|
|
161
|
-
role: GjcModelAssignmentTargetId | null,
|
|
162
|
-
thinkingLevel?: ThinkingLevel,
|
|
163
|
-
selector?: string,
|
|
164
|
-
) => void,
|
|
189
|
+
onSelect: RoleSelectCallback,
|
|
165
190
|
onCancel: () => void,
|
|
166
191
|
options?: { temporaryOnly?: boolean; initialSearchInput?: string },
|
|
167
192
|
) {
|
|
@@ -750,11 +775,13 @@ export class ModelSelectorComponent extends Container {
|
|
|
750
775
|
this.#listContainer.addChild(new Spacer(1));
|
|
751
776
|
this.#listContainer.addChild(new Text(theme.fg("muted", ` Action for: ${item.model.id}`), 0, 0));
|
|
752
777
|
this.#listContainer.addChild(new Spacer(1));
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
const target = GJC_MODEL_ASSIGNMENT_TARGETS[role];
|
|
778
|
+
const actionCount = this.#getActionCount(item.model);
|
|
779
|
+
for (let i = 0; i < actionCount; i++) {
|
|
756
780
|
const prefix = i === this.#selectedActionIndex ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
|
|
757
|
-
const
|
|
781
|
+
const role = GJC_MODEL_ASSIGNMENT_TARGET_IDS[i];
|
|
782
|
+
const label = role
|
|
783
|
+
? `Set as ${GJC_MODEL_ASSIGNMENT_TARGETS[role].tag ?? role.toUpperCase()} (${GJC_MODEL_ASSIGNMENT_TARGETS[role].name})`
|
|
784
|
+
: `${OPENAI_CODE_PROFILE_PRESET.label} (${OPENAI_CODE_PROFILE_PRESET.description})`;
|
|
758
785
|
this.#listContainer.addChild(
|
|
759
786
|
new Text(`${prefix}${i === this.#selectedActionIndex ? theme.fg("accent", label) : label}`, 0, 0),
|
|
760
787
|
);
|
|
@@ -782,6 +809,9 @@ export class ModelSelectorComponent extends Container {
|
|
|
782
809
|
#getCurrentRoleThinkingLevel(role: string): ThinkingLevel {
|
|
783
810
|
return this.#roles[role]?.thinkingLevel ?? ThinkingLevel.Inherit;
|
|
784
811
|
}
|
|
812
|
+
#getActionCount(model: Model): number {
|
|
813
|
+
return GJC_MODEL_ASSIGNMENT_TARGET_IDS.length + (supportsOpenAICodexPreset(model) ? 1 : 0);
|
|
814
|
+
}
|
|
785
815
|
|
|
786
816
|
#getSelectedItem(): ModelItem | CanonicalModelItem | undefined {
|
|
787
817
|
return this.#isCanonicalTab()
|
|
@@ -853,25 +883,27 @@ export class ModelSelectorComponent extends Container {
|
|
|
853
883
|
}
|
|
854
884
|
|
|
855
885
|
#handleActionMenuInput(keyData: string): void {
|
|
886
|
+
const item = this.#pendingActionItem;
|
|
887
|
+
if (!item) return;
|
|
888
|
+
const actionCount = this.#getActionCount(item.model);
|
|
856
889
|
if (matchesKey(keyData, "up")) {
|
|
857
|
-
this.#selectedActionIndex =
|
|
858
|
-
this.#selectedActionIndex === 0
|
|
859
|
-
? GJC_MODEL_ASSIGNMENT_TARGET_IDS.length - 1
|
|
860
|
-
: this.#selectedActionIndex - 1;
|
|
890
|
+
this.#selectedActionIndex = this.#selectedActionIndex === 0 ? actionCount - 1 : this.#selectedActionIndex - 1;
|
|
861
891
|
this.#updateList();
|
|
862
892
|
return;
|
|
863
893
|
}
|
|
864
894
|
if (matchesKey(keyData, "down")) {
|
|
865
|
-
this.#selectedActionIndex = (this.#selectedActionIndex + 1) %
|
|
895
|
+
this.#selectedActionIndex = (this.#selectedActionIndex + 1) % actionCount;
|
|
866
896
|
this.#updateList();
|
|
867
897
|
return;
|
|
868
898
|
}
|
|
869
899
|
if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
870
|
-
const item = this.#pendingActionItem;
|
|
871
|
-
if (!item) return;
|
|
872
|
-
const role = GJC_MODEL_ASSIGNMENT_TARGET_IDS[this.#selectedActionIndex];
|
|
873
900
|
this.#pendingActionItem = undefined;
|
|
874
|
-
this.#
|
|
901
|
+
const role = GJC_MODEL_ASSIGNMENT_TARGET_IDS[this.#selectedActionIndex];
|
|
902
|
+
if (role) {
|
|
903
|
+
this.#handleSelect(item, role);
|
|
904
|
+
} else {
|
|
905
|
+
this.#handlePresetSelect(item, OPENAI_CODE_PROFILE_PRESET);
|
|
906
|
+
}
|
|
875
907
|
return;
|
|
876
908
|
}
|
|
877
909
|
if (getKeybindings().matches(keyData, "tui.select.cancel")) {
|
|
@@ -910,6 +942,18 @@ export class ModelSelectorComponent extends Container {
|
|
|
910
942
|
this.#updateList();
|
|
911
943
|
}
|
|
912
944
|
}
|
|
945
|
+
#handlePresetSelect(item: ModelItem | CanonicalModelItem, preset: ModelAssignmentPreset): void {
|
|
946
|
+
const selectorValue = item.selector;
|
|
947
|
+
const assignments = resolvePresetAssignments(item.model, preset);
|
|
948
|
+
for (const [role, thinkingLevel] of Object.entries(assignments) as [
|
|
949
|
+
GjcModelAssignmentTargetId,
|
|
950
|
+
ThinkingLevel,
|
|
951
|
+
][]) {
|
|
952
|
+
this.#roles[role] = { model: item.model, thinkingLevel };
|
|
953
|
+
}
|
|
954
|
+
this.#onSelectCallback({ kind: "preset", model: item.model, selector: selectorValue, preset, assignments });
|
|
955
|
+
this.#updateList();
|
|
956
|
+
}
|
|
913
957
|
|
|
914
958
|
#handleSelect(
|
|
915
959
|
item: ModelItem | CanonicalModelItem,
|
|
@@ -927,7 +971,13 @@ export class ModelSelectorComponent extends Container {
|
|
|
927
971
|
|
|
928
972
|
// For temporary role, don't save to settings - just notify caller
|
|
929
973
|
if (role === null) {
|
|
930
|
-
this.#onSelectCallback(
|
|
974
|
+
this.#onSelectCallback({
|
|
975
|
+
kind: "assignment",
|
|
976
|
+
model: item.model,
|
|
977
|
+
role: null,
|
|
978
|
+
thinkingLevel: itemThinkingLevel,
|
|
979
|
+
selector: item.selector,
|
|
980
|
+
});
|
|
931
981
|
return;
|
|
932
982
|
}
|
|
933
983
|
|
|
@@ -939,7 +989,13 @@ export class ModelSelectorComponent extends Container {
|
|
|
939
989
|
this.#roles[role] = { model: item.model, thinkingLevel: selectedThinkingLevel };
|
|
940
990
|
|
|
941
991
|
// Notify caller (for updating agent state if needed)
|
|
942
|
-
this.#onSelectCallback(
|
|
992
|
+
this.#onSelectCallback({
|
|
993
|
+
kind: "assignment",
|
|
994
|
+
model: item.model,
|
|
995
|
+
role,
|
|
996
|
+
thinkingLevel: selectedThinkingLevel,
|
|
997
|
+
selector: selectorValue,
|
|
998
|
+
});
|
|
943
999
|
|
|
944
1000
|
// Update list to show new badges
|
|
945
1001
|
this.#updateList();
|
|
@@ -954,6 +1010,31 @@ function requiresExplicitThinkingChoice(model: Model): boolean {
|
|
|
954
1010
|
return model.reasoning === true && (model.provider === "openai" || model.provider === "openai-codex");
|
|
955
1011
|
}
|
|
956
1012
|
|
|
1013
|
+
function supportsOpenAICodexPreset(model: Model): boolean {
|
|
1014
|
+
return model.provider === "openai-codex" && model.reasoning === true;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
function resolvePresetAssignments(
|
|
1018
|
+
model: Model,
|
|
1019
|
+
preset: ModelAssignmentPreset,
|
|
1020
|
+
): Record<GjcModelAssignmentTargetId, ThinkingLevel> {
|
|
1021
|
+
const resolved = {} as Record<GjcModelAssignmentTargetId, ThinkingLevel>;
|
|
1022
|
+
for (const [role, requestedLevel] of Object.entries(preset.assignments) as [
|
|
1023
|
+
GjcModelAssignmentTargetId,
|
|
1024
|
+
ThinkingLevel,
|
|
1025
|
+
][]) {
|
|
1026
|
+
const clampedLevel =
|
|
1027
|
+
requestedLevel === ThinkingLevel.Off || requestedLevel === ThinkingLevel.Inherit
|
|
1028
|
+
? requestedLevel
|
|
1029
|
+
: clampThinkingLevelForModel(model, requestedLevel);
|
|
1030
|
+
if (!clampedLevel) {
|
|
1031
|
+
throw new Error(`Model ${model.provider}/${model.id} does not support ${requestedLevel} reasoning`);
|
|
1032
|
+
}
|
|
1033
|
+
resolved[role] = clampedLevel;
|
|
1034
|
+
}
|
|
1035
|
+
return resolved;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
957
1038
|
function getSelectableThinkingLevels(model: Model): ThinkingLevel[] {
|
|
958
1039
|
const levels: ThinkingLevel[] = [ThinkingLevel.Off];
|
|
959
1040
|
let efforts: readonly string[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SkillActiveEntry, WorkflowHudChip } from "../../../skill-state/active-state";
|
|
2
|
+
import { workflowReceiptStatus } from "../../../skill-state/workflow-state-contract";
|
|
2
3
|
|
|
3
4
|
const ANSI_RESET_FG = "\x1b[39m";
|
|
4
5
|
const ANSI_RESET_BOLD = "\x1b[22m";
|
|
@@ -60,6 +61,9 @@ function formatEntry(entry: SkillActiveEntry): string {
|
|
|
60
61
|
.map(formatChip)
|
|
61
62
|
.filter((chip): chip is string => Boolean(chip));
|
|
62
63
|
if (entry.stale === true) chips.unshift("warn:stale");
|
|
64
|
+
const receiptStatus = workflowReceiptStatus(entry.receipt);
|
|
65
|
+
if (receiptStatus === "stale") chips.unshift("warn:receipt=stale");
|
|
66
|
+
if (receiptStatus === "fresh") chips.push("receipt=fresh");
|
|
63
67
|
const summary = sanitizeHudPart(entry.hud?.summary);
|
|
64
68
|
return [base, summary, ...chips].filter(Boolean).join(" ");
|
|
65
69
|
}
|
|
@@ -110,10 +110,9 @@ const modelSegment: StatusLineSegment = {
|
|
|
110
110
|
},
|
|
111
111
|
};
|
|
112
112
|
|
|
113
|
-
function
|
|
113
|
+
function formatGoalUsage(current: number): string {
|
|
114
114
|
const used = formatNumber(current);
|
|
115
|
-
|
|
116
|
-
return `${used}/${formatNumber(budget)}`;
|
|
115
|
+
return used;
|
|
117
116
|
}
|
|
118
117
|
|
|
119
118
|
function renderGoalMode(ctx: SegmentContext, mode: { enabled: boolean; paused: boolean }): RenderedSegment {
|
|
@@ -131,10 +130,6 @@ function renderGoalMode(ctx: SegmentContext, mode: { enabled: boolean; paused: b
|
|
|
131
130
|
icon = theme.symbol("status.success");
|
|
132
131
|
color = "success";
|
|
133
132
|
break;
|
|
134
|
-
case "budget-limited":
|
|
135
|
-
icon = theme.symbol("status.warning");
|
|
136
|
-
color = "warning";
|
|
137
|
-
break;
|
|
138
133
|
case "dropped":
|
|
139
134
|
icon = theme.symbol("status.aborted");
|
|
140
135
|
color = "dim";
|
|
@@ -144,9 +139,9 @@ function renderGoalMode(ctx: SegmentContext, mode: { enabled: boolean; paused: b
|
|
|
144
139
|
}
|
|
145
140
|
|
|
146
141
|
const parts: string[] = [withIcon(icon, "Goal")];
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
149
|
-
parts.push(
|
|
142
|
+
const showUsage = ctx.session.settings.get("goal.statusInFooter") === true;
|
|
143
|
+
if (showUsage && goal) {
|
|
144
|
+
parts.push(formatGoalUsage(goal.tokensUsed));
|
|
150
145
|
}
|
|
151
146
|
return { content: theme.fg(color, parts.join(" ")), visible: true };
|
|
152
147
|
}
|
|
@@ -169,12 +164,6 @@ const modeSegment: StatusLineSegment = {
|
|
|
169
164
|
return renderGoalMode(ctx, goal);
|
|
170
165
|
}
|
|
171
166
|
|
|
172
|
-
const loop = ctx.loopMode;
|
|
173
|
-
if (loop?.enabled) {
|
|
174
|
-
const content = withIcon(theme.icon.loop, "Loop");
|
|
175
|
-
return { content: theme.fg("customMessageLabel", content), visible: true };
|
|
176
|
-
}
|
|
177
|
-
|
|
178
167
|
return { content: "", visible: false };
|
|
179
168
|
},
|
|
180
169
|
};
|
|
@@ -155,7 +155,6 @@ export class StatusLineComponent implements Component {
|
|
|
155
155
|
#subagentCount: number = 0;
|
|
156
156
|
#sessionStartTime: number = Date.now();
|
|
157
157
|
#planModeStatus: { enabled: boolean; paused: boolean } | null = null;
|
|
158
|
-
#loopModeStatus: { enabled: boolean } | null = null;
|
|
159
158
|
#goalModeStatus: { enabled: boolean; paused: boolean } | null = null;
|
|
160
159
|
#skillHudEntries: SkillActiveEntry[] = [];
|
|
161
160
|
#skillHudLastFetch = 0;
|
|
@@ -229,10 +228,6 @@ export class StatusLineComponent implements Component {
|
|
|
229
228
|
this.#planModeStatus = status ?? null;
|
|
230
229
|
}
|
|
231
230
|
|
|
232
|
-
setLoopModeStatus(status: { enabled: boolean } | undefined): void {
|
|
233
|
-
this.#loopModeStatus = status ?? null;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
231
|
setGoalModeStatus(status: { enabled: boolean; paused: boolean } | undefined): void {
|
|
237
232
|
this.#goalModeStatus = status ?? null;
|
|
238
233
|
}
|
|
@@ -611,7 +606,6 @@ export class StatusLineComponent implements Component {
|
|
|
611
606
|
width,
|
|
612
607
|
options: this.#resolveSettings().segmentOptions ?? {},
|
|
613
608
|
planMode: this.#planModeStatus,
|
|
614
|
-
loopMode: this.#loopModeStatus,
|
|
615
609
|
goalMode: this.#goalModeStatus,
|
|
616
610
|
usageStats,
|
|
617
611
|
contextPercent,
|
|
@@ -1248,7 +1248,31 @@ export class CommandController {
|
|
|
1248
1248
|
this.ctx.statusContainer.clear();
|
|
1249
1249
|
this.ctx.editor.onEscape = originalOnEscape;
|
|
1250
1250
|
}
|
|
1251
|
-
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
async handleContributionPrepCommand(customInstructions?: string): Promise<void> {
|
|
1254
|
+
this.ctx.editor.setText("");
|
|
1255
|
+
try {
|
|
1256
|
+
const result = await this.ctx.session.prepareContributionPrep({ customInstructions, spawnWorker: true });
|
|
1257
|
+
this.ctx.showStatus(
|
|
1258
|
+
[
|
|
1259
|
+
"Contribution prep artifacts written.",
|
|
1260
|
+
`Manifest: ${result.manifestPath}`,
|
|
1261
|
+
`Worker prompt: ${result.workerPromptPath}`,
|
|
1262
|
+
].join("\n"),
|
|
1263
|
+
);
|
|
1264
|
+
this.ctx.chatContainer.addChild(
|
|
1265
|
+
new Text(
|
|
1266
|
+
`${theme.fg("accent", `${theme.status.success} Contribution prep ready`)}\nManifest: ${result.manifestPath}`,
|
|
1267
|
+
1,
|
|
1268
|
+
1,
|
|
1269
|
+
),
|
|
1270
|
+
);
|
|
1271
|
+
this.ctx.ui.requestRender();
|
|
1272
|
+
} catch (error) {
|
|
1273
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1274
|
+
this.ctx.showError(`Contribution prep failed: ${message}`);
|
|
1275
|
+
}
|
|
1252
1276
|
}
|
|
1253
1277
|
}
|
|
1254
1278
|
|
|
@@ -51,15 +51,6 @@ export class InputController {
|
|
|
51
51
|
this.ctx.retryEscapeHandler,
|
|
52
52
|
);
|
|
53
53
|
this.ctx.editor.onEscape = () => {
|
|
54
|
-
if (this.ctx.loopModeEnabled) {
|
|
55
|
-
this.ctx.pauseLoop();
|
|
56
|
-
if (this.ctx.session.isStreaming) {
|
|
57
|
-
void this.#abortInteractive();
|
|
58
|
-
} else {
|
|
59
|
-
this.ctx.cancelPendingSubmission();
|
|
60
|
-
}
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
54
|
if (this.ctx.hasActiveBtw() && this.ctx.handleBtwEscape()) {
|
|
64
55
|
return;
|
|
65
56
|
}
|
|
@@ -292,12 +283,6 @@ export class InputController {
|
|
|
292
283
|
}
|
|
293
284
|
}
|
|
294
285
|
|
|
295
|
-
// While loop mode is on, every user-typed prompt becomes the new loop
|
|
296
|
-
// prompt that auto-resubmits after each yield.
|
|
297
|
-
if (this.ctx.loopModeEnabled) {
|
|
298
|
-
this.ctx.loopPrompt = text;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
286
|
// Queue input during compaction
|
|
302
287
|
if (this.ctx.session.isCompacting) {
|
|
303
288
|
if (this.ctx.pendingImages.length > 0) {
|
|
@@ -37,7 +37,7 @@ import { AgentDashboard } from "../components/agent-dashboard";
|
|
|
37
37
|
import { AssistantMessageComponent } from "../components/assistant-message";
|
|
38
38
|
import { ExtensionDashboard } from "../components/extensions";
|
|
39
39
|
import { HistorySearchComponent } from "../components/history-search";
|
|
40
|
-
import { ModelSelectorComponent } from "../components/model-selector";
|
|
40
|
+
import { ModelSelectorComponent, type ModelSelectorSelection } from "../components/model-selector";
|
|
41
41
|
import { OAuthSelectorComponent } from "../components/oauth-selector";
|
|
42
42
|
import { PluginSelectorComponent } from "../components/plugin-selector";
|
|
43
43
|
import {
|
|
@@ -431,8 +431,15 @@ export class SelectorController {
|
|
|
431
431
|
this.ctx.settings,
|
|
432
432
|
this.ctx.session.modelRegistry,
|
|
433
433
|
this.ctx.session.scopedModels,
|
|
434
|
-
async
|
|
434
|
+
async selection => {
|
|
435
435
|
try {
|
|
436
|
+
if (selection.kind === "preset") {
|
|
437
|
+
await this.#applyModelAssignmentPreset(selection);
|
|
438
|
+
done();
|
|
439
|
+
this.ctx.ui.requestRender();
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const { model, role, thinkingLevel, selector } = selection;
|
|
436
443
|
if (role === null) {
|
|
437
444
|
// Temporary: update agent state but don't persist to settings
|
|
438
445
|
await this.ctx.session.setModelTemporary(model, thinkingLevel);
|
|
@@ -483,6 +490,39 @@ export class SelectorController {
|
|
|
483
490
|
});
|
|
484
491
|
}
|
|
485
492
|
|
|
493
|
+
async #applyModelAssignmentPreset(selection: Extract<ModelSelectorSelection, { kind: "preset" }>): Promise<void> {
|
|
494
|
+
const { assignments, model, preset, selector } = selection;
|
|
495
|
+
const apiKey = await this.ctx.session.modelRegistry.getApiKey(model, this.ctx.session.sessionId);
|
|
496
|
+
if (!apiKey) {
|
|
497
|
+
throw new Error(`No API key for ${model.provider}/${model.id}`);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const defaultThinkingLevel = assignments.default;
|
|
501
|
+
await this.ctx.session.setModel(model, "default", {
|
|
502
|
+
selector,
|
|
503
|
+
thinkingLevel: defaultThinkingLevel,
|
|
504
|
+
});
|
|
505
|
+
if (defaultThinkingLevel && defaultThinkingLevel !== ThinkingLevel.Inherit) {
|
|
506
|
+
this.ctx.session.setThinkingLevel(defaultThinkingLevel);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const overrides = this.ctx.settings.get("task.agentModelOverrides");
|
|
510
|
+
const nextOverrides = { ...overrides };
|
|
511
|
+
for (const [targetRole, presetThinkingLevel] of Object.entries(assignments) as [
|
|
512
|
+
keyof Extract<ModelSelectorSelection, { kind: "preset" }>["assignments"],
|
|
513
|
+
ThinkingLevel,
|
|
514
|
+
][]) {
|
|
515
|
+
if (!targetRole || targetRole === "default") continue;
|
|
516
|
+
nextOverrides[targetRole] =
|
|
517
|
+
presetThinkingLevel === ThinkingLevel.Inherit ? selector : `${selector}:${presetThinkingLevel}`;
|
|
518
|
+
}
|
|
519
|
+
this.ctx.settings.set("task.agentModelOverrides", nextOverrides);
|
|
520
|
+
this.ctx.settings.getStorage()?.recordModelUsage(`${model.provider}/${model.id}`);
|
|
521
|
+
this.ctx.statusLine.invalidate();
|
|
522
|
+
this.ctx.updateEditorBorderColor();
|
|
523
|
+
this.ctx.showStatus(`${preset.label}: ${selector}`);
|
|
524
|
+
}
|
|
525
|
+
|
|
486
526
|
async showPluginSelector(mode: "install" | "uninstall" = "install"): Promise<void> {
|
|
487
527
|
const mgr = new MarketplaceManager({
|
|
488
528
|
marketplacesRegistryPath: getMarketplacesRegistryPath(),
|