marona 0.5.4
marona: ^0.5.4 copied to clipboard
Unified AI runtime and MCP/Skill gateway for online, hybrid, and offline applications.
marona #
Unified AI runtime and MCP/Skill gateway for Dart and Flutter.
dependencies:
marona: ^0.5.4
mode controls where execution is allowed, model selects the model for the
request, and input contains the user's request. Apps, Skills, discovery,
identity, permissions, approvals, service connections, and MCP execution remain
inside Marona.
Complete Request #
MARONA_API_KEY authenticates your application with Marona.
import 'dart:io';
import 'package:marona/marona.dart';
Future<void> main() async {
final identityToken = Platform.environment['MARONA_IDENTITY_TOKEN'];
final marona = Marona(
apiKey: Platform.environment['MARONA_API_KEY'],
mode: 'online',
);
try {
await marona.sync(
interfaceName: 'mobile_app',
identityToken: identityToken,
);
final response = await marona.client(
model: 'marona/default',
input: [
{'role': 'user', 'content': 'What is my group fund?'},
],
interfaceName: 'mobile_app',
identityToken: identityToken,
);
print(response.text);
} finally {
marona.close();
}
}
Images And Documents #
final response = await marona.client(
model: 'openai/gpt-4o',
input: [
{'role': 'developer', 'content': 'Keep answers clear and concise.'},
{
'role': 'user',
'content': [
{'type': 'input_text', 'text': 'Compare the image and report.'},
{'type': 'input_image', 'image_url': 'https://example.com/match.jpg'},
{
'type': 'input_file',
'filename': 'report.pdf',
'file_data': 'data:application/pdf;base64,...',
'detail': 'high',
},
],
},
],
interfaceName: 'mobile_app',
identityToken: identityToken,
);
Model Routes #
Only the model string changes:
'marona/default'
'openai/gpt-4o'
'anthropic/claude-sonnet-4'
'gemini/gemini-2.5-pro'
'ollama/llama3.2'
'litellm/local-llama'
'office/company-assistant'
'local/gemma-4-e2b'
Pass provider credentials and endpoints in application configuration:
final marona = Marona(
apiKey: maronaApiKey,
mode: 'hybrid',
providerCredentials: {
'openai': openAiApiKey,
'litellm': {'apiKey': liteLlmApiKey, 'endpoint': liteLlmBaseUrl},
},
providerEndpoints: {'ollama': 'http://127.0.0.1:11434'},
);
Register custom access or a downloaded in-process model:
Future<Map<String, dynamic>> officeNativeAdapter(LocalModelRequest request) async {
final result = await officeSdk.generate(request);
return {'choices': [{'message': {'role': 'assistant', 'content': result.text}}]};
}
await marona.models.register(
name: 'office/company-assistant',
provider: 'custom',
endpoint: 'https://models.office.example/v1',
model: 'company-assistant-v2',
apiKey: officeModelApiKey,
adapter: officeNativeAdapter,
);
await marona.models.register(
name: 'local/gemma-4-e2b',
executor: gemmaExecutor,
contextWindow: 2048,
maxOutputTokens: 128,
);
final response = await marona.client(
model: 'local/gemma-4-e2b',
input: [{'role': 'user', 'content': 'Hello offline'}],
interfaceName: 'mobile_app',
identityToken: identityToken,
);
Registration configures access. Select the model on client(...) or
message(...). models.use(...) is deprecated.
Install Apps For Offline Use #
Install selected Apps while the device has network access. Marona verifies and stores only those packages; it does not download every App available in Hub.
await marona.hub.install(
apps: ['sda-books', 'zimsec'],
identityToken: identityToken,
interfaceName: 'mobile_app',
);
Connect Installed Apps #
In offline mode, connect() reads local storage and performs no package
download.
final tools = await marona.hub.connect(
apps: ['sda-books', 'zimsec'],
adapter: 'tools',
identityToken: identityToken,
interfaceName: 'mobile_app',
);
final response = await marona.client(
model: 'local/gemma-4-e2b',
input: [
{'role': 'user', 'content': 'Find a ZIMSEC mathematics paper.'},
],
identityToken: identityToken,
tools: tools,
);
The model initially receives only marona__discover_capabilities. It answers
ordinary requests directly. When App data or an action is needed, Marona
searches the local SQLite catalog, supplies only matching exact schemas, and
executes the selected package locally.
Omit apps to expose every App installed on this device:
final tools = await marona.hub.connect(
adapter: 'tools',
identityToken: identityToken,
interfaceName: 'mobile_app',
);
Flutter hosts can pass installLocalApps and connectLocalApps to Marona
to bind this API to Application Support storage and their package executor.
To export governed Skills too, name them explicitly:
final tools = await marona.hub.connect(
apps: ['group-fund'],
skills: ['create-group-fund'],
adapter: 'tools',
);
When a Skill returns status: 'planned', call it again only after the user
approves, with {'request': 'Yes', 'approved': true}.
App tools execute their MCP target. Skill tools execute through Marona's governed runtime. Internal Skill steps and Skill-managed raw capabilities are not exported.
Publish A Skill #
final request = step(
id: 'understand-request',
type: 'reasoning',
instruction: 'Extract the group name and currency.',
inputs: {'message': '{{ context.user_message }}'},
outputs: {'name': 'string', 'currency': 'string'},
);
final permission = step(
id: 'confirm-create',
type: 'approval',
message: "Create '${request.output('name')}' in ${request.output('currency')}?",
outputs: {'approved': 'boolean'},
);
final create = step(
id: 'create-group',
type: 'app',
app: 'group-fund',
capability: 'group-fund.create_group',
instruction: 'Create the approved group.',
condition: permission.output('approved'),
inputs: {
'name': request.output('name'),
'currency': request.output('currency'),
},
outputs: {'group_id': 'string', 'name': 'string'},
);
final definition = SkillDefinition(
name: 'create-group-fund',
description: 'Create a group fund after explicit user approval.',
governs: ['group-fund.create_group'],
steps: [request, permission, create],
);
await marona.skills.publish(definition, version: '1.0.0');
Mode Rules #
online: network models and online MCP targets are allowed.hybrid: try the selected local/private model, then use Edge fallback.offline: use cached data, a local model, and installed offline-capable targets only.
Changing model never changes App, Skill, permission, approval, or MCP rules.