mirror of
https://github.com/wu736139669/hapi.git
synced 2026-11-06 01:11:37 +00:00
* feat(android): settled permission verdict details Port the web PermissionFooter verdict classification so a settled permission row explains HOW a request was decided, not just that it was. Behavior vs web (web/src/components/ToolCard/PermissionFooter.tsx:11-63): - codex family sessions (flavor family + Codex/Gemini/OpenCode/Copilot/ Cursor tool prefixes): approved + decision approved_for_session shows "Approved for session"; denied + decision abort shows "Aborted" with the wire reason appended when present. - claude sessions: mode acceptEdits shows "Approved: allow all edits" (previously the raw mode id was appended); decision approved_for_session or an allowedTools hit — plain tool name or the `Bash(command)` qualifier built from the tool input command/cmd — shows "Approved for session" (previously indistinguishable from a plain approval). - canceled now appends the wire reason (previously dropped). - resolved/pending/denied unchanged in shape; denied keeps the error tone and aborted shares it. Deliberate divergence: web's own settled footer renders only a minimal red reason box (PermissionFooter.tsx:207-220) — its formatPermissionSummary classification is unreachable there. Android renders the classified verdict line in the existing subdued row style instead, as intended. Implementation: new pure JVM classifier feature/chat/permissions/PermissionVerdict.kt (exhaustive status -> PermissionVerdictLabel mapping, unit-tested in PermissionVerdictTest); ToolCallBlockView.PermissionStateRow becomes a thin renderer over it. Strings added in en/zh-rCN/ru (ru mirrors en key order; zh values from web locales). Co-Authored-By: Claude Code <noreply@anthropic.com> via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * feat(android): persistent permission-mode chip + inactive gating Item 2 — persistent permission-mode chip above the composer, mirroring the web StatusBar mode span (StatusBar.tsx:253-265, 389-393): - New PermissionModeChip composable + PermissionModeChipRule pure object: resolve the raw wire mode, keep it only when the flavor offers it, and hide `default` everywhere except copilot (model=auto sessions still surface the chip). Unknown wire ids hide (no raw fallback). - Tone mapping follows the web PERMISSION_TONE_CLASSES (StatusBar.tsx: 43-48): neutral=onSurfaceVariant, info=primary, warning=tertiary, danger=error. Labels stay en catalog strings — web mode labels are hardcoded English, not i18n. - The chip consults the unfiltered flavor catalog: the concurrent-clients Safe-Yolo carve-out only narrows the switcher, never the chip. - Deliberate divergence: the web span is not clickable (its switcher is the composer settings sheet / Shift+Tab). Android has no Shift+Tab, so tapping the chip opens the session config sheet that hosts the switcher — same surface as the top-bar gear, no new top-bar icon (ChatScreen keeps its two-icons-max rule). Item 4 — permission-mode gating aligned with web (SessionChat.tsx: 2079, 2138-2163; HappyComposer.tsx:536-537, 1544-1549, 1960-1995): - Inactive sessions stay switchable, matching web: SessionChat passes allowSendWhenInactive unconditionally, so configurationControlsDisabled never trips for inactive threads and the offline notice stays informational only (the send path auto-resumes). - The permission section now disappears for copilot sessions under local terminal control — web withholds onPermissionModeChange there and the whole section then hides (HappyComposer.tsx:1595). Other flavors keep switching while terminal-controlled. - ChatViewModel.setPermissionMode gains the matching belt-and-suspenders guard; the controlledByUser derivation is extracted into terminalControlled() (controlledByUser && !concurrentClients, same as SessionChat.tsx:996) and shared with buildConfigUi. Tests: PermissionModeChipTest (6 cases — default/codex/copilot/pi/unknown/ safe-yolo), ChatViewModelInteractionTest +3 (withheld for terminal copilot; survives terminal claude + shared-clients copilot; inactive stays switchable). No new user-visible strings (chip label is catalog en, contentDescription reuses chat_config_permission_mode). Co-Authored-By: Claude Code <noreply@anthropic.com> via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * test(android): derive the dsh chip expectation from the flavor catalog The hardcoded "dsh offers no modes" assumption breaks on trees where dsh gained the codex-style mode list — derive the expectation from the catalog so the test pins the actual contract (the chip mirrors the flavor's offered list). Co-Authored-By: Claude Code <noreply@anthropic.com> via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * feat(android): permission input semantics parity (editor inputType, URL confirm, grok auto gate) Three behavior alignments with the web app's permission surfaces: 1. Editor inputType (web requestUserInput.ts:152-153, 191-195): a required text question with `inputType === 'editor'` is always answered and its note is pushed untrimmed (even the literal "user_note: "); non-editor notes stay trimmed and optional. Android: PermissionInputs.requestUserInputAnswerValues / isRequestUserInputAnswered now take the question, gate on the literal "editor" inputType; the footer's note field grows to minLines = 2 to invite multi-line answers. 2. request_user_input URL confirmation (web requestUserInput.ts:46-70, 129-141; RequestUserInputFooter.tsx:142-152): the sign-in url only survives when it parses as http/https; submitting requires the CLI-synthesized __mcp_url_confirmation option (description === url) to be selected, and submission opens the url before approving. Android: parseRequestUserInput + normalizeRequestUserInputUrl + isRequestUserInputUrlConfirmed; the footer enforces the confirm guard, then opens the url via ACTION_VIEW. Divergences: the raw wire string is compared (the CLI synthesizes the description from the same value web would re-normalize, so they are equivalent for every CLI-produced request), and the open-failure message is platform-adapted (no popup blocker on Android). 3. Grok Auto permission gate (web NewSessionForm index.tsx + grok models probe): when the machine reports `autoPermissionModeSupported === false` for the chosen cwd, the Auto option is disabled with an " (unavailable)" suffix, a hint line is shown, and an existing Auto selection resets to Default. The gate is grok-only (claude and other agents never gate) and tri-state — null (unknown, probe pending or failed) never disables. Android: GrokModelsResponse wire type (nullable field), HapiApi.getMachineGrokModelsForCwd, NewSessionViewModel probes on agent/cwd/machine changes (deduped, cancellable, existence-gated), OptionItem gains `enabled`, the permission dropdown honors it and renders the hint. In-session switching stays ungated, matching web. New strings in en / zh-rCN / ru (KEYSYNC: en and ru key order identical). Tests: RequestUserInputTest covers editor/untrimmed-note/url parsing/confirmation semantics; NewSessionViewModelTest covers gate disable+reset+hint, tri-state never-gate cases, and agent/cwd gating; HapiApiTest covers the cwd query param and nullable decoding. Co-Authored-By: Claude Code <noreply@anthropic.com> via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * fix(android): accept browser-valid sign-in URLs --------- Co-authored-by: Claude Code <noreply@anthropic.com> Co-authored-by: HAPI <noreply@hapi.run> Co-authored-by: weishu <twsxtd@gmail.com>