feat(base)!: one budget per person for all their assistants, the effort each takes, the operator's lists by page

A grant now says only which assistant a person may use, and keeps their
agreement; the limits are the person's (assistant_budget): one per UTC
day and month in money and in tokens, each optional, money counting the
assistants with prices, tokens every one (Halkyon Learn's ADR 0021, D58,
D59). assistantOperator.grant gives several people several assistants
with one budget, change sets one person's exactly, revoke takes back
everything; grantees and granteesOf list people with their assistants,
budget and use, 50 at a time. The migration carries each person's grant
limits into a budget: per unit, their sum, a grant without a limit making
it none.

assistants.env gives each assistant a default and a highest effort
(_EFFORT, _MAX_EFFORT; refused at start where the provider takes none);
ask takes one within that range, refusing another (invalidEffort), and
caps the output at its ceiling (D63).

instanceOperator.waiting and accounts come 50 at a time from an offset,
and counts gives the rows of the operator's screen (D62).

BREAKING CHANGE: the offer, the grants and the operator's lists changed
shape; Learn's server now asks for build 25.

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

View file

@ -19,8 +19,10 @@ import 'package:halkyon_base_server/src/generated/admission/waiting_state.dart'
as _iuvj70hb;
import 'package:halkyon_base_server/src/generated/assistant/assistant_answer.dart'
as _i07tvte4;
import 'package:halkyon_base_server/src/generated/assistant/assistant_grant_overview.dart'
as _ily61o76;
import 'package:halkyon_base_server/src/generated/assistant/assistant_grantee.dart'
as _ixuffldn;
import 'package:halkyon_base_server/src/generated/assistant/assistant_limits.dart'
as _imik1w54;
import 'package:halkyon_base_server/src/generated/assistant/assistant_month_figures.dart'
as _i5c40jr1;
import 'package:halkyon_base_server/src/generated/assistant/assistant_question.dart'
@ -49,6 +51,8 @@ import 'package:halkyon_base_server/src/generated/keys/keys_setup.dart'
as _ii64ejy6;
import 'package:halkyon_base_server/src/generated/operator/account_overview.dart'
as _iiuvgbjo;
import 'package:halkyon_base_server/src/generated/operator/operator_counts.dart'
as _i5bpu4l1;
import 'package:halkyon_base_server/src/generated/password/authenticator.dart'
as _ip34xal6;
import 'package:halkyon_base_server/src/generated/password/authenticator_enrolment.dart'
@ -601,21 +605,26 @@ class _AssistantOperatorEndpoint {
});
}
_ida.Future<List<_ily61o76.AssistantGrantOverview>> grants(
_ist.TestSessionBuilder sessionBuilder,
) async {
_ida.Future<List<_ixuffldn.AssistantGrantee>> grantees(
_ist.TestSessionBuilder sessionBuilder, {
required String startingWith,
required int offset,
}) async {
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
var _localUniqueSession =
(sessionBuilder as _ist.InternalTestSessionBuilder).internalBuild(
endpoint: 'assistantOperator',
method: 'grants',
method: 'grantees',
);
try {
var _localCallContext = await _endpointDispatch.getMethodCallContext(
createSessionCallback: (_) => _localUniqueSession,
endpointPath: 'assistantOperator',
methodName: 'grants',
parameters: _ist.testObjectToJson({}),
methodName: 'grantees',
parameters: _ist.testObjectToJson({
'startingWith': startingWith,
'offset': offset,
}),
serializationManager: _serializationManager,
);
var _localReturnValue =
@ -623,7 +632,38 @@ class _AssistantOperatorEndpoint {
_localUniqueSession,
_localCallContext.arguments,
)
as _ida.Future<List<_ily61o76.AssistantGrantOverview>>);
as _ida.Future<List<_ixuffldn.AssistantGrantee>>);
return _localReturnValue;
} finally {
await _localUniqueSession.close();
}
});
}
_ida.Future<List<_ixuffldn.AssistantGrantee>> granteesOf(
_ist.TestSessionBuilder sessionBuilder, {
required List<_is.UuidValue> accounts,
}) async {
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
var _localUniqueSession =
(sessionBuilder as _ist.InternalTestSessionBuilder).internalBuild(
endpoint: 'assistantOperator',
method: 'granteesOf',
);
try {
var _localCallContext = await _endpointDispatch.getMethodCallContext(
createSessionCallback: (_) => _localUniqueSession,
endpointPath: 'assistantOperator',
methodName: 'granteesOf',
parameters: _ist.testObjectToJson({'accounts': accounts}),
serializationManager: _serializationManager,
);
var _localReturnValue =
await (_localCallContext.method.call(
_localUniqueSession,
_localCallContext.arguments,
)
as _ida.Future<List<_ixuffldn.AssistantGrantee>>);
return _localReturnValue;
} finally {
await _localUniqueSession.close();
@ -633,10 +673,9 @@ class _AssistantOperatorEndpoint {
_ida.Future<void> grant(
_ist.TestSessionBuilder sessionBuilder, {
required _is.UuidValue account,
required String assistant,
required int? dailyLimit,
required int? monthlyLimit,
required List<_is.UuidValue> accounts,
required List<String> assistants,
required _imik1w54.AssistantLimits budget,
}) async {
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
var _localUniqueSession =
@ -649,11 +688,47 @@ class _AssistantOperatorEndpoint {
createSessionCallback: (_) => _localUniqueSession,
endpointPath: 'assistantOperator',
methodName: 'grant',
parameters: _ist.testObjectToJson({
'accounts': accounts,
'assistants': assistants,
'budget': budget,
}),
serializationManager: _serializationManager,
);
var _localReturnValue =
await (_localCallContext.method.call(
_localUniqueSession,
_localCallContext.arguments,
)
as _ida.Future<void>);
return _localReturnValue;
} finally {
await _localUniqueSession.close();
}
});
}
_ida.Future<void> change(
_ist.TestSessionBuilder sessionBuilder, {
required _is.UuidValue account,
required List<String> assistants,
required _imik1w54.AssistantLimits budget,
}) async {
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
var _localUniqueSession =
(sessionBuilder as _ist.InternalTestSessionBuilder).internalBuild(
endpoint: 'assistantOperator',
method: 'change',
);
try {
var _localCallContext = await _endpointDispatch.getMethodCallContext(
createSessionCallback: (_) => _localUniqueSession,
endpointPath: 'assistantOperator',
methodName: 'change',
parameters: _ist.testObjectToJson({
'account': account,
'assistant': assistant,
'dailyLimit': dailyLimit,
'monthlyLimit': monthlyLimit,
'assistants': assistants,
'budget': budget,
}),
serializationManager: _serializationManager,
);
@ -673,7 +748,6 @@ class _AssistantOperatorEndpoint {
_ida.Future<void> revoke(
_ist.TestSessionBuilder sessionBuilder, {
required _is.UuidValue account,
required String assistant,
}) async {
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
var _localUniqueSession =
@ -686,10 +760,7 @@ class _AssistantOperatorEndpoint {
createSessionCallback: (_) => _localUniqueSession,
endpointPath: 'assistantOperator',
methodName: 'revoke',
parameters: _ist.testObjectToJson({
'account': account,
'assistant': assistant,
}),
parameters: _ist.testObjectToJson({'account': account}),
serializationManager: _serializationManager,
);
var _localReturnValue =
@ -1283,9 +1354,40 @@ class _InstanceOperatorEndpoint {
final _is.SerializationManager _serializationManager;
_ida.Future<List<_iiuvgbjo.AccountOverview>> waiting(
_ida.Future<_i5bpu4l1.OperatorCounts> counts(
_ist.TestSessionBuilder sessionBuilder,
) async {
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
var _localUniqueSession =
(sessionBuilder as _ist.InternalTestSessionBuilder).internalBuild(
endpoint: 'instanceOperator',
method: 'counts',
);
try {
var _localCallContext = await _endpointDispatch.getMethodCallContext(
createSessionCallback: (_) => _localUniqueSession,
endpointPath: 'instanceOperator',
methodName: 'counts',
parameters: _ist.testObjectToJson({}),
serializationManager: _serializationManager,
);
var _localReturnValue =
await (_localCallContext.method.call(
_localUniqueSession,
_localCallContext.arguments,
)
as _ida.Future<_i5bpu4l1.OperatorCounts>);
return _localReturnValue;
} finally {
await _localUniqueSession.close();
}
});
}
_ida.Future<List<_iiuvgbjo.AccountOverview>> waiting(
_ist.TestSessionBuilder sessionBuilder, {
required int offset,
}) async {
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
var _localUniqueSession =
(sessionBuilder as _ist.InternalTestSessionBuilder).internalBuild(
@ -1297,7 +1399,7 @@ class _InstanceOperatorEndpoint {
createSessionCallback: (_) => _localUniqueSession,
endpointPath: 'instanceOperator',
methodName: 'waiting',
parameters: _ist.testObjectToJson({}),
parameters: _ist.testObjectToJson({'offset': offset}),
serializationManager: _serializationManager,
);
var _localReturnValue =
@ -1378,6 +1480,7 @@ class _InstanceOperatorEndpoint {
_ida.Future<List<_iiuvgbjo.AccountOverview>> accounts(
_ist.TestSessionBuilder sessionBuilder, {
required String startingWith,
required int offset,
}) async {
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
var _localUniqueSession =
@ -1390,7 +1493,10 @@ class _InstanceOperatorEndpoint {
createSessionCallback: (_) => _localUniqueSession,
endpointPath: 'instanceOperator',
methodName: 'accounts',
parameters: _ist.testObjectToJson({'startingWith': startingWith}),
parameters: _ist.testObjectToJson({
'startingWith': startingWith,
'offset': offset,
}),
serializationManager: _serializationManager,
);
var _localReturnValue =