{"id":47829439,"url":"https://github.com/memex-lab/dart_agent_core","last_synced_at":"2026-04-03T20:07:45.433Z","repository":{"id":342470145,"uuid":"1173599154","full_name":"memex-lab/dart_agent_core","owner":"memex-lab","description":"Dart framework for stateful AI agents — tool use, skills, sub-agent delegation, planning, streaming, and multi-provider LLM support (OpenAI,        Gemini, Bedrock).","archived":false,"fork":false,"pushed_at":"2026-03-31T14:42:49.000Z","size":201,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-31T15:06:13.246Z","etag":null,"topics":["agent","agent-framework","agents","ai","ai-agents","dart","flutter","tool-use"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/memex-lab.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-05T14:46:19.000Z","updated_at":"2026-03-31T14:43:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/memex-lab/dart_agent_core","commit_stats":null,"previous_names":["memex-lab/dart_agent_core"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/memex-lab/dart_agent_core","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memex-lab%2Fdart_agent_core","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memex-lab%2Fdart_agent_core/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memex-lab%2Fdart_agent_core/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memex-lab%2Fdart_agent_core/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/memex-lab","download_url":"https://codeload.github.com/memex-lab/dart_agent_core/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memex-lab%2Fdart_agent_core/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31374113,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T17:53:18.093Z","status":"ssl_error","status_checked_at":"2026-04-03T17:53:17.617Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","agent-framework","agents","ai","ai-agents","dart","flutter","tool-use"],"created_at":"2026-04-03T20:07:44.944Z","updated_at":"2026-04-03T20:07:45.423Z","avatar_url":"https://github.com/memex-lab.png","language":"Dart","readme":"\u003cdiv align=\"center\"\u003e\n\n# Dart Agent Core\n\n**A mobile-first, local-first Dart library for building stateful, tool-using AI agents**\n\n[English](README.md) | [简体中文](README.zh-CN.md)\n\n[![Pub Version](https://img.shields.io/pub/v/dart_agent_core?color=blue\u0026style=flat-square)](https://pub.dev/packages/dart_agent_core)\n[![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg?style=flat-square)](LICENSE)\n[![Dart SDK Version](https://badgen.net/pub/sdk-version/dart_agent_core?style=flat-square)](https://pub.dev/packages/dart_agent_core)\n\n\u003c/div\u003e\n\n`dart_agent_core` is a mobile-first, local-first Dart library that implements an agentic loop with tool use, state persistence, and multi-turn memory. It connects to LLM APIs (OpenAI, Gemini, AWS Bedrock) and handles the orchestration layer — tool calling, streaming, context management, and sub-agent delegation — entirely in Dart, which makes it suitable for use in Flutter apps without a Python or Node.js backend.\n\n---\n\n## Features\n\n- **Multi-provider support**: Unified `LLMClient` interface for OpenAI (Chat Completions \u0026 Responses API), Google Gemini, and Anthropic Claude via AWS Bedrock.\n- **Tool use**: Wrap any Dart function as a tool with a JSON Schema definition. The agent dispatches calls, feeds results back, and loops until done. Tools can return `AgentToolResult` to carry multimodal content, metadata, or a stop signal.\n- **Multimodal input**: `UserMessage` accepts text, images, audio, video, and documents as content parts. Model responses can include text, images, video, and audio.\n- **Stateful sessions**: `AgentState` tracks conversation history, token usage, active skills, plan, and custom metadata. `FileStateStorage` persists state to disk as JSON.\n- **Streaming**: `runStream()` yields `StreamingEvent`s for model chunks, tool call requests/results, and retries — suitable for real-time UI updates in Flutter.\n- **Pure Dart Skills**: Define modular capabilities (`Skill`) with their own system prompts and tools. Skills can be always-on (`forceActivate`) or toggled dynamically by the agent at runtime to save context window.\n- **File-system Skills**: Load Skills from `SKILL.md` files under a local directory root. With `javaScriptRuntime` configured, these Skills can execute JavaScript scripts via `RunJavaScript` and bridge channels.\n- **Sub-agent delegation**: Register named sub-agents or use `clone` to delegate tasks to a worker agent with an isolated context.\n- **Planning**: Optional `PlanMode` injects a `write_todos` tool that lets the agent maintain a step-by-step task list during execution.\n- **Context compression**: `LLMBasedContextCompressor` summarizes old messages into episodic memory when the token count exceeds a threshold. The agent can recall original messages via the built-in `retrieve_memory` tool.\n- **Loop detection**: `DefaultLoopDetector` catches repeated identical tool calls and can run periodic LLM-based diagnosis for subtler loops.\n- **Controller hooks**: `AgentController` provides request/response interception points around every major step (before run, before LLM call, before/after each tool call), allowing the host application to approve or stop execution.\n- **System callback**: A `systemCallback` function runs before every LLM call, letting you dynamically modify the system message, tools, or request messages.\n\n---\n\n## Installation\n\n```yaml\ndependencies:\n  dart_agent_core: ^1.0.6\n```\n\n---\n\n## Quick Start\n\n```dart\nimport 'dart:io';\nimport 'package:dart_agent_core/dart_agent_core.dart';\n\nString getWeather(String location) {\n  if (location.toLowerCase().contains('tokyo')) return 'Sunny, 25°C';\n  return 'Weather data not available for this location';\n}\n\nvoid main() async {\n  final apiKey = Platform.environment['OPENAI_API_KEY'] ?? '';\n  final client = OpenAIClient(apiKey: apiKey);\n  final modelConfig = ModelConfig(model: 'gpt-4o-mini');\n\n  final weatherTool = Tool(\n    name: 'get_weather',\n    description: 'Get the current weather for a city.',\n    executable: getWeather,\n    parameters: {\n      'type': 'object',\n      'properties': {\n        'location': {'type': 'string', 'description': 'City name, e.g. Tokyo'},\n      },\n      'required': ['location'],\n    },\n  );\n\n  final agent = StatefulAgent(\n    name: 'weather_agent',\n    client: client,\n    tools: [weatherTool],\n    modelConfig: modelConfig,\n    state: AgentState.empty(),\n    systemPrompts: ['You are a helpful assistant.'],\n  );\n\n  final responses = await agent.run([\n    UserMessage.text('What is the weather like in Tokyo right now?'),\n  ]);\n\n  print((responses.last as ModelMessage).textOutput);\n}\n```\n\n---\n\n## Supported Providers\n\n### OpenAI (Chat Completions)\n\n```dart\nfinal client = OpenAIClient(\n  apiKey: Platform.environment['OPENAI_API_KEY'] ?? '',\n  // baseUrl defaults to 'https://api.openai.com'\n  // Override for Azure OpenAI or compatible proxies\n);\n```\n\n### OpenAI (Responses API)\n\nUses the newer stateful Responses API. The client automatically extracts `responseId` from `ModelMessage` and passes it as `previous_response_id` on subsequent requests, so only new messages are sent.\n\n```dart\nfinal client = ResponsesClient(\n  apiKey: Platform.environment['OPENAI_API_KEY'] ?? '',\n);\n```\n\n### Google Gemini\n\n```dart\nfinal client = GeminiClient(\n  apiKey: Platform.environment['GEMINI_API_KEY'] ?? '',\n);\n```\n\n### AWS Bedrock (Claude)\n\nUses AWS Signature V4 for authentication instead of a simple API key.\n\n```dart\nfinal client = BedrockClaudeClient(\n  region: 'us-east-1',\n  accessKeyId: Platform.environment['AWS_ACCESS_KEY_ID'] ?? '',\n  secretAccessKey: Platform.environment['AWS_SECRET_ACCESS_KEY'] ?? '',\n);\n```\n\nAll clients support HTTP proxies via `proxyUrl` and configurable retry/timeout parameters. See [Providers doc](doc/providers.md) for details.\n\n---\n\n## Tool Use\n\nWrap any Dart function (sync or async) as a tool. The agent parses the LLM's function call JSON, maps arguments to your function's parameters, executes it, and feeds the result back.\n\n```dart\nfinal tool = Tool(\n  name: 'search_products',\n  description: 'Search the product catalog.',\n  executable: searchProducts,\n  parameters: {\n    'type': 'object',\n    'properties': {\n      'query': {'type': 'string'},\n      'maxResults': {'type': 'integer'},\n    },\n    'required': ['query'],\n  },\n  namedParameters: ['maxResults'], // maps to Dart named parameters\n);\n```\n\nTools can access the current session state via `AgentCallToolContext.current` without explicit parameters:\n\n```dart\nString checkBalance(String currency) {\n  final userId = AgentCallToolContext.current?.state.metadata['user_id'];\n  return fetchBalance(userId, currency);\n}\n```\n\nReturn `AgentToolResult` for advanced control:\n\n```dart\nFuture\u003cAgentToolResult\u003e generateChart(String query) async {\n  final imageBytes = await chartService.render(query);\n  return AgentToolResult(\n    content: ImagePart(base64Encode(imageBytes), 'image/png'),\n    stopFlag: true,  // stop the agent loop after this tool\n    metadata: {'chart_type': 'bar'},\n  );\n}\n```\n\nSee [Tools \u0026 Planning doc](doc/tools_and_planning.md) for positional/named parameter mapping, async tools, and more.\n\n---\n\n## Skill System\n\n`dart_agent_core` supports two Skill types:\n\n1) **Pure Dart Skills** (`Skill` objects)\n2) **File-system Skills** (`SKILL.md` files discovered from a root directory)\n\nThese two modes are mutually exclusive in `StatefulAgent` (use one or the other per agent instance).\n\n### Pure Dart Skills\n\nPure Dart Skills are modular capability units — a system prompt plus optional tools bundled under a name. The agent can activate/deactivate Skills at runtime to keep the context window focused.\n\n```dart\nclass CodeReviewSkill extends Skill {\n  CodeReviewSkill() : super(\n    name: 'code_review',\n    description: 'Review code for bugs and style issues.',\n    systemPrompt: 'You are an expert code reviewer. Check for security issues and logic errors.',\n    tools: [readFileTool, lintTool],\n  );\n}\n\nfinal agent = StatefulAgent(\n  ...\n  skills: [CodeReviewSkill(), DataAnalysisSkill()],\n);\n```\n\n- **Dynamic skills** (default): Start inactive. The agent gains `activate_skills` / `deactivate_skills` tools to toggle them based on the current task.\n- **Always-on skills** (`forceActivate: true`): Permanently active, cannot be deactivated.\n\n### File-system Skills (`SKILL.md`)\n\nFile-system Skill mode loads Skills from local folders: discover available Skills, read `SKILL.md` on demand, and inject Skill content into conversation context when activated.\n\n```dart\nfinal agent = StatefulAgent(\n  ...\n  // Required file tools should be provided by host app (for example: Read, LS).\n  tools: [readTool, lsTool],\n  skillDirectoryPath: '/absolute/path/to/skills_root',\n  javaScriptRuntime: NodeJavaScriptRuntime(), // optional, enables RunJavaScript\n  skills: null, // do not use with skillDirectoryPath\n);\n```\n\nWhen `javaScriptRuntime` is configured in File-system Skill mode, the framework exposes `RunJavaScript`.\n\n#### Flutter configuration for `RunJavaScript`\n\nIn Flutter apps, configure a custom `JavaScriptRuntime` implementation (for example using `flutter_js`) and pass it to `StatefulAgent`.\n\n1. Add dependency in your Flutter app:\n\n```yaml\ndependencies:\n  flutter_js: ^0.8.7\n```\n\n2. Implement `JavaScriptRuntime` and inject it:\n\n```dart\nimport 'package:dart_agent_core/dart_agent_core.dart';\nimport 'package:flutter_js/flutter_js.dart' as flutter_js;\n\nfinal agent = StatefulAgent(\n  ...\n  skillDirectoryPath: '/absolute/path/to/skills_root',\n  javaScriptRuntime: FlutterJavaScriptRuntime(\n    runtime: flutter_js.getJavascriptRuntime(),\n  ),\n);\n```\n\n3. (Optional) Register bridge channels for native capabilities:\n\n```dart\nagent.registerJavaScriptBridgeChannel('local.greeting', (payload, context) {\n  final name = (payload['name'] ?? 'friend').toString();\n  return {'message': 'Hello, $name'};\n});\n```\n\nReference implementation:\n- See `lib/src/agent/javascript_runtime.dart` (the commented `FlutterJavaScriptRuntime` example).\n\nBridge channels can be extended by host apps via:\n- `registerJavaScriptBridgeChannel(channel, handler)`\n- `unregisterJavaScriptBridgeChannel(channel)`\n\n---\n\n## Sub-Agent Delegation\n\nRegister sub-agents for specialized or parallelizable work. Each worker runs in its own isolated `AgentState`.\n\n```dart\nfinal agent = StatefulAgent(\n  ...\n  subAgents: [\n    SubAgent(\n      name: 'researcher',\n      description: 'Searches the web and summarizes findings.',\n      agentFactory: (parent) =\u003e StatefulAgent(\n        name: 'researcher',\n        client: parent.client,\n        modelConfig: parent.modelConfig,\n        state: AgentState.empty(),\n        tools: [webSearchTool],\n        isSubAgent: true,\n      ),\n    ),\n  ],\n);\n```\n\nThe agent uses the built-in `delegate_task` tool to dispatch work:\n\n- `assignee: 'clone'` — creates a copy of the current agent with clean context.\n- `assignee: 'researcher'` — uses a registered named sub-agent.\n\n---\n\n## Streaming\n\n`runStream()` yields fine-grained events for Flutter UI integration:\n\n```dart\nawait for (final event in agent.runStream([UserMessage.text('Hello')])) {\n  switch (event.eventType) {\n    case StreamingEventType.modelChunkMessage:\n      final chunk = event.data as ModelMessage;\n      // update text in UI incrementally\n      break;\n    case StreamingEventType.fullModelMessage:\n      // complete assembled message for this turn\n      break;\n    case StreamingEventType.functionCallRequest:\n      // model requested tool calls\n      break;\n    case StreamingEventType.functionCallResult:\n      // tool execution finished\n      break;\n    default:\n      break;\n  }\n}\n```\n\n---\n\n## Planning\n\nPass `planMode: PlanMode.auto` (or `PlanMode.must`) to enable the planner. This injects a `write_todos` tool that the agent uses to create and update a task list with statuses: `pending`, `in_progress`, `completed`, `cancelled`.\n\n```dart\nfinal agent = StatefulAgent(\n  ...\n  planMode: PlanMode.auto,\n);\n```\n\nReact to plan changes via `AgentController`:\n\n```dart\ncontroller.on\u003cPlanChangedEvent\u003e((event) {\n  for (final step in event.plan.steps) {\n    print('[${step.status.name}] ${step.description}');\n  }\n});\n```\n\n---\n\n## Context Compression\n\nFor long-running sessions, attach a compressor to automatically summarize old messages when token usage exceeds a threshold:\n\n```dart\nfinal agent = StatefulAgent(\n  ...\n  compressor: LLMBasedContextCompressor(\n    client: client,\n    modelConfig: ModelConfig(model: 'gpt-4o-mini'),\n    totalTokenThreshold: 64000,\n    keepRecentMessageSize: 10,\n  ),\n);\n```\n\nCompressed history is stored as episodic memories. The agent can retrieve the original messages via the built-in `retrieve_memory` tool when the summary isn't detailed enough.\n\n---\n\n## Controller Hooks\n\n`AgentController` provides lifecycle interception points:\n\n```dart\nfinal controller = AgentController();\n\n// Pub/Sub: observe events\ncontroller.on\u003cAfterToolCallEvent\u003e((event) {\n  print('Tool ${event.result.name} finished');\n});\n\n// Request/Response: approve or block steps\ncontroller.registerHandler\u003cBeforeToolCallRequest, BeforeToolCallResponse\u003e(\n  (request) async {\n    if (request.functionCall.name == 'delete_files') {\n      return BeforeToolCallResponse(approve: false);\n    }\n    return BeforeToolCallResponse(approve: true);\n  },\n);\n\nfinal agent = StatefulAgent(..., controller: controller);\n```\n\n---\n\n## System Callback\n\nFor dynamic per-call modifications, use `systemCallback` — it runs before every LLM call and can modify the system message, tools, and request messages:\n\n```dart\nfinal agent = StatefulAgent(\n  ...\n  systemCallback: (agent, systemMessage, tools, messages) async {\n    final updated = SystemMessage(\n      '${systemMessage?.content ?? ''}\\nCurrent time: ${DateTime.now()}',\n    );\n    return (updated, tools, messages);\n  },\n);\n```\n\n---\n\n## Examples\n\nSee the [`example/`](example) directory:\n\n- [Basic agent with tool use](example/simple_agent_example.dart)\n- [Streaming responses](example/simple_agent_stream_example.dart)\n- [Persistent state across sessions](example/simple_agent_with_state_example.dart)\n- [Planning with write_todos](example/simple_agent_with_plan_example.dart)\n- [Dynamic skill system](example/simple_agent_with_skills_example.dart)\n- [File-system Skills + JavaScript scripts execute](example/simple_agent_with_directory_skills_example.dart)\n- [Sub-agent delegation](example/simple_agent_with_sub_agent_example.dart)\n- [Controller hooks (observe \u0026 block)](example/simple_agent_with_controller_example.dart)\n- [Claude extended thinking via Bedrock](example/simple_agent_with_thinking_example.dart)\n- [OpenAI](example/simple_agent_with_openai_example.dart)\n- [Gemini](example/simple_agent_with_gemini_example.dart)\n- [Claude (direct Anthropic API)](example/simple_agent_with_claude_example.dart)\n- [Kimi (Moonshot AI)](example/simple_agent_with_kimi_example.dart)\n- [Kimi vision (image analysis)](example/simple_agent_with_kimi_vision_example.dart)\n- [Qwen (Alibaba DashScope)](example/simple_agent_with_qwen_example.dart)\n- [Zhipu GLM](example/simple_agent_with_glm_example.dart)\n- [Volcengine Doubao-Seed](example/simple_agent_with_seed_example.dart)\n- [MiniMax](example/simple_agent_with_minimax_example.dart)\n- [Ollama (local)](example/simple_agent_with_ollama_example.dart)\n- [OpenRouter](example/simple_agent_with_openrouter_example.dart)\n\n---\n\n## Documentation\n\n- [Architecture \u0026 Lifecycle](doc/architecture.md) — Agent loop, streaming events, controller hooks, loop detection, cancellation\n- [LLM Providers \u0026 Configuration](doc/providers.md) — OpenAI, Gemini, Bedrock setup, ModelConfig, proxy support\n- [Tools \u0026 Planning](doc/tools_and_planning.md) — Tool creation, parameter mapping, AgentToolResult, skills, sub-agents, planner\n- [State \u0026 Memory Management](doc/state_and_memory.md) — AgentState, FileStateStorage, context compression, episodic memory\n\n---\n\n## Contributing\n\nBug reports and pull requests are welcome. Please open an issue first for significant changes.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmemex-lab%2Fdart_agent_core","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmemex-lab%2Fdart_agent_core","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmemex-lab%2Fdart_agent_core/lists"}