@gajae-code/coding-agent 0.7.1 → 0.7.3
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 +57 -0
- package/dist/types/cli/mcp-cli.d.ts +25 -0
- package/dist/types/cli/notify-cli.d.ts +2 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/commands/mcp.d.ts +70 -0
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/settings-schema.d.ts +39 -2
- package/dist/types/deep-interview/plaintext-gate-guard.d.ts +11 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +1 -1
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +2 -0
- package/dist/types/modes/components/status-line/git-utils.d.ts +6 -0
- package/dist/types/modes/theme/defaults/index.d.ts +99 -0
- package/dist/types/notifications/attachment-registry.d.ts +17 -0
- package/dist/types/notifications/chat-adapters.d.ts +9 -0
- package/dist/types/notifications/config.d.ts +9 -1
- package/dist/types/notifications/engine.d.ts +59 -0
- package/dist/types/notifications/managed-daemon.d.ts +48 -0
- package/dist/types/notifications/operator-runtime.d.ts +52 -0
- package/dist/types/notifications/telegram-daemon.d.ts +73 -16
- package/dist/types/notifications/threaded-inbound.d.ts +19 -0
- package/dist/types/notifications/threaded-render.d.ts +6 -1
- package/dist/types/notifications/topic-registry.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/tools/composer-bash-policy.d.ts +14 -0
- package/dist/types/tools/fetch.d.ts +23 -0
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/telegram-send.d.ts +32 -0
- package/dist/types/web/insane/bridge.d.ts +103 -0
- package/dist/types/web/insane/url-guard.d.ts +25 -0
- package/dist/types/web/scrapers/types.d.ts +5 -0
- package/dist/types/web/scrapers/utils.d.ts +7 -1
- package/dist/types/web/search/provider.d.ts +18 -1
- package/dist/types/web/search/providers/insane.d.ts +53 -0
- package/dist/types/web/search/providers/text-citations.d.ts +23 -0
- package/dist/types/web/search/types.d.ts +12 -4
- package/package.json +10 -8
- package/scripts/verify-insane-vendor.ts +132 -0
- package/src/cli/args.ts +1 -1
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/mcp-cli.ts +272 -0
- package/src/cli/notify-cli.ts +152 -5
- package/src/cli.ts +6 -2
- package/src/commands/mcp.ts +117 -0
- package/src/commands/team.ts +1 -1
- package/src/config/keybindings.ts +2 -2
- package/src/config/settings-schema.ts +30 -1
- package/src/deep-interview/plaintext-gate-guard.ts +94 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +4 -3
- package/src/defaults/gjc/skills/ralplan/SKILL.md +11 -4
- package/src/defaults/gjc/skills/team/SKILL.md +3 -2
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/shared-events.ts +1 -0
- package/src/gjc-runtime/launch-tmux.ts +17 -3
- package/src/gjc-runtime/ledger-event-renderer.ts +1 -0
- package/src/gjc-runtime/ralplan-runtime.ts +2 -2
- package/src/gjc-runtime/tmux-common.ts +3 -1
- package/src/gjc-runtime/ultragoal-guard.ts +25 -8
- package/src/gjc-runtime/workflow-manifest.generated.json +29 -0
- package/src/gjc-runtime/workflow-manifest.ts +7 -2
- package/src/hooks/skill-state.ts +57 -0
- package/src/internal-urls/docs-index.generated.ts +14 -11
- package/src/lsp/config.ts +16 -3
- package/src/lsp/defaults.json +7 -0
- package/src/lsp/types.ts +2 -0
- package/src/modes/bridge/bridge-mode.ts +11 -0
- package/src/modes/components/custom-editor.ts +2 -0
- package/src/modes/components/footer.ts +2 -3
- package/src/modes/components/model-selector.ts +12 -0
- package/src/modes/components/status-line/git-utils.ts +25 -0
- package/src/modes/components/status-line.ts +10 -11
- package/src/modes/components/welcome.ts +2 -3
- package/src/modes/controllers/event-controller.ts +15 -0
- package/src/modes/controllers/selector-controller.ts +3 -0
- package/src/modes/interactive-mode.ts +48 -3
- package/src/modes/shared/agent-wire/scopes.ts +1 -1
- package/src/modes/theme/defaults/gruvbox-dark.json +99 -0
- package/src/modes/theme/defaults/index.ts +2 -0
- package/src/modes/utils/context-usage.ts +2 -2
- package/src/notifications/attachment-registry.ts +23 -0
- package/src/notifications/chat-adapters.ts +147 -0
- package/src/notifications/config.ts +23 -2
- package/src/notifications/engine.ts +100 -0
- package/src/notifications/index.ts +180 -38
- package/src/notifications/managed-daemon.ts +163 -0
- package/src/notifications/operator-runtime.ts +171 -0
- package/src/notifications/telegram-daemon.ts +553 -236
- package/src/notifications/threaded-inbound.ts +60 -4
- package/src/notifications/threaded-render.ts +20 -2
- package/src/notifications/topic-registry.ts +5 -0
- package/src/session/agent-session.ts +82 -51
- package/src/slash-commands/helpers/parse.ts +2 -1
- package/src/tools/bash.ts +9 -0
- package/src/tools/composer-bash-policy.ts +96 -0
- package/src/tools/fetch.ts +94 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/telegram-send.ts +137 -0
- package/src/web/insane/bridge.ts +350 -0
- package/src/web/insane/url-guard.ts +159 -0
- package/src/web/scrapers/types.ts +143 -45
- package/src/web/scrapers/utils.ts +70 -19
- package/src/web/search/provider.ts +77 -18
- package/src/web/search/providers/anthropic.ts +70 -3
- package/src/web/search/providers/codex.ts +1 -119
- package/src/web/search/providers/gemini.ts +99 -0
- package/src/web/search/providers/insane.ts +551 -0
- package/src/web/search/providers/openai-compatible.ts +66 -32
- package/src/web/search/providers/text-citations.ts +111 -0
- package/src/web/search/types.ts +13 -2
- package/vendor/insane-search/LICENSE +21 -0
- package/vendor/insane-search/MANIFEST.json +24 -0
- package/vendor/insane-search/engine/__init__.py +23 -0
- package/vendor/insane-search/engine/__main__.py +128 -0
- package/vendor/insane-search/engine/bias_check.py +183 -0
- package/vendor/insane-search/engine/executor.py +254 -0
- package/vendor/insane-search/engine/fetch_chain.py +725 -0
- package/vendor/insane-search/engine/learning.py +175 -0
- package/vendor/insane-search/engine/phase0.py +214 -0
- package/vendor/insane-search/engine/safety.py +91 -0
- package/vendor/insane-search/engine/templates/package.json +11 -0
- package/vendor/insane-search/engine/templates/playwright_mobile_chrome.js +188 -0
- package/vendor/insane-search/engine/templates/playwright_real_chrome.js +243 -0
- package/vendor/insane-search/engine/tests/test_hardening.py +57 -0
- package/vendor/insane-search/engine/tests/test_smoke.py +152 -0
- package/vendor/insane-search/engine/tests/test_u1.py +200 -0
- package/vendor/insane-search/engine/tests/test_u4.py +131 -0
- package/vendor/insane-search/engine/tests/test_u5.py +163 -0
- package/vendor/insane-search/engine/tests/test_u7.py +124 -0
- package/vendor/insane-search/engine/transport.py +211 -0
- package/vendor/insane-search/engine/url_transforms.py +98 -0
- package/vendor/insane-search/engine/validators.py +331 -0
- package/vendor/insane-search/engine/waf_detector.py +214 -0
- package/vendor/insane-search/engine/waf_profiles.yaml +162 -0
package/src/lsp/config.ts
CHANGED
|
@@ -88,6 +88,7 @@ function normalizeServerConfig(name: string, config: RawServerConfig): ServerCon
|
|
|
88
88
|
: isRecord(config.initializationOptions)
|
|
89
89
|
? config.initializationOptions
|
|
90
90
|
: undefined;
|
|
91
|
+
const supersedes = normalizeStringArray(config.supersedes);
|
|
91
92
|
|
|
92
93
|
return {
|
|
93
94
|
...config,
|
|
@@ -96,6 +97,7 @@ function normalizeServerConfig(name: string, config: RawServerConfig): ServerCon
|
|
|
96
97
|
fileTypes,
|
|
97
98
|
rootMarkers,
|
|
98
99
|
...(initOptions ? { initOptions } : {}),
|
|
100
|
+
...(supersedes ? { supersedes } : {}),
|
|
99
101
|
};
|
|
100
102
|
}
|
|
101
103
|
|
|
@@ -144,6 +146,18 @@ function mergeServers(
|
|
|
144
146
|
return merged;
|
|
145
147
|
}
|
|
146
148
|
|
|
149
|
+
function applyServerPrecedence(servers: Record<string, ServerConfig>): Record<string, ServerConfig> {
|
|
150
|
+
const suppressed = new Set<string>();
|
|
151
|
+
for (const config of Object.values(servers)) {
|
|
152
|
+
for (const serverName of config.supersedes ?? []) {
|
|
153
|
+
suppressed.add(serverName);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (suppressed.size === 0) return servers;
|
|
157
|
+
|
|
158
|
+
return Object.fromEntries(Object.entries(servers).filter(([name]) => !suppressed.has(name)));
|
|
159
|
+
}
|
|
160
|
+
|
|
147
161
|
function applyRuntimeDefaults(servers: Record<string, ServerConfig>): Record<string, ServerConfig> {
|
|
148
162
|
const updated: Record<string, ServerConfig> = { ...servers };
|
|
149
163
|
|
|
@@ -422,9 +436,8 @@ export function loadConfig(cwd: string): LspConfig {
|
|
|
422
436
|
detected[name] = { ...config, resolvedCommand: resolved };
|
|
423
437
|
}
|
|
424
438
|
|
|
425
|
-
return { servers: detected, idleTimeoutMs };
|
|
439
|
+
return { servers: applyServerPrecedence(detected), idleTimeoutMs };
|
|
426
440
|
}
|
|
427
|
-
|
|
428
441
|
// Merge overrides with defaults and filter to available servers
|
|
429
442
|
const mergedWithRuntime = applyRuntimeDefaults(mergedServers);
|
|
430
443
|
const available: Record<string, ServerConfig> = {};
|
|
@@ -437,7 +450,7 @@ export function loadConfig(cwd: string): LspConfig {
|
|
|
437
450
|
available[name] = { ...config, resolvedCommand: resolved };
|
|
438
451
|
}
|
|
439
452
|
|
|
440
|
-
return { servers: available, idleTimeoutMs };
|
|
453
|
+
return { servers: applyServerPrecedence(available), idleTimeoutMs };
|
|
441
454
|
}
|
|
442
455
|
|
|
443
456
|
// =============================================================================
|
package/src/lsp/defaults.json
CHANGED
|
@@ -342,6 +342,13 @@
|
|
|
342
342
|
"fileTypes": [".php"],
|
|
343
343
|
"rootMarkers": ["composer.json", ".phpactor.json", ".phpactor.yml"]
|
|
344
344
|
},
|
|
345
|
+
"csharp-ls": {
|
|
346
|
+
"command": "csharp-ls",
|
|
347
|
+
"args": [],
|
|
348
|
+
"fileTypes": [".cs", ".csx"],
|
|
349
|
+
"rootMarkers": ["*.sln", "*.slnx", "*.csproj", "global.json"],
|
|
350
|
+
"supersedes": ["omnisharp"]
|
|
351
|
+
},
|
|
345
352
|
"omnisharp": {
|
|
346
353
|
"command": "omnisharp",
|
|
347
354
|
"args": ["-z", "--hostPID", "$PID", "--encoding", "utf-8", "--languageserver"],
|
package/src/lsp/types.ts
CHANGED
|
@@ -358,6 +358,8 @@ export interface ServerConfig {
|
|
|
358
358
|
/** Per-server warmup timeout in milliseconds. Overrides the global WARMUP_TIMEOUT_MS for this server during startup. */
|
|
359
359
|
warmupTimeoutMs?: number;
|
|
360
360
|
capabilities?: ServerCapabilities;
|
|
361
|
+
/** Names of lower-precedence servers to drop when this server is available. */
|
|
362
|
+
supersedes?: string[];
|
|
361
363
|
/** If true, this is a linter/formatter server (e.g., Biome) - used only for diagnostics/actions, not type intelligence */
|
|
362
364
|
isLinter?: boolean;
|
|
363
365
|
/** Resolved absolute path to the command binary (set during config loading) */
|
|
@@ -153,6 +153,14 @@ function jsonResponse(status: number, body: unknown): Response {
|
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
function isBridgeControllerOwner(options: BridgeFetchHandlerOptions, ownerToken: string): boolean {
|
|
157
|
+
if (!ownerToken) return false;
|
|
158
|
+
const ownerTokens = [options.permissionBroker?.ownerToken, options.uiBroker?.ownerToken].filter(
|
|
159
|
+
(token): token is string => typeof token === "string" && token.length > 0,
|
|
160
|
+
);
|
|
161
|
+
return ownerTokens.length > 0 && ownerTokens.every(token => token === ownerToken);
|
|
162
|
+
}
|
|
163
|
+
|
|
156
164
|
function parseBridgeScopes(value: string | undefined): readonly BridgeCommandScope[] {
|
|
157
165
|
if (!value?.trim()) return DEFAULT_BRIDGE_SCOPES;
|
|
158
166
|
const allowed = new Set(BRIDGE_COMMAND_SCOPES);
|
|
@@ -425,6 +433,9 @@ export function createBridgeFetchHandler(options: BridgeFetchHandlerOptions): (r
|
|
|
425
433
|
"answer" in payload &&
|
|
426
434
|
(correlationId === (payload as RpcWorkflowGateResponse).gate_id || correlationId.startsWith("wg_"))
|
|
427
435
|
) {
|
|
436
|
+
if (!isBridgeControllerOwner(options, ownerToken)) {
|
|
437
|
+
return jsonResponse(403, { status: "rejected", code: "not_controller" });
|
|
438
|
+
}
|
|
428
439
|
try {
|
|
429
440
|
const resolution = await options.unattendedControlPlane?.resolveGate({
|
|
430
441
|
gate_id: (payload as RpcWorkflowGateResponse).gate_id,
|
|
@@ -18,6 +18,7 @@ type ConfigurableEditorAction = Extract<
|
|
|
18
18
|
| "app.editor.external"
|
|
19
19
|
| "app.history.search"
|
|
20
20
|
| "app.message.dequeue"
|
|
21
|
+
| "app.message.followUp"
|
|
21
22
|
| "app.message.queue"
|
|
22
23
|
| "app.clipboard.pasteImage"
|
|
23
24
|
| "app.clipboard.copyPrompt"
|
|
@@ -40,6 +41,7 @@ const CONFIGURABLE_EDITOR_ACTIONS = [
|
|
|
40
41
|
"app.thinking.toggle",
|
|
41
42
|
"app.editor.external",
|
|
42
43
|
"app.history.search",
|
|
44
|
+
"app.message.followUp",
|
|
43
45
|
"app.message.queue",
|
|
44
46
|
"app.message.dequeue",
|
|
45
47
|
"app.clipboard.pasteImage",
|
|
@@ -8,6 +8,7 @@ import { shortenPath } from "../../tools/render-utils";
|
|
|
8
8
|
import * as git from "../../utils/git";
|
|
9
9
|
import { sanitizeStatusText } from "../shared";
|
|
10
10
|
import { getContextUsageLevel, getContextUsageThemeColor } from "./status-line/context-thresholds";
|
|
11
|
+
import { resolveCurrentBranch } from "./status-line/git-utils";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Footer component that shows pwd, token stats, and context usage
|
|
@@ -103,9 +104,7 @@ export class FooterComponent implements Component {
|
|
|
103
104
|
return this.#cachedBranch;
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
this.#cachedBranch =
|
|
108
|
-
headState === null ? null : headState.kind === "ref" ? (headState.branchName ?? headState.ref) : "detached";
|
|
107
|
+
this.#cachedBranch = resolveCurrentBranch(getProjectDir()).branch;
|
|
109
108
|
return this.#cachedBranch;
|
|
110
109
|
}
|
|
111
110
|
|
|
@@ -221,6 +221,8 @@ export class ModelSelectorComponent extends Container {
|
|
|
221
221
|
#scopedModels: ReadonlyArray<ScopedModelItem>;
|
|
222
222
|
#temporaryOnly: boolean;
|
|
223
223
|
#currentModel?: Model;
|
|
224
|
+
#currentThinkingLevel?: ThinkingLevel;
|
|
225
|
+
#activeModelProfile?: string;
|
|
224
226
|
#isFastForProvider: (provider?: string) => boolean = () => false;
|
|
225
227
|
#isFastForSubagentProvider: (provider?: string) => boolean = () => false;
|
|
226
228
|
#pendingActionItem?: ModelItem | CanonicalModelItem;
|
|
@@ -258,6 +260,8 @@ export class ModelSelectorComponent extends Container {
|
|
|
258
260
|
sessionId?: string;
|
|
259
261
|
isFastForProvider?: (provider?: string) => boolean;
|
|
260
262
|
isFastForSubagentProvider?: (provider?: string) => boolean;
|
|
263
|
+
currentThinkingLevel?: ThinkingLevel;
|
|
264
|
+
activeModelProfile?: string;
|
|
261
265
|
},
|
|
262
266
|
) {
|
|
263
267
|
super();
|
|
@@ -271,6 +275,8 @@ export class ModelSelectorComponent extends Container {
|
|
|
271
275
|
this.#temporaryOnly = options?.temporaryOnly ?? false;
|
|
272
276
|
this.#authSessionId = options?.sessionId;
|
|
273
277
|
this.#currentModel = _currentModel;
|
|
278
|
+
this.#currentThinkingLevel = options?.currentThinkingLevel;
|
|
279
|
+
this.#activeModelProfile = options?.activeModelProfile;
|
|
274
280
|
this.#isFastForProvider = options?.isFastForProvider ?? (() => false);
|
|
275
281
|
this.#isFastForSubagentProvider = options?.isFastForSubagentProvider ?? (() => false);
|
|
276
282
|
const initialSearchInput = options?.initialSearchInput;
|
|
@@ -367,6 +373,12 @@ export class ModelSelectorComponent extends Container {
|
|
|
367
373
|
};
|
|
368
374
|
}
|
|
369
375
|
}
|
|
376
|
+
if (this.#activeModelProfile && this.#currentModel) {
|
|
377
|
+
this.#roles.default = {
|
|
378
|
+
model: this.#currentModel,
|
|
379
|
+
thinkingLevel: this.#currentThinkingLevel ?? ThinkingLevel.Inherit,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
370
382
|
}
|
|
371
383
|
|
|
372
384
|
#sortModels(models: ModelItem[]): void {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { GitHeadState } from "../../../utils/git";
|
|
2
|
+
import * as git from "../../../utils/git";
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Extract "owner/repo" from a GitHub remote URL.
|
|
3
6
|
* Handles HTTPS, SSH (scp-style), and git:// protocols.
|
|
@@ -40,3 +43,25 @@ export function canReuseCachedPr(
|
|
|
40
43
|
): boolean {
|
|
41
44
|
return cachedPr !== undefined && currentContext !== null && isSamePrCacheContext(cachedContext, currentContext);
|
|
42
45
|
}
|
|
46
|
+
|
|
47
|
+
export interface CurrentBranchState {
|
|
48
|
+
readonly branch: string | null;
|
|
49
|
+
readonly repoId: string | null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function resolveCurrentBranch(
|
|
53
|
+
cwd: string,
|
|
54
|
+
resolveHead: (cwd: string) => GitHeadState | null = git.head.resolveSync,
|
|
55
|
+
): CurrentBranchState {
|
|
56
|
+
try {
|
|
57
|
+
const head = resolveHead(cwd);
|
|
58
|
+
if (!head) return { branch: null, repoId: null };
|
|
59
|
+
return {
|
|
60
|
+
branch: head.kind === "ref" ? (head.branchName ?? head.ref) : "detached",
|
|
61
|
+
repoId: head.headPath,
|
|
62
|
+
};
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (error instanceof Error) return { branch: null, repoId: null };
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
createPrCacheContext,
|
|
21
21
|
isSamePrCacheContext,
|
|
22
22
|
type PrCacheContext,
|
|
23
|
+
resolveCurrentBranch,
|
|
23
24
|
} from "./status-line/git-utils";
|
|
24
25
|
import { getPreset } from "./status-line/presets";
|
|
25
26
|
import { renderSegment, type SegmentContext } from "./status-line/segments";
|
|
@@ -303,19 +304,13 @@ export class StatusLineComponent implements Component {
|
|
|
303
304
|
this.#cachedPrContext = undefined;
|
|
304
305
|
}
|
|
305
306
|
#getCurrentBranch(): string | null {
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
if (this.#cachedBranch !== undefined && this.#cachedBranchRepoId === gitHeadPath) {
|
|
307
|
+
const current = resolveCurrentBranch(getProjectDir());
|
|
308
|
+
if (this.#cachedBranch !== undefined && this.#cachedBranchRepoId === current.repoId) {
|
|
309
309
|
return this.#cachedBranch;
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
this.#cachedBranchRepoId =
|
|
313
|
-
|
|
314
|
-
this.#cachedBranch = null;
|
|
315
|
-
return null;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
this.#cachedBranch = head.kind === "ref" ? (head.branchName ?? head.ref) : "detached";
|
|
312
|
+
this.#cachedBranchRepoId = current.repoId;
|
|
313
|
+
this.#cachedBranch = current.branch;
|
|
319
314
|
|
|
320
315
|
return this.#cachedBranch ?? null;
|
|
321
316
|
}
|
|
@@ -680,7 +675,11 @@ export class StatusLineComponent implements Component {
|
|
|
680
675
|
const effectiveSettings = this.#resolveSettings();
|
|
681
676
|
const separatorDef = getSeparator(effectiveSettings.separator ?? "powerline-thin", theme);
|
|
682
677
|
|
|
683
|
-
|
|
678
|
+
// Use the subtle surface tone (the same elevated background as user-message
|
|
679
|
+
// bubbles) instead of the heavy `statusLineBg` block, so the rail layers
|
|
680
|
+
// just above the base background as a quiet zone rather than a solid bar.
|
|
681
|
+
// Resolving through a semantic slot keeps it correct across every theme.
|
|
682
|
+
const bgAnsi = theme.getBgAnsi("userMessageBg");
|
|
684
683
|
const fgAnsi = theme.getFgAnsi("text");
|
|
685
684
|
const sepAnsi = theme.getFgAnsi("statusLineSep");
|
|
686
685
|
|
|
@@ -74,9 +74,8 @@ export class WelcomeComponent implements Component {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
render(termWidth: number): string[] {
|
|
77
|
-
// Box dimensions
|
|
78
|
-
const
|
|
79
|
-
const boxWidth = Math.min(maxWidth, Math.max(0, termWidth - 2));
|
|
77
|
+
// Box dimensions track the live viewport so wide terminals feel intentionally full-screen.
|
|
78
|
+
const boxWidth = Math.max(0, termWidth - 2);
|
|
80
79
|
if (boxWidth < 4) {
|
|
81
80
|
return [];
|
|
82
81
|
}
|
|
@@ -201,6 +201,7 @@ export class EventController {
|
|
|
201
201
|
this.ctx.statusContainer.clear();
|
|
202
202
|
}
|
|
203
203
|
this.#cancelIdleCompaction();
|
|
204
|
+
this.ctx.updateEditorBorderColor();
|
|
204
205
|
this.ctx.ensureLoadingAnimation();
|
|
205
206
|
this.ctx.ui.requestRender();
|
|
206
207
|
}
|
|
@@ -633,6 +634,7 @@ export class EventController {
|
|
|
633
634
|
this.#readToolCallArgs.clear();
|
|
634
635
|
this.#readToolCallAssistantComponents.clear();
|
|
635
636
|
this.#lastAssistantComponent = undefined;
|
|
637
|
+
this.ctx.updateEditorBorderColor();
|
|
636
638
|
this.ctx.ui.requestRender();
|
|
637
639
|
this.#scheduleIdleCompaction();
|
|
638
640
|
this.sendCompletionNotification();
|
|
@@ -642,6 +644,7 @@ export class EventController {
|
|
|
642
644
|
event: Extract<AgentSessionEvent, { type: "auto_compaction_start" }>,
|
|
643
645
|
): Promise<void> {
|
|
644
646
|
this.#cancelIdleCompaction();
|
|
647
|
+
this.ctx.updateEditorBorderColor();
|
|
645
648
|
this.ctx.autoCompactionEscapeHandler = this.ctx.editor.onEscape;
|
|
646
649
|
this.ctx.editor.onEscape = () => {
|
|
647
650
|
this.ctx.session.abortCompaction();
|
|
@@ -672,13 +675,20 @@ export class EventController {
|
|
|
672
675
|
this.ctx.autoCompactionLoader = undefined;
|
|
673
676
|
this.ctx.statusContainer.clear();
|
|
674
677
|
}
|
|
678
|
+
this.ctx.updateEditorBorderColor();
|
|
675
679
|
const isHandoffAction = event.action === "handoff";
|
|
680
|
+
const continuationDisabled = event.continuationSkipReason === "auto_continue_disabled_non_resumable_tail";
|
|
676
681
|
if (event.aborted) {
|
|
677
682
|
this.ctx.showStatus(isHandoffAction ? "Auto-handoff cancelled" : "Auto context-full maintenance cancelled");
|
|
678
683
|
} else if (event.result) {
|
|
679
684
|
this.ctx.rebuildChatFromMessages();
|
|
680
685
|
this.ctx.statusLine.invalidate();
|
|
681
686
|
this.ctx.updateEditorTopBorder();
|
|
687
|
+
if (continuationDisabled && !isHandoffAction) {
|
|
688
|
+
this.ctx.showStatus("Context overflow recovery skipped: auto_continue_disabled_non_resumable_tail");
|
|
689
|
+
} else if (event.willRetry && !isHandoffAction) {
|
|
690
|
+
this.ctx.showStatus("Context overflow maintenance completed");
|
|
691
|
+
}
|
|
682
692
|
} else if (event.errorMessage) {
|
|
683
693
|
this.ctx.showWarning(event.errorMessage);
|
|
684
694
|
} else if (isHandoffAction) {
|
|
@@ -691,6 +701,11 @@ export class EventController {
|
|
|
691
701
|
} else if (event.skipped) {
|
|
692
702
|
// Benign skip: no model selected, no candidate models available, or nothing
|
|
693
703
|
// to compact yet. Not a failure — suppress the warning.
|
|
704
|
+
if (continuationDisabled) {
|
|
705
|
+
this.ctx.showStatus("Context overflow recovery skipped: auto_continue_disabled_non_resumable_tail");
|
|
706
|
+
} else if (event.willRetry && !isHandoffAction) {
|
|
707
|
+
this.ctx.showStatus("Context overflow maintenance skipped");
|
|
708
|
+
}
|
|
694
709
|
} else {
|
|
695
710
|
this.ctx.showWarning("Auto context-full maintenance failed; continuing without maintenance");
|
|
696
711
|
}
|
|
@@ -745,6 +745,9 @@ export class SelectorController {
|
|
|
745
745
|
{
|
|
746
746
|
...options,
|
|
747
747
|
sessionId: this.ctx.session.sessionId,
|
|
748
|
+
currentThinkingLevel: this.ctx.session.thinkingLevel,
|
|
749
|
+
activeModelProfile:
|
|
750
|
+
this.ctx.session.getActiveModelProfile?.() ?? this.ctx.settings.get("modelProfile.default"),
|
|
748
751
|
isFastForProvider: provider => this.ctx.session.isFastForProvider(provider),
|
|
749
752
|
isFastForSubagentProvider: provider => this.ctx.session.isFastForSubagentProvider(provider),
|
|
750
753
|
},
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
import { APP_NAME, adjustHsv, getProjectDir, hsvToRgb, isEnoent, logger, postmortem, prompt } from "@gajae-code/utils";
|
|
30
30
|
import chalk from "chalk";
|
|
31
31
|
import { AsyncJobManager } from "../async";
|
|
32
|
-
import { KeybindingsManager } from "../config/keybindings";
|
|
32
|
+
import { type AppKeybinding, KeybindingsManager } from "../config/keybindings";
|
|
33
33
|
import { isSettingsInitialized, type Settings, settings } from "../config/settings";
|
|
34
34
|
import { DEFAULT_GJC_DEFINITION_NAMES } from "../defaults/gjc-defaults";
|
|
35
35
|
import type {
|
|
@@ -117,6 +117,24 @@ import type { CompactionQueuedMessage, InteractiveModeContext, SubmittedUserInpu
|
|
|
117
117
|
import { UiHelpers } from "./utils/ui-helpers";
|
|
118
118
|
|
|
119
119
|
const INTERACTIVE_ABORT_CLEANUP_TIMEOUT_MS = 5_000;
|
|
120
|
+
const DEFAULT_COMPOSER_PLACEHOLDER = "Type your message...";
|
|
121
|
+
const FRIENDLY_KEY_PARTS: Record<string, string> = {
|
|
122
|
+
alt: "Alt",
|
|
123
|
+
cmd: "Command",
|
|
124
|
+
command: "Command",
|
|
125
|
+
ctrl: "Control",
|
|
126
|
+
enter: "Enter",
|
|
127
|
+
meta: process.platform === "darwin" ? "Command" : "Meta",
|
|
128
|
+
option: "Option",
|
|
129
|
+
shift: "Shift",
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
function formatShortcutForPlaceholder(key: string): string {
|
|
133
|
+
return key
|
|
134
|
+
.split("+")
|
|
135
|
+
.map(part => FRIENDLY_KEY_PARTS[part.toLowerCase()] ?? part)
|
|
136
|
+
.join("+");
|
|
137
|
+
}
|
|
120
138
|
|
|
121
139
|
const HINT_SHIMMER_PALETTE: ShimmerPalette = {
|
|
122
140
|
low: "dim",
|
|
@@ -137,11 +155,11 @@ function getShellInputPrefix(isNoContext: boolean): string {
|
|
|
137
155
|
|
|
138
156
|
function configureDefaultComposerChrome(editor: CustomEditor): void {
|
|
139
157
|
editor.setBorderVisible(true);
|
|
140
|
-
editor.setBorderStyle("
|
|
158
|
+
editor.setBorderStyle("round");
|
|
141
159
|
editor.setClosedBorderBox(true);
|
|
142
160
|
editor.setPromptGutter(undefined);
|
|
143
161
|
editor.setInputPrefix(getDefaultInputPrefix());
|
|
144
|
-
editor.setPlaceholder(
|
|
162
|
+
editor.setPlaceholder(DEFAULT_COMPOSER_PLACEHOLDER);
|
|
145
163
|
editor.setPaddingX(1);
|
|
146
164
|
editor.setTopBorder(undefined);
|
|
147
165
|
}
|
|
@@ -550,6 +568,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
550
568
|
this.ui.addChild(this.hookWidgetContainerAbove);
|
|
551
569
|
this.ui.addChild(this.editorContainer);
|
|
552
570
|
this.ui.addChild(this.hookWidgetContainerBelow);
|
|
571
|
+
this.ui.setBottomPinnedComponent(this.statusLine);
|
|
553
572
|
this.ui.setFocus(this.editor);
|
|
554
573
|
|
|
555
574
|
this.#inputController.setupKeyHandlers();
|
|
@@ -903,6 +922,31 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
903
922
|
this.editor.setMaxHeight(this.#computeEditorMaxHeight());
|
|
904
923
|
}
|
|
905
924
|
|
|
925
|
+
#isPromptDeliveryBusy(): boolean {
|
|
926
|
+
return this.session.isStreaming || this.session.isCompacting;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
#getFirstKeyForAction(action: AppKeybinding): string | undefined {
|
|
930
|
+
return this.keybindings.getKeys(action)[0];
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
#getMessageQueueShortcut(): string | undefined {
|
|
934
|
+
const preferredAction: AppKeybinding =
|
|
935
|
+
process.platform === "darwin" ? "app.message.followUp" : "app.message.queue";
|
|
936
|
+
const fallbackAction: AppKeybinding =
|
|
937
|
+
process.platform === "darwin" ? "app.message.queue" : "app.message.followUp";
|
|
938
|
+
return this.#getFirstKeyForAction(preferredAction) ?? this.#getFirstKeyForAction(fallbackAction);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
#getComposerPlaceholder(): string {
|
|
942
|
+
if (!this.#isPromptDeliveryBusy()) return DEFAULT_COMPOSER_PLACEHOLDER;
|
|
943
|
+
const enterAction = this.settings.get("busyPromptMode") === "steer" ? "Steering" : "Message Queueing";
|
|
944
|
+
const parts = [`Enter: ${enterAction}`];
|
|
945
|
+
const queueKey = this.#getMessageQueueShortcut();
|
|
946
|
+
if (queueKey) parts.push(`${formatShortcutForPlaceholder(queueKey)}: Message Queueing`);
|
|
947
|
+
return `${DEFAULT_COMPOSER_PLACEHOLDER} ${parts.join(" · ")}`;
|
|
948
|
+
}
|
|
949
|
+
|
|
906
950
|
updateEditorChrome(): void {
|
|
907
951
|
if (this.isBashMode) {
|
|
908
952
|
this.editor.borderColor = this.isBashNoContext
|
|
@@ -926,6 +970,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
926
970
|
if (!this.isBashMode) {
|
|
927
971
|
this.editor.setInputPrefix(getDefaultInputPrefix());
|
|
928
972
|
}
|
|
973
|
+
this.editor.setPlaceholder(this.#getComposerPlaceholder());
|
|
929
974
|
this.#setComposerTopBorder();
|
|
930
975
|
this.ui.requestRender();
|
|
931
976
|
}
|
|
@@ -73,7 +73,7 @@ const RPC_COMMAND_SCOPE_REGISTRY: Record<RpcCommandType, BridgeCommandScope> = {
|
|
|
73
73
|
get_login_providers: "admin",
|
|
74
74
|
login: "admin",
|
|
75
75
|
negotiate_unattended: "control",
|
|
76
|
-
workflow_gate_response: "
|
|
76
|
+
workflow_gate_response: "control",
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
export const RPC_COMMAND_TYPES: readonly RpcCommandType[] = Object.keys(RPC_COMMAND_SCOPE_REGISTRY) as RpcCommandType[];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/can1357/gajae-code/main/packages/coding-agent/theme-schema.json",
|
|
3
|
+
"name": "gruvbox-dark",
|
|
4
|
+
"vars": {
|
|
5
|
+
"bgHard": "#1d2021",
|
|
6
|
+
"surface": "#32302f",
|
|
7
|
+
"surfaceBright": "#504945",
|
|
8
|
+
"borderNeutral": "#504945",
|
|
9
|
+
"borderSubtle": "#3c3836",
|
|
10
|
+
"fg": "#ebdbb2",
|
|
11
|
+
"muted": "#a89984",
|
|
12
|
+
"dim": "#7c6f64",
|
|
13
|
+
"gray": "#928374",
|
|
14
|
+
"red": "#fb4934",
|
|
15
|
+
"green": "#b8bb26",
|
|
16
|
+
"yellow": "#fabd2f",
|
|
17
|
+
"blue": "#83a598",
|
|
18
|
+
"purple": "#d3869b",
|
|
19
|
+
"aqua": "#8ec07c",
|
|
20
|
+
"orange": "#fe8019",
|
|
21
|
+
"diffRemovalRed": "#cc241d"
|
|
22
|
+
},
|
|
23
|
+
"colors": {
|
|
24
|
+
"accent": "orange",
|
|
25
|
+
"border": "borderNeutral",
|
|
26
|
+
"borderAccent": "orange",
|
|
27
|
+
"borderMuted": "borderSubtle",
|
|
28
|
+
"success": "green",
|
|
29
|
+
"error": "red",
|
|
30
|
+
"warning": "yellow",
|
|
31
|
+
"muted": "muted",
|
|
32
|
+
"dim": "dim",
|
|
33
|
+
"text": "fg",
|
|
34
|
+
"thinkingText": "muted",
|
|
35
|
+
"selectedBg": "surfaceBright",
|
|
36
|
+
"userMessageBg": "surface",
|
|
37
|
+
"userMessageText": "fg",
|
|
38
|
+
"customMessageBg": "surface",
|
|
39
|
+
"customMessageText": "fg",
|
|
40
|
+
"customMessageLabel": "orange",
|
|
41
|
+
"toolPendingBg": "surface",
|
|
42
|
+
"toolSuccessBg": "#283626",
|
|
43
|
+
"toolErrorBg": "#3c2323",
|
|
44
|
+
"toolTitle": "fg",
|
|
45
|
+
"toolOutput": "muted",
|
|
46
|
+
"mdHeading": "yellow",
|
|
47
|
+
"mdLink": "blue",
|
|
48
|
+
"mdLinkUrl": "muted",
|
|
49
|
+
"mdCode": "aqua",
|
|
50
|
+
"mdCodeBlock": "fg",
|
|
51
|
+
"mdCodeBlockBorder": "borderNeutral",
|
|
52
|
+
"mdQuote": "muted",
|
|
53
|
+
"mdQuoteBorder": "borderNeutral",
|
|
54
|
+
"mdHr": "dim",
|
|
55
|
+
"mdListBullet": "orange",
|
|
56
|
+
"toolDiffAdded": "green",
|
|
57
|
+
"toolDiffRemoved": "diffRemovalRed",
|
|
58
|
+
"toolDiffContext": "muted",
|
|
59
|
+
"syntaxComment": "gray",
|
|
60
|
+
"syntaxKeyword": "red",
|
|
61
|
+
"syntaxFunction": "green",
|
|
62
|
+
"syntaxVariable": "blue",
|
|
63
|
+
"syntaxString": "green",
|
|
64
|
+
"syntaxNumber": "purple",
|
|
65
|
+
"syntaxType": "yellow",
|
|
66
|
+
"syntaxOperator": "aqua",
|
|
67
|
+
"syntaxPunctuation": "muted",
|
|
68
|
+
"thinkingOff": "dim",
|
|
69
|
+
"thinkingMinimal": "muted",
|
|
70
|
+
"thinkingLow": "aqua",
|
|
71
|
+
"thinkingMedium": "yellow",
|
|
72
|
+
"thinkingHigh": "orange",
|
|
73
|
+
"thinkingXhigh": "red",
|
|
74
|
+
"bashMode": "green",
|
|
75
|
+
"pythonMode": "yellow",
|
|
76
|
+
"statusLineBg": "bgHard",
|
|
77
|
+
"statusLineSep": "dim",
|
|
78
|
+
"statusLineModel": "orange",
|
|
79
|
+
"statusLinePath": "blue",
|
|
80
|
+
"statusLineGitClean": "green",
|
|
81
|
+
"statusLineGitDirty": "yellow",
|
|
82
|
+
"statusLineContext": "aqua",
|
|
83
|
+
"statusLineSpend": "yellow",
|
|
84
|
+
"statusLineStaged": "green",
|
|
85
|
+
"statusLineDirty": "yellow",
|
|
86
|
+
"statusLineUntracked": "diffRemovalRed",
|
|
87
|
+
"statusLineOutput": "fg",
|
|
88
|
+
"statusLineCost": "orange",
|
|
89
|
+
"statusLineSubagents": "purple"
|
|
90
|
+
},
|
|
91
|
+
"export": {
|
|
92
|
+
"pageBg": "#1d2021",
|
|
93
|
+
"cardBg": "#282828",
|
|
94
|
+
"infoBg": "#32302f"
|
|
95
|
+
},
|
|
96
|
+
"symbols": {
|
|
97
|
+
"preset": "unicode"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import blue_crab from "./blue-crab.json" with { type: "json" };
|
|
2
2
|
import claude_code from "./claude-code.json" with { type: "json" };
|
|
3
3
|
import codex from "./codex.json" with { type: "json" };
|
|
4
|
+
import gruvbox_dark from "./gruvbox-dark.json" with { type: "json" };
|
|
4
5
|
import opencode from "./opencode.json" with { type: "json" };
|
|
5
6
|
import red_claw from "./red-claw.json" with { type: "json" };
|
|
6
7
|
|
|
@@ -8,6 +9,7 @@ export const defaultThemes = {
|
|
|
8
9
|
"blue-crab": blue_crab,
|
|
9
10
|
"claude-code": claude_code,
|
|
10
11
|
codex,
|
|
12
|
+
"gruvbox-dark": gruvbox_dark,
|
|
11
13
|
opencode,
|
|
12
14
|
"red-claw": red_claw,
|
|
13
15
|
};
|
|
@@ -204,14 +204,14 @@ export function computeContextBreakdown(
|
|
|
204
204
|
if (contextWindow > 0) {
|
|
205
205
|
const compactionSettings = session.settings.getGroup("compaction") as CompactionSettings;
|
|
206
206
|
if (compactionSettings.enabled && compactionSettings.strategy !== "off") {
|
|
207
|
-
const threshold = resolveThresholdTokens(contextWindow, compactionSettings
|
|
207
|
+
const threshold = resolveThresholdTokens(contextWindow, compactionSettings);
|
|
208
208
|
autoCompactBufferTokens = Math.max(0, contextWindow - threshold);
|
|
209
209
|
} else {
|
|
210
210
|
autoCompactBufferTokens = 0;
|
|
211
211
|
}
|
|
212
212
|
// Even when fully disabled, fall back to a sensible reserve floor for display.
|
|
213
213
|
if (autoCompactBufferTokens === 0 && compactionSettings.enabled) {
|
|
214
|
-
autoCompactBufferTokens = effectiveReserveTokens(contextWindow, compactionSettings
|
|
214
|
+
autoCompactBufferTokens = effectiveReserveTokens(contextWindow, compactionSettings);
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
autoCompactBufferTokens = Math.min(autoCompactBufferTokens, Math.max(0, contextWindow - usedTokens));
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-wide registry mapping a session id to a sink that delivers a local
|
|
3
|
+
* file to the session's connected Telegram chat as a document. Registered by
|
|
4
|
+
* the notifications extension; consumed by the telegram_send tool.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Delivers a local file to the session's Telegram chat. */
|
|
8
|
+
export type TelegramFileSink = (file: { path: string; caption?: string }) => Promise<{ ok: boolean; error?: string }>;
|
|
9
|
+
|
|
10
|
+
const sinks = new Map<string, TelegramFileSink>();
|
|
11
|
+
|
|
12
|
+
/** Register `sink` for `sessionId`. Returns a disposer that clears it. */
|
|
13
|
+
export function registerTelegramFileSink(sessionId: string, sink: TelegramFileSink): () => void {
|
|
14
|
+
sinks.set(sessionId, sink);
|
|
15
|
+
return () => {
|
|
16
|
+
if (sinks.get(sessionId) === sink) sinks.delete(sessionId);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The Telegram file sink for `sessionId`, if one is registered. */
|
|
21
|
+
export function getTelegramFileSink(sessionId: string): TelegramFileSink | undefined {
|
|
22
|
+
return sinks.get(sessionId);
|
|
23
|
+
}
|