@gajae-code/coding-agent 0.2.4 → 0.3.0
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 +27 -0
- package/README.md +1 -1
- package/dist/types/async/job-manager.d.ts +145 -2
- package/dist/types/commands/harness.d.ts +37 -0
- package/dist/types/config/settings-schema.d.ts +13 -3
- package/dist/types/config/settings.d.ts +3 -1
- package/dist/types/deep-interview/render-middleware.d.ts +5 -0
- package/dist/types/discovery/helpers.d.ts +1 -0
- package/dist/types/exec/bash-executor.d.ts +8 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +6 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/gjc-runtime/restricted-role-agent-bash.d.ts +2 -0
- package/dist/types/gjc-runtime/state-graph.d.ts +4 -0
- package/dist/types/gjc-runtime/state-migrations.d.ts +24 -0
- package/dist/types/gjc-runtime/state-renderer.d.ts +65 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/state-validation.d.ts +6 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +137 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +81 -7
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +54 -0
- package/dist/types/harness-control-plane/classifier.d.ts +13 -0
- package/dist/types/harness-control-plane/control-endpoint.d.ts +30 -0
- package/dist/types/harness-control-plane/finalize.d.ts +47 -0
- package/dist/types/harness-control-plane/frame-mapper.d.ts +29 -0
- package/dist/types/harness-control-plane/operate.d.ts +35 -0
- package/dist/types/harness-control-plane/owner.d.ts +46 -0
- package/dist/types/harness-control-plane/preserve.d.ts +19 -0
- package/dist/types/harness-control-plane/receipts.d.ts +88 -0
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +66 -0
- package/dist/types/harness-control-plane/seams.d.ts +21 -0
- package/dist/types/harness-control-plane/session-lease.d.ts +65 -0
- package/dist/types/harness-control-plane/state-machine.d.ts +19 -0
- package/dist/types/harness-control-plane/storage.d.ts +53 -0
- package/dist/types/harness-control-plane/types.d.ts +162 -0
- package/dist/types/hooks/skill-keywords.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +2 -29
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
- package/dist/types/modes/components/hook-selector.d.ts +1 -0
- package/dist/types/modes/components/skill-hud/render.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/theme/defaults/index.d.ts +45 -9477
- package/dist/types/modes/theme/theme.d.ts +1 -5
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/sdk.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +8 -0
- package/dist/types/session/streaming-output.d.ts +11 -0
- package/dist/types/skill-state/active-state.d.ts +3 -0
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +1 -1
- package/dist/types/skill-state/workflow-state-contract.d.ts +24 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/types.d.ts +56 -3
- package/dist/types/tools/bash-allowed-prefixes.d.ts +5 -0
- package/dist/types/tools/bash.d.ts +24 -0
- package/dist/types/tools/cron.d.ts +110 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/monitor.d.ts +54 -0
- package/dist/types/tools/subagent.d.ts +11 -1
- package/dist/types/web/search/index.d.ts +1 -0
- package/dist/types/web/search/provider.d.ts +11 -4
- package/dist/types/web/search/providers/duckduckgo.d.ts +57 -0
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +7 -7
- package/src/async/job-manager.ts +522 -6
- package/src/cli/agents-cli.ts +3 -0
- package/src/cli/auth-broker-cli.ts +1 -0
- package/src/cli/config-cli.ts +10 -2
- package/src/cli.ts +2 -0
- package/src/commands/harness.ts +592 -0
- package/src/commands/team.ts +36 -39
- package/src/config/settings-schema.ts +15 -2
- package/src/config/settings.ts +49 -7
- package/src/deep-interview/render-middleware.ts +366 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +9 -2
- package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -4
- package/src/defaults/gjc/skills/team/SKILL.md +47 -21
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +78 -11
- package/src/discovery/helpers.ts +5 -0
- package/src/eval/js/shared/rewrite-imports.ts +1 -2
- package/src/exec/bash-executor.ts +20 -9
- package/src/extensibility/custom-tools/types.ts +1 -0
- package/src/extensibility/extensions/types.ts +6 -0
- package/src/extensibility/shared-events.ts +1 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +40 -21
- package/src/gjc-runtime/goal-mode-request.ts +11 -3
- package/src/gjc-runtime/ralplan-runtime.ts +27 -10
- package/src/gjc-runtime/restricted-role-agent-bash.ts +5 -0
- package/src/gjc-runtime/state-graph.ts +86 -0
- package/src/gjc-runtime/state-migrations.ts +132 -0
- package/src/gjc-runtime/state-renderer.ts +345 -0
- package/src/gjc-runtime/state-runtime.ts +733 -21
- package/src/gjc-runtime/state-validation.ts +49 -0
- package/src/gjc-runtime/state-writer.ts +718 -0
- package/src/gjc-runtime/team-runtime.ts +1083 -89
- package/src/gjc-runtime/ultragoal-runtime.ts +348 -19
- package/src/gjc-runtime/workflow-manifest.generated.json +1497 -0
- package/src/gjc-runtime/workflow-manifest.ts +425 -0
- package/src/harness-control-plane/classifier.ts +128 -0
- package/src/harness-control-plane/control-endpoint.ts +137 -0
- package/src/harness-control-plane/finalize.ts +222 -0
- package/src/harness-control-plane/frame-mapper.ts +286 -0
- package/src/harness-control-plane/operate.ts +225 -0
- package/src/harness-control-plane/owner.ts +553 -0
- package/src/harness-control-plane/preserve.ts +102 -0
- package/src/harness-control-plane/receipts.ts +216 -0
- package/src/harness-control-plane/rpc-adapter.ts +276 -0
- package/src/harness-control-plane/seams.ts +39 -0
- package/src/harness-control-plane/session-lease.ts +388 -0
- package/src/harness-control-plane/state-machine.ts +97 -0
- package/src/harness-control-plane/storage.ts +257 -0
- package/src/harness-control-plane/types.ts +214 -0
- package/src/hooks/skill-keywords.ts +4 -2
- package/src/hooks/skill-state.ts +25 -42
- package/src/internal-urls/docs-index.generated.ts +6 -4
- package/src/lsp/render.ts +1 -1
- package/src/modes/acp/acp-agent.ts +1 -1
- package/src/modes/acp/acp-client-bridge.ts +1 -1
- package/src/modes/components/agent-dashboard.ts +1 -1
- package/src/modes/components/assistant-message.ts +5 -1
- package/src/modes/components/diff.ts +2 -2
- package/src/modes/components/hook-selector.ts +72 -2
- package/src/modes/components/skill-hud/render.ts +7 -2
- package/src/modes/controllers/event-controller.ts +71 -6
- package/src/modes/controllers/extension-ui-controller.ts +6 -0
- package/src/modes/controllers/input-controller.ts +19 -3
- package/src/modes/controllers/selector-controller.ts +3 -2
- package/src/modes/interactive-mode.ts +21 -2
- package/src/modes/theme/defaults/index.ts +0 -196
- package/src/modes/theme/theme.ts +35 -35
- package/src/modes/types.ts +2 -0
- package/src/prompts/agents/architect.md +5 -1
- package/src/prompts/agents/critic.md +5 -1
- package/src/prompts/agents/executor.md +13 -0
- package/src/prompts/agents/frontmatter.md +1 -0
- package/src/prompts/agents/planner.md +5 -1
- package/src/prompts/tools/bash.md +9 -0
- package/src/prompts/tools/cron.md +25 -0
- package/src/prompts/tools/monitor.md +30 -0
- package/src/prompts/tools/subagent.md +33 -3
- package/src/runtime-mcp/oauth-flow.ts +4 -2
- package/src/sdk.ts +7 -0
- package/src/session/agent-session.ts +247 -38
- package/src/session/session-manager.ts +13 -1
- package/src/session/streaming-output.ts +21 -0
- package/src/skill-state/active-state.ts +222 -78
- package/src/skill-state/deep-interview-mutation-guard.ts +91 -13
- package/src/skill-state/initial-phase.ts +2 -0
- package/src/skill-state/workflow-state-contract.ts +26 -0
- package/src/task/agents.ts +1 -0
- package/src/task/executor.ts +51 -8
- package/src/task/index.ts +120 -8
- package/src/task/render.ts +6 -3
- package/src/task/types.ts +57 -3
- package/src/tools/ask.ts +28 -7
- package/src/tools/bash-allowed-prefixes.ts +169 -0
- package/src/tools/bash.ts +190 -29
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/cron.ts +665 -0
- package/src/tools/index.ts +20 -2
- package/src/tools/monitor.ts +136 -0
- package/src/tools/subagent.ts +255 -64
- package/src/vim/engine.ts +3 -3
- package/src/web/search/index.ts +31 -18
- package/src/web/search/provider.ts +57 -12
- package/src/web/search/providers/duckduckgo.ts +279 -0
- package/src/web/search/types.ts +2 -0
- package/src/modes/theme/dark.json +0 -95
- package/src/modes/theme/defaults/alabaster.json +0 -93
- package/src/modes/theme/defaults/amethyst.json +0 -96
- package/src/modes/theme/defaults/anthracite.json +0 -93
- package/src/modes/theme/defaults/basalt.json +0 -91
- package/src/modes/theme/defaults/birch.json +0 -95
- package/src/modes/theme/defaults/dark-abyss.json +0 -91
- package/src/modes/theme/defaults/dark-arctic.json +0 -104
- package/src/modes/theme/defaults/dark-aurora.json +0 -95
- package/src/modes/theme/defaults/dark-catppuccin.json +0 -107
- package/src/modes/theme/defaults/dark-cavern.json +0 -91
- package/src/modes/theme/defaults/dark-copper.json +0 -95
- package/src/modes/theme/defaults/dark-cosmos.json +0 -90
- package/src/modes/theme/defaults/dark-cyberpunk.json +0 -102
- package/src/modes/theme/defaults/dark-dracula.json +0 -98
- package/src/modes/theme/defaults/dark-eclipse.json +0 -91
- package/src/modes/theme/defaults/dark-ember.json +0 -95
- package/src/modes/theme/defaults/dark-equinox.json +0 -90
- package/src/modes/theme/defaults/dark-forest.json +0 -96
- package/src/modes/theme/defaults/dark-github.json +0 -105
- package/src/modes/theme/defaults/dark-gruvbox.json +0 -112
- package/src/modes/theme/defaults/dark-lavender.json +0 -95
- package/src/modes/theme/defaults/dark-lunar.json +0 -89
- package/src/modes/theme/defaults/dark-midnight.json +0 -95
- package/src/modes/theme/defaults/dark-monochrome.json +0 -94
- package/src/modes/theme/defaults/dark-monokai.json +0 -98
- package/src/modes/theme/defaults/dark-nebula.json +0 -90
- package/src/modes/theme/defaults/dark-nord.json +0 -97
- package/src/modes/theme/defaults/dark-ocean.json +0 -101
- package/src/modes/theme/defaults/dark-one.json +0 -100
- package/src/modes/theme/defaults/dark-poimandres.json +0 -141
- package/src/modes/theme/defaults/dark-rainforest.json +0 -91
- package/src/modes/theme/defaults/dark-reef.json +0 -91
- package/src/modes/theme/defaults/dark-retro.json +0 -92
- package/src/modes/theme/defaults/dark-rose-pine.json +0 -96
- package/src/modes/theme/defaults/dark-sakura.json +0 -95
- package/src/modes/theme/defaults/dark-slate.json +0 -95
- package/src/modes/theme/defaults/dark-solarized.json +0 -97
- package/src/modes/theme/defaults/dark-solstice.json +0 -90
- package/src/modes/theme/defaults/dark-starfall.json +0 -91
- package/src/modes/theme/defaults/dark-sunset.json +0 -99
- package/src/modes/theme/defaults/dark-swamp.json +0 -90
- package/src/modes/theme/defaults/dark-synthwave.json +0 -103
- package/src/modes/theme/defaults/dark-taiga.json +0 -91
- package/src/modes/theme/defaults/dark-terminal.json +0 -95
- package/src/modes/theme/defaults/dark-tokyo-night.json +0 -101
- package/src/modes/theme/defaults/dark-tundra.json +0 -91
- package/src/modes/theme/defaults/dark-twilight.json +0 -91
- package/src/modes/theme/defaults/dark-volcanic.json +0 -91
- package/src/modes/theme/defaults/graphite.json +0 -92
- package/src/modes/theme/defaults/light-arctic.json +0 -107
- package/src/modes/theme/defaults/light-aurora-day.json +0 -91
- package/src/modes/theme/defaults/light-canyon.json +0 -91
- package/src/modes/theme/defaults/light-catppuccin.json +0 -106
- package/src/modes/theme/defaults/light-cirrus.json +0 -90
- package/src/modes/theme/defaults/light-coral.json +0 -95
- package/src/modes/theme/defaults/light-cyberpunk.json +0 -96
- package/src/modes/theme/defaults/light-dawn.json +0 -90
- package/src/modes/theme/defaults/light-dunes.json +0 -91
- package/src/modes/theme/defaults/light-eucalyptus.json +0 -95
- package/src/modes/theme/defaults/light-forest.json +0 -100
- package/src/modes/theme/defaults/light-frost.json +0 -95
- package/src/modes/theme/defaults/light-github.json +0 -115
- package/src/modes/theme/defaults/light-glacier.json +0 -91
- package/src/modes/theme/defaults/light-gruvbox.json +0 -108
- package/src/modes/theme/defaults/light-haze.json +0 -90
- package/src/modes/theme/defaults/light-honeycomb.json +0 -95
- package/src/modes/theme/defaults/light-lagoon.json +0 -91
- package/src/modes/theme/defaults/light-lavender.json +0 -95
- package/src/modes/theme/defaults/light-meadow.json +0 -91
- package/src/modes/theme/defaults/light-mint.json +0 -95
- package/src/modes/theme/defaults/light-monochrome.json +0 -101
- package/src/modes/theme/defaults/light-ocean.json +0 -99
- package/src/modes/theme/defaults/light-one.json +0 -99
- package/src/modes/theme/defaults/light-opal.json +0 -91
- package/src/modes/theme/defaults/light-orchard.json +0 -91
- package/src/modes/theme/defaults/light-paper.json +0 -95
- package/src/modes/theme/defaults/light-poimandres.json +0 -141
- package/src/modes/theme/defaults/light-prism.json +0 -90
- package/src/modes/theme/defaults/light-retro.json +0 -98
- package/src/modes/theme/defaults/light-sand.json +0 -95
- package/src/modes/theme/defaults/light-savanna.json +0 -91
- package/src/modes/theme/defaults/light-solarized.json +0 -102
- package/src/modes/theme/defaults/light-soleil.json +0 -90
- package/src/modes/theme/defaults/light-sunset.json +0 -99
- package/src/modes/theme/defaults/light-synthwave.json +0 -98
- package/src/modes/theme/defaults/light-tokyo-night.json +0 -111
- package/src/modes/theme/defaults/light-wetland.json +0 -91
- package/src/modes/theme/defaults/light-zenith.json +0 -89
- package/src/modes/theme/defaults/limestone.json +0 -94
- package/src/modes/theme/defaults/mahogany.json +0 -97
- package/src/modes/theme/defaults/marble.json +0 -93
- package/src/modes/theme/defaults/obsidian.json +0 -91
- package/src/modes/theme/defaults/onyx.json +0 -91
- package/src/modes/theme/defaults/pearl.json +0 -93
- package/src/modes/theme/defaults/porcelain.json +0 -91
- package/src/modes/theme/defaults/quartz.json +0 -96
- package/src/modes/theme/defaults/sandstone.json +0 -95
- package/src/modes/theme/defaults/titanium.json +0 -90
- package/src/modes/theme/light.json +0 -93
package/src/lsp/render.ts
CHANGED
|
@@ -103,7 +103,7 @@ export function renderResult(
|
|
|
103
103
|
args?: LspParams,
|
|
104
104
|
): Component {
|
|
105
105
|
const content = result.content?.[0];
|
|
106
|
-
if (
|
|
106
|
+
if (content?.type !== "text" || !("text" in content) || !content.text) {
|
|
107
107
|
const icon = formatStatusIcon("warning", theme, options.spinnerFrame);
|
|
108
108
|
const header = `${icon} LSP`;
|
|
109
109
|
return new Text([header, theme.fg("dim", "No result")].join("\n"), 0, 0);
|
|
@@ -272,7 +272,7 @@ async function elicitFromAcpClient(
|
|
|
272
272
|
finish(undefined);
|
|
273
273
|
});
|
|
274
274
|
const response = await promise;
|
|
275
|
-
if (
|
|
275
|
+
if (response?.action !== "accept" || !response.content) {
|
|
276
276
|
return undefined;
|
|
277
277
|
}
|
|
278
278
|
return response.content.value;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ACP-side `ClientBridge` implementation. Wraps `AgentSideConnection` so the
|
|
3
|
-
* `read`/`write`/`bash`/`edit` tools (and the permission gate in
|
|
3
|
+
* `read`/`write`/`bash`/`monitor`/`edit` tools (and the permission gate in
|
|
4
4
|
* `AgentSession`) can route through the client when it advertises the
|
|
5
5
|
* relevant capabilities at `initialize` time.
|
|
6
6
|
*/
|
|
@@ -121,7 +121,7 @@ function matchAgent(agent: DashboardAgent, query: string): boolean {
|
|
|
121
121
|
function extractAssistantText(messages: AgentMessage[]): string | null {
|
|
122
122
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
123
123
|
const message = messages[i];
|
|
124
|
-
if (
|
|
124
|
+
if (message?.role !== "assistant") continue;
|
|
125
125
|
const blocks = message.content;
|
|
126
126
|
if (!Array.isArray(blocks)) continue;
|
|
127
127
|
const text = blocks
|
|
@@ -2,6 +2,7 @@ import type { AssistantMessage, ImageContent, Usage } from "@gajae-code/ai";
|
|
|
2
2
|
import { Container, Image, ImageProtocol, Markdown, Spacer, TERMINAL, Text } from "@gajae-code/tui";
|
|
3
3
|
import { formatNumber } from "@gajae-code/utils";
|
|
4
4
|
import { settings } from "../../config/settings";
|
|
5
|
+
import { renderDeepInterviewAssistantText } from "../../deep-interview/render-middleware";
|
|
5
6
|
import { getMarkdownTheme, theme } from "../../modes/theme/theme";
|
|
6
7
|
import { isSilentAbort } from "../../session/messages";
|
|
7
8
|
import { resolveImageOptions } from "../../tools/render-utils";
|
|
@@ -153,7 +154,10 @@ export class AssistantMessageComponent extends Container {
|
|
|
153
154
|
if (content.type === "text" && content.text.trim()) {
|
|
154
155
|
// Assistant text messages with no background - trim the text
|
|
155
156
|
// Set paddingY=0 to avoid extra spacing before tool executions
|
|
156
|
-
|
|
157
|
+
const text = content.text.trim();
|
|
158
|
+
this.#contentContainer.addChild(
|
|
159
|
+
renderDeepInterviewAssistantText(text, theme) ?? new Markdown(text, 1, 0, getMarkdownTheme()),
|
|
160
|
+
);
|
|
157
161
|
} else if (content.type === "thinking" && content.thinking.trim()) {
|
|
158
162
|
// Add spacing only when another visible assistant content block follows.
|
|
159
163
|
// This avoids a superfluous blank line before separately-rendered tool execution blocks.
|
|
@@ -151,7 +151,7 @@ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): s
|
|
|
151
151
|
const removedLines: { lineNum: string; content: string }[] = [];
|
|
152
152
|
while (i < lines.length) {
|
|
153
153
|
const p = parseDiffLine(lines[i]);
|
|
154
|
-
if (
|
|
154
|
+
if (p?.prefix !== "-") break;
|
|
155
155
|
removedLines.push({ lineNum: p.lineNum, content: p.content });
|
|
156
156
|
i++;
|
|
157
157
|
}
|
|
@@ -159,7 +159,7 @@ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): s
|
|
|
159
159
|
const addedLines: { lineNum: string; content: string }[] = [];
|
|
160
160
|
while (i < lines.length) {
|
|
161
161
|
const p = parseDiffLine(lines[i]);
|
|
162
|
-
if (
|
|
162
|
+
if (p?.prefix !== "+") break;
|
|
163
163
|
addedLines.push({ lineNum: p.lineNum, content: p.content });
|
|
164
164
|
i++;
|
|
165
165
|
}
|
|
@@ -39,6 +39,7 @@ export interface HookSelectorOptions {
|
|
|
39
39
|
* byte-identical to the previous implementation for all consumers.
|
|
40
40
|
*/
|
|
41
41
|
wrapFocused?: boolean;
|
|
42
|
+
scrollTitleRows?: number;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
class OutlinedList extends Container {
|
|
@@ -63,6 +64,57 @@ class OutlinedList extends Container {
|
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
class ScrollableTitle extends Container {
|
|
68
|
+
#markdown: Markdown;
|
|
69
|
+
#maxRows: number;
|
|
70
|
+
#scrollOffset = 0;
|
|
71
|
+
#lastMaxScrollOffset = 0;
|
|
72
|
+
|
|
73
|
+
constructor(title: string, maxRows: number) {
|
|
74
|
+
super();
|
|
75
|
+
this.#maxRows = Math.max(1, Math.floor(maxRows));
|
|
76
|
+
this.#markdown = new Markdown(title, 1, 0, getMarkdownTheme(), { color: t => theme.fg("accent", t) });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setText(text: string): void {
|
|
80
|
+
this.#markdown.setText(text);
|
|
81
|
+
this.#scrollOffset = 0;
|
|
82
|
+
this.invalidate();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
scrollBy(rows: number): void {
|
|
86
|
+
if (rows === 0) return;
|
|
87
|
+
const nextOffset = Math.max(0, Math.min(this.#lastMaxScrollOffset, this.#scrollOffset + rows));
|
|
88
|
+
if (nextOffset === this.#scrollOffset) return;
|
|
89
|
+
this.#scrollOffset = nextOffset;
|
|
90
|
+
this.invalidate();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
render(width: number): string[] {
|
|
94
|
+
const lines = this.#markdown.render(width);
|
|
95
|
+
const maxScrollOffset = Math.max(0, lines.length - this.#maxRows);
|
|
96
|
+
this.#lastMaxScrollOffset = maxScrollOffset;
|
|
97
|
+
this.#scrollOffset = Math.max(0, Math.min(this.#scrollOffset, maxScrollOffset));
|
|
98
|
+
|
|
99
|
+
const visibleLines = lines.slice(this.#scrollOffset, this.#scrollOffset + this.#maxRows);
|
|
100
|
+
if (maxScrollOffset === 0 || visibleLines.length === 0) {
|
|
101
|
+
return visibleLines;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const indicator =
|
|
105
|
+
this.#scrollOffset === 0
|
|
106
|
+
? theme.fg("dim", " PgDn↓")
|
|
107
|
+
: this.#scrollOffset >= maxScrollOffset
|
|
108
|
+
? theme.fg("dim", " PgUp↑")
|
|
109
|
+
: theme.fg("dim", " PgUp/PgDn↕");
|
|
110
|
+
const lastIndex = visibleLines.length - 1;
|
|
111
|
+
const availableWidth = Math.max(1, width - visibleWidth(indicator));
|
|
112
|
+
const fittedLine = truncateToWidth(visibleLines[lastIndex] ?? "", availableWidth);
|
|
113
|
+
visibleLines[lastIndex] = `${fittedLine}${indicator}`;
|
|
114
|
+
return visibleLines;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
66
118
|
/**
|
|
67
119
|
* Width-aware list child that owns wrapped focused-option layout.
|
|
68
120
|
*
|
|
@@ -199,7 +251,8 @@ export class HookSelectorComponent extends Container {
|
|
|
199
251
|
#focusAwareList: FocusAwareList | undefined;
|
|
200
252
|
#onSelectCallback: (option: string) => void;
|
|
201
253
|
#onCancelCallback: () => void;
|
|
202
|
-
#titleComponent: Markdown;
|
|
254
|
+
#titleComponent: Markdown | ScrollableTitle;
|
|
255
|
+
#scrollableTitle: ScrollableTitle | undefined;
|
|
203
256
|
#baseTitle: string;
|
|
204
257
|
#countdown: CountdownTimer | undefined;
|
|
205
258
|
#onLeftCallback: (() => void) | undefined;
|
|
@@ -207,6 +260,7 @@ export class HookSelectorComponent extends Container {
|
|
|
207
260
|
#onExternalEditorCallback: (() => void) | undefined;
|
|
208
261
|
#wrapFocused: boolean;
|
|
209
262
|
#outline: boolean;
|
|
263
|
+
#scrollTitleRows: number | undefined;
|
|
210
264
|
constructor(
|
|
211
265
|
title: string,
|
|
212
266
|
options: string[],
|
|
@@ -231,7 +285,15 @@ export class HookSelectorComponent extends Container {
|
|
|
231
285
|
this.addChild(new DynamicBorder());
|
|
232
286
|
this.addChild(new Spacer(1));
|
|
233
287
|
|
|
234
|
-
|
|
288
|
+
const scrollTitleRows =
|
|
289
|
+
opts?.scrollTitleRows === undefined ? undefined : Math.max(1, Math.floor(opts.scrollTitleRows));
|
|
290
|
+
this.#scrollTitleRows = scrollTitleRows;
|
|
291
|
+
if (scrollTitleRows === undefined) {
|
|
292
|
+
this.#titleComponent = new Markdown(title, 1, 0, getMarkdownTheme(), { color: t => theme.fg("accent", t) });
|
|
293
|
+
} else {
|
|
294
|
+
this.#scrollableTitle = new ScrollableTitle(title, scrollTitleRows);
|
|
295
|
+
this.#titleComponent = this.#scrollableTitle;
|
|
296
|
+
}
|
|
235
297
|
this.addChild(this.#titleComponent);
|
|
236
298
|
this.addChild(new Spacer(1));
|
|
237
299
|
|
|
@@ -319,6 +381,14 @@ export class HookSelectorComponent extends Container {
|
|
|
319
381
|
// Reset countdown on any interaction
|
|
320
382
|
this.#countdown?.reset();
|
|
321
383
|
|
|
384
|
+
if (this.#scrollTitleRows !== undefined && matchesKey(keyData, "pageUp")) {
|
|
385
|
+
this.#scrollableTitle?.scrollBy(-this.#scrollTitleRows);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (this.#scrollTitleRows !== undefined && matchesKey(keyData, "pageDown")) {
|
|
389
|
+
this.#scrollableTitle?.scrollBy(this.#scrollTitleRows);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
322
392
|
if (matchesKey(keyData, "up") || keyData === "k") {
|
|
323
393
|
this.#selectedIndex = Math.max(0, this.#selectedIndex - 1);
|
|
324
394
|
this.#updateList();
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
collapsePlanningPipeline,
|
|
3
|
+
type SkillActiveEntry,
|
|
4
|
+
type WorkflowHudChip,
|
|
5
|
+
} from "../../../skill-state/active-state";
|
|
2
6
|
import { workflowReceiptStatus } from "../../../skill-state/workflow-state-contract";
|
|
3
7
|
|
|
4
8
|
const ANSI_RESET_FG = "\x1b[39m";
|
|
@@ -69,7 +73,8 @@ function formatEntry(entry: SkillActiveEntry): string {
|
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
export function renderSkillHudBar(entries: readonly SkillActiveEntry[], width: number): string | null {
|
|
72
|
-
const
|
|
76
|
+
const visible = collapsePlanningPipeline(entries.filter(entry => entry.active !== false));
|
|
77
|
+
const active = visible.filter(entry => sanitizeHudPart(entry.skill)).sort(compareEntries);
|
|
73
78
|
if (active.length === 0 || width <= 0) return null;
|
|
74
79
|
const body = active.map(formatEntry).join(" + ");
|
|
75
80
|
const prefix = `${ANSI_BORDER}◆${ANSI_RESET_FG} ${ANSI_BOLD}${ANSI_ACCENT}hud${ANSI_RESET_FG}${ANSI_RESET_BOLD} `;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { INTENT_FIELD } from "@gajae-code/agent-core";
|
|
2
2
|
import { calculatePromptTokens } from "@gajae-code/agent-core/compaction/compaction";
|
|
3
3
|
import type { AssistantMessage, ImageContent } from "@gajae-code/ai";
|
|
4
|
+
import { parseRateLimitReason } from "@gajae-code/ai";
|
|
4
5
|
import { type Component, Loader, TERMINAL, Text } from "@gajae-code/tui";
|
|
5
6
|
import { settings } from "../../config/settings";
|
|
6
7
|
import { AssistantMessageComponent } from "../../modes/components/assistant-message";
|
|
@@ -24,6 +25,24 @@ type AgentSessionEventKind = AgentSessionEvent["type"];
|
|
|
24
25
|
|
|
25
26
|
const IRC_MESSAGE_VISIBLE_TTL_MS = 10_000;
|
|
26
27
|
|
|
28
|
+
function friendlyRetryReason(errorMessage: string | undefined): string {
|
|
29
|
+
if (!errorMessage) return "";
|
|
30
|
+
switch (parseRateLimitReason(errorMessage)) {
|
|
31
|
+
case "RATE_LIMIT_EXCEEDED":
|
|
32
|
+
return "rate limited";
|
|
33
|
+
case "QUOTA_EXHAUSTED":
|
|
34
|
+
return "usage limit";
|
|
35
|
+
case "MODEL_CAPACITY_EXHAUSTED":
|
|
36
|
+
return "overloaded";
|
|
37
|
+
case "SERVER_ERROR":
|
|
38
|
+
return "server error";
|
|
39
|
+
default:
|
|
40
|
+
return /network|connection|socket|fetch failed|terminated|timeout|timed out|stream/i.test(errorMessage)
|
|
41
|
+
? "connection error"
|
|
42
|
+
: "transient error";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
27
46
|
type AgentSessionEventHandlers = {
|
|
28
47
|
[E in AgentSessionEventKind]: (event: Extract<AgentSessionEvent, { type: E }>) => Promise<void>;
|
|
29
48
|
};
|
|
@@ -71,6 +90,15 @@ export class EventController {
|
|
|
71
90
|
|
|
72
91
|
dispose(): void {
|
|
73
92
|
this.#cancelIdleCompaction();
|
|
93
|
+
this.#clearRetryCountdown();
|
|
94
|
+
if (this.ctx.retryEscapeHandler) {
|
|
95
|
+
this.ctx.editor.onEscape = this.ctx.retryEscapeHandler;
|
|
96
|
+
this.ctx.retryEscapeHandler = undefined;
|
|
97
|
+
}
|
|
98
|
+
if (this.ctx.retryLoader) {
|
|
99
|
+
this.ctx.retryLoader.stop();
|
|
100
|
+
this.ctx.retryLoader = undefined;
|
|
101
|
+
}
|
|
74
102
|
for (const timer of this.#ircExpiryTimers.values()) {
|
|
75
103
|
clearTimeout(timer);
|
|
76
104
|
}
|
|
@@ -166,6 +194,7 @@ export class EventController {
|
|
|
166
194
|
}
|
|
167
195
|
if (this.ctx.retryLoader) {
|
|
168
196
|
this.ctx.retryLoader.stop();
|
|
197
|
+
this.#clearRetryCountdown();
|
|
169
198
|
this.ctx.retryLoader = undefined;
|
|
170
199
|
this.ctx.statusContainer.clear();
|
|
171
200
|
}
|
|
@@ -648,21 +677,56 @@ export class EventController {
|
|
|
648
677
|
this.ctx.ui.requestRender();
|
|
649
678
|
}
|
|
650
679
|
|
|
680
|
+
#clearRetryCountdown(): void {
|
|
681
|
+
if (this.ctx.retryCountdownTimer) {
|
|
682
|
+
clearInterval(this.ctx.retryCountdownTimer);
|
|
683
|
+
this.ctx.retryCountdownTimer = undefined;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
651
687
|
async #handleAutoRetryStart(event: Extract<AgentSessionEvent, { type: "auto_retry_start" }>): Promise<void> {
|
|
652
|
-
|
|
688
|
+
// Preserve the ORIGINAL editor Escape handler across repeated retry
|
|
689
|
+
// starts: auto_retry_end only fires at final success/failure, so a
|
|
690
|
+
// second auto_retry_start must not snapshot the prior retry handler.
|
|
691
|
+
if (!this.ctx.retryEscapeHandler) {
|
|
692
|
+
this.ctx.retryEscapeHandler = this.ctx.editor.onEscape;
|
|
693
|
+
}
|
|
694
|
+
let escPressed = false;
|
|
653
695
|
this.ctx.editor.onEscape = () => {
|
|
654
|
-
|
|
696
|
+
if (!escPressed) {
|
|
697
|
+
// First Esc: skip the backoff and retry immediately.
|
|
698
|
+
escPressed = true;
|
|
699
|
+
this.ctx.session.retryNow();
|
|
700
|
+
} else {
|
|
701
|
+
// Second Esc: cancel the retry entirely.
|
|
702
|
+
this.ctx.session.abortRetry();
|
|
703
|
+
}
|
|
655
704
|
};
|
|
656
705
|
this.ctx.statusContainer.clear();
|
|
657
|
-
|
|
658
|
-
this.ctx.retryLoader
|
|
706
|
+
// Stop any prior retry loader/timer before installing a new one.
|
|
707
|
+
this.ctx.retryLoader?.stop();
|
|
708
|
+
this.#clearRetryCountdown();
|
|
709
|
+
const reason = friendlyRetryReason(event.errorMessage);
|
|
710
|
+
const attemptLabel = event.unbounded ? `attempt ${event.attempt}` : `${event.attempt}/${event.maxAttempts}`;
|
|
711
|
+
const reasonSuffix = reason ? ` — ${reason}` : "";
|
|
712
|
+
const deadline = Date.now() + event.delayMs;
|
|
713
|
+
const buildMessage = () => {
|
|
714
|
+
const remainingSeconds = Math.max(0, Math.round((deadline - Date.now()) / 1000));
|
|
715
|
+
// First Esc retries immediately; a second Esc cancels.
|
|
716
|
+
return `Retrying (${attemptLabel})${reasonSuffix}, next in ${remainingSeconds}s… (esc to retry now)`;
|
|
717
|
+
};
|
|
718
|
+
const retryLoader = new Loader(
|
|
659
719
|
this.ctx.ui,
|
|
660
720
|
spinner => theme.fg("warning", spinner),
|
|
661
721
|
text => theme.fg("muted", text),
|
|
662
|
-
|
|
722
|
+
buildMessage(),
|
|
663
723
|
getSymbolTheme().spinnerFrames,
|
|
664
724
|
);
|
|
665
|
-
this.ctx.
|
|
725
|
+
this.ctx.retryLoader = retryLoader;
|
|
726
|
+
this.ctx.retryCountdownTimer = setInterval(() => {
|
|
727
|
+
retryLoader.setMessage(buildMessage());
|
|
728
|
+
}, 1000);
|
|
729
|
+
this.ctx.statusContainer.addChild(retryLoader);
|
|
666
730
|
this.ctx.ui.requestRender();
|
|
667
731
|
}
|
|
668
732
|
|
|
@@ -673,6 +737,7 @@ export class EventController {
|
|
|
673
737
|
}
|
|
674
738
|
if (this.ctx.retryLoader) {
|
|
675
739
|
this.ctx.retryLoader.stop();
|
|
740
|
+
this.#clearRetryCountdown();
|
|
676
741
|
this.ctx.retryLoader = undefined;
|
|
677
742
|
this.ctx.statusContainer.clear();
|
|
678
743
|
}
|
|
@@ -590,6 +590,11 @@ export class ExtensionUiController {
|
|
|
590
590
|
dialogOptions?.signal,
|
|
591
591
|
);
|
|
592
592
|
const maxVisible = Math.max(4, Math.min(15, this.ctx.ui.terminal.rows - 12));
|
|
593
|
+
const requestedTitleRows = dialogOptions?.scrollTitleRows;
|
|
594
|
+
const selectorChromeRows = 7;
|
|
595
|
+
const availableTitleRows = this.ctx.ui.terminal.rows - maxVisible - selectorChromeRows;
|
|
596
|
+
const scrollTitleRows =
|
|
597
|
+
requestedTitleRows === undefined ? undefined : Math.max(1, Math.min(requestedTitleRows, availableTitleRows));
|
|
593
598
|
this.ctx.hookSelector = new HookSelectorComponent(
|
|
594
599
|
title,
|
|
595
600
|
options,
|
|
@@ -624,6 +629,7 @@ export class ExtensionUiController {
|
|
|
624
629
|
tui: this.ctx.ui,
|
|
625
630
|
outline: dialogOptions?.outline,
|
|
626
631
|
wrapFocused: dialogOptions?.wrapFocused,
|
|
632
|
+
scrollTitleRows,
|
|
627
633
|
maxVisible,
|
|
628
634
|
},
|
|
629
635
|
);
|
|
@@ -31,7 +31,7 @@ export class InputController {
|
|
|
31
31
|
constructor(private ctx: InteractiveModeContext) {}
|
|
32
32
|
|
|
33
33
|
#abortInteractive(): Promise<void> {
|
|
34
|
-
return this.ctx.session.abort({ timeoutMs: INTERACTIVE_ABORT_CLEANUP_TIMEOUT_MS });
|
|
34
|
+
return this.ctx.session.abort({ timeoutMs: INTERACTIVE_ABORT_CLEANUP_TIMEOUT_MS, cause: "user_interrupt" });
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
setupKeyHandlers(): void {
|
|
@@ -64,6 +64,7 @@ export class InputController {
|
|
|
64
64
|
} else if (this.ctx.isBashMode) {
|
|
65
65
|
this.ctx.editor.setText("");
|
|
66
66
|
this.ctx.isBashMode = false;
|
|
67
|
+
this.ctx.isBashNoContext = false;
|
|
67
68
|
this.ctx.updateEditorBorderColor();
|
|
68
69
|
} else if (this.ctx.session.isEvalRunning) {
|
|
69
70
|
this.ctx.session.abortEval();
|
|
@@ -172,11 +173,17 @@ export class InputController {
|
|
|
172
173
|
|
|
173
174
|
this.ctx.editor.onChange = (text: string) => {
|
|
174
175
|
const wasBashMode = this.ctx.isBashMode;
|
|
176
|
+
const wasBashNoContext = this.ctx.isBashNoContext;
|
|
175
177
|
const wasPythonMode = this.ctx.isPythonMode;
|
|
176
178
|
const trimmed = text.trimStart();
|
|
177
|
-
this.ctx.isBashMode =
|
|
179
|
+
this.ctx.isBashMode = trimmed.startsWith("!");
|
|
180
|
+
this.ctx.isBashNoContext = trimmed.startsWith("!!");
|
|
178
181
|
this.ctx.isPythonMode = trimmed.startsWith("$") && !trimmed.startsWith("${");
|
|
179
|
-
if (
|
|
182
|
+
if (
|
|
183
|
+
wasBashMode !== this.ctx.isBashMode ||
|
|
184
|
+
wasBashNoContext !== this.ctx.isBashNoContext ||
|
|
185
|
+
wasPythonMode !== this.ctx.isPythonMode
|
|
186
|
+
) {
|
|
180
187
|
this.ctx.updateEditorBorderColor();
|
|
181
188
|
}
|
|
182
189
|
};
|
|
@@ -260,6 +267,7 @@ export class InputController {
|
|
|
260
267
|
this.ctx.editor.addToHistory(text);
|
|
261
268
|
await this.ctx.handleBashCommand(command, isExcluded);
|
|
262
269
|
this.ctx.isBashMode = false;
|
|
270
|
+
this.ctx.isBashNoContext = false;
|
|
263
271
|
this.ctx.updateEditorBorderColor();
|
|
264
272
|
return;
|
|
265
273
|
}
|
|
@@ -560,6 +568,14 @@ export class InputController {
|
|
|
560
568
|
this.ctx.retryLoader.stop();
|
|
561
569
|
this.ctx.retryLoader = undefined;
|
|
562
570
|
}
|
|
571
|
+
if (this.ctx.retryCountdownTimer) {
|
|
572
|
+
clearInterval(this.ctx.retryCountdownTimer);
|
|
573
|
+
this.ctx.retryCountdownTimer = undefined;
|
|
574
|
+
}
|
|
575
|
+
if (this.ctx.retryEscapeHandler) {
|
|
576
|
+
this.ctx.editor.onEscape = this.ctx.retryEscapeHandler;
|
|
577
|
+
this.ctx.retryEscapeHandler = undefined;
|
|
578
|
+
}
|
|
563
579
|
this.ctx.statusContainer.clear();
|
|
564
580
|
this.ctx.statusLine.dispose();
|
|
565
581
|
|
|
@@ -57,12 +57,13 @@ import { TreeSelectorComponent } from "../components/tree-selector";
|
|
|
57
57
|
import { UserMessageSelectorComponent } from "../components/user-message-selector";
|
|
58
58
|
import type { SessionObserverRegistry } from "../session-observer-registry";
|
|
59
59
|
|
|
60
|
-
const CALLBACK_SERVER_PROVIDERS = new Set<
|
|
60
|
+
const CALLBACK_SERVER_PROVIDERS = new Set<string>([
|
|
61
61
|
"anthropic",
|
|
62
62
|
"openai-codex",
|
|
63
63
|
"gitlab-duo",
|
|
64
64
|
"google-gemini-cli",
|
|
65
65
|
"google-antigravity",
|
|
66
|
+
"xai",
|
|
66
67
|
]);
|
|
67
68
|
|
|
68
69
|
const MANUAL_LOGIN_TIP = "Tip: You can complete pairing with /login <redirect URL>.";
|
|
@@ -239,7 +240,7 @@ export class SelectorController {
|
|
|
239
240
|
});
|
|
240
241
|
},
|
|
241
242
|
);
|
|
242
|
-
return { component: selector, focus: selector };
|
|
243
|
+
return { component: selector, focus: selector.getSelectList() };
|
|
243
244
|
});
|
|
244
245
|
});
|
|
245
246
|
}
|
|
@@ -112,12 +112,23 @@ const HINT_SHIMMER_PALETTE: ShimmerPalette = {
|
|
|
112
112
|
high: "borderAccent",
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
+
function getDefaultInputPrefix(): string {
|
|
116
|
+
return `${theme.fg("accent", ">")} `;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getShellInputPrefix(isNoContext: boolean): string {
|
|
120
|
+
const shellLabel = isNoContext
|
|
121
|
+
? theme.fg("warning", theme.bold("shell no-context"))
|
|
122
|
+
: theme.fg("bashMode", theme.bold("shell"));
|
|
123
|
+
return `${shellLabel} ${getDefaultInputPrefix()}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
115
126
|
function configureDefaultComposerChrome(editor: CustomEditor): void {
|
|
116
127
|
editor.setBorderVisible(true);
|
|
117
128
|
editor.setBorderStyle("sharp");
|
|
118
129
|
editor.setClosedBorderBox(true);
|
|
119
130
|
editor.setPromptGutter(undefined);
|
|
120
|
-
editor.setInputPrefix(
|
|
131
|
+
editor.setInputPrefix(getDefaultInputPrefix());
|
|
121
132
|
editor.setPlaceholder("Type your message...");
|
|
122
133
|
editor.setPaddingX(1);
|
|
123
134
|
editor.setTopBorder(undefined);
|
|
@@ -236,6 +247,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
236
247
|
isInitialized = false;
|
|
237
248
|
isBackgrounded = false;
|
|
238
249
|
isBashMode = false;
|
|
250
|
+
isBashNoContext = false;
|
|
239
251
|
toolOutputExpanded = false;
|
|
240
252
|
todoExpanded = false;
|
|
241
253
|
planModeEnabled = false;
|
|
@@ -264,6 +276,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
264
276
|
}
|
|
265
277
|
autoCompactionEscapeHandler?: () => void;
|
|
266
278
|
retryEscapeHandler?: () => void;
|
|
279
|
+
retryCountdownTimer?: ReturnType<typeof setInterval>;
|
|
267
280
|
unsubscribe?: () => void;
|
|
268
281
|
onInputCallback?: (input: SubmittedUserInput) => void;
|
|
269
282
|
optimisticUserMessageSignature: string | undefined = undefined;
|
|
@@ -808,7 +821,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
808
821
|
|
|
809
822
|
updateEditorChrome(): void {
|
|
810
823
|
if (this.isBashMode) {
|
|
811
|
-
this.editor.borderColor =
|
|
824
|
+
this.editor.borderColor = this.isBashNoContext
|
|
825
|
+
? (str: string) => theme.fg("warning", str)
|
|
826
|
+
: theme.getBashModeBorderColor();
|
|
827
|
+
this.editor.setInputPrefix(getShellInputPrefix(this.isBashNoContext));
|
|
812
828
|
} else if (this.isPythonMode) {
|
|
813
829
|
this.editor.borderColor = theme.getPythonModeBorderColor();
|
|
814
830
|
} else {
|
|
@@ -823,6 +839,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
823
839
|
this.editor.borderColor = theme.getThinkingBorderColor(level);
|
|
824
840
|
}
|
|
825
841
|
}
|
|
842
|
+
if (!this.isBashMode) {
|
|
843
|
+
this.editor.setInputPrefix(getDefaultInputPrefix());
|
|
844
|
+
}
|
|
826
845
|
this.#setComposerTopBorder();
|
|
827
846
|
this.ui.requestRender();
|
|
828
847
|
}
|