feat(assistant)!: no hand-off and no connector; the field searches the web (ADR 0022)

The developer decided on 23 Oct to drop the hand-off to an AI assistant
outside the app, and Claude's connector with it, in v0.19.0, the app and
the server together: build 22 raises minimumAppBuild anyway, so no app
calling the connector's endpoints uses the instance after it. An instance
is to have one way to an assistant, its operator's (ADR 0021, proposed).

The app: the field at the foot of Home, and the button in the Library, on
a studyset and while studying, search the web alone, through the engine
chosen in Settings or Android's own search, with the parts of a card the
person picks; a studyset's name and description are never offered. Gone:
the assistants a question opened in (AiAssistant, AssistantHandOff), the
switch between them and the web, Settings' choice of assistant, Connect
Claude with its route, cubit, repository and gateway, and their texts. A
settings file from before keeps the old keys until the next change of a
setting writes it anew; nothing reads them.

The server: lib/src/assistant/ (MCP, its OAuth, the five tools, connect
codes and tokens), AssistantEndpoint, the daily forgetting of the
connector's secrets (its scheduled call cancelled at start), its lines in
the deletion hook, instance.info().assistantConnectorUrl (nullable, so an
older app reads it as none), and Caddy's forwarding of /mcp, /oauth/* and
/.well-known/oauth-*. The migration drops the connector's five tables,
children first:

  DROP TABLE "assistant_token" CASCADE;       -- tokens of a connection
  DROP TABLE "assistant_grant" CASCADE;       -- OAuth codes of a connection
  DROP TABLE "assistant_connection" CASCADE;  -- a person's connected client
  DROP TABLE "assistant_connect_code" CASCADE;
  DROP TABLE "assistant_client" CASCADE;      -- OAuth clients registered

No other table references them, so CASCADE drops only their own foreign
keys. For the minute between the maintenance run and the switch, the old
server answers the connector with errors and fails to delete an account
(its hook still deletes from these tables); ADR 0022 says so.

