ahp_flutter 0.1.0 copy "ahp_flutter: ^0.1.0" to clipboard
ahp_flutter: ^0.1.0 copied to clipboard

Flutter bindings for the Agent Host Protocol — scoped runtime, refcounted channel subscriptions, and rebuild narrowing for streaming state.

ahp_flutter #

Flutter bindings for the Agent Host Protocol.

Built on Flutter's own primitives — InheritedWidget, InheritedNotifier, ValueNotifier — rather than a state-management package, so adopting AHP does not mean adopting an opinion about state management too. The core ahp_sdk package is pure Dart and stream-based; this adapts it to the widget tree.

Full documentation lives at https://mosesgameli.github.io/ahp/flutter/setup/.

Install #

flutter pub add ahp_flutter

The bindings re-export the whole SDK, so this is the only import a Flutter app needs:

import 'package:ahp_flutter/ahp_flutter.dart';

Usage #

Scope a runtime, then watch channels beneath it:

AhpProvider(
  runtime: AhpRuntime(
    connect: () async => AhpConnection(
      await WebSocketAhpTransport.connect(Uri.parse('ws://localhost:51234')),
    ),
    clientId: 'my-app',
  ),
  child: const MyApp(),
);
AhpChannel<RootState>(
  channel: Uri.parse('ahp-root://'),
  builder: (context, root, _) => Text('${root.agents.length} agents'),
);

Rebuild narrowing #

A chat transcript genuinely changes on every streamed token, so watching whole channel state rebuilds the world per token. Three layers keep that affordable, cheapest first.

The host coalesces. SubscribeOptions.maxLatencyMs defaults to one frame for chat and terminal channels, so the host batches server-side. This is strictly cheaper than anything the client can do and needs no code.

Selectors compare by identity first. The reducers are contracted to return untouched sub-objects unchanged, so an identity check hits on the common case.

Structure narrows separately from content. Select the id list outside and the per-id entry inside; appending a token then rebuilds exactly one row:

AhpSelector<ChatState, List<String>>(
  channel: chat,
  select: (s) => s.messageIds,          // identity-stable across a token
  builder: (context, ids, _) => ListView.builder(
    itemCount: ids.length,
    itemBuilder: (c, i) => _Row(key: ValueKey(ids[i]), id: ids[i]),
  ),
);

Watching is a lease, not a read #

There is deliberately no context.ahpWatch(...). Watching acquires a refcounted subscription, and a context extension has no dispose hook to release it — every rebuild would leak one, and it would fire an outbound subscribe from inside build. Use AhpChannel, AhpSelector, or AhpChannelMixin on a State, all of which release on dispose.

Example #

example/ is a macOS app that mirrors a live host: connection status, agent list, and session count, each narrowed to its own slice. It also reconnects on its own when the host restarts.

code agent host --port 51234 --without-connection-token
cd example && flutter run -d macos

Testing #

Widget tests drive a real AhpRuntime against a fake AhpClient, with two settings that keep its timers from outliving the tree:

AhpRuntime(
  connect: () async => fakeClient,
  clientId: 'test',
  keepaliveInterval: null,          // no periodic ping
  lingerDuration: Duration.zero,    // tear subscriptions down inline
);

Status #

Early, and tracking a DRAFT protocol. See CHANGELOG.md.

LICENSE is still the flutter create placeholder — pick one before publishing.

1
likes
160
points
36
downloads

Documentation

Documentation
API reference

Publisher

verified publishermosesgameli.com

Weekly Downloads

Flutter bindings for the Agent Host Protocol — scoped runtime, refcounted channel subscriptions, and rebuild narrowing for streaming state.

Repository (GitHub)
View/report issues

Topics

#agent #protocol #state-management #websocket

License

MIT (license)

Dependencies

ahp_sdk, flutter

More

Packages that depend on ahp_flutter