mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 01:57:57 +00:00
* Source control actions start chats and terminals on their saved model A "start an agent" action's model lived only in free-text CLI arguments, which a native chat never reads, so AI Fix opened a chat on the default model. Each action now saves a structured model and effort that both routes apply: chat create options, or the agent's own flags in a terminal (winning over the same flag in CLI arguments). Desktop, dialog, fix-checks and mobile launches carry it; Settings rows get Model/Effort selects and the CLI arguments field says it is terminal-only. Tracked in STA-11102. * Name the model in the repository override note * Type the dialog's model hand-off; keep Agent in English in Japanese copy * fix: preserve action chat model choices through launch * Preserve action selection ownership across chat launches * Keep terminal host contract fixtures aligned with preflight * fix: wait for action Settings agent inventory * fix: consolidate direct agent routing imports * fix: use standard action option labels * fix: align action launch contracts with shared types * fix: sync action option localization metadata * test: provide action Settings inventory in mounted fixtures * Keep action options row height stable while loading * Let each Source Control action choose its chat permission mode An action's saved chat choice may now carry a permission mode, with or without a model; "Same as new chats" saves nothing. A model is replaced only when the action names one, so a permission-only choice keeps the chat's remembered model and options. * fix(native-chat): leave older hosts their own permission mode and keep action options readable - An action's saved permission mode is sent only to hosts that advertise agentSession.create.options-mode.v1. Older hosts get no mode, in create options or a later setOption, so the chat starts on that host's new-chat default, as a phone launch to such a host already does. Hosts from 1.4.225 to 1.4.227 refused a mode their tables lacked (OpenCode, Grok, OMP), leaving the action unable to start. - Settings action rows give Model and Permissions full rows in the options column so saved values are readable; the loading placeholder reserves the same rows. - Remove two unused locale keys; fix two stale comments. - Tests: the older-host create carries no permission mode; a permission mode picked alone keeps the remembered model on an acquired create. * fix(settings): reserve one options row while loading an action with nothing saved Terminal and permissions-only rows no longer shrink when loading ends; a chat agent with a model list may grow by its Model row once known. * fix(native-chat): keep the create options mode a checked literal type * fix(settings): share verified action option fields * fix(settings): keep action selectors on standard typography * feat(agents): Create worktree and continuation pickers offer chat or CLI per agent, like the "+" menu The Create worktree, Create folder and session-continuation agent pickers now list the same rows as the tab bar "+" menu: chat agents first, each with a terminal button that picks the agent's CLI instead, then the agents that only run in a terminal, labelled "(CLI)". A terminal pick shows as "Claude (CLI)" and starts the agent in a terminal even when chats are the default. A continuation into a subfolder shows chat as unavailable there. * fix(agents): picker rows keep the combobox item spacing instead of restyling CommandItem * chore: drop accidental lockfile churn * fix(agents): type the surface picker's props and the composer's agent rows * fix(agents): submit the picker selection captured by each form * test(agents): include repository identity in continuation fixture * Keep the launch files under their length limits after the main merge The structured route imports its result types where they are defined, and the two older-host action-launch tests share one setup helper. * feat(source-control): Source Control AI actions pick chat or CLI with the unified agent picker, and saved actions remember it The shared Source Control AI action dialog (commit/push failure fix, checks fix, resolve conflicts, resolve comments, PR-page fix checks) now lists the same rows as the tab-bar "+" menu: chat rows with a hover terminal button, and "(CLI)" rows. The pick carries chat-or-terminal into the launch. - One inventory: the dialog's own detection is gone; agent list, default selection, status copy, Start gating and auto-start all read the same host observation the "+" menu reads (the worktree's host, or the repo's host for the PR page's not-yet-created workspace). - The captured launch choice threads through runSourceControlAgentActionStart into launchAgentInNewTab, and on the PR page through the fix dialog into both launchWorkItemDirect planning sites. - Saved recipes gain optional `surface: 'chat' | 'terminal'`, bound to the recipe agent like launchPreferences: normalized, inherited, copied on Customize, dropped on agent change, compared for receipts and matches. A pick saves a surface only where the agent had a chat row. - Default paths (split-button primary, fix checks with and without a workspace, saved auto-start) request a terminal only for a saved terminal; a saved chat follows the settings route so it can still fall back to a terminal. - Settings rows are unchanged here; the dialog layout keeps following the settings route. * Import Source Control action ids and the agent setter once in the recipe test * One action settings section for chat and terminal launches A Source Control action's Model, model options and Permissions now apply whether it opens a chat or a terminal; terminals receive them as the agent's own flags, and a repository's own CLI arguments keep the flags it sets. Extra CLI arguments stay terminal-only. * Make saved source control starts explicit and preserve recipe intent * fix(source-control-ai): one terminal projection for saved action choices Ask in a terminal now states its permission instead of leaving it to the user's own config: Claude and Grok pass --permission-mode default, Codex passes -a on-request -s read-only -c approvals_reviewer="user" (the interactive CLI has no untrusted policy, so a read-only sandbox is what asks before edits). Agents whose CLI has no flag for a mode show the existing "Applies to chats only" hint. The strict launch-preferences projection now requires the launch agent at every caller, composes model ids through the resolver's own step, carries a mode only where the agent's terminal has a flag for it, and the local string wire keeps booleans as 'true'/'false', so Cursor Thinking/Fast reach the terminal and an OpenCode permission-only choice is not refused. The argument parser knows flag arity, so removing a boolean permission flag keeps other options' values, following positionals and '--'. Pre-admission and executor share one terminal admission check on the options the terminal will receive. Customize with Save launches the recipe as it will resolve once saved; legacy partial repository overrides are edited as the complete recipe they resolve to. Empty Extra CLI arguments start collapsed. * fix(source-control-ai): an edited repository action saves complete Every Settings edit of a repository action (agent, model, permission, prompt or arguments) now starts from the complete recipe the action resolves to, whether its stored override is owned or a legacy partial. The resolved agent selection is kept (never an agent only detected to run a default), an explicit null args clear stays null, and empty args stay empty instead of being dropped. Clearing the last preference therefore means the action agent's own defaults rather than re-inheriting Global. The action text draft is lossless: unset or cleared prompt and args stay so until the user types, and the inputs show them as empty, so saving a prompt no longer writes empty args that suppress the agent's default flags. Untouched sibling actions are not rewritten by the edit. * Keep source control correction and completion independent of launch eligibility * fix(settings): style the Extra CLI arguments toggle through Button's own variant and size CI's changed-code quality check refuses restyle classes on Button and CollapsibleContent. Use the ghost variant at size xs with a margin, and put the content spacing on a plain inner wrapper. * Verify saved action launches and lossless repository reloads * Update review refresh consumer after React commit * Preserve independent source control actions and phone option precedence * Repair launch contract test fixtures * Save source control action choices before starting the agent * Report source control recipe write failures without holding agent launch * fix: keep saved action and restart launch recipes consistent * Report pull request action save failures and correct recipe boundary types * fix: describe inherited arguments and chat-only permissions accurately * test: check repository action settings fixture type * test: map saved action options into terminal compiler inputs * Strip Unicode format characters from check log excerpts * test: remove redundant source control action runner cases * fix: retain saved action controls during agent detection * fix(i18n): retire obsolete Portuguese action labels * test: align folder launch fallback mock with outcome export * test: complete source control recovery controller fixture * Keep action argument visibility aligned with selected launch route * fix: carry resolved connector launch intent and mode together * fix: share host agent preferences across source control actions * fix: keep preference refresh tied to observation inputs * Fix optional agent preference contracts and typed fixtures * Require preference keys while preserving undefined values * test: pin shared action launch branches to recorded agent output * test: preserve saved source control choices through detection and migration * test: align launch fixtures with integrated result contract * test: retain recipe and restart contracts after main integration * Unify Source Control action permission settings * Keep native Ask copy with its owning change * Preserve action drafts and old-host permission intent * Consume rejected Source Control action saves * Follow the moved Settings persistence fixture * Keep permission integration contracts in the cross-version test project * Include the action permission inheritance label in English catalogs * Align launch contracts with shared permission recipes * Preserve settings snapshot for Source Control recovery * Align saved action persistence fixture with shared agent picker * Keep Source Control action controls within their Settings columns * Use primitive variants for compact action settings controls * Avoid returning from the launch test store setter * Show action arguments only for terminal defaults and widen recipe rows * Apply terminal-only action arguments visibility to repository overrides * Align action permission control with model controls * Use shared settings label for action permissions * Hide action model controls until argument migration ships * Show action arguments for explicit CLI picks * Preserve Unicode joiners in check log excerpts
162 lines
5.6 KiB
TypeScript
162 lines
5.6 KiB
TypeScript
import { isCustomAgentId } from './commit-message-agent-spec'
|
|
import {
|
|
normalizeRepoSourceControlAiOverrides,
|
|
resolveSourceControlActionRecipe
|
|
} from './source-control-ai'
|
|
import type { SourceControlActionId, SourceControlActionRecipe } from './source-control-ai-actions'
|
|
import { filterEnabledTuiAgents, TUI_AGENT_AUTO_PICK_ORDER } from './tui-agent-selection'
|
|
import type { GlobalSettings } from './global-settings-types'
|
|
import type { Repo } from './repo-types'
|
|
import type { TuiAgent } from './tui-agent'
|
|
|
|
export function readSourceControlLaunchRecipeAgentId(
|
|
recipe: Pick<SourceControlActionRecipe, 'agentId'> | null | undefined
|
|
): TuiAgent | null {
|
|
const agentId = recipe?.agentId
|
|
return agentId && !isCustomAgentId(agentId) ? agentId : null
|
|
}
|
|
|
|
export function pickSourceControlLaunchAgent(args: {
|
|
savedAgent?: TuiAgent | null
|
|
defaultAgent: TuiAgent | 'blank' | null | undefined
|
|
detectedAgents: TuiAgent[]
|
|
disabledAgents?: TuiAgent[]
|
|
chosenOpensAsChat?: (agent: TuiAgent) => boolean
|
|
}): TuiAgent | null {
|
|
const enabledAgents = filterEnabledTuiAgents(args.detectedAgents, args.disabledAgents)
|
|
const usable = (agent: TuiAgent): boolean =>
|
|
enabledAgents.includes(agent) ||
|
|
(filterEnabledTuiAgents([agent], args.disabledAgents).length > 0 &&
|
|
args.chosenOpensAsChat?.(agent) === true)
|
|
if (args.savedAgent && usable(args.savedAgent)) {
|
|
return args.savedAgent
|
|
}
|
|
if (args.defaultAgent && args.defaultAgent !== 'blank' && usable(args.defaultAgent)) {
|
|
return args.defaultAgent
|
|
}
|
|
return TUI_AGENT_AUTO_PICK_ORDER.find((agent) => enabledAgents.includes(agent)) ?? null
|
|
}
|
|
|
|
export type SourceControlLaunchAgentScope = {
|
|
/** Agent the button would launch, after applying any repo override. */
|
|
effectiveAgentId: TuiAgent | null
|
|
/** Agent the button would launch without a repo override (global recipe or default). */
|
|
globalAgentId: TuiAgent | null
|
|
/** Repo silently pins a different agent than the global launch would use; surface before launching. */
|
|
overridesGlobalAgent: boolean
|
|
}
|
|
|
|
export function resolveSourceControlLaunchAgentScope(input: {
|
|
settings:
|
|
| Partial<Pick<GlobalSettings, 'sourceControlAi' | 'commitMessageAi' | 'defaultTuiAgent'>>
|
|
| null
|
|
| undefined
|
|
repo?: Pick<Repo, 'sourceControlAi'> | null
|
|
actionId: SourceControlActionId
|
|
}): SourceControlLaunchAgentScope {
|
|
const effectiveAgentId = readSourceControlLaunchRecipeAgentId(
|
|
resolveSourceControlActionRecipe({
|
|
settings: input.settings,
|
|
repo: input.repo,
|
|
actionId: input.actionId
|
|
})
|
|
)
|
|
const globalRecipeAgentId = readSourceControlLaunchRecipeAgentId(
|
|
resolveSourceControlActionRecipe({
|
|
settings: input.settings,
|
|
repo: null,
|
|
actionId: input.actionId
|
|
})
|
|
)
|
|
// Compare against the actual no-override launch, including default-agent fallback when no global recipe names one.
|
|
const defaultTuiAgent = input.settings?.defaultTuiAgent
|
|
const globalAgentId =
|
|
globalRecipeAgentId ?? (defaultTuiAgent && defaultTuiAgent !== 'blank' ? defaultTuiAgent : null)
|
|
const hasRepoAgentOverride =
|
|
normalizeRepoSourceControlAiOverrides(input.repo?.sourceControlAi)?.actionOverrides?.[
|
|
input.actionId
|
|
]?.agentId !== undefined
|
|
return {
|
|
effectiveAgentId,
|
|
globalAgentId,
|
|
overridesGlobalAgent:
|
|
hasRepoAgentOverride && effectiveAgentId !== null && effectiveAgentId !== globalAgentId
|
|
}
|
|
}
|
|
|
|
export type SourceControlActionRecipeOverrideField =
|
|
| 'agent'
|
|
| 'commandTemplate'
|
|
| 'agentArgs'
|
|
| 'launchPreferences'
|
|
| 'surface'
|
|
|
|
export type SourceControlActionRecipeOverride = {
|
|
repoId: string
|
|
repoName: string
|
|
fields: SourceControlActionRecipeOverrideField[]
|
|
}
|
|
|
|
export type SourceControlActionRecipeOverrideSummary = {
|
|
count: number
|
|
overrides: SourceControlActionRecipeOverride[]
|
|
}
|
|
|
|
type NormalizedRepoSourceControlAiOverrides = NonNullable<
|
|
ReturnType<typeof normalizeRepoSourceControlAiOverrides>
|
|
>
|
|
|
|
function hasActionOverride(
|
|
overrides: NormalizedRepoSourceControlAiOverrides['actionOverrides'] | undefined,
|
|
actionId: SourceControlActionId
|
|
): boolean {
|
|
return Object.hasOwn(overrides ?? {}, actionId)
|
|
}
|
|
|
|
function readRecipeOverrideFields(
|
|
recipe:
|
|
| NonNullable<NormalizedRepoSourceControlAiOverrides['actionOverrides']>[SourceControlActionId]
|
|
| null
|
|
| undefined
|
|
): SourceControlActionRecipeOverrideField[] {
|
|
const fields: SourceControlActionRecipeOverrideField[] = []
|
|
if (Object.hasOwn(recipe ?? {}, 'agentId')) {
|
|
fields.push('agent')
|
|
}
|
|
if (Object.hasOwn(recipe ?? {}, 'commandInputTemplate')) {
|
|
fields.push('commandTemplate')
|
|
}
|
|
if (Object.hasOwn(recipe ?? {}, 'agentArgs')) {
|
|
fields.push('agentArgs')
|
|
}
|
|
if (Object.hasOwn(recipe ?? {}, 'launchPreferences')) {
|
|
fields.push('launchPreferences')
|
|
}
|
|
if (Object.hasOwn(recipe ?? {}, 'surface')) {
|
|
fields.push('surface')
|
|
}
|
|
return fields
|
|
}
|
|
|
|
/** Warn global editors about repos whose own recipes will retain their settings. */
|
|
export function summarizeReposOverridingActionRecipe(input: {
|
|
repos: readonly Pick<Repo, 'id' | 'displayName' | 'sourceControlAi'>[]
|
|
actionId: SourceControlActionId
|
|
}): SourceControlActionRecipeOverrideSummary {
|
|
const overrides: SourceControlActionRecipeOverride[] = []
|
|
for (const repo of input.repos) {
|
|
const actionOverrides = normalizeRepoSourceControlAiOverrides(
|
|
repo.sourceControlAi
|
|
)?.actionOverrides
|
|
if (!hasActionOverride(actionOverrides, input.actionId)) {
|
|
continue
|
|
}
|
|
overrides.push({
|
|
repoId: repo.id,
|
|
repoName: repo.displayName,
|
|
fields: readRecipeOverrideFields(actionOverrides?.[input.actionId])
|
|
})
|
|
}
|
|
return { count: overrides.length, overrides }
|
|
}
|