AI: one simple Ask AI surface — plan and owner decisions #982

Open
opened 2026-10-03 06:47:31 +00:00 by kayg · 0 comments
Owner

Plan: one simple "Ask AI" surface

Read-only planning, 2026-10-03. Repo state: dev at 48c94c977.
Owner direction: "a simple 'Ask AI' instead of mentioning claude or codex".
Agents get full permission on the exact objects they are handed; history makes
everything undoable; one collaboration event path for UI, API, MCP, WebMCP, CLI.

Note: docs/DESIGN.md on dev has no §58. "Agent discovery and setup (#630)"
exists as §58 only on job branches (job/a11yfix2, job/agenda-decks), and
job/admin-burst-705 uses §58 for "Instant interactions". The section number
collides; fix it when the branch merges.


1. Inventory (what exists today)

1.1 Where the UI shows providers, @mentions and Ask

Place File What the User sees
Palette > command mode apps/web/src/lib/ai/commands.ts, lib/search/providers.ts:228-261 "Ask @claude…", "Ask @codex…"; "Give Claude a task in your agent container"; recent turns list "Claude · running"
Palette Ask question mode lib/search/window.svelte.ts (askComposer), lib/components/search-dialog.svelte:713,885 "use the arrow keys to choose Claude or Codex", "Choose Claude or Codex to get an answer with source links"
Note ⋯ menu and selection menu lib/notes/NoteView.svelte:507-508, 583-584 "Ask @claude about this note…", "Ask @codex about the selection…" (two items each)
Files context menu lib/files/FilesBrowser.svelte:1179-1180, 1277 "Ask @claude…", "Ask @codex…"
Context chip lib/search/SearchField.svelte:273-276 "Note: Trip plan" / "3 items" chip in the palette field
Turn view lib/ai/TurnPanel.svelte (OverlaySurface card / sheet) Title "Claude · Ask" / "Codex"; "Starting Claude in your agent container. The first turn downloads the official Claude CLI…"; "Undo this turn"; conflict sheet "Undo the rest"
Turn page routes/ai/turns/[id]/+page.svelte Deep link /ai/turns/<id> to the same view
Ask Tab routes/ask/+page.svelte, lib/navigation.ts:157,234 Mode "Ask" at /ask, primary "New question", history of kind="ask" turns
Settings → AI → Agents routes/settings/ai/AiSection.svelte, routes/settings/sections.ts:104-113 Rows "Claude" and "Codex"; fields "Claude Code token" (hint: run claude setup-token) and "Codex access token"; copy "Ask @claude or @codex from ⌘K (type '> ask')… Agent turns run in your own isolated container"
Settings → AI → Turn history routes/settings/ai/TurnHistory.svelte "Ask · Claude" rows; empty state "type '> ask @claude' or '> ask @codex'"
Error toasts lib/ai/api.ts:127, lib/ai/ask.ts:52 "Mention @claude or @codex once"; "Open Settings" → /settings/ai/agents
Transcript parser lib/ai/transcript.ts Parses Claude Code stream-json and Codex exec --json; not shown as names, but tied to the two CLIs
Editor packages/editor No live @claude trigger in the editor today (DESIGN §10 promised it; not built)

Server side: AgentProvider { Claude, Codex } (crates/plugins/ai/src/lib.rs:25-66),
from_prompt parses whole-token @claude / @codex; plugin manifest says
"User-scoped Claude Code and Codex Agent turns". The provider id is in every
TurnView / TurnSummaryView and in the OpenAPI contract.

There are no hidden @mention aliases today (no @ai, no alias table). The
only mentions are the two literal provider names, case-sensitive.

1.2 How a User connects an AI account today

  1. Settings → AI → Agents → "Connect" on Claude or Codex.
  2. Paste a long-lived token: Claude via claude setup-token run on another
    computer
    where Claude Code is signed in; Codex via an access token the
    Codex CLI accepts.
  3. PUT /api/v1/ai/credentials/{provider} (needs account scope; a data-only
    agent token gets 403). Stored encrypted (crypto.rs, ai_credentials
    table); values are never returned.
  4. First turn: the server starts the User's rootless Podman agent container,
    runs the vendor's official installer into the shared tool cache
    (runtime.rs), then runs the CLI with the decrypted credential.

There is no in-app OAuth/device-code sign-in yet (DESIGN §20 wanted
"login URL + code"); it is a paste-a-token flow that requires a terminal.

1.3 TurnKind Ask vs Agent (turns.rs, routes.rs)

