feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
import 'package:clock/clock.dart';
|
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
|
import 'package:halkyon_app/halkyon_app.dart';
|
|
|
|
|
import 'package:halkyon_app/testing.dart';
|
|
|
|
|
import 'package:halkyon_llm/halkyon_llm.dart';
|
|
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
// The person's assistants in an app's settings, the operator granting them
|
|
|
|
|
// to several people at once with one budget each (D58), and the use by
|
|
|
|
|
// month, the oldest on the left (Halkyon Learn's ADR 0021, step 2, D57).
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
|
|
|
|
|
const _claude = OfferedAssistant(
|
|
|
|
|
id: 'claude',
|
|
|
|
|
name: 'Claude',
|
|
|
|
|
protocol: 'anthropic',
|
|
|
|
|
host: 'api.anthropic.com',
|
|
|
|
|
model: 'claude-sonnet-5-5',
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const _own = OwnAssistant(
|
|
|
|
|
id: 'own-1',
|
|
|
|
|
name: 'My GPT',
|
|
|
|
|
protocol: LlmProtocol.openai,
|
|
|
|
|
model: 'gpt-5',
|
|
|
|
|
apiKey: 'sk-own',
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const _setUp = AssistantsSetUp(
|
|
|
|
|
assistants: [
|
|
|
|
|
SetUpAssistant(
|
|
|
|
|
id: 'claude',
|
|
|
|
|
name: 'Claude',
|
|
|
|
|
protocol: 'anthropic',
|
|
|
|
|
host: 'api.anthropic.com',
|
|
|
|
|
model: 'claude-sonnet-5-5',
|
|
|
|
|
priceInput: 3,
|
|
|
|
|
priceOutput: 15,
|
|
|
|
|
),
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
SetUpAssistant(
|
|
|
|
|
id: 'opus',
|
|
|
|
|
name: 'Opus',
|
|
|
|
|
protocol: 'anthropic',
|
|
|
|
|
host: 'api.anthropic.com',
|
|
|
|
|
model: 'claude-opus-5-5',
|
|
|
|
|
),
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
group('AssistantsCubit', () {
|
|
|
|
|
late FakeAssistantGateway gateway;
|
|
|
|
|
late AssistantRepository repository;
|
|
|
|
|
late MemoryOwnAssistantStore store;
|
|
|
|
|
late AssistantsCubit cubit;
|
|
|
|
|
|
|
|
|
|
setUp(() {
|
|
|
|
|
gateway = FakeAssistantGateway()
|
|
|
|
|
..offer = const AssistantsOffer(assistants: [_claude], ownKeys: true);
|
|
|
|
|
repository = AssistantRepository(gateway, llm: FakeLlmClient());
|
|
|
|
|
store = MemoryOwnAssistantStore();
|
|
|
|
|
cubit = AssistantsCubit(repository, ownAssistants: store);
|
|
|
|
|
addTearDown(() async {
|
|
|
|
|
await cubit.close();
|
|
|
|
|
await repository.dispose();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('reads what is offered, and the own assistants as they '
|
|
|
|
|
'change', () async {
|
|
|
|
|
await cubit.load();
|
|
|
|
|
expect(cubit.state.loading, isFalse);
|
|
|
|
|
expect(cubit.state.offer.assistants, [_claude]);
|
|
|
|
|
expect(cubit.state.showsAnything, isTrue);
|
|
|
|
|
|
|
|
|
|
expect(await cubit.saveOwn(_own), OwnAssistantSaved.kept);
|
|
|
|
|
await pumpEventQueue();
|
|
|
|
|
expect(cubit.state.own, [_own]);
|
|
|
|
|
|
|
|
|
|
await cubit.removeOwn('own-1');
|
|
|
|
|
await pumpEventQueue();
|
|
|
|
|
expect(cubit.state.own, isEmpty);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('shows nothing where nothing is offered, own keys are not '
|
|
|
|
|
'allowed, and none are kept', () async {
|
|
|
|
|
gateway.offer = AssistantsOffer.none;
|
|
|
|
|
await cubit.load();
|
|
|
|
|
|
|
|
|
|
expect(cubit.state.showsAnything, isFalse);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("a key isn't kept where the settings wouldn't be sealed", () async {
|
|
|
|
|
store.unsealed = true;
|
|
|
|
|
|
|
|
|
|
expect(await cubit.saveOwn(_own), OwnAssistantSaved.refused);
|
|
|
|
|
expect(store.assistants, isEmpty);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('agreeing reads the offer anew', () async {
|
|
|
|
|
await cubit.load();
|
|
|
|
|
await cubit.agree('claude');
|
|
|
|
|
|
|
|
|
|
expect(cubit.state.offer.assistants.single.agreed, isTrue);
|
|
|
|
|
expect(cubit.state.agreeing, isNull);
|
|
|
|
|
expect(cubit.state.problem, isNull);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('a refused agreement is the problem, the offer as it was', () async {
|
|
|
|
|
await cubit.load();
|
|
|
|
|
gateway.failing = const AssistantFailed(AssistantProblem.notAllowed);
|
|
|
|
|
|
|
|
|
|
await cubit.agree('claude');
|
|
|
|
|
|
|
|
|
|
expect(
|
|
|
|
|
cubit.state.problem,
|
|
|
|
|
const AssistantFailed(AssistantProblem.notAllowed),
|
|
|
|
|
);
|
|
|
|
|
expect(cubit.state.agreeing, isNull);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("an offer that can't be read offers nothing", () async {
|
|
|
|
|
gateway.failing = const AssistantFailed(AssistantProblem.failed);
|
|
|
|
|
await cubit.load();
|
|
|
|
|
|
|
|
|
|
expect(
|
|
|
|
|
cubit.state.unreadable,
|
|
|
|
|
const AssistantFailed(AssistantProblem.failed),
|
|
|
|
|
);
|
|
|
|
|
expect(cubit.state.offer, AssistantsOffer.none);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('an app keeping no own assistants keeps none', () async {
|
|
|
|
|
final without = AssistantsCubit(repository);
|
|
|
|
|
addTearDown(without.close);
|
|
|
|
|
|
|
|
|
|
expect(await without.saveOwn(_own), OwnAssistantSaved.failed);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
group('AssistantGrantCubit', () {
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
late FakeAssistantOperatorGateway gateway;
|
|
|
|
|
late FakeInstanceOperatorGateway accounts;
|
|
|
|
|
late AssistantOperatorRepository repository;
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
|
|
|
|
|
final anna = OperatedAccount(
|
|
|
|
|
id: 'anna',
|
|
|
|
|
handle: 'anna',
|
|
|
|
|
since: DateTime(2026),
|
|
|
|
|
);
|
|
|
|
|
final ben = OperatedAccount(
|
|
|
|
|
id: 'ben',
|
|
|
|
|
handle: 'ben',
|
|
|
|
|
since: DateTime(2026),
|
|
|
|
|
);
|
|
|
|
|
const budget = AssistantBudget(dailyCostMicros: 1000000, monthlyTokens: 9);
|
|
|
|
|
|
|
|
|
|
AssistantGrantCubit cubitOf({
|
|
|
|
|
List<OperatedAccount> accounts = const [],
|
|
|
|
|
AssistantGrantee? grantee,
|
|
|
|
|
}) {
|
|
|
|
|
final cubit = AssistantGrantCubit(
|
|
|
|
|
repository,
|
|
|
|
|
setUp: _setUp,
|
|
|
|
|
accounts: accounts,
|
|
|
|
|
grantee: grantee,
|
|
|
|
|
);
|
|
|
|
|
addTearDown(cubit.close);
|
|
|
|
|
return cubit;
|
|
|
|
|
}
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
|
|
|
|
|
setUp(() {
|
|
|
|
|
gateway = FakeAssistantOperatorGateway()
|
|
|
|
|
..set = _setUp
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
..accounts.addAll({'anna': 'anna', 'ben': 'ben'});
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
accounts = FakeInstanceOperatorGateway()
|
|
|
|
|
..operates = true
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
..add(anna)
|
|
|
|
|
..add(ben);
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
repository = AssistantOperatorRepository(gateway, accounts);
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
addTearDown(repository.dispose);
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
});
|
|
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
test('grants each person each assistant chosen, all with one '
|
|
|
|
|
'budget', () async {
|
|
|
|
|
final cubit = cubitOf(accounts: [anna])
|
|
|
|
|
..add(ben)
|
|
|
|
|
..chooseAll(const ['claude', 'opus'], on: true);
|
|
|
|
|
expect(cubit.state.canSave, isTrue);
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
expect(await cubit.save(budget), isTrue);
|
|
|
|
|
|
|
|
|
|
expect(gateway.calls.last, 'grant anna ben claude opus');
|
|
|
|
|
expect(gateway.people['ben']!.budget, budget);
|
|
|
|
|
expect(cubit.state.busy, isFalse);
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
});
|
|
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
test('a person is added once, and taken off again', () {
|
|
|
|
|
final cubit = cubitOf()
|
|
|
|
|
..add(anna)
|
|
|
|
|
..add(anna)
|
|
|
|
|
..add(ben)
|
|
|
|
|
..remove('anna');
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
expect(cubit.state.accounts, [ben]);
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
});
|
|
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
test("names the people whose budget a grant replaces, as they're "
|
|
|
|
|
'added', () async {
|
|
|
|
|
await repository.grant(
|
|
|
|
|
accounts: ['ben'],
|
|
|
|
|
assistants: ['claude'],
|
|
|
|
|
budget: budget,
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
);
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
final cubit = cubitOf(accounts: [anna]);
|
|
|
|
|
await pumpEventQueue();
|
|
|
|
|
expect(cubit.state.replaced, isEmpty);
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
cubit.add(ben);
|
|
|
|
|
await pumpEventQueue();
|
|
|
|
|
expect([for (final each in cubit.state.replaced) each.account], ['ben']);
|
|
|
|
|
|
|
|
|
|
cubit.remove('ben');
|
|
|
|
|
await pumpEventQueue();
|
|
|
|
|
expect(cubit.state.replaced, isEmpty);
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
});
|
|
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
test('chooses assistants one by one, or a whole provider', () {
|
|
|
|
|
final cubit = cubitOf()..choose('claude', on: true);
|
|
|
|
|
expect(cubit.state.chosen, {'claude'});
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
cubit.chooseAll(const ['claude', 'opus'], on: true);
|
|
|
|
|
expect(cubit.state.chosen, {'claude', 'opus'});
|
|
|
|
|
|
|
|
|
|
cubit.chooseAll(const ['claude', 'opus'], on: false);
|
|
|
|
|
expect(cubit.state.chosen, isEmpty);
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
});
|
|
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
test('saves nothing without a person and an assistant', () async {
|
|
|
|
|
final nobody = cubitOf()..choose('claude', on: true);
|
|
|
|
|
final nothing = cubitOf(accounts: [anna]);
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
for (final cubit in [nobody, nothing]) {
|
|
|
|
|
expect(cubit.state.canSave, isFalse);
|
|
|
|
|
expect(await cubit.save(budget), isFalse);
|
|
|
|
|
}
|
|
|
|
|
expect(gateway.calls.where((call) => call.startsWith('grant ')), isEmpty);
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
});
|
|
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
test("the instance's refusal is the sheet's, and nothing is "
|
|
|
|
|
'granted', () async {
|
|
|
|
|
final cubit = cubitOf(accounts: [anna])..choose('claude', on: true);
|
|
|
|
|
|
|
|
|
|
expect(
|
|
|
|
|
await cubit.save(const AssistantBudget(dailyTokens: -1)),
|
|
|
|
|
isFalse,
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
cubit.state.refused,
|
|
|
|
|
const OperatorFailed(OperatorProblem.invalidLimit),
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
);
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
expect(cubit.state.busy, isFalse);
|
|
|
|
|
expect(gateway.people, isEmpty);
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
});
|
|
|
|
|
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
test("changing starts from the person's assistants still set, and "
|
|
|
|
|
'gives them exactly those chosen', () async {
|
|
|
|
|
final grantee = AssistantGrantee(
|
|
|
|
|
account: 'anna',
|
|
|
|
|
handle: 'anna',
|
|
|
|
|
grants: [
|
|
|
|
|
AssistantGrant(assistant: 'claude', grantedAt: DateTime(2026)),
|
|
|
|
|
AssistantGrant(
|
|
|
|
|
assistant: 'gone',
|
|
|
|
|
assistantSet: false,
|
|
|
|
|
grantedAt: DateTime(2026),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
budget: budget,
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
);
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
final cubit = cubitOf(grantee: grantee);
|
|
|
|
|
expect(cubit.state.chosen, {'claude'});
|
|
|
|
|
|
|
|
|
|
cubit
|
|
|
|
|
..choose('claude', on: false)
|
|
|
|
|
..choose('opus', on: true);
|
|
|
|
|
expect(await cubit.save(AssistantBudget.none), isTrue);
|
|
|
|
|
|
|
|
|
|
expect(gateway.calls.last, 'change anna opus');
|
|
|
|
|
expect(gateway.people['anna']!.budget, AssistantBudget.none);
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
group('AssistantUseCubit', () {
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
test('offers this UTC month and the two before, the oldest first, and '
|
|
|
|
|
'reads the one shown', () async {
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
final asked = <DateTime>[];
|
|
|
|
|
final use = AssistantUse(
|
|
|
|
|
assistant: 'claude',
|
|
|
|
|
month: DateTime.utc(2026),
|
|
|
|
|
requests: 2,
|
|
|
|
|
);
|
|
|
|
|
final cubit = withClock(
|
|
|
|
|
Clock.fixed(DateTime.utc(2026, 1, 31, 23, 30)),
|
|
|
|
|
() => AssistantUseCubit((month) async {
|
|
|
|
|
asked.add(month);
|
|
|
|
|
return [use];
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
addTearDown(cubit.close);
|
|
|
|
|
|
|
|
|
|
expect(cubit.state.months, [
|
|
|
|
|
DateTime.utc(2025, 11),
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
DateTime.utc(2025, 12),
|
|
|
|
|
DateTime.utc(2026),
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
]);
|
feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and
effort:
- The operator's screen is an overview (Waiting, Operators, Accounts,
Assistants, each with its count) whose rows open screens of their own,
read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so
hundreds of accounts push nothing else down (D62).
- A handle is picked among the accounts there are (AccountSuggestionField):
appointing an operator and granting take no other (D61).
- AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set
up by provider, with their efforts and prices; the people granted, found
by their handle's start; one sheet granting several assistants, a
provider's at once, to several people, each with one budget in money and
tokens a day and a month, or changing one person's (D58, D59).
- Assistants are grouped by provider wherever they are listed, each under
its name with a dot in its colour (ProviderLabel, D60).
- AssistantsCard shows what is left of the person's budget for all their
assistants together; the months of the figures run oldest first (D57).
- AssistantChoice says the efforts a question may ask at and the ceiling
on output each asks for (D63); a person's own key gets none and keeps
4,096 tokens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 16:05:49 +01:00
|
|
|
expect(cubit.state.month, DateTime.utc(2026));
|
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
|
|
|
await cubit.show();
|
|
|
|
|
expect(cubit.state.use, [use]);
|
|
|
|
|
expect(cubit.state.loading, isFalse);
|
|
|
|
|
|
|
|
|
|
await cubit.show(DateTime.utc(2025, 11));
|
|
|
|
|
expect(cubit.state.month, DateTime.utc(2025, 11));
|
|
|
|
|
expect(asked, [DateTime.utc(2026), DateTime.utc(2025, 11)]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("says when the use couldn't be read", () async {
|
|
|
|
|
final cubit = AssistantUseCubit(
|
|
|
|
|
(month) async => throw const AssistantFailed(AssistantProblem.failed),
|
|
|
|
|
);
|
|
|
|
|
addTearDown(cubit.close);
|
|
|
|
|
|
|
|
|
|
await cubit.show();
|
|
|
|
|
|
|
|
|
|
expect(cubit.state.failed, isTrue);
|
|
|
|
|
expect(cubit.state.use, isEmpty);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|