@galda/cli 0.10.51 → 0.10.52

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.
@@ -0,0 +1,13 @@
1
+ // Claude Code accepts these stable aliases and resolves them to the current
2
+ // model versions for the active account and provider.
3
+ export const CLAUDE_MODELS = Object.freeze([
4
+ 'sonnet',
5
+ 'fable',
6
+ 'best',
7
+ 'opus',
8
+ 'haiku',
9
+ 'default',
10
+ 'sonnet[1m]',
11
+ 'opus[1m]',
12
+ 'opusplan',
13
+ ]);
@@ -0,0 +1,42 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+
5
+ export const FALLBACK_CODEX_MODELS = [
6
+ 'gpt-5.6-sol',
7
+ 'gpt-5.6-terra',
8
+ 'gpt-5.6-luna',
9
+ 'gpt-5.5',
10
+ 'gpt-5.4',
11
+ 'gpt-5.4-mini',
12
+ 'gpt-5.3-codex-spark',
13
+ ];
14
+
15
+ export function parseCodexModelCache(raw, fallback = FALLBACK_CODEX_MODELS) {
16
+ try {
17
+ const parsed = JSON.parse(String(raw));
18
+ const models = Array.isArray(parsed?.models)
19
+ ? parsed.models
20
+ .filter((model) => model?.visibility === 'list' && typeof model.slug === 'string')
21
+ .map((model) => model.slug.trim())
22
+ .filter(Boolean)
23
+ : [];
24
+ return models.length ? [...new Set(models)] : fallback.slice();
25
+ } catch {
26
+ return fallback.slice();
27
+ }
28
+ }
29
+
30
+ // Codex refreshes this cache itself. Reading it gives Galda the account's real
31
+ // picker catalog without launching Codex, making a network request, or running
32
+ // an installer. Older CLIs and first-run installs simply use the fallback.
33
+ export function loadCodexModels({ cacheFile, readFile = readFileSync } = {}) {
34
+ const file = cacheFile
35
+ || process.env.MANAGER_CODEX_MODELS_FILE
36
+ || join(process.env.CODEX_HOME || join(homedir(), '.codex'), 'models_cache.json');
37
+ try {
38
+ return parseCodexModelCache(readFile(file, 'utf8'));
39
+ } catch {
40
+ return FALLBACK_CODEX_MODELS.slice();
41
+ }
42
+ }
package/engine/server.mjs CHANGED
@@ -20,10 +20,12 @@ import { resolve, dirname, join, basename } from 'node:path';
20
20
  import { homedir } from 'node:os';
21
21
  import { fileURLToPath } from 'node:url';
22
22
  import { pathToFileURL } from 'node:url';
23
- import { needsProjectFolder, folderLabel, chooseFolderScript, parseChosenFolder, buildFolderQuestion, resolveFolderAnswer, classifyFolderTarget, buildFolderInitConfirm, isFolderInitConfirmed, needsReviewPreference, buildReviewPreferenceQuestion, resolveReviewPreferenceAnswer, routeQuestionAnswer, notUnderstoodNote, parseRelocalizedQuestion, classifyAuthProbe, authBannerText, makeSigninChallenge, parseClaimResponse } from './lib.mjs';
23
+ import { needsProjectFolder, folderLabel, chooseFolderScript, parseChosenFolder, buildFolderQuestion, resolveFolderAnswer, classifyFolderTarget, buildFolderInitConfirm, isFolderInitConfirmed, needsReviewPreference, buildReviewPreferenceQuestion, resolveReviewPreferenceAnswer, routeQuestionAnswer, notUnderstoodNote, parseRelocalizedQuestion, classifyAuthProbe, authBannerText, makeSigninChallenge, parseClaimResponse, isCommandOnPath } from './lib.mjs';
24
24
  import { parseStreamEvents, parseCodexEvents, buildCodexArgs, parsePlan, refinePlanTasks, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, isForcedStop, taskStatusAfterVerify, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, buildGoalProofMd, buildGoalPrBody, buildReviewSummaryPrompt, parseReviewSummary, buildReviewRuleSection, nextGoalStatus, isPrMerged, isPrApproved, reviewToDoneStatus, advanceReviewGoal, revertReviewGoal, approveGoal, dismissGoal, rejectGoal, reopenGoal, permissionModeFor, isPlanReview, nextAfterPlanApprove, GOAL_MODES, parseSkillFrontmatter, collectSkills, retestGoal, cancelRetestGoal, computeRetestOutcome, revertGoal, planRevertActions, archiveGoal, unarchiveGoal, undismissGoal, parseNumstat, truncateDiffText, sumUsage, resolveEntryUrl, parseGitLog, diffNewCommits, replayQueueLog, reconcileOrphanGoals, reorderQueue, sortQueueByPriority, TASK_PRIORITIES, validateWorkflowColumns, DEFAULT_WORKFLOW_COLUMNS, validateReviewDefinition, DEFAULT_REVIEW_DEFINITION, resolveTestGate, buildEphemeralSeedLog, buildEphemeralSeedProjects, trimTaskActivityForState, buildAskContext, WORKER_TOOLS, workerPrompt, verifyCfAccessJwt, resolveIdentity, goalVisibleTo, clampParallelLimit, canStartMore, nextRunnableTasks, goalsConflict, detectConflicts, pickFoldTarget, hasTestRelevantChanges, parseFailingTestNames, classifyTestGate, isInconclusiveTestRun, countInfraFlakes, classifyRunFailures, classifyVerifyGate, buildExecutionPlan, buildContextHandoffSummary, buildFailurePostmortem, appendFailureMemory, latestFailurePolicy, classifyChangeRisk, checkRunBudget, usageBudgetTokens, isRateLimited, nextResumeDelay, checkFreeTierLimit, resolveEntitlement, resolveCachedEntitlement, FREE_TIER_LIMITS, QUEUED_GOAL_STATUSES, verifyLicenseToken, licenseTokenPayload, shouldRefreshLicense, shouldEmitSetupCompleted, pickAnalyticsUid, shouldEmitFreeExhausted, detectRequestLanguage, isNothingVerifiable, classifyComposerIntentHeuristic, detectPauseIntent, buildIntentPrompt, parseIntentResponse, buildBoardSnapshot, validateBoardSnapshot, boardIsEmpty, decideBoardPull, resolveConnectedAgents, pickAvailableAgent, pickUtilityAgent, utilityModel, liveTakeoverDecision, shouldOpenBrowserOnBoot, goalTaskTitle, buildGoalTask, RUN_DECL_FILE, parseRunDeclaration, SHOT_DIR, collectShotNames, parseGoalAddress, REPLY_OUTCOMES, REPLY_INTENTS, buildReplyIntentPrompt, parseReplyIntent, buildGoalMessage, serializeGoalMessage, parseGoalMessages } from './lib.mjs';
