jeo-code 0.12.0 → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.ja.md +26 -5
- package/README.ko.md +25 -5
- package/README.md +25 -5
- package/README.zh.md +26 -5
- package/package.json +1 -1
- package/src/agent/antigravity-cli.ts +499 -0
- package/src/agent/session.ts +25 -0
- package/src/ai/model-cache.ts +7 -2
- package/src/ai/model-catalog.ts +4 -2
- package/src/ai/model-discovery.ts +89 -29
- package/src/ai/model-manager.ts +7 -0
- package/src/ai/provider-ranking.ts +2 -2
- package/src/ai/provider-status.ts +21 -2
- package/src/ai/providers/custom-providers.ts +1 -1
- package/src/ai/types.ts +1 -1
- package/src/commands/auth.ts +7 -1
- package/src/commands/doctor.ts +18 -1
- package/src/commands/launch/agy-turn.ts +60 -0
- package/src/commands/launch.ts +111 -4
- package/src/util/provider-error.ts +3 -0
package/src/commands/launch.ts
CHANGED
|
@@ -54,6 +54,10 @@ import chalk from "chalk";
|
|
|
54
54
|
import { callLlm, type Message } from "../agent/loop";
|
|
55
55
|
import { friendlyProviderError } from "../util/provider-error";
|
|
56
56
|
import { readGlobalConfig, saveConfigPatch, resolveWikiRoot } from "../agent/state";
|
|
57
|
+
import { AgyCliError, type AgyTurnResult } from "../agent/antigravity-cli";
|
|
58
|
+
import { runPreToolHooks, runPostTurnHooks } from "../agent/hooks";
|
|
59
|
+
import { runAgySessionTurn } from "./launch/agy-turn";
|
|
60
|
+
import { listProviderModels } from "../ai/model-discovery";
|
|
57
61
|
import { rememberModelPatch, recentModelsForDisplay } from "../agent/model-recency";
|
|
58
62
|
import { describeModel, describeAllProviders, describeProvider, resolveProvider, thinkingMaxTokens, resolveMaxOutputTokens, thinkingToReasoningEffort, discoverModels, flattenModels, resolveSelection, catalogMetadata, catalogByProvider, liveProviderCatalogModels, resolveRoleModel, CODEX_MODELS, qualifyModelId, modelServableWithConfig, isLocalProviderReachable } from "../ai";
|
|
59
63
|
import { rehydrateLiveModels, writeModelCache } from "../ai/model-cache";
|
|
@@ -350,7 +354,7 @@ export function providerPickEntries(live: ProviderModelsResult[], want: Provider
|
|
|
350
354
|
if (fromLive.length) return fromLive;
|
|
351
355
|
// An OpenAI or Antigravity empty/error response is authoritative for this picker.
|
|
352
356
|
// Offering the static capability catalog here would fabricate account availability.
|
|
353
|
-
if (want === "openai" || want === "antigravity") return [];
|
|
357
|
+
if (want === "openai" || want === "antigravity" || want === "agy") return [];
|
|
354
358
|
const catalog = catalogByProvider(want);
|
|
355
359
|
if (catalog.length) {
|
|
356
360
|
return catalog.map((m, i) => ({ index: i + 1, provider: want, model: qualifyModelId(m.providerModel, want) }));
|
|
@@ -420,9 +424,18 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
420
424
|
} catch {}
|
|
421
425
|
const cfg = await readGlobalConfig();
|
|
422
426
|
const defaultModel = cfg.defaultModel;
|
|
423
|
-
|
|
427
|
+
let initialSessionModel =
|
|
424
428
|
flags.model ??
|
|
425
429
|
(flags.modelRole ? resolveRoleModel(flags.modelRole, cfg) : flags.provider ? providerDefaultModel(flags.provider) : undefined);
|
|
430
|
+
if (flags.provider === "agy" && !flags.model && !flags.modelRole) {
|
|
431
|
+
const available = await listProviderModels("agy", { config: cfg });
|
|
432
|
+
if (!available.ok || !available.models.length) {
|
|
433
|
+
console.error(sanitizeForTerminal(`error: ${available.error ?? "the official agy CLI returned no available models"}`));
|
|
434
|
+
process.exitCode = 1;
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
initialSessionModel = available.models[0];
|
|
438
|
+
}
|
|
426
439
|
if (flags.provider && initialSessionModel) {
|
|
427
440
|
const { provider } = await describeModel(initialSessionModel);
|
|
428
441
|
if (provider !== flags.provider) {
|
|
@@ -955,6 +968,8 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
955
968
|
const subagentRegistry = new SubagentRegistry();
|
|
956
969
|
const jobRegistry = new JobRegistry();
|
|
957
970
|
let activeTui: LaunchTui | null = null;
|
|
971
|
+
let activeAgyController: AbortController | undefined;
|
|
972
|
+
let activeAgyTurn: Promise<AgyTurnResult> | undefined;
|
|
958
973
|
const monitorRegistry = new MonitorRegistry({
|
|
959
974
|
onLine: (record, line) => {
|
|
960
975
|
const notice = `[monitor ${record.id}] ${line}`;
|
|
@@ -968,6 +983,8 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
968
983
|
const cleanupSession = async (): Promise<void> => {
|
|
969
984
|
if (cleanedUp) return;
|
|
970
985
|
cleanedUp = true;
|
|
986
|
+
activeAgyController?.abort();
|
|
987
|
+
await activeAgyTurn?.catch(() => undefined);
|
|
971
988
|
subagentRegistry.cancelAll();
|
|
972
989
|
jobRegistry.cancelAll();
|
|
973
990
|
monitorRegistry.cancelAll();
|
|
@@ -1048,6 +1065,96 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
1048
1065
|
// gjc-style); a string → show that compact label instead of the raw input.
|
|
1049
1066
|
opts?: { userCard?: string | null },
|
|
1050
1067
|
): Promise<{ done: boolean; steps: number; reply: string; rendered: boolean; usage: string }> => {
|
|
1068
|
+
const selectedModel = sessionModel || defaultModel;
|
|
1069
|
+
if (selectedModel.startsWith("agy/")) {
|
|
1070
|
+
const oneShot = !!flags.print || flags.message.length > 0 || !process.stdin.isTTY;
|
|
1071
|
+
if (flags.noTools || flags.tools || flags.maxSteps > 0 || flags.systemPrompt || flags.appendSystemPrompt || flags.thinking) {
|
|
1072
|
+
if (oneShot) process.exitCode = 1;
|
|
1073
|
+
throw new AgyCliError("The official agy agent owns its tools, system prompt, step budget, and reasoning settings. Remove jeo's raw-model tool/system/step/thinking flags and select an exact agy model slug instead.", "protocol", 1);
|
|
1074
|
+
}
|
|
1075
|
+
const cliTui = useTui ? new LaunchTui({ model: selectedModel, provider: "agy", sessionId, maxSteps: 0, cwd, branch }) : null;
|
|
1076
|
+
const notice = (message: string): void => {
|
|
1077
|
+
const safe = sanitizeForTerminal(message);
|
|
1078
|
+
if (cliTui) cliTui.events().onNotice?.(safe);
|
|
1079
|
+
else console.error(safe);
|
|
1080
|
+
};
|
|
1081
|
+
let hardExit = false;
|
|
1082
|
+
const harness = createInFlightAbortHarness({
|
|
1083
|
+
captureEsc: !!cliTui,
|
|
1084
|
+
onAbortNotice: notice,
|
|
1085
|
+
onNoise: () => cliTui?.repaint(),
|
|
1086
|
+
pasteActive: () => queueBusyPasteActive?.() ?? false,
|
|
1087
|
+
onBufferedInput: chunk => {
|
|
1088
|
+
if (queueBusyInput?.(chunk) && jeoEnv("NO_LIVE_DRAFT") !== "1") {
|
|
1089
|
+
cliTui?.setLivePromptInput(expandSentinel(queueBusySnapshot?.().text ?? ""));
|
|
1090
|
+
}
|
|
1091
|
+
},
|
|
1092
|
+
onHardExit: () => { hardExit = true; },
|
|
1093
|
+
});
|
|
1094
|
+
activeAgyController = harness.controller;
|
|
1095
|
+
activeTui = cliTui;
|
|
1096
|
+
interactiveTurnActive = !!cliTui;
|
|
1097
|
+
lastUserInput = userInput;
|
|
1098
|
+
lastTurnReasoning = "";
|
|
1099
|
+
lastTurnArtifacts = [];
|
|
1100
|
+
try {
|
|
1101
|
+
cliTui?.start();
|
|
1102
|
+
cliTui?.setTurnTitle(userInput);
|
|
1103
|
+
const card = opts?.userCard === undefined ? userInput : opts.userCard;
|
|
1104
|
+
if (card?.trim()) cliTui?.flushUserCard(card);
|
|
1105
|
+
notice("Official agy agent: uses its own tools, sandbox, and permissions; jeo approvals do not mediate its inner tool calls.");
|
|
1106
|
+
const pre = await runPreToolHooks(cwd, "agy", { model: selectedModel, prompt: userInput }, harness.controller.signal, notice);
|
|
1107
|
+
if (pre.vetoed) throw new AgyCliError(pre.error ?? "Official CLI delegation blocked by a jeo hook", "upstream", 1);
|
|
1108
|
+
sessionNotifyEndpoint?.sendContextUpdate("turn_start", notifyRedact ? "[redacted]" : userInput.slice(0, 200), { model: selectedModel });
|
|
1109
|
+
let streamed = "";
|
|
1110
|
+
activeAgyTurn = runAgySessionTurn({
|
|
1111
|
+
model: selectedModel, prompt: userInput, cwd, history, sessionId, images,
|
|
1112
|
+
signal: harness.controller.signal,
|
|
1113
|
+
mode: flags.modelRole === "plan" ? "plan" : undefined,
|
|
1114
|
+
onNotice: notice,
|
|
1115
|
+
onText: cliTui ? text => {
|
|
1116
|
+
streamed += sanitizeForTerminal(text);
|
|
1117
|
+
cliTui.events().onModelStream?.(streamed);
|
|
1118
|
+
} : undefined,
|
|
1119
|
+
});
|
|
1120
|
+
const result = await activeAgyTurn;
|
|
1121
|
+
const post = await runPostTurnHooks(cwd, "agy", { model: selectedModel, prompt: userInput }, true, result.response, harness.controller.signal, notice);
|
|
1122
|
+
if (post.diags.length) throw new AgyCliError(`Official CLI completed, but a jeo post-turn hook failed: ${post.diags.map(diag => diag.output).join("\n")}`, "upstream", 1);
|
|
1123
|
+
const reply = sanitizeForTerminal(result.response);
|
|
1124
|
+
lastReply = result.response;
|
|
1125
|
+
lastActiveModel = selectedModel;
|
|
1126
|
+
if (result.usage) {
|
|
1127
|
+
sessionUsage.inputTokens += result.usage.inputTokens;
|
|
1128
|
+
sessionUsage.outputTokens += result.usage.outputTokens;
|
|
1129
|
+
cliTui?.events().onUsage?.(result.usage);
|
|
1130
|
+
}
|
|
1131
|
+
sessionUsage.turns++;
|
|
1132
|
+
const usage = result.usage ? `\n${formatUsage(result.usage)}` : "";
|
|
1133
|
+
sessionNotifyEndpoint?.sendContextUpdate("turn_end", notifyRedact ? "[redacted]" : reply.slice(0, 200), { model: selectedModel, usage });
|
|
1134
|
+
sessionNotifyEndpoint?.sendTurnStream(notifyRedact ? "[redacted]" : reply);
|
|
1135
|
+
cliTui?.events().onStep?.(result.steps);
|
|
1136
|
+
cliTui?.finish(reply);
|
|
1137
|
+
return { done: true, steps: result.steps, reply, rendered: !!cliTui, usage };
|
|
1138
|
+
} catch (error) {
|
|
1139
|
+
if (oneShot) {
|
|
1140
|
+
process.exitCode = error instanceof AgyCliError
|
|
1141
|
+
? (error.kind === "cancelled" ? 130 : Math.max(1, error.exitCode ?? 1))
|
|
1142
|
+
: 1;
|
|
1143
|
+
}
|
|
1144
|
+
cliTui?.finish(`! ${friendlyProviderError(error)}`, { ok: false });
|
|
1145
|
+
throw error;
|
|
1146
|
+
} finally {
|
|
1147
|
+
harness.dispose();
|
|
1148
|
+
activeAgyTurn = undefined;
|
|
1149
|
+
activeAgyController = undefined;
|
|
1150
|
+
activeTui = null;
|
|
1151
|
+
interactiveTurnActive = false;
|
|
1152
|
+
if (hardExit) {
|
|
1153
|
+
process.exitCode = 130;
|
|
1154
|
+
if (!oneShot) forceExitFromCtrlC();
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1051
1158
|
const turnConfig = await readGlobalConfig();
|
|
1052
1159
|
// Fall back to the model resolved at THIS session's start (frozen snapshot), NOT
|
|
1053
1160
|
// the live global default. A concurrent `jeo` session running `/model` persists the
|
|
@@ -1965,7 +2072,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
1965
2072
|
// > 0) and stays silent (no stdout noise) since one-shot output may be
|
|
1966
2073
|
// piped/parsed by a script.
|
|
1967
2074
|
const maybeDistillOneShot = async (): Promise<void> => {
|
|
1968
|
-
if (sessionUsage.turns > 0) {
|
|
2075
|
+
if (sessionUsage.turns > 0 && !(sessionModel || defaultModel).startsWith("agy/")) {
|
|
1969
2076
|
await spawnDetachedDistill(history, cwd, sessionModel || defaultModel);
|
|
1970
2077
|
}
|
|
1971
2078
|
};
|
|
@@ -5152,7 +5259,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
5152
5259
|
// hermes-style experience distill (plan/gjc-inheritance.md B6) — now handed to a
|
|
5153
5260
|
// DETACHED child (round-16): /exit and ^C^C return the shell IMMEDIATELY instead
|
|
5154
5261
|
// of blocking up to 20s on a final LLM call. The child writes MEMORY.md atomically.
|
|
5155
|
-
if (sessionUsage.turns > 0) {
|
|
5262
|
+
if (sessionUsage.turns > 0 && !(sessionModel || defaultModel).startsWith("agy/")) {
|
|
5156
5263
|
const spawned = await spawnDetachedDistill(history, cwd, sessionModel || defaultModel);
|
|
5157
5264
|
if (spawned) console.log(chalk.gray("(session memory distilling in background)"));
|
|
5158
5265
|
}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
import { isUsageLimitError, isRefusalError, isConnectionError, ConnectionContextError } from "./retry";
|
|
8
8
|
import { companyLabel } from "../ai/model-catalog";
|
|
9
9
|
import { ProviderHttpError, ProviderStreamError } from "../ai/providers/errors";
|
|
10
|
+
import { AgyCliError } from "../agent/antigravity-cli";
|
|
11
|
+
import { sanitizeForTerminal } from "../tui/components/code-view";
|
|
10
12
|
// Re-export: engine.ts and callers import the refusal predicate from here; the
|
|
11
13
|
// definition lives in retry.ts so defaultRetryable can fail fast without a cycle.
|
|
12
14
|
export { isRefusalError } from "./retry";
|
|
@@ -71,6 +73,7 @@ function retryAfterMsOf(err: unknown): number | undefined {
|
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
export function friendlyProviderError(err: unknown): string {
|
|
76
|
+
if (err instanceof AgyCliError) return sanitizeForTerminal(err.message);
|
|
74
77
|
if (err instanceof ConnectionContextError) {
|
|
75
78
|
const provider = companyLabel(err.provider);
|
|
76
79
|
const target = err.baseUrl ? ` at ${err.baseUrl}` : "";
|