mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-11 19:50:05 +00:00
* feat(config): add agent config descriptor protocol and advertise via runner capability Introduce shared agent configuration descriptors covering model, effort, permission, and secondary settings per agent flavor, plus the canonical HAPI YOLO -> native permission mode mapping. Runners advertise the builtin descriptors through the runner-state capability so hubs and web can render configuration without hardcoded flavor branches. Migrate the OpenCode create-session model picker from a bespoke radio list to the shared SelectControl combobox. * feat(web): render create-session permission from agent config descriptor Replace the flavor-branched Grok/Codex-family/YOLO permission block with a descriptor-driven PermissionField. Pi now reports permission as managed instead of silently ignoring the YOLO toggle, and YOLO-only flavors show the native permission mode the preference maps to. Removes the superseded GrokPermissionModeSelector and CodexFamilyPermissionModeSelector components. * ci: retry flaky claudeRemote 5s-timeout failure * fix(web): persist explicit OpenCode Default selection instead of restoring a concrete model The parent initialization effect treated every null selected model as 'uninitialized' and auto-picked a concrete advertised model, clobbering the user's explicit Default choice (and a restored Default preference). null now means explicit Default and is preserved; only undefined (no choice made yet) triggers probe-based initialization. Add parent-level regression tests for Default persistence and remembered-model restore. * fix(web): accept undefined selected model in OpencodeModelSelector props * feat(web+cli+hub): unify create-session model/effort fields and add Pi model/effort support Pi's agent config descriptor now advertises model (machine) and effort (static thinking levels) for create AND session availability: - cli: ListPiModelsForMachine RPC runs 'pi --list-models' (cached, inflight deduped) and parses the provider/model table; startup model match accepts provider-qualified ids - hub: GET /api/machines/:id/pi-models route + rpcGateway/syncEngine passthrough - web: NewSession renders Pi models grouped by provider through the generic ModelSelector and a new descriptor-driven EffortField (replaces the per-flavor LaunchEffortSelector/ReasoningEffortSelector pair); launch payload forwards Pi model + thinking-level effort (runner already supported --model/ --effort for pi) * fix(web): render Pi provider groups in ModelSelector and scope Grok availability warning - ModelSelector now renders grouped options as <optgroup> (Pi models are provider-grouped; identical modelIds from different providers stay distinct) - PermissionField only receives autoPermissionModeSupported for Grok — a cached Grok probe result no longer leaks the Grok warning onto other agents Addresses HAPI Bot Minor findings on #1469. * fix(web): drop Object.groupBy from ModelSelector; revalidate restored Pi models against the catalog - ModelSelector buckets options with a reduce instead of Object.groupBy (Safari < 17.4 has no polyfill — New Session would throw on those clients) - Pi restored model/effort are cleared when the value is absent from the live machine catalog, and Create waits for the catalog while a non-default Pi choice is being validated (mirrors Codex/Grok/Copilot handling) Addresses HAPI Bot findings on #1469. * fix(pi+web): serialize startup model before thinking level; hide Pi launch controls during history import - PiSession gains startupModelSettled; the startup set_thinking_level waits for the requested model's set_model attempt to settle first, so a level the default model rejects is not lost before the requested model is confirmed (set_model and set_thinking_level were already serialized by the runtime mutation lock; this pins the model-first ordering) - Create Session hides Pi model/effort controls while a Pi history import is selected — the import reopens the native session as-is and would silently ignore launch-only model/effort values Addresses HAPI Bot findings on #1469. * fix(pi): settle startup-model gate when model discovery fails or returns no models A failed or empty get_available_models response would leave the startupModelSettled gate unresolved, stranding a requested startup effort indefinitely. Resolve the gate on the error path and the empty-models path; adds regression tests for both.
79 lines
3.0 KiB
TypeScript
79 lines
3.0 KiB
TypeScript
import { RPC_METHODS } from './rpcMethods'
|
|
|
|
/**
|
|
* Capabilities the current runner generation advertises to the hub.
|
|
*
|
|
* `piExistingSessionResume` gates the Pi native-history resume path in
|
|
* hub/src/sync/syncEngine.ts: the runner must be able to hand back an
|
|
* existing native Pi process. The runner declares it at registration
|
|
* (HTTP POST /machines) and again on every socket connect, because the hub
|
|
* merges registration-time runner state only for brand-new machines and the
|
|
* socket heartbeat owns the persisted runner_state afterwards — without the
|
|
* socket-side advertisement, a runner upgraded in place would never get its
|
|
* new capabilities observed by the hub.
|
|
*/
|
|
import { getBuiltinAgentConfigDescriptors } from './agentConfig'
|
|
|
|
export const RUNNER_CAPABILITIES = {
|
|
piExistingSessionResume: true as const,
|
|
agentConfigs: getBuiltinAgentConfigDescriptors()
|
|
}
|
|
|
|
export type RunnerCapabilities = typeof RUNNER_CAPABILITIES
|
|
|
|
/**
|
|
* Machine-scoped capabilities runners advertise on connect.
|
|
* Hub features that hard-depend on a machine RPC must list that capability
|
|
* in {@link REQUIRED_MACHINE_CAPABILITIES} so skew surfaces as a banner
|
|
* instead of a silent fail-closed product bug.
|
|
*/
|
|
export const MACHINE_CAPABILITIES = {
|
|
CursorChatStoreStatus: RPC_METHODS.CursorChatStoreStatus,
|
|
StopRunner: RPC_METHODS.StopRunner,
|
|
} as const
|
|
|
|
export type MachineCapability =
|
|
(typeof MACHINE_CAPABILITIES)[keyof typeof MACHINE_CAPABILITIES]
|
|
|
|
/** Capabilities this CLI generation registers on the machine socket. */
|
|
export const CURRENT_MACHINE_CAPABILITIES: readonly MachineCapability[] = [
|
|
MACHINE_CAPABILITIES.CursorChatStoreStatus,
|
|
MACHINE_CAPABILITIES.StopRunner,
|
|
]
|
|
|
|
/**
|
|
* Capabilities the hub requires on every connected runner for features it
|
|
* hard-depends on. Missing entries → operator-visible skew banner (+ optional
|
|
* stop-runner ensure when a newer binary is already on disk).
|
|
*/
|
|
export const REQUIRED_MACHINE_CAPABILITIES: readonly MachineCapability[] = [
|
|
MACHINE_CAPABILITIES.CursorChatStoreStatus,
|
|
]
|
|
|
|
export function missingRequiredCapabilities(
|
|
advertised: readonly string[] | null | undefined,
|
|
): MachineCapability[] {
|
|
const set = new Set(advertised ?? [])
|
|
return REQUIRED_MACHINE_CAPABILITIES.filter((cap) => !set.has(cap))
|
|
}
|
|
|
|
export function isMachineCapabilitySkewed(
|
|
advertised: readonly string[] | null | undefined,
|
|
): boolean {
|
|
return missingRequiredCapabilities(advertised).length > 0
|
|
}
|
|
|
|
/** True when the running process started from a different CLI binary/mtime than what's installed now. */
|
|
export function cliBinaryUpdatedOnDisk(metadata: {
|
|
startedCliMtimeMs?: number | null
|
|
installedCliMtimeMs?: number | null
|
|
} | null | undefined): boolean {
|
|
const started = metadata?.startedCliMtimeMs
|
|
const installed = metadata?.installedCliMtimeMs
|
|
return typeof started === 'number'
|
|
&& typeof installed === 'number'
|
|
&& Number.isFinite(started)
|
|
&& Number.isFinite(installed)
|
|
&& started !== installed
|
|
}
|