Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions lib/chat/panels/context_usage_panel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ class ContextUsagePanel extends StatefulWidget {
/// Claude Code's categories (by their English names, as it reports
/// them) as the usage views' parts: an agent's definition is in the
/// Agent tool's, the servers' instructions with their tools, the slash
/// commands with the skills.
@visibleForTesting
/// commands with the skills. The phone is told them so too.
static String? partOf(String category) =>
switch (category.toLowerCase().trim()) {
'system prompt' => 'system',
Expand Down
25 changes: 25 additions & 0 deletions lib/link/workspace_bridge.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import '../bots/bots.dart';
import '../chat/chat_models.dart';
import '../chat/chat_session.dart';
import '../chat/composer/composer_files.dart' show fileReferenceText;
import '../chat/panels/context_usage_panel.dart' show ContextUsagePanel;
import '../chat/rewind_files_setting.dart';
import '../customize/customization_store.dart';
import '../customize/customizations.dart';
Expand Down Expand Up @@ -1287,9 +1288,11 @@ class _Follow {
? null
: _from > 0 || session.hasEarlierHistory || session.loadingEarlier;
final goal = _goal(session.goal);
final context = _context(session.context);
final restText = jsonEncode([
earlier,
goal,
context,
session.isStreaming,
interaction,
[for (final choice in choices) choice.toJson()],
Expand Down Expand Up @@ -1328,9 +1331,31 @@ class _Follow {
tasks: tasks,
earlier: earlier,
goal: goal,
context: context,
);
}

/// [usage] as the phone reads it (see [LinkSessionUpdate.context]).
static Map<String, Object?>? _context(ContextUsage? usage) {
if (usage == null || usage.window <= 0) return null;
final parts = <String, int>{};
for (final segment in usage.segments) {
if (segment.kind != ContextKind.used || segment.tokens <= 0) continue;
final part = ContextUsagePanel.partOf(segment.label) ?? segment.label;
parts[part] = (parts[part] ?? 0) + segment.tokens;
}
return {
'window': usage.window,
'used': usage.used,
if (parts.isNotEmpty)
'parts': [
for (final MapEntry(:key, :value) in parts.entries)
{'part': key, 'tokens': value},
],
'compactAt': ?usage.detail?.autoCompactAt,
};
}

/// [goal] as the phone reads it (see [LinkSessionUpdate.goal]).
static Map<String, Object?>? _goal(KernelGoal? goal) => switch (goal) {
null => null,
Expand Down
13 changes: 13 additions & 0 deletions packages/bao_link/lib/src/protocol.dart
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ class LinkSessionUpdate {
this.tasks = const [],
this.earlier,
this.goal,
this.context,
});

factory LinkSessionUpdate.fromJson(Map<String, Object?> json) =>
Expand Down Expand Up @@ -779,6 +780,10 @@ class LinkSessionUpdate {
final Map<String, Object?> map => map,
_ => null,
},
context: switch (json['context']) {
final Map<String, Object?> map => map,
_ => null,
},
);

final String session;
Expand Down Expand Up @@ -814,6 +819,13 @@ class LinkSessionUpdate {
/// "duration", "tokens"}`, its times in milliseconds.
final Map<String, Object?>? goal;

/// How full the agent's context is, if its kernel says: `{"window",
/// "used", "parts": [{"part", "tokens"}], "compactAt"}`, a part by the
/// desktop's key for it (`system`, `tools`, `mcp`, `memory`, `skills`,
/// `messages`) or the kernel's own name, `compactAt` where it is
/// compacted, in tokens.
final Map<String, Object?>? context;

Map<String, Object?> toJson() => {
'session': session,
'start': start,
Expand All @@ -826,6 +838,7 @@ class LinkSessionUpdate {
if (tasks.isNotEmpty) 'tasks': [for (final task in tasks) task.toJson()],
'earlier': ?earlier,
'goal': ?goal,
'context': ?context,
};
}

Expand Down
26 changes: 26 additions & 0 deletions packages/bao_link/test/link_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,32 @@ void main() {
}
});

test('a session update carries how full the context is, when told', () {
const plain = LinkSessionUpdate(
session: 's',
start: 0,
items: [],
count: 0,
);
expect(plain.toJson().containsKey('context'), isFalse);
const context = {
'window': 200000,
'used': 52000,
'parts': [
{'part': 'messages', 'tokens': 40000},
],
'compactAt': 167000,
};
const update = LinkSessionUpdate(
session: 's',
start: 0,
items: [],
count: 0,
context: context,
);
expect(LinkSessionUpdate.fromJson(update.toJson()).context, context);
});

test('a bot\'s picture is told from an emoji by its file\'s name', () {
expect(isAvatarImage('avatar-0a1b2c3d4e5f.jpg'), isTrue);
expect(isAvatarImage('avatar-0a1b2c3d4e5f.PNG'), isFalse);
Expand Down
Loading