25
25
  import { createSerialQueue } from './lib.mjs';
26
26
  import { collectProjectRules } from './lib.mjs';
27
+ import { FALLBACK_CODEX_MODELS, loadCodexModels } from './codex-models.mjs';
28
+ import { CLAUDE_MODELS } from './claude-models.mjs';
27
29
  import { openPR } from './pr.mjs';
28
30
  import { runVerification, exerciseUi } from './verify.mjs';
29
31
 
@@ -71,8 +73,8 @@ const WORKER_TIMEOUT_MS = Number(process.env.MANAGER_WORKER_TIMEOUT_MS) || 15 *
71
73
  // review card showed a truncated report and called it evidence. Keep a cap —
72
74
  // this lands in persisted state — but one that fits a real answer.
73
75
  const RESULT_MAX = 20000;
74
- const MODELS = ['sonnet', 'opus', 'haiku'];
75
- const CODEX_MODELS = ['gpt-5.5', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.3-codex-spark'];
76
+ const MODELS = CLAUDE_MODELS;
77
+ let CODEX_MODELS = FALLBACK_CODEX_MODELS.slice();
76
78
  const WORKER_AGENTS = ['claude-code', 'codex'];
77
79
  const CLAUDE_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max'];
78
80
  const CODEX_EFFORTS = ['low', 'medium', 'high', 'xhigh'];
@@ -647,14 +649,25 @@ let _syncDirtyAgain = false; // a save landed mid-push → coalesce into
647
649
  // Which worker agent CLIs are installed on this machine — the launcher
648
650
  // (bin/manager-for-ai.mjs) detects them (codex via a PATH lookup, never executed)
649
651
  // and passes the set here. Galda offers only these, so a Codex-only machine gets
650
- // a working board and a claude-only one never shows Codex. Defaults to Claude
651
- // Code when unset (tests / running the server directly).
652
- const envAgents = (process.env.MANAGER_AVAILABLE_AGENTS || 'claude-code')
652
+ // a working board and a claude-only one never shows Codex.
653
+ // The packaged launcher sets MANAGER_AVAILABLE_AGENTS after its safe CLI probe.
654
+ // Direct starts (launchd, `npm start`, development) bypass that launcher, so do
655
+ // the same non-executing PATH lookup here instead of silently becoming
656
+ // Claude-only. Never invoke an agent, npx, or an installer during detection.
657
+ const configuredAgents = process.env.MANAGER_AVAILABLE_AGENTS;
658
+ const detectedAgents = configuredAgents === undefined
659
+ ? [
660
+ isCommandOnPath('claude', { pathEnv: process.env.PATH || '', sep: process.platform === 'win32' ? ';' : ':', pathExt: process.env.PATHEXT || '' }) && 'claude-code',
661
+ isCommandOnPath('codex', { pathEnv: process.env.PATH || '', sep: process.platform === 'win32' ? ';' : ':', pathExt: process.env.PATHEXT || '' }) && 'codex',
662
+ ].filter(Boolean).join(',')
663
+ : configuredAgents;
664
+ const envAgents = (detectedAgents || 'claude-code')
653
665
  .split(',').map((a) => a.trim()).filter(Boolean);
654
666
  const AVAILABLE_AGENTS = resolveConnectedAgents({
655
667
  claude: envAgents.includes('claude-code'),
656
668
  codex: envAgents.includes('codex'),
657
669
  });
670
+ if (AVAILABLE_AGENTS.includes('codex')) CODEX_MODELS = loadCodexModels();
658
671
 
659
672
  // ---- state ---------------------------------------------------------------
660
673
  // Restarts must not lose pending work: tasks that never started stay
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@galda/cli",
3
- "version": "0.10.51",
3
+ "version": "0.10.52",
4
4
  "type": "module",
5
5
  "description": "Galda - hand off work to Claude Code or Codex, get proof back. Runs on your existing subscription, no extra API cost.",
6
6
  "scripts": {