feat(assistant): a question opens in the AI assistant chosen, Proton's Lumo by default

Questions asked from the app went to Claude alone. Settings › Assistant
now chooses where they open (decided 2026-10-14): Lumo, the default, by
its guest chat with the question in the address's fragment (Lumo's
`prefill`, which needs no account and reaches no server; the address for
one's own account loses the question when nobody is signed in), ChatGPT,
Claude, Mistral, Perplexity, Duck.ai, Grok, Kagi Assistant, HuggingChat,
or an address of one's own with %s. Each link fills the question in for
the person to send (Perplexity answers at once, Grok asks first); Gemini,
which has no such link, and Copilot, whose stopped working, are left out.
The field names the assistant ("Ask Lumo…"). A device that sent questions
to Claude goes on asking an assistant, now Lumo. Connecting Claude to read
studysets stays as it was.

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-14 13:19:51 +02:00
commit 8dde0fa42a
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
2 changed files with 701 additions and 207 deletions

View file

@ -89,7 +89,7 @@ void main() {
expect(store.writes, [after]);
});
test('asking Claude, turned on, keeps the rest and is kept', () async {
test('asking an assistant, turned on, keeps the rest and is kept', () async {
final store = MemoryDeviceSettingsStore();
final bloc = DeviceSettingsBloc(
store,
@ -103,7 +103,7 @@ void main() {
expect(store.writes, [after]);
});
test('a copy for another choice keeps asking Claude', () {
test('a copy for another choice keeps asking an assistant', () {
const before = DeviceSettings(assistant: true);
expect(before.withAppearance(Appearance.light).assistant, isTrue);

View file

@ -56,7 +56,7 @@ void main() {
});
test(
"doesn't ask Claude until turned on, then keeps it, and the rest",
"doesn't ask an assistant until turned on, then keeps it, and the rest",
() async {
expect((await store.read()).assistant, isFalse);
@ -70,15 +70,18 @@ void main() {
},
);
test('keeps where questions go and the search engine, its own address '
'too', () async {
test('keeps where questions go, the assistant and the search engine, '
'their own addresses too', () async {
final first = await store.read();
expect(first.assistantTarget, AssistantTarget.claude);
expect(first.assistantTarget, AssistantTarget.assistant);
expect(first.aiAssistant, isNull);
expect(first.searchEngine, isNull);
await store.write(
const DeviceSettings(
assistantTarget: AssistantTarget.web,
aiAssistant: 'custom',
customAssistantUrl: 'https://chat.example.org/?q=%s',
searchEngine: 'custom',
customSearchUrl: 'https://search.example.org/?q=%s',
),
@ -86,10 +89,21 @@ void main() {
final read = await FileDeviceSettingsStore(file).read();
expect(read.assistantTarget, AssistantTarget.web);
expect(read.aiAssistant, 'custom');
expect(read.customAssistantUrl, 'https://chat.example.org/?q=%s');
expect(read.searchEngine, 'custom');
expect(read.customSearchUrl, 'https://search.example.org/?q=%s');
});
test('questions a device sent to Claude, the only assistant before, go to '
'the assistant chosen', () async {
file.writeAsStringSync('{"assistant": true, "assistantTarget": "claude"}');
final read = await FileDeviceSettingsStore(file).read();
expect(read.assistantTarget, AssistantTarget.assistant);
expect(read.aiAssistant, isNull);
});
test('keeps a return to the device language', () async {
await store.write(const DeviceSettings(language: 'en-US'));
await store.write(const DeviceSettings(appearance: Appearance.light));