BREAKING CHANGE: a connected Claude gets no answer from the deploy on, and
the app no longer opens a question in an AI assistant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-23 15:33:53 +02:00
commit ca24eb9cf9
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -2125,47 +2125,12 @@
"feedbackNeedsIdentity": "Sign in to report to the author and suggest.",
"kanjiExampleWords": "Words with it",
"settingsAssistant": "Assistant",
"settingsAskAssistant": "Ask an AI assistant or search the web",
"settingsAskAssistantSubtitle": "Off unless you turn it on. Adds a field on Home and a button in the Library, on a studyset and while studying. A question opens in the AI assistant you choose or as a web search, with only the context you choose; nothing is sent from here.",
"assistantHint": "Ask {assistant}…",
"@assistantHint": {"placeholders": {"assistant": {"type": "String"}}},
"assistantHintStudyset": "Ask {assistant} about {title}…",
"@assistantHintStudyset": {"placeholders": {"assistant": {"type": "String"}, "title": {"type": "String"}}},
"assistantHintCard": "Ask {assistant} about this card…",
"@assistantHintCard": {"placeholders": {"assistant": {"type": "String"}}},
"assistantSend": "Ask in {assistant}",
"@assistantSend": {"placeholders": {"assistant": {"type": "String"}}},
"assistantOpen": "Ask or search",
"settingsAssistant": "Web search",
"settingsAskAssistant": "Search the web from the app",
"settingsAskAssistantSubtitle": "Off unless you turn it on. Adds a field on Home and a button in the Library, on a studyset and while studying. A question opens as a web search, with only the parts of a card you choose; nothing is sent from here.",
"assistantOpen": "Search the web",
"assistantClose": "Close",
"assistantOpenFailed": "{assistant} couldn't be opened.",
"@assistantOpenFailed": {"placeholders": {"assistant": {"type": "String"}}},
"assistantConnectTitle": "Connect Claude",
"assistantConnectSubtitle": "Let Claude read your studysets",
"assistantConnectIntro": "Claude can read your studysets, cards, notes and progress on {instance} through Halkyon Learn's connector, and answer about them. It can't change anything. What it reads goes to Anthropic, under their privacy policy.",
"@assistantConnectIntro": {"placeholders": {"instance": {"type": "String"}}},
"assistantStepAdd": "In Claude, open Settings › Connectors and add a custom connector named Halkyon Learn, with this address. Added once, it works in Claude's apps too.",
"assistantStepCode": "Claude then opens a page asking for a code. Get one here: it works once, for ten minutes.",
"assistantShowCode": "Show a code",
"assistantNewCode": "New code",
"assistantCodeValid": "Valid until {time}",
"@assistantCodeValid": {"placeholders": {"time": {"type": "String"}}},
"assistantCopy": "Copy",
"assistantCopied": "Copied",
"assistantConnected": "Connected",
"assistantNoneConnected": "Nothing connected yet.",
"assistantConnectedOn": "connected {date}",
"@assistantConnectedOn": {"placeholders": {"date": {"type": "String"}}},
"assistantLastRead": "last read {date}",
"@assistantLastRead": {"placeholders": {"date": {"type": "String"}}},
"assistantDisconnect": "Disconnect",
"assistantDisconnectTitle": "Disconnect {name}?",
"@assistantDisconnectTitle": {"placeholders": {"name": {"type": "String"}}},
"assistantDisconnectMessage": "It can't read your studysets any more. You can connect it again with a new code.",
"assistantNotOffered": "This instance doesn't offer a connector yet. Questions still open in the AI assistant you chose, with what they are about.",
"assistantUnreachable": "The instance can't be reached. Try again once you are online.",
"assistantRetry": "Try again",
"assistantLineFront": "Front: {text}",
"@assistantLineFront": {"description": "A line of context handed to the AI assistant after the question.", "placeholders": {"text": {"type": "String"}}},
@ -2175,23 +2140,14 @@
"@assistantLineHint": {"placeholders": {"number": {"type": "int"}, "text": {"type": "String"}}},
"assistantLineExplanation": "Explanation: {text}",
"@assistantLineExplanation": {"placeholders": {"text": {"type": "String"}}},
"assistantLineStudyset": "Studyset: “{title}” in Halkyon Learn on {instance}",
"@assistantLineStudyset": {"placeholders": {"title": {"type": "String"}, "instance": {"type": "String"}}},
"assistantLineDescription": "Description: {text}",
"@assistantLineDescription": {"placeholders": {"text": {"type": "String"}}},
"assistantChipStudyset": "Studyset: {title}",
"@assistantChipStudyset": {"placeholders": {"title": {"type": "String"}}},
"assistantContextLabel": "Send along",
"assistantTargetWeb": "Web",
"assistantSwitchToWeb": "Search the web instead",
"assistantSwitchToAssistant": "Ask {assistant} instead",
"@assistantSwitchToAssistant": {"placeholders": {"assistant": {"type": "String"}}},
"assistantHintWeb": "Search the web…",
"assistantSendWeb": "Search",
"assistantSearchFailed": "The search couldn't be opened.",
"settingsSearchEngine": "Search engine",
"settingsAiAssistant": "AI assistant",
"aiAssistantCustomHint": "https://example.org/chat?q=%s",
"searchEngineDevice": "The device's search",
"searchEngineCustom": "Your own address",
"searchEngineCustomHint": "https://example.org/search?q=%s",
@ -2437,7 +2393,7 @@
"studysetPrivateBody": "Everything in it is end-to-end encrypted on your devices: the instance can't read it, publish it or show it to Claude. Copies in the instance's backups stay readable until those expire.",
"studysetReadableTitle": "Make “{title}” non-private?",
"@studysetReadableTitle": {"placeholders": {"title": {"type": "String"}}},
"studysetReadableBody": "Everything in it is decrypted and synced again, so the instance can read it: you can publish it, and Claude can read it through the connector.",
"studysetReadableBody": "Everything in it is decrypted and synced again, so the instance can read it: you can publish it.",
"studysetPrivateNoKeys": "Sign in with your password on this device to make studysets private.",
"studysetPrivateSomeLocked": "Some of it couldn't be opened on this device and stayed as it was.",
"publishTagsSent": "The names of the tags that go with it, which the instance learns: {names}",
@ -2479,7 +2435,7 @@
"studysetPrivacyPrivateBody": "Everything in it is end-to-end encrypted: only your devices can read it. The instance can't publish it, nor show it to Claude. Making it non-private decrypts it and syncs it again, so the instance can read it.",
"studysetPrivacyReadableTitle": "“{title}” isn't private",
"@studysetPrivacyReadableTitle": {"placeholders": {"title": {"type": "String"}}},
"studysetPrivacyReadableBody": "The instance can read it: you can publish it, and Claude can read it through the connector. Making it private encrypts it end to end on your devices; copies in the instance's backups stay readable until those expire.",
"studysetPrivacyReadableBody": "The instance can read it: you can publish it. Making it private encrypts it end to end on your devices; copies in the instance's backups stay readable until those expire.",
"studysetPrivacyTopic": "Its privacy is that of the studyset it's in.",
"studysetPrivacyPublish": "Publish…"
}