@gajae-code/coding-agent 0.9.5 → 0.10.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 +54 -0
- package/dist/types/cli/classify-install-target.d.ts +0 -10
- package/dist/types/cli/daemon-cli.d.ts +2 -0
- package/dist/types/cli/initial-message.d.ts +1 -1
- package/dist/types/cli/malloc-env-guard.d.ts +65 -0
- package/dist/types/cli/malloc-env-guard.test.d.ts +1 -0
- package/dist/types/cli/notify-cli.d.ts +3 -1
- package/dist/types/cli/session-picker.d.ts +4 -3
- package/dist/types/cli/update-cli.d.ts +22 -3
- package/dist/types/cli.d.ts +4 -0
- package/dist/types/commands/daemon.d.ts +6 -2
- package/dist/types/commands/launch.d.ts +1 -0
- package/dist/types/commands/notify.d.ts +6 -0
- package/dist/types/commit/analysis/conventional.d.ts +1 -1
- package/dist/types/commit/analysis/summary.d.ts +1 -1
- package/dist/types/commit/changelog/generate.d.ts +1 -1
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +47 -7
- package/dist/types/coordinator-mcp/model-preset.d.ts +45 -0
- package/dist/types/coordinator-mcp/server.d.ts +67 -6
- package/dist/types/daemon/control-types.d.ts +10 -0
- package/dist/types/daemon/operator-contract.d.ts +47 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.d.ts +4 -0
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
- package/dist/types/gjc-runtime/launch-tmux.d.ts +26 -7
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +34 -3
- package/dist/types/gjc-runtime/tmux-common.d.ts +6 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +309 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +49 -17
- package/dist/types/internal-urls/local-protocol.d.ts +13 -9
- package/dist/types/main.d.ts +66 -2
- package/dist/types/modes/acp/acp-agent.d.ts +19 -2
- package/dist/types/modes/acp/acp-mode.d.ts +12 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -1
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/gajae-pet-widget.d.ts +59 -0
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +32 -0
- package/dist/types/modes/components/pet-selector.d.ts +11 -0
- package/dist/types/modes/components/session-selector.d.ts +22 -9
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +1 -0
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/controllers/event-controller.d.ts +4 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +24 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +22 -0
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +8 -0
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +5 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +45 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +42 -2
- package/dist/types/modes/theme/defaults/index.d.ts +568 -568
- package/dist/types/modes/types.d.ts +28 -2
- package/dist/types/modes/utils/abort-message.d.ts +1 -1
- package/dist/types/modes/utils/irc-message.d.ts +38 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +13 -4
- package/dist/types/notifications/config.d.ts +34 -0
- package/dist/types/notifications/daemon-paths.d.ts +7 -0
- package/dist/types/notifications/index.d.ts +27 -0
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +12 -4
- package/dist/types/notifications/notification-service.d.ts +144 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +27 -9
- package/dist/types/notifications/telegram-daemon-control.d.ts +6 -0
- package/dist/types/notifications/telegram-daemon.d.ts +130 -3
- package/dist/types/notifications/telegram-reference.d.ts +19 -2
- package/dist/types/notifications/topic-registry.d.ts +24 -4
- package/dist/types/registry/agent-registry.d.ts +2 -0
- package/dist/types/rlm/index.d.ts +1 -1
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +26 -2
- package/dist/types/session/cache-economics.d.ts +79 -0
- package/dist/types/session/provider-safety-stop.d.ts +5 -0
- package/dist/types/session/session-manager.d.ts +124 -4
- package/dist/types/session/session-storage.d.ts +155 -13
- package/dist/types/setup/credential-auto-import.d.ts +2 -2
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/receipt.d.ts +3 -1
- package/dist/types/task/types.d.ts +8 -0
- package/dist/types/tools/ask-answer-registry.d.ts +5 -0
- package/dist/types/tools/ask.d.ts +25 -1
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/browser/tab-supervisor.d.ts +6 -0
- package/dist/types/tools/index.d.ts +58 -6
- package/dist/types/tools/telegram-send.d.ts +7 -0
- package/dist/types/utils/external-editor.d.ts +2 -0
- package/package.json +9 -9
- package/scripts/compile-args.ts +8 -2
- package/scripts/g004-tmux-smoke.ts +196 -36
- package/src/cli/daemon-cli.ts +19 -29
- package/src/cli/malloc-env-guard.test.ts +104 -0
- package/src/cli/malloc-env-guard.ts +149 -0
- package/src/cli/notify-cli.ts +78 -14
- package/src/cli/session-picker.ts +23 -40
- package/src/cli/update-cli.ts +56 -37
- package/src/cli.ts +50 -10
- package/src/commands/daemon.ts +19 -5
- package/src/commands/harness.ts +515 -47
- package/src/commands/launch.ts +11 -3
- package/src/commands/notify.ts +5 -1
- package/src/commands/session.ts +6 -1
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-profile-activation.ts +1 -1
- package/src/config/model-profiles.ts +23 -23
- package/src/config/settings-schema.ts +41 -1
- package/src/coordinator-mcp/model-preset.ts +100 -0
- package/src/coordinator-mcp/server.ts +2653 -425
- package/src/daemon/control-types.ts +11 -0
- package/src/daemon/operator-contract.ts +121 -0
- package/src/deep-interview/render-middleware.ts +67 -2
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +57 -17
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.ts +15 -2
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +5 -1
- package/src/gjc-runtime/launch-tmux.ts +702 -135
- package/src/gjc-runtime/session-state-sidecar.ts +756 -135
- package/src/gjc-runtime/team-runtime.ts +6 -0
- package/src/gjc-runtime/tmux-common.ts +26 -7
- package/src/gjc-runtime/tmux-gc.ts +52 -9
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +185 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +2060 -0
- package/src/gjc-runtime/tmux-sessions.ts +871 -68
- package/src/internal-urls/docs-index.generated.ts +9 -8
- package/src/internal-urls/local-protocol.ts +29 -10
- package/src/main.ts +298 -75
- package/src/modes/acp/acp-agent.ts +728 -98
- package/src/modes/acp/acp-mode.ts +29 -2
- package/src/modes/components/assistant-message.ts +32 -3
- package/src/modes/components/bash-execution.ts +43 -13
- package/src/modes/components/custom-editor.ts +5 -0
- package/src/modes/components/gajae-pet-widget.ts +360 -0
- package/src/modes/components/hook-selector.ts +12 -0
- package/src/modes/components/irc-sidebar.ts +206 -0
- package/src/modes/components/pet-selector.ts +55 -0
- package/src/modes/components/session-selector.ts +222 -221
- package/src/modes/components/settings-selector.ts +10 -0
- package/src/modes/components/tool-execution.ts +47 -6
- package/src/modes/components/user-message.ts +26 -9
- package/src/modes/controllers/command-controller.ts +28 -2
- package/src/modes/controllers/event-controller.ts +159 -36
- package/src/modes/controllers/extension-ui-controller.ts +36 -17
- package/src/modes/controllers/input-controller.ts +8 -2
- package/src/modes/controllers/selector-controller.ts +62 -15
- package/src/modes/interactive-mode.ts +183 -7
- package/src/modes/irc-observation-ledger.ts +127 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +45 -2
- package/src/modes/shared/agent-wire/unattended-session.ts +288 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +130 -20
- package/src/modes/types.ts +34 -2
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/irc-message.ts +138 -0
- package/src/modes/utils/ui-helpers.ts +216 -50
- package/src/notifications/config.ts +44 -0
- package/src/notifications/daemon-paths.ts +8 -0
- package/src/notifications/index.ts +535 -75
- package/src/notifications/lifecycle-control-runtime.ts +930 -77
- package/src/notifications/notification-service.ts +710 -0
- package/src/notifications/rate-limit-pool.ts +46 -12
- package/src/notifications/telegram-daemon-cli.ts +2 -0
- package/src/notifications/telegram-daemon-control.ts +27 -2
- package/src/notifications/telegram-daemon.ts +635 -94
- package/src/notifications/telegram-reference.ts +73 -13
- package/src/notifications/topic-registry.ts +86 -9
- package/src/prompts/system/irc-peer-roster.md +1 -0
- package/src/prompts/system/subagent-system-prompt.md +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/tools/irc.md +1 -1
- package/src/registry/agent-registry.ts +3 -0
- package/src/sdk.ts +52 -1
- package/src/secrets/obfuscator.ts +4 -2
- package/src/session/agent-session.ts +396 -145
- package/src/session/cache-economics.ts +275 -0
- package/src/session/provider-safety-stop.ts +9 -0
- package/src/session/session-dump-format.ts +6 -0
- package/src/session/session-manager.ts +809 -39
- package/src/session/session-storage.ts +582 -38
- package/src/slash-commands/builtin-registry.ts +143 -9
- package/src/task/executor.ts +12 -3
- package/src/task/index.ts +8 -0
- package/src/task/receipt.ts +7 -2
- package/src/task/types.ts +26 -0
- package/src/tools/ask-answer-registry.ts +25 -0
- package/src/tools/ask.ts +355 -33
- package/src/tools/browser/tab-supervisor.ts +63 -6
- package/src/tools/browser.ts +8 -3
- package/src/tools/index.ts +81 -6
- package/src/tools/sqlite-reader.ts +8 -4
- package/src/tui/output-block.ts +18 -6
- package/src/utils/external-editor.ts +6 -1
|
@@ -47,6 +47,15 @@ export interface DaemonOperationOptions {
|
|
|
47
47
|
spawnIfStopped?: boolean;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
export interface DaemonRecovery {
|
|
51
|
+
/** Machine-readable recovery category for automation branching. */
|
|
52
|
+
reason: "ownership_mismatch";
|
|
53
|
+
/** One-line human summary of why the operation could not proceed. */
|
|
54
|
+
summary: string;
|
|
55
|
+
/** Ordered, copy-pasteable remediation steps. */
|
|
56
|
+
steps: string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
50
59
|
export interface DaemonOperationResult {
|
|
51
60
|
kind: DaemonKind;
|
|
52
61
|
action: Exclude<DaemonAction, "list">;
|
|
@@ -55,6 +64,8 @@ export interface DaemonOperationResult {
|
|
|
55
64
|
after?: DaemonStatus;
|
|
56
65
|
warnings: string[];
|
|
57
66
|
message: string;
|
|
67
|
+
/** Present when the operation was refused with an actionable recovery path (e.g. ownership mismatch). */
|
|
68
|
+
recovery?: DaemonRecovery;
|
|
58
69
|
}
|
|
59
70
|
|
|
60
71
|
export interface BuiltInDaemonController {
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared operator contract for the `gjc daemon` command surface.
|
|
3
|
+
*
|
|
4
|
+
* One source of truth for the beginner-safe operational vocabulary so the
|
|
5
|
+
* guided human surface and the machine-readable JSON never drift:
|
|
6
|
+
* - the terse action verbs plus operator aliases (e.g. `restart` -> `reload`),
|
|
7
|
+
* - process exit codes,
|
|
8
|
+
* - concise vs. `--verbose` human rendering of statuses and results, and
|
|
9
|
+
* - actionable recovery guidance for a token/chat ownership mismatch.
|
|
10
|
+
*
|
|
11
|
+
* The full roots/debug payload lives behind `--verbose` (human) or `--json`
|
|
12
|
+
* (automation); the default human output stays a concise per-daemon result.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { DaemonAction, DaemonOperationResult, DaemonRecovery, DaemonStatus } from "./control-types";
|
|
16
|
+
|
|
17
|
+
/** Canonical daemon actions accepted as the leading verb. */
|
|
18
|
+
export const DAEMON_CANONICAL_ACTIONS: readonly DaemonAction[] = ["list", "status", "stop", "reload"];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Operator-friendly aliases resolved to a canonical action. `restart` is the
|
|
22
|
+
* routine "reload if running, otherwise start" verb; it maps onto `reload`,
|
|
23
|
+
* whose default already spawns a fresh owner when none is running.
|
|
24
|
+
*/
|
|
25
|
+
export const DAEMON_ACTION_ALIASES: Readonly<Record<string, DaemonAction>> = {
|
|
26
|
+
restart: "reload",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Every token accepted in the leading action position (canonical + aliases). */
|
|
30
|
+
export const DAEMON_ACTION_TOKENS: readonly string[] = [
|
|
31
|
+
...DAEMON_CANONICAL_ACTIONS,
|
|
32
|
+
...Object.keys(DAEMON_ACTION_ALIASES),
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/** Resolve a leading verb (canonical or alias) to its canonical action. */
|
|
36
|
+
export function resolveDaemonAction(token: string | undefined): DaemonAction | undefined {
|
|
37
|
+
if (token === undefined) return undefined;
|
|
38
|
+
if ((DAEMON_CANONICAL_ACTIONS as readonly string[]).includes(token)) return token as DaemonAction;
|
|
39
|
+
return DAEMON_ACTION_ALIASES[token];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Exit codes for `gjc daemon`. Backward compatible: success 0, any failure 1. */
|
|
43
|
+
export const DAEMON_EXIT = { ok: 0, failure: 1 } as const;
|
|
44
|
+
|
|
45
|
+
/** Human-facing headline when a spawn/reload is refused by a live foreign identity. */
|
|
46
|
+
export const OWNERSHIP_MISMATCH_MESSAGE =
|
|
47
|
+
"refused: a live Telegram daemon with a different bot token or chat already owns this workspace";
|
|
48
|
+
|
|
49
|
+
/** Structured, actionable recovery for a token/chat ownership mismatch. */
|
|
50
|
+
export function ownershipMismatchRecovery(): DaemonRecovery {
|
|
51
|
+
return {
|
|
52
|
+
reason: "ownership_mismatch",
|
|
53
|
+
summary: "A live Telegram daemon owned by a different bot token or chat already holds this workspace.",
|
|
54
|
+
steps: [
|
|
55
|
+
"Confirm the intended bot token and chat with `gjc notify` (or edit notifications.telegram in your config).",
|
|
56
|
+
"If the running daemon is stale or unwanted, stop it with `gjc daemon stop --force`, then rerun `gjc daemon restart`.",
|
|
57
|
+
"If it is the correct daemon, no action is needed — this workspace attaches automatically once the identities match.",
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function timestamp(ms: number | undefined): string | undefined {
|
|
63
|
+
if (ms === undefined) return undefined;
|
|
64
|
+
const date = new Date(ms);
|
|
65
|
+
return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Render one daemon status. Concise by default (single head line + any
|
|
70
|
+
* actionable warning); `--verbose` adds runtime detail and the full roots list.
|
|
71
|
+
*/
|
|
72
|
+
export function formatDaemonStatus(status: DaemonStatus, opts: { verbose?: boolean } = {}): string {
|
|
73
|
+
const lines: string[] = [];
|
|
74
|
+
if (!status.configured) {
|
|
75
|
+
lines.push(`${status.kind}: not configured`);
|
|
76
|
+
if (status.runtime.warning) lines.push(` warning: ${status.runtime.warning}`);
|
|
77
|
+
return lines.join("\n");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const meta: string[] = [];
|
|
81
|
+
if (status.pid !== undefined) meta.push(`pid ${status.pid}`);
|
|
82
|
+
if (status.ownerId) meta.push(`owner ${status.ownerId}`);
|
|
83
|
+
const rootCount = status.rootCount ?? status.roots?.length ?? 0;
|
|
84
|
+
if (rootCount > 0) meta.push(`${rootCount} root${rootCount === 1 ? "" : "s"}`);
|
|
85
|
+
|
|
86
|
+
let head = `${status.kind}: ${status.health}`;
|
|
87
|
+
if (meta.length > 0) head += ` (${meta.join(", ")})`;
|
|
88
|
+
if (status.detail) head += ` — ${status.detail}`;
|
|
89
|
+
lines.push(head);
|
|
90
|
+
if (status.runtime.warning) lines.push(` warning: ${status.runtime.warning}`);
|
|
91
|
+
|
|
92
|
+
if (opts.verbose) {
|
|
93
|
+
lines.push(` runtime: ${status.runtime.mode} (${status.runtime.execPath})`);
|
|
94
|
+
const started = timestamp(status.startedAt);
|
|
95
|
+
if (started) lines.push(` started: ${started}`);
|
|
96
|
+
const heartbeat = timestamp(status.heartbeatAt);
|
|
97
|
+
if (heartbeat) lines.push(` heartbeat: ${heartbeat}`);
|
|
98
|
+
const roots = status.roots ?? [];
|
|
99
|
+
lines.push(` roots: ${rootCount}`);
|
|
100
|
+
for (const root of roots) lines.push(` - ${root}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return lines.join("\n");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Render one operation result. Keeps the stable `<kind> <action>: ok|failed —
|
|
108
|
+
* <message>` head line, then any warnings, then actionable recovery steps.
|
|
109
|
+
*/
|
|
110
|
+
export function formatDaemonResult(result: DaemonOperationResult): string {
|
|
111
|
+
const lines = [`${result.kind} ${result.action}: ${result.ok ? "ok" : "failed"} — ${result.message}`];
|
|
112
|
+
for (const warning of result.warnings) lines.push(` warning: ${warning}`);
|
|
113
|
+
if (result.recovery) {
|
|
114
|
+
lines.push(` ${result.recovery.summary}`);
|
|
115
|
+
lines.push(" to recover:");
|
|
116
|
+
result.recovery.steps.forEach((step, i) => {
|
|
117
|
+
lines.push(` ${i + 1}. ${step}`);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return lines.join("\n");
|
|
121
|
+
}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type Component,
|
|
3
|
+
Container,
|
|
4
|
+
isViewportAnchorSourceRenderer,
|
|
5
|
+
Markdown,
|
|
6
|
+
Spacer,
|
|
7
|
+
Text,
|
|
8
|
+
type ViewportAnchorProvider,
|
|
9
|
+
type ViewportAnchorRender,
|
|
10
|
+
type ViewportAnchorSource,
|
|
11
|
+
type ViewportAnchorSourceRenderer,
|
|
12
|
+
} from "@gajae-code/tui";
|
|
2
13
|
import { getMarkdownTheme, type Theme } from "../modes/theme/theme";
|
|
3
14
|
|
|
4
15
|
interface RoundQuestionModel {
|
|
@@ -48,6 +59,60 @@ interface ThresholdModel {
|
|
|
48
59
|
|
|
49
60
|
type DeepInterviewModel = RoundQuestionModel | TopologyQuestionModel | ProgressModel | ThresholdModel;
|
|
50
61
|
|
|
62
|
+
class DeepInterviewSemanticContainer extends Container implements ViewportAnchorProvider, ViewportAnchorSourceRenderer {
|
|
63
|
+
#nextSource = 0;
|
|
64
|
+
|
|
65
|
+
override addChild(component: Component): void {
|
|
66
|
+
super.addChild(component);
|
|
67
|
+
if (isViewportAnchorSourceRenderer(component)) {
|
|
68
|
+
this.setViewportAnchorSource(component, { id: `deep-interview-part:${this.#nextSource++}` });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
override renderWithViewportAnchors(width: number): ViewportAnchorRender {
|
|
73
|
+
const lines = super.renderWithViewportAnchors(width).lines;
|
|
74
|
+
return { lines, anchors: lines.map(() => null) };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
renderWithViewportAnchorSource(width: number, source: ViewportAnchorSource): ViewportAnchorRender {
|
|
78
|
+
const rendered = super.renderWithViewportAnchors(width);
|
|
79
|
+
const extents = new Map<string, { graphemeEnd: number; cellEnd: number }>();
|
|
80
|
+
for (const anchor of rendered.anchors) {
|
|
81
|
+
if (anchor === null) continue;
|
|
82
|
+
const extent = extents.get(anchor.id);
|
|
83
|
+
if (extent) {
|
|
84
|
+
extent.graphemeEnd = Math.max(extent.graphemeEnd, anchor.graphemeEnd);
|
|
85
|
+
extent.cellEnd = Math.max(extent.cellEnd, anchor.cellEnd);
|
|
86
|
+
} else {
|
|
87
|
+
extents.set(anchor.id, { graphemeEnd: anchor.graphemeEnd, cellEnd: anchor.cellEnd });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const offsets = new Map<string, { grapheme: number; cell: number }>();
|
|
91
|
+
let grapheme = 0;
|
|
92
|
+
let cell = 0;
|
|
93
|
+
for (const [id, extent] of extents) {
|
|
94
|
+
offsets.set(id, { grapheme, cell });
|
|
95
|
+
grapheme += extent.graphemeEnd;
|
|
96
|
+
cell += extent.cellEnd;
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
lines: rendered.lines,
|
|
100
|
+
anchors: rendered.anchors.map(anchor => {
|
|
101
|
+
if (anchor === null) return null;
|
|
102
|
+
const offset = offsets.get(anchor.id);
|
|
103
|
+
if (!offset) throw new Error(`Missing deep-interview anchor offset for ${anchor.id}`);
|
|
104
|
+
return {
|
|
105
|
+
id: source.id,
|
|
106
|
+
graphemeStart: offset.grapheme + anchor.graphemeStart,
|
|
107
|
+
graphemeEnd: offset.grapheme + anchor.graphemeEnd,
|
|
108
|
+
cellStart: offset.cell + anchor.cellStart,
|
|
109
|
+
cellEnd: offset.cell + anchor.cellEnd,
|
|
110
|
+
};
|
|
111
|
+
}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
51
116
|
function normalizeText(text: string): string {
|
|
52
117
|
if (typeof text !== "string") return "";
|
|
53
118
|
return text.trim().replaceAll("\r\n", "\n");
|
|
@@ -258,7 +323,7 @@ function renderPipeSummary(title: string, value: string | undefined): string | u
|
|
|
258
323
|
}
|
|
259
324
|
|
|
260
325
|
function renderModel(model: DeepInterviewModel, uiTheme: Theme): Component {
|
|
261
|
-
const container = new
|
|
326
|
+
const container = new DeepInterviewSemanticContainer();
|
|
262
327
|
if (model.kind === "round-question") {
|
|
263
328
|
const meta = [
|
|
264
329
|
`Round ${model.round}`,
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Model definitions for Grok CLI's API.
|
|
3
3
|
*/
|
|
4
|
+
import { Effort } from '@gajae-code/ai/model-thinking';
|
|
4
5
|
|
|
5
6
|
// ─── Cost constants ($/M tokens) ──────────────────────────────────────────────
|
|
6
7
|
|
|
7
8
|
const COST_BUILD = { input: 1, output: 2, cacheRead: 0.2, cacheWrite: 0.2 };
|
|
8
9
|
const COST_COMPOSER_FAST = { input: 3, output: 15, cacheRead: 0.5, cacheWrite: 0 };
|
|
9
10
|
const COST_43 = { input: 1.25, output: 2.5, cacheRead: 0.2, cacheWrite: 0 };
|
|
11
|
+
const COST_45 = { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 };
|
|
10
12
|
const COST_420 = { input: 2, output: 6, cacheRead: 0.2, cacheWrite: 0 };
|
|
11
13
|
|
|
12
14
|
// ─── Model type ───────────────────────────────────────────────────────────────
|
|
@@ -24,6 +26,8 @@ export interface GrokCliModelConfig {
|
|
|
24
26
|
};
|
|
25
27
|
contextWindow: number;
|
|
26
28
|
maxTokens: number;
|
|
29
|
+
/** Highest reasoning effort supported by this model. */
|
|
30
|
+
maxReasoningEffort?: Effort;
|
|
27
31
|
/** Models that don't support reasoning.effort get a thinkingLevelMap. */
|
|
28
32
|
thinkingLevelMap?: Record<string, string | null>;
|
|
29
33
|
}
|
|
@@ -69,6 +73,18 @@ const FALLBACK_MODELS: GrokCliModelConfig[] = [
|
|
|
69
73
|
contextWindow: 1_000_000,
|
|
70
74
|
maxTokens: 30_000,
|
|
71
75
|
},
|
|
76
|
+
{
|
|
77
|
+
// Official metadata and pricing: https://docs.x.ai/developers/models/grok-4.5
|
|
78
|
+
id: 'grok-4.5',
|
|
79
|
+
name: 'Grok 4.5',
|
|
80
|
+
reasoning: true,
|
|
81
|
+
input: ['text', 'image'],
|
|
82
|
+
cost: COST_45,
|
|
83
|
+
contextWindow: 500_000,
|
|
84
|
+
maxTokens: 30_000,
|
|
85
|
+
// https://docs.x.ai/developers/model-capabilities/text/reasoning caps Grok 4.5 at high.
|
|
86
|
+
maxReasoningEffort: Effort.High,
|
|
87
|
+
},
|
|
72
88
|
{
|
|
73
89
|
id: 'grok-4.20-0309-reasoning',
|
|
74
90
|
name: 'Grok 4.20 Reasoning',
|
|
@@ -106,15 +122,37 @@ const FALLBACK_MODELS: GrokCliModelConfig[] = [
|
|
|
106
122
|
},
|
|
107
123
|
];
|
|
108
124
|
|
|
109
|
-
|
|
125
|
+
// Official aliases: https://docs.x.ai/developers/models/grok-4.5
|
|
126
|
+
const MODEL_ALIASES: Readonly<Record<string, string>> = {
|
|
127
|
+
'grok-4.5-latest': 'grok-4.5',
|
|
128
|
+
'grok-build-latest': 'grok-4.5',
|
|
129
|
+
};
|
|
110
130
|
|
|
111
|
-
|
|
131
|
+
function getModelName(modelId: string): string {
|
|
112
132
|
const parts = modelId.split('/');
|
|
113
|
-
|
|
133
|
+
return (parts.at(-1) ?? modelId).toLowerCase();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getCanonicalModelName(modelId: string): string {
|
|
137
|
+
const name = getModelName(modelId);
|
|
138
|
+
return MODEL_ALIASES[name] ?? name;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function getMaxReasoningEffort(modelId: string): Effort | undefined {
|
|
142
|
+
const name = getCanonicalModelName(modelId);
|
|
143
|
+
return FALLBACK_MODELS.find(
|
|
144
|
+
(model) => name === model.id.toLowerCase() || name.startsWith(`${model.id.toLowerCase()}-`),
|
|
145
|
+
)?.maxReasoningEffort;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const EFFORT_CAPABLE_PREFIXES = ['grok-3-mini', 'grok-4.20-multi-agent', 'grok-4.3', 'grok-4.5'];
|
|
149
|
+
|
|
150
|
+
export function supportsReasoningEffort(modelId: string): boolean {
|
|
151
|
+
const name = getCanonicalModelName(modelId);
|
|
114
152
|
if (!EFFORT_CAPABLE_PREFIXES.some((prefix) => name.startsWith(prefix))) {
|
|
115
153
|
return false;
|
|
116
154
|
}
|
|
117
|
-
const model = resolveModels().find((entry) => entry.id
|
|
155
|
+
const model = resolveModels().find((entry) => getCanonicalModelName(entry.id) === name);
|
|
118
156
|
if (model) {
|
|
119
157
|
if (!model.reasoning) return false;
|
|
120
158
|
if (!model.thinkingLevelMap) return true;
|
|
@@ -139,17 +177,19 @@ export function resolveModels(): GrokCliModelConfig[] {
|
|
|
139
177
|
.filter(Boolean);
|
|
140
178
|
if (env.length === 0) return FALLBACK_MODELS;
|
|
141
179
|
|
|
142
|
-
const byId = new Map(FALLBACK_MODELS.map((
|
|
143
|
-
return env.map(
|
|
144
|
-
(id)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
180
|
+
const byId = new Map(FALLBACK_MODELS.map((model) => [model.id.toLowerCase(), model]));
|
|
181
|
+
return env.map((id) => {
|
|
182
|
+
const model = byId.get(getCanonicalModelName(id));
|
|
183
|
+
if (model) return model.id === id ? model : { ...model, id };
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
id,
|
|
187
|
+
name: id,
|
|
188
|
+
reasoning: true,
|
|
189
|
+
input: ['text'] as ('text' | 'image')[],
|
|
190
|
+
cost: COST_BUILD,
|
|
191
|
+
contextWindow: 1_000_000,
|
|
192
|
+
maxTokens: 30_000,
|
|
193
|
+
};
|
|
194
|
+
});
|
|
155
195
|
}
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
23
23
|
import { extname, isAbsolute, resolve, sep } from 'node:path';
|
|
24
24
|
import { fileURLToPath } from 'node:url';
|
|
25
|
-
import {
|
|
25
|
+
import { Effort, THINKING_EFFORTS } from '@gajae-code/ai/model-thinking';
|
|
26
|
+
import { getMaxReasoningEffort, supportsReasoningEffort } from '../models/catalog.js';
|
|
26
27
|
|
|
27
28
|
// ─── Content text extraction ─────────────────────────────────────────────────
|
|
28
29
|
|
|
@@ -266,6 +267,18 @@ function stripUnsupportedTopLevelFields(next: Record<string, unknown>): void {
|
|
|
266
267
|
delete next.truncation;
|
|
267
268
|
}
|
|
268
269
|
|
|
270
|
+
function normalizeReasoningEffort(modelId: string, value: unknown): unknown {
|
|
271
|
+
const effort = value === Effort.Minimal ? Effort.Low : value;
|
|
272
|
+
if (typeof effort !== 'string') return effort;
|
|
273
|
+
|
|
274
|
+
const maxEffort = getMaxReasoningEffort(modelId);
|
|
275
|
+
if (maxEffort === undefined) return effort;
|
|
276
|
+
|
|
277
|
+
const effortIndex = THINKING_EFFORTS.indexOf(effort as Effort);
|
|
278
|
+
const maxEffortIndex = THINKING_EFFORTS.indexOf(maxEffort);
|
|
279
|
+
return effortIndex > maxEffortIndex ? maxEffort : effort;
|
|
280
|
+
}
|
|
281
|
+
|
|
269
282
|
// ─── Main sanitization ────────────────────────────────────────────────────────
|
|
270
283
|
|
|
271
284
|
/**
|
|
@@ -334,7 +347,7 @@ export function sanitizePayload(
|
|
|
334
347
|
if (supportsReasoningEffort(modelId)) {
|
|
335
348
|
const reasoning = next.reasoning as Record<string, unknown> | undefined;
|
|
336
349
|
if (reasoning) {
|
|
337
|
-
const effort =
|
|
350
|
+
const effort = normalizeReasoningEffort(modelId, reasoning.effort);
|
|
338
351
|
next.reasoning = reasoning.summary !== undefined ? { effort } : { ...reasoning, effort };
|
|
339
352
|
}
|
|
340
353
|
} else {
|
|
@@ -36,7 +36,11 @@ export default function registerGrokCli(api: ExtensionAPI) {
|
|
|
36
36
|
name: m.name,
|
|
37
37
|
reasoning: m.reasoning,
|
|
38
38
|
thinking: m.reasoning
|
|
39
|
-
? {
|
|
39
|
+
? {
|
|
40
|
+
minLevel: Effort.Low,
|
|
41
|
+
maxLevel: m.maxReasoningEffort ?? Effort.XHigh,
|
|
42
|
+
mode: 'effort',
|
|
43
|
+
}
|
|
40
44
|
: undefined,
|
|
41
45
|
input: m.input,
|
|
42
46
|
cost: m.cost,
|