large update

This commit is contained in:
ZacharyZhang-NY committed 2026-03-29 14:08:15 -04:00
1 parent 2ed952f19c
commit 65e85135c1
111 files changed
+3936 -2933

No files matched your search

@@ -28,7 +28,7 @@ import {
isGeminiUnknownSessionError,
parseGeminiJsonl,
} from "./parse.js";
import { firstNonEmptyLine } from "./utils.js";
import { firstNonEmptyLine } from "@agentcompany/adapter-utils/test-utils";
const __moduleDir = path.dirname(fileURLToPath(import.meta.url));
const SKILLS_CANDIDATES = [
@@ -276,7 +276,9 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
}
}
const commandNotes = (() => {
const notes: string[] = ["Prompt is passed to Gemini as the final positional argument."];
const notes: string[] = [useStdin
? "Prompt passed via stdin (exceeds command-line length limit)."
: "Prompt is passed to Gemini as the final positional argument."];
notes.push("Added --approval-mode yolo for unattended execution.");
if (!instructionsFilePath) return notes;
if (instructionsPrefix.length > 0) {
@@ -305,6 +307,18 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
const apiAccessNote = renderApiAccessNote(env);
const prompt = `${instructionsPrefix}${envNote}${apiAccessNote}${renderedPrompt}`;
// When the prompt is short enough for a command-line argument, pass it
// directly as a positional arg (Gemini CLI's native interface). When it
// exceeds the OS limit (~32k chars on Windows), write it to a temp file
// and pass the content via stdin to avoid ENAMETOOLONG.
const SAFE_ARG_LIMIT = 30_000;
const useStdin = prompt.length > SAFE_ARG_LIMIT;
let promptFilePath: string | null = null;
if (useStdin) {
promptFilePath = path.join(os.tmpdir(), `agentcompany-gemini-prompt-${runId}.txt`);
await fs.writeFile(promptFilePath, prompt, "utf8");
}
const buildArgs = (resumeSessionId: string | null) => {
const args = ["--output-format", "stream-json"];
if (resumeSessionId) args.push("--resume", resumeSessionId);
@@ -316,8 +330,6 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
args.push("--sandbox=none");
}
if (extraArgs.length > 0) {
// Filter out flags the adapter already manages to prevent duplicates
// (e.g. user shell aliases that inject --approval-mode or --sandbox)
const managedFlags = [
"--approval-mode",
"--output-format",
@@ -327,7 +339,9 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
const cleaned = filterConflictingArgs(extraArgs, managedFlags);
if (cleaned.length > 0) args.push(...cleaned);
}
args.push(prompt);
if (!useStdin) {
args.push(prompt);
}
return args;
};
@@ -354,6 +368,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
timeoutSec,
graceSec,
onLog,
...(useStdin ? { stdin: prompt } : {}),
});
return {
proc,
@@ -442,20 +457,27 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
};
};
const initial = await runAttempt(sessionId);
if (
sessionId &&
!initial.proc.timedOut &&
(initial.proc.exitCode ?? 0) !== 0 &&
isGeminiUnknownSessionError(initial.proc.stdout, initial.proc.stderr)
) {
await onLog(
"stderr",
`[agent-company] Gemini resume session "${sessionId}" is unavailable; retrying with a fresh session.\n`,
);
const retry = await runAttempt(null);
return toResult(retry, true, true);
}
try {
const initial = await runAttempt(sessionId);
if (
sessionId &&
!initial.proc.timedOut &&
(initial.proc.exitCode ?? 0) !== 0 &&
isGeminiUnknownSessionError(initial.proc.stdout, initial.proc.stderr)
) {
await onLog(
"stderr",
`[agent-company] Gemini resume session "${sessionId}" is unavailable; retrying with a fresh session.\n`,
);
const retry = await runAttempt(null);
return toResult(retry, true, true);
}
return toResult(initial);
return toResult(initial);
} finally {
// Clean up temp prompt file if used
if (promptFilePath) {
await fs.unlink(promptFilePath).catch(() => {});
}
}
}
@@ -8,64 +8,6 @@ export {
isGeminiTurnLimitResult,
} from "./parse.js";
export { listGeminiSkills, syncGeminiSkills } from "./skills.js";
import type { AdapterSessionCodec } from "@agentcompany/adapter-utils";
import { createStandardSessionCodec } from "@agentcompany/adapter-utils/session-codec";
function readNonEmptyString(value: unknown): string | null {
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
}
export const sessionCodec: AdapterSessionCodec = {
deserialize(raw: unknown) {
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
const record = raw as Record<string, unknown>;
const sessionId =
readNonEmptyString(record.sessionId) ??
readNonEmptyString(record.session_id) ??
readNonEmptyString(record.sessionID);
if (!sessionId) return null;
const cwd =
readNonEmptyString(record.cwd) ??
readNonEmptyString(record.workdir) ??
readNonEmptyString(record.folder);
const workspaceId = readNonEmptyString(record.workspaceId) ?? readNonEmptyString(record.workspace_id);
const repoUrl = readNonEmptyString(record.repoUrl) ?? readNonEmptyString(record.repo_url);
const repoRef = readNonEmptyString(record.repoRef) ?? readNonEmptyString(record.repo_ref);
return {
sessionId,
...(cwd ? { cwd } : {}),
...(workspaceId ? { workspaceId } : {}),
...(repoUrl ? { repoUrl } : {}),
...(repoRef ? { repoRef } : {}),
};
},
serialize(params: Record<string, unknown> | null) {
if (!params) return null;
const sessionId =
readNonEmptyString(params.sessionId) ??
readNonEmptyString(params.session_id) ??
readNonEmptyString(params.sessionID);
if (!sessionId) return null;
const cwd =
readNonEmptyString(params.cwd) ??
readNonEmptyString(params.workdir) ??
readNonEmptyString(params.folder);
const workspaceId = readNonEmptyString(params.workspaceId) ?? readNonEmptyString(params.workspace_id);
const repoUrl = readNonEmptyString(params.repoUrl) ?? readNonEmptyString(params.repo_url);
const repoRef = readNonEmptyString(params.repoRef) ?? readNonEmptyString(params.repo_ref);
return {
sessionId,
...(cwd ? { cwd } : {}),
...(workspaceId ? { workspaceId } : {}),
...(repoUrl ? { repoUrl } : {}),
...(repoRef ? { repoRef } : {}),
};
},
getDisplayId(params: Record<string, unknown> | null) {
if (!params) return null;
return (
readNonEmptyString(params.sessionId) ??
readNonEmptyString(params.session_id) ??
readNonEmptyString(params.sessionID)
);
},
};
export const sessionCodec = createStandardSessionCodec({ extraSessionIdKeys: ["sessionID"] });
@@ -1,4 +1,3 @@
import path from "node:path";
import type {
AdapterEnvironmentCheck,
AdapterEnvironmentTestContext,
@@ -14,32 +13,9 @@ import {
parseObject,
runChildProcess,
} from "@agentcompany/adapter-utils/server-utils";
import { summarizeStatus, isNonEmpty, commandLooksLike, summarizeProbeDetail } from "@agentcompany/adapter-utils/test-utils";
import { DEFAULT_GEMINI_LOCAL_MODEL } from "../index.js";
import { detectGeminiAuthRequired, parseGeminiJsonl } from "./parse.js";
import { firstNonEmptyLine } from "./utils.js";
function summarizeStatus(checks: AdapterEnvironmentCheck[]): AdapterEnvironmentTestResult["status"] {
if (checks.some((check) => check.level === "error")) return "fail";
if (checks.some((check) => check.level === "warn")) return "warn";
return "pass";
}
function isNonEmpty(value: unknown): value is string {
return typeof value === "string" && value.trim().length > 0;
}
function commandLooksLike(command: string, expected: string): boolean {
const base = path.basename(command).toLowerCase();
return base === expected || base === `${expected}.cmd` || base === `${expected}.exe`;
}
function summarizeProbeDetail(stdout: string, stderr: string, parsedError: string | null): string | null {
const raw = parsedError?.trim() || firstNonEmptyLine(stderr) || firstNonEmptyLine(stdout);
if (!raw) return null;
const clean = raw.replace(/\s+/g, " ").trim();
const max = 240;
return clean.length > max ? `${clean.slice(0, max - 1)}…` : clean;
}
export async function testEnvironment(
ctx: AdapterEnvironmentTestContext,
@@ -1,8 +0,0 @@
export function firstNonEmptyLine(text: string): string {
return (
text
.split(/\r?\n/)
.map((line) => line.trim())
.find(Boolean) ?? ""
);
}