Files
hapi/shared/src/copilotModes.ts
T
f10fbc7496 feat(cli): add GitHub Copilot CLI agent support via ACP (#1245)
* feat(cli): add GitHub Copilot CLI agent support via ACP

Wrap `copilot --acp --stdio` for remote sessions and spawn the native TUI locally, with full hub/web integration for spawn, resume, and permissions.

Fixes tiann/hapi#362

Co-Authored-By: HAPI <noreply@hapi.run>
Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(copilot): agent modes, models, slash/file UX, local session sync

Add Interactive/Plan/Autopilot (fleet is slash-only), subscription-aware
model discovery, web StatusBar/permission UX, @ file mentions, and fix
local Safe Yolo plus session-id locator for handoff/resume.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: re-trigger Codex PR review after auth outage

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: retry Codex PR review

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(copilot): preserve agent mode on resume and apply via ACP set_mode

Resume was dropping copilotAgentMode so Plan/Autopilot reset to interactive.
Also switch local/remote mode application to --mode / session set_mode instead of slash prompts.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(copilot): wake remote loop when agent mode changes

Empty isolated queue tick lets setMode apply without inventing a user prompt.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(copilot): confirm mode changes before persisting

Await Copilot mode changes and expose discovered models so session state reflects backend acceptance.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(copilot): guard mode discovery and slash updates

Keep model probes within runner roots and preserve active sessions when mode switching is unavailable or rejected.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(copilot): preserve resume and auto semantics

Deduplicate Copilot resume rows, apply Auto explicitly, and fail closed on denied permissions.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(copilot): close permission and model discovery gaps

Keep write-capable commands pending in read-only mode, extend model probe RPCs, and preserve explicit model validation before session creation.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(copilot): persist runtime model and agent mode

Fallback to ACP model options when direct model switching is unavailable and retain Copilot agent mode across hub restarts.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(copilot): normalize composer auto selection

Use the null session sentinel for Copilot Auto so the composer selects and resets default models consistently.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(copilot): reject local permission mode changes

* style(copilot): remove trailing blank line

* fix(copilot): secure local config handoffs

* fix(copilot): reject local agent mode slashes

* fix(copilot): reject mode changes during turns

* fix(copilot): consume rejected slash updates

* fix(copilot): preserve thinking across slash handling

* fix(copilot): stabilize async config changes

* fix(copilot): roll back rejected startup model

* fix(copilot): preserve cancellation and file mentions

* fix(copilot): hide local permission controls

* fix(deps): support clean workspace installs

* test(copilot): account for spawn mode argument

* fix(copilot): attribute usage to active model

---------

Co-authored-by: HAPI <noreply@hapi.run>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 08:19:12 +08:00

46 lines
1.3 KiB
TypeScript

export const COPILOT_AGENT_MODES = [
'interactive',
'plan',
'autopilot',
] as const
export type CopilotAgentMode = typeof COPILOT_AGENT_MODES[number]
export const COPILOT_AGENT_MODE_LABELS: Record<CopilotAgentMode, string> = {
interactive: 'Interactive',
plan: 'Plan',
autopilot: 'Autopilot',
}
export type CopilotAgentModeOption = {
mode: CopilotAgentMode
label: string
}
export function getCopilotAgentModeLabel(mode: CopilotAgentMode): string {
return COPILOT_AGENT_MODE_LABELS[mode]
}
export function getCopilotAgentModeOptions(): CopilotAgentModeOption[] {
return COPILOT_AGENT_MODES.map((mode) => ({
mode,
label: getCopilotAgentModeLabel(mode)
}))
}
export function isCopilotAgentMode(value: unknown): value is CopilotAgentMode {
return typeof value === 'string'
&& (COPILOT_AGENT_MODES as readonly string[]).includes(value)
}
/**
* Coerce legacy / invalid values. `fleet` was briefly treated as an agent mode;
* it is a slash command (`/fleet`) orthogonal to interactive/plan/autopilot.
*/
export function normalizeCopilotAgentMode(value: unknown): CopilotAgentMode {
if (value === 'fleet') {
return 'interactive'
}
return isCopilotAgentMode(value) ? value : 'interactive'
}