@galda/cli 0.10.107 → 0.10.109
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/app/index.html +34 -4
- package/engine/lib.mjs +46 -1
- package/engine/server.mjs +2 -2
- package/package.json +1 -1
package/app/index.html
CHANGED
|
@@ -3498,7 +3498,7 @@ const savedTheme = (() => {
|
|
|
3498
3498
|
if (v !== s) localStorage.setItem('theme', v);
|
|
3499
3499
|
return v;
|
|
3500
3500
|
})();
|
|
3501
|
-
const state = { repos: null, repoHome: '', /* GET /api/repos — the folders a project can point at (Context Bar) */ projects: [], goals: [], tasks: [], act: {}, actAt: {}, todos: {}, chatTurns: {}, active: localStorage.getItem('sel:project') || 'default', editing: null, attach: [], connectCommand: '', models: ['sonnet'], agentModels: null, agentEfforts: null, agents: ['claude-code', 'codex'], auth: null, expanded: new Set(), openThreads: new Set(), queueOrder: {}, externalActivity: [], selectedGoal: null, composerDetachedGoal: null, workflowColumns: {}, wfDraft: null, reviewDefinitions: {}, rdDraft: null, runningCounts: {}, parallelLimits: null, connectedApp: localStorage.getItem('sel:agent') || 'claude-code', reviewOpenGoal: null, reviewApprovals: {}, goalDetailOpen: null, qreplyOpen: new Set(), logCollapsed: localStorage.getItem('logCollapsed') !== '0', logEntries: [], layout: localStorage.getItem('layout') || 'flagship', theme: savedTheme, boardskin: localStorage.getItem('boardskin') || 'auto', summaryPattern: Number(localStorage.getItem('summaryPattern')) || 3, peraSpot: 0, skill: null, skillsCache: null, projectRules: {}, rulesOpen: false,
|
|
3501
|
+
const state = { repos: null, repoHome: '', /* GET /api/repos — the folders a project can point at (Context Bar) */ projects: [], goals: [], tasks: [], act: {}, actAt: {}, todos: {}, chatTurns: {}, active: localStorage.getItem('sel:project') || 'default', editing: null, attach: [], connectCommand: '', models: ['sonnet'], agentModels: null, agentEfforts: null, agents: ['claude-code', 'codex'], auth: null, expanded: new Set(), openThreads: new Set(), queueOrder: {}, externalActivity: [], selectedGoal: null, composerDetachedGoal: null, workflowColumns: {}, wfDraft: null, reviewDefinitions: {}, rdDraft: null, runningCounts: {}, parallelLimits: null, hostReadiness: [], connectedApp: localStorage.getItem('sel:agent') || 'claude-code', reviewOpenGoal: null, reviewApprovals: {}, goalDetailOpen: null, qreplyOpen: new Set(), logCollapsed: localStorage.getItem('logCollapsed') !== '0', logEntries: [], layout: localStorage.getItem('layout') || 'flagship', theme: savedTheme, boardskin: localStorage.getItem('boardskin') || 'auto', summaryPattern: Number(localStorage.getItem('summaryPattern')) || 3, peraSpot: 0, skill: null, skillsCache: null, projectRules: {}, rulesOpen: false,
|
|
3502
3502
|
// per-message send overrides set via slash-commands (reset after each send,
|
|
3503
3503
|
// except model which is sticky in localStorage). palette = open command list.
|
|
3504
3504
|
// Scoped per project (msgByProject) so a skill/mode picked in one project can't leak
|
|
@@ -8790,6 +8790,7 @@ async function refresh(){
|
|
|
8790
8790
|
}
|
|
8791
8791
|
state.connectCommand = s.connectCommand ?? ''; state.models = s.models ?? ['sonnet']; state.agentModels = s.agentModels ?? null; state.agentEfforts = s.agentEfforts ?? null; state.agents = s.agents ?? ['claude-code', 'codex']; state.uiVersion = s.uiVersion;
|
|
8792
8792
|
state.billing = s.billing ?? { licensed: false, email: null };
|
|
8793
|
+
state.hostReadiness = s.hostReadiness ?? [];
|
|
8793
8794
|
renderAppSelect(); // picker follows the server's installed-agent set; may correct a stale connectedApp
|
|
8794
8795
|
renderAuthBanner(s.auth); // only the selected worker's auth can raise a banner
|
|
8795
8796
|
setAgentModel(state.connectedApp);
|
|
@@ -9553,7 +9554,7 @@ function closeSummaryPera(){ $('summaryOverlay').classList.remove('show'); }
|
|
|
9553
9554
|
// 4 archived / 5 reverted — session-local; the server round-trip is what
|
|
9554
9555
|
// settles it (see saSync's "returned to review" reset).
|
|
9555
9556
|
// ============================================================================
|
|
9556
|
-
const SA = { open: false, items: [], diffs: {}, actLog: {}, msgs: {}, convOpen: new Set(), shotBox: {}, detOpen: new Set(), diffOpen: new Set(), attachments: {}, focusGid: null, focusScrolled: false, focusTimer: null, cur: null, solo: null, lastMarkup: null };
|
|
9557
|
+
const SA = { open: false, items: [], diffs: {}, actLog: {}, msgs: {}, msgCounts: {}, convOpen: new Set(), shotBox: {}, detOpen: new Set(), diffOpen: new Set(), attachments: {}, focusGid: null, focusScrolled: false, focusTimer: null, cur: null, solo: null, lastMarkup: null };
|
|
9557
9558
|
// Same convention as the existing ?dev=1 devbar flag — off unless someone opts in by URL.
|
|
9558
9559
|
const SA_DEV = new URLSearchParams(location.search).get('dev') === '1';
|
|
9559
9560
|
// task-971: 白黒(モノクロ)版のレールを ?railmono=1 で見比べられるようにする。
|
|
@@ -10118,7 +10119,13 @@ function saReqStackRows(it, attemptEvidence){
|
|
|
10118
10119
|
const gk = it.goalId;
|
|
10119
10120
|
const reqMsgs = SA.msgs[gk];
|
|
10120
10121
|
const reqAsks = Array.isArray(reqMsgs) ? reqMsgs.filter((m) => m?.from === 'you' && String(m.text || '').trim()) : [];
|
|
10121
|
-
|
|
10122
|
+
// The frozen attempt ledger is task-backed and available on first paint. Use it
|
|
10123
|
+
// before the asynchronously loaded conversation so Request #2/#3 cannot vanish
|
|
10124
|
+
// because the browser still holds an older messages cache.
|
|
10125
|
+
const ledgerAsks = Array.isArray(it.attempt?.requirements)
|
|
10126
|
+
? it.attempt.requirements.filter((r) => ['initial', 'reply'].includes(r?.kind) && String(r.text || '').trim())
|
|
10127
|
+
: [];
|
|
10128
|
+
const reqItems = ledgerAsks.length ? ledgerAsks : reqAsks.length ? reqAsks : (it.goalText ? [{ text: it.goalText }] : []);
|
|
10122
10129
|
const row = (m, idx) => {
|
|
10123
10130
|
const text = String(m.text || '').trim();
|
|
10124
10131
|
const isLatest = idx === reqItems.length - 1;
|
|
@@ -10583,8 +10590,11 @@ async function saLoadActivityLog(taskId){
|
|
|
10583
10590
|
// request writes an honest line instead of an empty box — the same rule the
|
|
10584
10591
|
// implementation log follows.
|
|
10585
10592
|
async function saLoadMessages(goalId){
|
|
10586
|
-
if (!Number.isFinite(goalId)
|
|
10593
|
+
if (!Number.isFinite(goalId)) return;
|
|
10594
|
+
const expectedCount = Number(SA.items.find((x) => x.goalId === goalId)?.messageCount) || 0;
|
|
10595
|
+
if (SA.msgs[goalId] !== undefined && SA.msgCounts[goalId] === expectedCount) return;
|
|
10587
10596
|
SA.msgs[goalId] = null; // in flight — a second toggle must not re-fetch
|
|
10597
|
+
SA.msgCounts[goalId] = expectedCount;
|
|
10588
10598
|
let msgs = [];
|
|
10589
10599
|
try {
|
|
10590
10600
|
const r = await fetch(withKey(`/api/goals/${goalId}/messages`));
|
|
@@ -12540,6 +12550,7 @@ function gearSetHTML(){
|
|
|
12540
12550
|
${billingRow}
|
|
12541
12551
|
${stability}
|
|
12542
12552
|
${gearRulesHTML()}
|
|
12553
|
+
${hostReadinessRow()}
|
|
12543
12554
|
<div class="srow dis"><div class="sl">Data folder<span class="sd">Runs are stored in the engine's chat-runs folder on the host</span></div></div>`;
|
|
12544
12555
|
}
|
|
12545
12556
|
async function loadStability(){
|
|
@@ -12552,6 +12563,25 @@ async function loadStability(){
|
|
|
12552
12563
|
// does NOT own these rules, it only surfaces them (editing is done in an editor or
|
|
12553
12564
|
// by asking the AI in chat). §3 段階開示 — when the project has no such file the row
|
|
12554
12565
|
// is absent entirely (never an empty container; the "ask once" flow is §1.5b).
|
|
12566
|
+
// Two capabilities degrade silently depending on the machine: without Chrome
|
|
12567
|
+
// nothing can screenshot or verify in a browser, and without a native picker
|
|
12568
|
+
// "Open folder…" cannot open Finder. The server states them in /api/state
|
|
12569
|
+
// (hostReadiness); this is where a person can read them before spending an hour
|
|
12570
|
+
// treating the absence as a bug.
|
|
12571
|
+
//
|
|
12572
|
+
// Same row shape as Data folder — a host fact, no chip, nothing to press (§SETTINGS
|
|
12573
|
+
// 行=ラベル+説明). When everything is available it stays dimmed like its neighbour;
|
|
12574
|
+
// when something is missing the row is NOT dimmed, because a dimmed row at .38 is
|
|
12575
|
+
// exactly the thing nobody reads. No colour and no fill either way (§1.5-8: an
|
|
12576
|
+
// exception is a small inline element, never a painted surface) — this is a
|
|
12577
|
+
// capability note, not an error.
|
|
12578
|
+
function hostReadinessRow(){
|
|
12579
|
+
const checks = state.hostReadiness || [];
|
|
12580
|
+
if (!checks.length) return ''; // nothing measured → no row (§1.5-3)
|
|
12581
|
+
const missing = checks.filter((c) => !c.ok);
|
|
12582
|
+
const detail = (missing.length ? missing : checks).map((c) => c.detail).join(' · ');
|
|
12583
|
+
return `<div class="srow${missing.length ? '' : ' dis'}"><div class="sl">This machine<span class="sd">${esc(detail)}</span></div></div>`;
|
|
12584
|
+
}
|
|
12555
12585
|
function gearRulesHTML(){
|
|
12556
12586
|
const r = state.projectRules[state.active];
|
|
12557
12587
|
if (!r || !r.files || !r.files.length) return '';
|
package/engine/lib.mjs
CHANGED
|
@@ -1174,7 +1174,7 @@ export function buildReviewAttemptLedger({ goal = null, tasks = [] } = {}) {
|
|
|
1174
1174
|
const outcome = [...settled].sort((a, b) => time(b) - time(a))[0] ?? null;
|
|
1175
1175
|
const requirements = [
|
|
1176
1176
|
goal?.text ? { id: `goal-${goal.id}-initial`, kind: 'initial', text: String(goal.text), at: goal.createdAt ?? null } : null,
|
|
1177
|
-
...own.filter((task) => task?.reply && task?.detail).map((task) => ({
|
|
1177
|
+
...own.filter((task) => task?.reply && task?.detail && (!task.replyFrom || task.replyFrom === 'you')).map((task) => ({
|
|
1178
1178
|
id: task.attemptId || `task-${task.id}`,
|
|
1179
1179
|
kind: 'reply', text: String(task.detail), at: task.createdAt ?? null,
|
|
1180
1180
|
})),
|
|
@@ -1896,6 +1896,51 @@ export function canStartMore(runningCount, limit) {
|
|
|
1896
1896
|
return Number.isFinite(limit) && limit > 0 && (runningCount ?? 0) < limit;
|
|
1897
1897
|
}
|
|
1898
1898
|
|
|
1899
|
+
// A goal sitting in `stacked` is not being worked on and did not fail — it is
|
|
1900
|
+
// waiting. Say why, and say what has to happen for it to start, because "nothing
|
|
1901
|
+
// is happening" is indistinguishable from "it is broken" from the outside.
|
|
1902
|
+
//
|
|
1903
|
+
// Keyed by GOAL id, not task id: a stacked goal has no task yet. That is the whole
|
|
1904
|
+
// point of the slot accounting (#541) — a goal holds its slot for as long as it is
|
|
1905
|
+
// in Doing, so the ones beyond the cap stay `stacked` instead of quietly planning
|
|
1906
|
+
// and making the board claim four things are being worked on when two are.
|
|
1907
|
+
//
|
|
1908
|
+
// The implementation of this shipped once (goal 058a11c, 2026-07-26) and was lost
|
|
1909
|
+
// when PR #396 integrated the stability roadmap: the test came across, the server
|
|
1910
|
+
// and app halves did not. main has carried a red test for it ever since.
|
|
1911
|
+
export function buildQueueWaits({ goals = [], projectPaused = () => false, projectCap = 2, globalCap = 4 } = {}) {
|
|
1912
|
+
const waits = {};
|
|
1913
|
+
const isActive = (g) => g.status === 'planning' || g.status === 'running';
|
|
1914
|
+
const activeEverywhere = goals.filter(isActive);
|
|
1915
|
+
for (const goal of goals) {
|
|
1916
|
+
if (goal?.status !== 'stacked') continue;
|
|
1917
|
+
const active = activeEverywhere.filter((g) => g.projectId === goal.projectId);
|
|
1918
|
+
if (projectPaused(goal.projectId)) {
|
|
1919
|
+
waits[goal.id] = {
|
|
1920
|
+
reason: 'this project is paused',
|
|
1921
|
+
condition: 'you resume the project',
|
|
1922
|
+
targetGoalIds: [],
|
|
1923
|
+
};
|
|
1924
|
+
} else if (active.length < projectCap && activeEverywhere.length >= globalCap) {
|
|
1925
|
+
// This project has room; the machine does not. Naming the other projects'
|
|
1926
|
+
// goals here would point at work the person may not even be looking at, so
|
|
1927
|
+
// the target stays the global limit itself.
|
|
1928
|
+
waits[goal.id] = {
|
|
1929
|
+
reason: `all ${globalCap} slots across every project are already Doing`,
|
|
1930
|
+
condition: 'one of them finishes, fails, or moves to Review',
|
|
1931
|
+
targetGoalIds: activeEverywhere.map((g) => g.id),
|
|
1932
|
+
};
|
|
1933
|
+
} else {
|
|
1934
|
+
waits[goal.id] = {
|
|
1935
|
+
reason: `all ${projectCap} project slots are already Doing`,
|
|
1936
|
+
condition: 'one of them finishes, fails, or moves to Review',
|
|
1937
|
+
targetGoalIds: active.map((g) => g.id),
|
|
1938
|
+
};
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
return waits;
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1899
1944
|
// Pick up to `limit` tasks from the front of `waiting` that are safe to start
|
|
1900
1945
|
// RIGHT NOW, preserving same-goal ordering: a goal already represented in
|
|
1901
1946
|
// `runningGoalIds` (has an in-flight task) contributes nothing this round,
|
package/engine/server.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import { homedir } from 'node:os';
|
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
22
|
import { pathToFileURL } from 'node:url';
|
|
23
23
|
import { needsProjectFolder, folderLabel, chooseFolderScript, parseChosenFolder, buildFolderQuestion, resolveFolderAnswer, classifyFolderTarget, buildFolderInitConfirm, isFolderInitConfirmed, needsReviewPreference, buildReviewPreferenceQuestion, resolveReviewPreferenceAnswer, routeQuestionAnswer, notUnderstoodNote, parseRelocalizedQuestion, classifyAuthProbe, authBannerText, makeSigninChallenge, parseClaimResponse, isCommandOnPath, hostReadiness, parseRequestedWorkspaceDir } from './lib.mjs';
|
|
24
|
-
import { parseStreamEvents, parseCodexEvents, buildCodexArgs, parsePlan, refinePlanTasks, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, classifyWorkerFailure, isForcedStop, taskStatusAfterVerify, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, buildReviewAttemptLedger, latestGoalOutcomeTask, buildDirtyWorkspacePrBlock, 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, rebasePreviewUrl, shouldCreateGoalPR, normalizePullRequestUrl, 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, autoFoldReviewsEnabled, parseGitUnifiedDiffLocations, hasTestRelevantChanges, parseFailingTestNames, classifyTestGate, classifyPrSafety, isInconclusiveTestRun, countInfraFlakes, classifyRunFailures, classifyVerifyGate, buildVerificationInfraError, verificationInfraErrorFrom, 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, testFailureReason, manualTestRetryPrompt, isNothingVerifiable, isNothingVerifiableForPR, isSuspiciouslyIncompleteDone, classifyComposerIntentHeuristic, detectPauseIntent, buildIntentPrompt, parseIntentResponse, buildBoardSnapshot, validateBoardSnapshot, boardIsEmpty, decideBoardPull, resolveConnectedAgents, pickAvailableAgent, pickUtilityAgent, utilityModel, liveTakeoverDecision, shouldOpenBrowserOnBoot, goalTaskTitle, buildGoalTask, shouldUsePlannerForGoal, RUN_DECL_FILE, REQUIREMENT_EVIDENCE_FILE, parseRequirementEvidenceDocument, parseRunDeclaration, SHOT_DIR, collectShotNames, parseGoalAddress, REPLY_OUTCOMES, buildGoalMessage, serializeGoalMessage, parseGoalMessages, computeInternalQualityMetrics, classifyInternalQualityTaskError, resolveWorkspaceMode, goalSessionFor, setGoalAgentSession, switchGoalAgentSession, parseApprovalRequest } from './lib.mjs';
|
|
24
|
+
import { parseStreamEvents, parseCodexEvents, buildCodexArgs, parsePlan, refinePlanTasks, canEditGoal, canDeleteGoal, shouldAskClarification, workerExitReason, classifyWorkerFailure, isForcedStop, taskStatusAfterVerify, workerResultText, taskCountsAsComplete, resolveWantsPR, resolveGoalSource, buildGoalSummary, buildReviewAttemptLedger, latestGoalOutcomeTask, buildDirtyWorkspacePrBlock, 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, rebasePreviewUrl, shouldCreateGoalPR, normalizePullRequestUrl, 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, buildQueueWaits, nextRunnableTasks, goalsConflict, detectConflicts, pickFoldTarget, autoFoldReviewsEnabled, parseGitUnifiedDiffLocations, hasTestRelevantChanges, parseFailingTestNames, classifyTestGate, classifyPrSafety, isInconclusiveTestRun, countInfraFlakes, classifyRunFailures, classifyVerifyGate, buildVerificationInfraError, verificationInfraErrorFrom, 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, testFailureReason, manualTestRetryPrompt, isNothingVerifiable, isNothingVerifiableForPR, isSuspiciouslyIncompleteDone, classifyComposerIntentHeuristic, detectPauseIntent, buildIntentPrompt, parseIntentResponse, buildBoardSnapshot, validateBoardSnapshot, boardIsEmpty, decideBoardPull, resolveConnectedAgents, pickAvailableAgent, pickUtilityAgent, utilityModel, liveTakeoverDecision, shouldOpenBrowserOnBoot, goalTaskTitle, buildGoalTask, shouldUsePlannerForGoal, RUN_DECL_FILE, REQUIREMENT_EVIDENCE_FILE, parseRequirementEvidenceDocument, parseRunDeclaration, SHOT_DIR, collectShotNames, parseGoalAddress, REPLY_OUTCOMES, buildGoalMessage, serializeGoalMessage, parseGoalMessages, computeInternalQualityMetrics, classifyInternalQualityTaskError, resolveWorkspaceMode, goalSessionFor, setGoalAgentSession, switchGoalAgentSession, parseApprovalRequest } from './lib.mjs';
|
|
25
25
|
import { migrateRequirementModel, validateRequirementModel } from './requirement-model.mjs';
|
|
26
26
|
import { buildManagerVerificationChecks, buildRequirementVerificationEvidence, mergeRequirementVerificationEvidence } from './requirement-verification.mjs';
|
|
27
27
|
import { createSerialQueue } from './lib.mjs';
|
|
@@ -4375,7 +4375,7 @@ const server = createServer(async (req, res) => {
|
|
|
4375
4375
|
const dirty = projectDirtyInfo(p.dir);
|
|
4376
4376
|
return { ...p, ...(branch ? { branch } : {}), ...dirty };
|
|
4377
4377
|
});
|
|
4378
|
-
return json(res, 200, { projects: projectsWithBranch, goals: visibleGoals, tasks: trimTaskActivityForState(visibleTasks), models: MODELS, agentModels: { 'claude-code': MODELS, codex: CODEX_MODELS }, agentEfforts: { 'claude-code': CLAUDE_EFFORTS, codex: CODEX_EFFORTS }, agents: AVAILABLE_AGENTS, connectCommand, queueOrder, externalActivity, workflowColumns: projectColumns, reviewDefinitions: projectReviewDefs, runningCounts, parallelLimits, uiVersion: uiVersion(), billing, hostReadiness: hostReadinessNow(), auth: publicAuth() });
|
|
4378
|
+
return json(res, 200, { projects: projectsWithBranch, goals: visibleGoals, tasks: trimTaskActivityForState(visibleTasks), models: MODELS, agentModels: { 'claude-code': MODELS, codex: CODEX_MODELS }, agentEfforts: { 'claude-code': CLAUDE_EFFORTS, codex: CODEX_EFFORTS }, agents: AVAILABLE_AGENTS, connectCommand, queueOrder, externalActivity, workflowColumns: projectColumns, reviewDefinitions: projectReviewDefs, runningCounts, parallelLimits, uiVersion: uiVersion(), billing, hostReadiness: hostReadinessNow(), queueWaits: buildQueueWaits({ goals: visibleGoals, projectPaused: isProjectPaused, projectCap: projectParallelCap(), globalCap: GLOBAL_MAX_PARALLEL }), auth: publicAuth() });
|
|
4379
4379
|
}
|
|
4380
4380
|
|
|
4381
4381
|
// Read-only stability snapshot: separates implementation failures from
|
package/package.json
CHANGED