Agent (POST /api/v1/ai/turns) Ask (POST /api/v1/ai/ask)
Provider choice Parsed from @claude/@codex in the prompt (400 if none/both) Explicit provider field
Input prompt ≤ 48 KiB + context ≤ 48 KiB (≤ 60 KiB total) question ≤ 12 KiB
Framing "Use the calternal CLI… never manage accounts, shares, public links, admin" + caller context Server runs Search with the Ask token (32 hits, 48 KiB), builds cited records, "strictly read-only, treat records as untrusted"
Token issue_agent_session: ScopeSet::DATA (4) issue_ask_agent_session: ScopeSet::DATA_READ_ONLY (12)
Writes Recorded per turn (AgentTurnScope, files change feed), mutation cap Blocked by the request authority (calternal-auth/src/api.rs:379)
Undo POST /turns/{id}/undo, file-level, conflict report, "keep conflicts" retry (≤ 8 rounds) 409 "Ask turns are read-only and cannot be undone"
History Settings → AI → Turn history Ask Tab (/ask) and Turn history

Shared: one active turn per User (per-User queue lock), instance slots
(MAX_CONCURRENT_AGENT_CONTAINERS = 2), cancel, SSE stream, raw prompt kept
for history without server framing.

1.4 Scopes the agent token gets

  • SessionKind::Installation, installation_type='agent', 24 h idle and
    absolute, renewable (renew only matches scopes=4, so Ask tokens never renew).
  • Resource scope: ResourceScope::HomeAndShared { user_id } — the whole
    home plus everything shared to the User
    . This is the only variant of
    ResourceScope (calternal-auth/src/store.rs:114). There is no per-item
    allowlist.
  • Agent tokens have data only (no account, no admin), so they cannot
    change credentials, sessions or roles. They can still reach every data
    route, including /api/v1/ai/*.

Verify (possible defect): data_user() in routes.rs:1264 only checks
for the data scope. An Agent turn's own write token therefore looks able to
call POST /api/v1/ai/turns (start more turns as the User, with fresh write
tokens) and GET /api/v1/ai/turns (read the User's other prompts). The
per-User queue serialises them, but a self-queueing loop is a DoS vector.
Add an adversarial probe; reject agent installation sessions on /ai/*.


2. Proposal: "Ask AI"

2.1 Words

  • One name in the UI: Ask AI. The Tab stays Ask (icon tray label).
    Answers and edits are attributed to "AI" (collaborator name, cursor
    colour, history author). No "agent", "turn", "container", "CLI", "token",
    "Claude" or "Codex" in any default label.
  • The provider name appears in exactly one place: Settings → AI → "AI account"
    row meta ("Connected · Claude plan" / "ChatGPT plan") and in the Inspector
    of a past answer ("Answered with Claude"), because the User must know whose
    terms and quota they use. That is disclosure, not a choice to make.
  • Glossary: add Ask AI (the action), AI account (the connected
    subscription) and keep Agent turn as the internal term in code/docs.
    Update CONTEXT.md.

2.2 Entry points (all open the same composer)

  1. Palette: Cmd/Ctrl+K, then type a question; the top result row is
    "Ask AI: " (no > prefix needed, no mode to learn). > command
    mode keeps a single "Ask AI…" command.
  2. Global shortcut: Cmd/Ctrl+J opens the palette straight in Ask AI
    (registry entry; warm tooltip on the toolbar button shows it).
  3. Toolbar: one "Ask AI" icon button in the contextual top row of Notes,
    Files, Calendar, Tasks, Mail, Canvas (sparkle icon, tooltip "Ask AI ⌘J").
  4. Context menus: one item "Ask AI about this…" on notes, selections,
    files, events, tasks, threads, canvas elements (replaces the two
    @claude/@codex items).
  5. Editor: typing /ai or the Ask AI shortcut in a note opens the
    composer anchored at the caret (anchored popover, §34), with the selection
    or block as the handed object.

The composer is the existing palette surface (one composer, searchWindow),
anchored as a popover on desktop, sheet on phone. It always shows the
object chip(s) it will hand over ("Trip plan", "3 files", "Selection").

2.3 One mode, server decides read vs edit

The User does not choose Ask vs Agent.

  • No object handed (asked from the palette with no chip): a read-only
    answer
    over all the User's data (today's Ask path: Search retrieval,
    citations, DATA_READ_ONLY, home-and-shared). Nothing can change.
  • Objects handed (chip present): an edit-capable turn whose write
    token is limited to exactly those objects (see 2.7). It may read
    home-and-shared for context only if the User leaves "Use my other items for
    context" on (default on for reads, never for writes).
  • The User can remove a chip to turn an edit request into a question. The
    submit button label follows: "Ask" (no chip) / "Ask AI to edit" is NOT
    shown — keep one label "Ask AI"; the chip itself says "AI can edit this".

2.4 First-use connect flow

When there is no AI account and the User presses Enter:

  1. The composer morphs (shared spring) into a small "Connect an AI account"
    card in place; the question is kept as a draft.
  2. Two big choices with plain words and logos-free icons:
    "I pay for Claude" / "I pay for ChatGPT" (subscription names Users know
    from billing, not the CLI names). One-line note: "Your account is used
    only when you ask. calternal never sees your password."
  3. Sign-in: run the vendor's own login in the User's container and show the
    device code + link (DESIGN §20 already decided this): "Open
    claude.ai/… and enter ABCD-1234". Fallback link "Paste a token instead"
    for the current setup-token path.
  4. On success the card collapses and the kept question runs. Settings → AI
    shows the same flow (deep link /settings/ai/account).
  5. Admin disabled AI or no slots → real empty state, no connect prompt.

2.5 How the provider is chosen without jargon

  • One AI account per User is the default. If the User connects both, the
    last connected one is the default; Settings → AI → "Use for Ask AI" picks
    one. No per-question picker in the composer.
  • API keeps provider optional on POST /api/v1/ai/ask and on a new
    unified endpoint; the server fills the User's default. Explicit provider
    stays for API/CLI/MCP power use.

2.6 Where answers and edits appear

  • Answers (read-only): stream into the same anchored popover under the
    question, with citation chips that are §33 deep links. "Open in Ask" moves
    it to the Ask Tab thread. Esc closes; the turn keeps running and its toast
    "AI answered" reopens it. Loading never waits for motion.
  • Edits: appear live in the object through the collaboration event
    path (Yjs for Notes/Canvas, the same action routes for Tasks/Events/Files),
    with the "AI" collaborator cursor and a small status pill at the anchor
    ("AI is editing… · Stop"). No separate result window.
  • When done, an inline bar at the anchor: "AI changed 3 items · Undo · Review".
    "Review" opens the Inspector (Cmd+I) on the change list (the current
    TurnPanel content, moved from an overlay card into the Inspector).
  • No right sidebar. The current OverlaySurface Turn view stays only as the
    full-page /ai/turns/<id> deep link target.

2.7 Permissions: item-ID allowlist on the data token

  • Add ResourceScope::Items { user_id, items: Vec<ItemRef> } where ItemRef
    is a stable identity (calternal-id, item ID, event UID, task ID, canvas
    element parent), max N (e.g. 256) items, plus optional
    read_context: HomeAndShared.
  • The request authority checks every write against the allowlist; the
    Files/Notes/Tasks/Calendar/Mail/Money layers already resolve paths to item
    IDs, so the check runs after resolution, never on strings.
  • Folders handed over expand to "this folder and its descendants at the time
    of the turn, plus items the turn creates inside it".
  • Creating new items: allowed only inside a handed folder or as children of a
    handed note/canvas (e.g. new task from a note). Everything else → 403 with a
    stable error code the agent CLI can show.
  • Never: shares, public links, accounts, admin, AI routes (fix 1.4 defect).
  • Token TTL drops from 24 h to the turn's lifetime (+ grace), revoked on
    finish (already done on finish; keep).

2.8 Undo and history

  • Every AI write carries author = agent turn ID (§61 shape). For live
    documents (Canvas first, then Notes) undo is per-author undo from the
    §61 log: it keeps later human edits and lists them ("2 items you edited
    were kept"). For files, keep today's file-level undo with conflict report
    until §61 covers them.
  • Cmd/Ctrl+Z right after an AI edit undoes the whole AI turn as one step
    (§10 "each agent turn is one undo step") when focus is in the edited object.
  • Ask Tab = history. It lists every Ask AI request (answers and edits) as
    threads, newest first, filterable "Answers / Edits", each with Copy link,
    status, changed items, Undo. Settings → AI → Turn history is removed (one
    history, no duplicate).

2.9 One event path

  • The UI, API, CLI, MCP and WebMCP start Ask AI through one action in the
    #484 action registry: ai.ask { question, items?: ItemRef[], provider? }.
    /api/v1/ai/turns and /api/v1/ai/ask become thin legacy wrappers.
  • Edits by the agent go through the same collaboration/action routes as a
    human (no special agent write routes), so attribution, isolation, undo and
    live presence come for free.

2.10 The @mention aliases

  • @claude / @codex are removed from every label, hint and error.
  • Keep them as hidden aliases for one release, both in the palette (> ask @claude … still works and picks that provider) and in
    AgentProvider::from_prompt for API clients, so existing muscle memory and
    scripts do not break. Add @ai as the visible-free generic alias (default
    provider). No UI mentions them; the Keyboard/Help sheet does not list them.
  • Remove the aliases after one release if usage logs (counts only) are ~0.

2.11 Privacy, invisible and default-safe

  • Default: read-only answers; writes only to handed objects; AI never sees
    items the User did not hand it for edits. For read-only answers over
    "all my data", only Search results go to the provider (already true).
  • Mail, Money and Health-like areas are excluded from AI context unless
    handed explicitly
    (default-safe), without a setting the User must find.
  • Shared items owned by others are read-context only if the share grants
    view, never writable by AI unless handed and the share grants edit.
  • One sentence under the composer the first time only: "AI reads what you
    hand it. You can undo every change." Then never again.

3. Open decisions for the owner (grilling format)

  1. Name. Is the action "Ask AI" and the attribution "AI" (not
    "Assistant", not a mascot name)?
    Recommend: yes, "Ask AI" for the action, "AI" as collaborator name, Tab
    stays "Ask".
  2. One mode or two? Should the server infer read-only answer vs edit from
    "objects handed or not", with no Ask/Agent choice for the User?
    Recommend: yes, inference by chip; removing the chip makes it a question.
  3. Edit without confirmation? Do AI edits on handed objects apply live
    immediately (undo afterwards), or wait for an "Apply" step?
    Recommend: apply live, because history + per-author undo make it safe and
    the owner asked for "full permissions on the objects they are handed".
  4. Scope of "handed". Does handing a folder grant its descendants and new
    children; does handing a note grant creating Tasks/Events from it?
    Recommend: folder = subtree at turn start + new children inside it; a
    note may create Tasks/Events that link back to it; nothing else.
  5. Read context for edits. May an edit turn read the User's other items
    (home-and-shared) for context?
    Recommend: yes, read-only, excluding Mail and Money unless handed.
  6. Provider choice. One default AI account per User, no per-question
    picker; provider name only in Settings and the Inspector?
    Recommend: yes.
  7. Sign-in. Device-code sign-in inside the container as the main path,
    token paste as fallback?
    Recommend: yes (it is already §20; confirm it replaces the current
    paste-only flow).
  8. @claude / @codex. Keep as hidden aliases for one release, add @ai,
    then remove?
    Recommend: yes.
  9. History home. Ask Tab is the only history; remove Settings → AI →
    Turn history?
    Recommend: yes.
  10. Shortcut. Cmd/Ctrl+J for Ask AI?
    Recommend: yes, if the registry has no conflict (Cmd+J is free in the
    registry? verify; Chrome uses Cmd+J for Downloads on macOS only when not
    captured, apps commonly override it).

Not asked (already decided): no right sidebar, anchored popovers, warm
tooltips, one event path (§60), per-author undo (§61), BYO subscription (§19).


4. Implementation issues

Can start now (independent of the owner answers) are marked [now]. "Dn" = decision n in section 3; "issue n" = an issue in this list.

  1. [now] AI routes reject agent sessions — data_user() accepts any
    data token, so a running turn can start turns and read history. Reject
    installation_type='agent' on /api/v1/ai/*; add adversarial probe +
    regression test. Merge blocker class (DoS / isolation).
  2. [now] ResourceScope::Items allowlist — add an item-ID allowlist
    variant to ResourceScope, issue/renew/store it, enforce writes after
    path→item resolution in Files/Notes/Tasks/Calendar, 403 with stable code.
    Unit + cross-user matrix tests (#331).
  3. [now] Unified ai.ask action — one registry action {question, items?, provider?} that picks Ask (no items) or an item-scoped Agent turn;
    default provider from Settings; old /turns and /ask become wrappers.
    OpenAPI, CLI calternal ask, MCP and WebMCP tools from the registry.
  4. [now] Default AI account setting — store "Use for Ask AI" provider
    per User; API + CLI + MCP parity; server fills missing provider.
  5. [after D1, D6] Copy sweep: no provider names — replace every
    @claude/@codex/Claude/Codex/agent/turn/container label in
    lib/ai, lib/search, NoteView, FilesBrowser, TurnPanel, Settings with
    "Ask AI"/"AI"; one context-menu item; update CONTEXT.md glossary.
  6. [after D8] Hidden aliases — keep @claude/@codex parsing in
    palette and from_prompt, add @ai → default provider, remove from all
    hints, help and errors; count alias use (counts only).
  7. [after D7; issue 4] Device-code sign-in — run vendor login in the container,
    stream the code + URL to an in-place "Connect an AI account" card in the
    composer and Settings → AI; keep token paste as fallback; keep the draft
    question and run it after connect.
  8. [after D1, D2, D4; issues 2, 3] Ask AI composer and entry points — palette top row "Ask
    AI: …", Cmd+J, toolbar button with warm tooltip, "Ask AI about this…" in
    context menus, /ai in the editor; object chips with "AI can edit this".
    Anchored popover desktop / sheet phone; keyboard + screen reader.
  9. [after D3; issues 3, 8] Answers inline, review in the Inspector — stream answers
    with citation deep links in the popover; edit status pill at the anchor;
    "AI changed N items · Undo · Review"; move change list into the Inspector;
    keep /ai/turns/<id> as full-view deep link.
  10. [after §61 bench] Per-author undo for AI turns — author = turn ID on
    every collaboration update; Cmd+Z after an AI edit undoes the turn as one
    step; kept-items report; files keep today's undo until migrated.
  11. [after D9; issue 9] Ask Tab as the only history — threads for answers and
    edits, filter, Copy link, Undo; remove Settings → AI → Turn history;
    redirect its deep link to /ask.
  12. [after D5] Privacy defaults — exclude Mail and Money from AI read
    context unless handed; first-use one-line notice; tests that a non-handed
    Mail item never reaches the provider prompt.

Each feature issue adds a bench profile (palette open → first token, turn
start latency) and an adversarial probe section in tests/adversarial/.

# Plan: one simple "Ask AI" surface Read-only planning, 2026-10-03. Repo state: `dev` at 48c94c977. Owner direction: "a simple 'Ask AI' instead of mentioning claude or codex". Agents get full permission on the exact objects they are handed; history makes everything undoable; one collaboration event path for UI, API, MCP, WebMCP, CLI. Note: `docs/DESIGN.md` on `dev` has no §58. "Agent discovery and setup (#630)" exists as §58 only on job branches (`job/a11yfix2`, `job/agenda-decks`), and `job/admin-burst-705` uses §58 for "Instant interactions". The section number collides; fix it when the branch merges. --- ## 1. Inventory (what exists today) ### 1.1 Where the UI shows providers, @mentions and Ask | Place | File | What the User sees | |---|---|---| | Palette `>` command mode | `apps/web/src/lib/ai/commands.ts`, `lib/search/providers.ts:228-261` | "Ask @claude…", "Ask @codex…"; "Give Claude a task in your agent container"; recent turns list "Claude · running" | | Palette Ask question mode | `lib/search/window.svelte.ts` (`askComposer`), `lib/components/search-dialog.svelte:713,885` | "use the arrow keys to choose Claude or Codex", "Choose Claude or Codex to get an answer with source links" | | Note ⋯ menu and selection menu | `lib/notes/NoteView.svelte:507-508, 583-584` | "Ask @claude about this note…", "Ask @codex about the selection…" (two items each) | | Files context menu | `lib/files/FilesBrowser.svelte:1179-1180, 1277` | "Ask @claude…", "Ask @codex…" | | Context chip | `lib/search/SearchField.svelte:273-276` | "Note: Trip plan" / "3 items" chip in the palette field | | Turn view | `lib/ai/TurnPanel.svelte` (OverlaySurface card / sheet) | Title "Claude · Ask" / "Codex"; "Starting Claude in your agent container. The first turn downloads the official Claude CLI…"; "Undo this turn"; conflict sheet "Undo the rest" | | Turn page | `routes/ai/turns/[id]/+page.svelte` | Deep link `/ai/turns/<id>` to the same view | | Ask Tab | `routes/ask/+page.svelte`, `lib/navigation.ts:157,234` | Mode "Ask" at `/ask`, primary "New question", history of `kind="ask"` turns | | Settings → AI → Agents | `routes/settings/ai/AiSection.svelte`, `routes/settings/sections.ts:104-113` | Rows "Claude" and "Codex"; fields "Claude Code token" (hint: run `claude setup-token`) and "Codex access token"; copy "Ask @claude or @codex from ⌘K (type '> ask')… Agent turns run in your own isolated container" | | Settings → AI → Turn history | `routes/settings/ai/TurnHistory.svelte` | "Ask · Claude" rows; empty state "type '> ask @claude' or '> ask @codex'" | | Error toasts | `lib/ai/api.ts:127`, `lib/ai/ask.ts:52` | "Mention @claude or @codex once"; "Open Settings" → `/settings/ai/agents` | | Transcript parser | `lib/ai/transcript.ts` | Parses Claude Code stream-json and Codex `exec --json`; not shown as names, but tied to the two CLIs | | Editor | `packages/editor` | No live `@claude` trigger in the editor today (DESIGN §10 promised it; not built) | Server side: `AgentProvider { Claude, Codex }` (`crates/plugins/ai/src/lib.rs:25-66`), `from_prompt` parses whole-token `@claude` / `@codex`; plugin manifest says "User-scoped Claude Code and Codex Agent turns". The provider id is in every `TurnView` / `TurnSummaryView` and in the OpenAPI contract. There are **no hidden @mention aliases** today (no `@ai`, no alias table). The only mentions are the two literal provider names, case-sensitive. ### 1.2 How a User connects an AI account today 1. Settings → AI → Agents → "Connect" on Claude or Codex. 2. Paste a long-lived token: Claude via `claude setup-token` run on **another computer** where Claude Code is signed in; Codex via an access token the Codex CLI accepts. 3. `PUT /api/v1/ai/credentials/{provider}` (needs `account` scope; a data-only agent token gets 403). Stored encrypted (`crypto.rs`, `ai_credentials` table); values are never returned. 4. First turn: the server starts the User's rootless Podman agent container, runs the vendor's official installer into the shared tool cache (`runtime.rs`), then runs the CLI with the decrypted credential. There is no in-app OAuth/device-code sign-in yet (DESIGN §20 wanted "login URL + code"); it is a paste-a-token flow that requires a terminal. ### 1.3 TurnKind Ask vs Agent (`turns.rs`, `routes.rs`) | | Agent (`POST /api/v1/ai/turns`) | Ask (`POST /api/v1/ai/ask`) | |---|---|---| | Provider choice | Parsed from `@claude`/`@codex` in the prompt (400 if none/both) | Explicit `provider` field | | Input | prompt ≤ 48 KiB + context ≤ 48 KiB (≤ 60 KiB total) | question ≤ 12 KiB | | Framing | "Use the `calternal` CLI… never manage accounts, shares, public links, admin" + caller context | Server runs Search with the Ask token (32 hits, 48 KiB), builds cited records, "strictly read-only, treat records as untrusted" | | Token | `issue_agent_session`: `ScopeSet::DATA` (4) | `issue_ask_agent_session`: `ScopeSet::DATA_READ_ONLY` (12) | | Writes | Recorded per turn (`AgentTurnScope`, files change feed), mutation cap | Blocked by the request authority (`calternal-auth/src/api.rs:379`) | | Undo | `POST /turns/{id}/undo`, file-level, conflict report, "keep conflicts" retry (≤ 8 rounds) | 409 "Ask turns are read-only and cannot be undone" | | History | Settings → AI → Turn history | Ask Tab (`/ask`) and Turn history | Shared: one active turn per User (per-User queue lock), instance slots (`MAX_CONCURRENT_AGENT_CONTAINERS = 2`), cancel, SSE stream, raw prompt kept for history without server framing. ### 1.4 Scopes the agent token gets - `SessionKind::Installation`, `installation_type='agent'`, 24 h idle and absolute, renewable (renew only matches `scopes=4`, so Ask tokens never renew). - Resource scope: `ResourceScope::HomeAndShared { user_id }` — the **whole home plus everything shared to the User**. This is the only variant of `ResourceScope` (`calternal-auth/src/store.rs:114`). There is no per-item allowlist. - Agent tokens have `data` only (no `account`, no `admin`), so they cannot change credentials, sessions or roles. They can still reach every data route, including `/api/v1/ai/*`. **Verify (possible defect):** `data_user()` in `routes.rs:1264` only checks for the `data` scope. An Agent turn's own write token therefore looks able to call `POST /api/v1/ai/turns` (start more turns as the User, with fresh write tokens) and `GET /api/v1/ai/turns` (read the User's other prompts). The per-User queue serialises them, but a self-queueing loop is a DoS vector. Add an adversarial probe; reject agent installation sessions on `/ai/*`. --- ## 2. Proposal: "Ask AI" ### 2.1 Words - One name in the UI: **Ask AI**. The Tab stays **Ask** (icon tray label). Answers and edits are attributed to **"AI"** (collaborator name, cursor colour, history author). No "agent", "turn", "container", "CLI", "token", "Claude" or "Codex" in any default label. - The provider name appears in exactly one place: Settings → AI → "AI account" row meta ("Connected · Claude plan" / "ChatGPT plan") and in the Inspector of a past answer ("Answered with Claude"), because the User must know whose terms and quota they use. That is disclosure, not a choice to make. - Glossary: add **Ask AI** (the action), **AI account** (the connected subscription) and keep **Agent turn** as the internal term in code/docs. Update `CONTEXT.md`. ### 2.2 Entry points (all open the same composer) 1. **Palette:** Cmd/Ctrl+K, then type a question; the top result row is "Ask AI: <text>" (no `>` prefix needed, no mode to learn). `>` command mode keeps a single "Ask AI…" command. 2. **Global shortcut:** Cmd/Ctrl+J opens the palette straight in Ask AI (registry entry; warm tooltip on the toolbar button shows it). 3. **Toolbar:** one "Ask AI" icon button in the contextual top row of Notes, Files, Calendar, Tasks, Mail, Canvas (sparkle icon, tooltip "Ask AI ⌘J"). 4. **Context menus:** one item "Ask AI about this…" on notes, selections, files, events, tasks, threads, canvas elements (replaces the two @claude/@codex items). 5. **Editor:** typing `/ai` or the Ask AI shortcut in a note opens the composer anchored at the caret (anchored popover, §34), with the selection or block as the handed object. The composer is the existing palette surface (one composer, `searchWindow`), anchored as a popover on desktop, sheet on phone. It always shows the **object chip(s)** it will hand over ("Trip plan", "3 files", "Selection"). ### 2.3 One mode, server decides read vs edit The User does not choose Ask vs Agent. - **No object handed** (asked from the palette with no chip): a **read-only answer** over all the User's data (today's Ask path: Search retrieval, citations, `DATA_READ_ONLY`, home-and-shared). Nothing can change. - **Objects handed** (chip present): an **edit-capable turn** whose write token is limited to exactly those objects (see 2.7). It may read home-and-shared for context only if the User leaves "Use my other items for context" on (default on for reads, never for writes). - The User can remove a chip to turn an edit request into a question. The submit button label follows: "Ask" (no chip) / "Ask AI to edit" is NOT shown — keep one label "Ask AI"; the chip itself says "AI can edit this". ### 2.4 First-use connect flow When there is no AI account and the User presses Enter: 1. The composer morphs (shared spring) into a small "Connect an AI account" card **in place**; the question is kept as a draft. 2. Two big choices with plain words and logos-free icons: "I pay for Claude" / "I pay for ChatGPT" (subscription names Users know from billing, not the CLI names). One-line note: "Your account is used only when you ask. calternal never sees your password." 3. Sign-in: run the vendor's own login in the User's container and show the **device code + link** (DESIGN §20 already decided this): "Open claude.ai/… and enter ABCD-1234". Fallback link "Paste a token instead" for the current setup-token path. 4. On success the card collapses and the kept question runs. Settings → AI shows the same flow (deep link `/settings/ai/account`). 5. Admin disabled AI or no slots → real empty state, no connect prompt. ### 2.5 How the provider is chosen without jargon - One **AI account** per User is the default. If the User connects both, the last connected one is the default; Settings → AI → "Use for Ask AI" picks one. No per-question picker in the composer. - API keeps `provider` optional on `POST /api/v1/ai/ask` and on a new unified endpoint; the server fills the User's default. Explicit provider stays for API/CLI/MCP power use. ### 2.6 Where answers and edits appear - **Answers** (read-only): stream into the same anchored popover under the question, with citation chips that are §33 deep links. "Open in Ask" moves it to the Ask Tab thread. Esc closes; the turn keeps running and its toast "AI answered" reopens it. Loading never waits for motion. - **Edits:** appear **live in the object** through the collaboration event path (Yjs for Notes/Canvas, the same action routes for Tasks/Events/Files), with the "AI" collaborator cursor and a small status pill at the anchor ("AI is editing… · Stop"). No separate result window. - When done, an inline bar at the anchor: "AI changed 3 items · Undo · Review". "Review" opens the Inspector (Cmd+I) on the change list (the current TurnPanel content, moved from an overlay card into the Inspector). - No right sidebar. The current OverlaySurface Turn view stays only as the full-page `/ai/turns/<id>` deep link target. ### 2.7 Permissions: item-ID allowlist on the data token - Add `ResourceScope::Items { user_id, items: Vec<ItemRef> }` where `ItemRef` is a stable identity (calternal-id, item ID, event UID, task ID, canvas element parent), max N (e.g. 256) items, plus optional `read_context: HomeAndShared`. - The request authority checks every **write** against the allowlist; the Files/Notes/Tasks/Calendar/Mail/Money layers already resolve paths to item IDs, so the check runs after resolution, never on strings. - Folders handed over expand to "this folder and its descendants at the time of the turn, plus items the turn creates inside it". - Creating new items: allowed only inside a handed folder or as children of a handed note/canvas (e.g. new task from a note). Everything else → 403 with a stable error code the agent CLI can show. - Never: shares, public links, accounts, admin, AI routes (fix 1.4 defect). - Token TTL drops from 24 h to the turn's lifetime (+ grace), revoked on finish (already done on finish; keep). ### 2.8 Undo and history - Every AI write carries `author = agent turn ID` (§61 shape). For live documents (Canvas first, then Notes) undo is **per-author undo** from the §61 log: it keeps later human edits and lists them ("2 items you edited were kept"). For files, keep today's file-level undo with conflict report until §61 covers them. - **Cmd/Ctrl+Z right after an AI edit** undoes the whole AI turn as one step (§10 "each agent turn is one undo step") when focus is in the edited object. - **Ask Tab = history.** It lists every Ask AI request (answers and edits) as threads, newest first, filterable "Answers / Edits", each with Copy link, status, changed items, Undo. Settings → AI → Turn history is removed (one history, no duplicate). ### 2.9 One event path - The UI, API, CLI, MCP and WebMCP start Ask AI through **one action** in the #484 action registry: `ai.ask { question, items?: ItemRef[], provider? }`. `/api/v1/ai/turns` and `/api/v1/ai/ask` become thin legacy wrappers. - Edits by the agent go through the same collaboration/action routes as a human (no special agent write routes), so attribution, isolation, undo and live presence come for free. ### 2.10 The @mention aliases - `@claude` / `@codex` are removed from every label, hint and error. - Keep them as **hidden aliases** for one release, both in the palette (`> ask @claude …` still works and picks that provider) and in `AgentProvider::from_prompt` for API clients, so existing muscle memory and scripts do not break. Add `@ai` as the visible-free generic alias (default provider). No UI mentions them; the Keyboard/Help sheet does not list them. - Remove the aliases after one release if usage logs (counts only) are ~0. ### 2.11 Privacy, invisible and default-safe - Default: read-only answers; writes only to handed objects; AI never sees items the User did not hand it **for edits**. For read-only answers over "all my data", only Search results go to the provider (already true). - Mail, Money and Health-like areas are **excluded from AI context unless handed explicitly** (default-safe), without a setting the User must find. - Shared items owned by others are read-context only if the share grants view, never writable by AI unless handed and the share grants edit. - One sentence under the composer the first time only: "AI reads what you hand it. You can undo every change." Then never again. --- ## 3. Open decisions for the owner (grilling format) 1. **Name.** Is the action "Ask AI" and the attribution "AI" (not "Assistant", not a mascot name)? *Recommend:* yes, "Ask AI" for the action, "AI" as collaborator name, Tab stays "Ask". 2. **One mode or two?** Should the server infer read-only answer vs edit from "objects handed or not", with no Ask/Agent choice for the User? *Recommend:* yes, inference by chip; removing the chip makes it a question. 3. **Edit without confirmation?** Do AI edits on handed objects apply live immediately (undo afterwards), or wait for an "Apply" step? *Recommend:* apply live, because history + per-author undo make it safe and the owner asked for "full permissions on the objects they are handed". 4. **Scope of "handed".** Does handing a folder grant its descendants and new children; does handing a note grant creating Tasks/Events from it? *Recommend:* folder = subtree at turn start + new children inside it; a note may create Tasks/Events that link back to it; nothing else. 5. **Read context for edits.** May an edit turn read the User's other items (home-and-shared) for context? *Recommend:* yes, read-only, excluding Mail and Money unless handed. 6. **Provider choice.** One default AI account per User, no per-question picker; provider name only in Settings and the Inspector? *Recommend:* yes. 7. **Sign-in.** Device-code sign-in inside the container as the main path, token paste as fallback? *Recommend:* yes (it is already §20; confirm it replaces the current paste-only flow). 8. **@claude / @codex.** Keep as hidden aliases for one release, add `@ai`, then remove? *Recommend:* yes. 9. **History home.** Ask Tab is the only history; remove Settings → AI → Turn history? *Recommend:* yes. 10. **Shortcut.** Cmd/Ctrl+J for Ask AI? *Recommend:* yes, if the registry has no conflict (Cmd+J is free in the registry? verify; Chrome uses Cmd+J for Downloads on macOS only when not captured, apps commonly override it). Not asked (already decided): no right sidebar, anchored popovers, warm tooltips, one event path (§60), per-author undo (§61), BYO subscription (§19). --- ## 4. Implementation issues Can start **now** (independent of the owner answers) are marked [now]. "Dn" = decision n in section 3; "issue n" = an issue in this list. 1. **[now] AI routes reject agent sessions** — `data_user()` accepts any data token, so a running turn can start turns and read history. Reject `installation_type='agent'` on `/api/v1/ai/*`; add adversarial probe + regression test. Merge blocker class (DoS / isolation). 2. **[now] ResourceScope::Items allowlist** — add an item-ID allowlist variant to `ResourceScope`, issue/renew/store it, enforce writes after path→item resolution in Files/Notes/Tasks/Calendar, 403 with stable code. Unit + cross-user matrix tests (#331). 3. **[now] Unified `ai.ask` action** — one registry action `{question, items?, provider?}` that picks Ask (no items) or an item-scoped Agent turn; default provider from Settings; old `/turns` and `/ask` become wrappers. OpenAPI, CLI `calternal ask`, MCP and WebMCP tools from the registry. 4. **[now] Default AI account setting** — store "Use for Ask AI" provider per User; API + CLI + MCP parity; server fills missing `provider`. 5. **[after D1, D6] Copy sweep: no provider names** — replace every @claude/@codex/Claude/Codex/agent/turn/container label in `lib/ai`, `lib/search`, NoteView, FilesBrowser, TurnPanel, Settings with "Ask AI"/"AI"; one context-menu item; update CONTEXT.md glossary. 6. **[after D8] Hidden aliases** — keep `@claude`/`@codex` parsing in palette and `from_prompt`, add `@ai` → default provider, remove from all hints, help and errors; count alias use (counts only). 7. **[after D7; issue 4] Device-code sign-in** — run vendor login in the container, stream the code + URL to an in-place "Connect an AI account" card in the composer and Settings → AI; keep token paste as fallback; keep the draft question and run it after connect. 8. **[after D1, D2, D4; issues 2, 3] Ask AI composer and entry points** — palette top row "Ask AI: …", Cmd+J, toolbar button with warm tooltip, "Ask AI about this…" in context menus, `/ai` in the editor; object chips with "AI can edit this". Anchored popover desktop / sheet phone; keyboard + screen reader. 9. **[after D3; issues 3, 8] Answers inline, review in the Inspector** — stream answers with citation deep links in the popover; edit status pill at the anchor; "AI changed N items · Undo · Review"; move change list into the Inspector; keep `/ai/turns/<id>` as full-view deep link. 10. **[after §61 bench] Per-author undo for AI turns** — author = turn ID on every collaboration update; Cmd+Z after an AI edit undoes the turn as one step; kept-items report; files keep today's undo until migrated. 11. **[after D9; issue 9] Ask Tab as the only history** — threads for answers and edits, filter, Copy link, Undo; remove Settings → AI → Turn history; redirect its deep link to `/ask`. 12. **[after D5] Privacy defaults** — exclude Mail and Money from AI read context unless handed; first-use one-line notice; tests that a non-handed Mail item never reaches the provider prompt. Each feature issue adds a bench profile (palette open → first token, turn start latency) and an adversarial probe section in `tests/adversarial/`.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#982
No description provided.