Manifest Basics
Flo discovers tools and skills by file name:
**/*.tool.yamldefines one tool**/*.skill.yamldefines one skill**/*.schedule.yamldefines one bundle-managed schedule**/*.prompts.yamldefines one bundle-managed runtime prompt manifest
SKILL.md discovery is not supported.
Tool Manifest Shape
Every tool manifest must include:
namedescriptioninput_schemaexecution
Optional fields include:
timeout_msretry_policyvaultstaterequires_permissionsdirect_callscript_tools
Example:
name: capture_example
description: Open a page and return the current URL.
input_schema:
type: object
properties:
url:
type: string
required: [url]
additionalProperties: false
execution:
type: script
script_file: scripts/capture_example.mts
entrypoint: run
timeout_ms: 30000
Tool field behavior:
-
supported
agentdslash commands are/new,/call, and/web; bare/newstarts a fresh task without model stages, while/new <request>runs normally in a fresh task -
direct_call- defaults to
false - when
true, the tool may be invoked explicitly through/call <tool_id> - it also makes the tool reachable from
flo.callTool(...)even when the tool is outside the current selected-skill tool set - it does not automatically expose helper tools to the LLM
- defaults to
-
script_tools- defaults to
[] - declares helper tool ids callable from this tool through
flo.callTool(...) - these helpers are not automatically exposed to the LLM
- these helpers are not listed by
/callunless the helper tool also setsdirect_call: true
- defaults to
-
requires_permissions- defaults to
[] - declares permission ids required before the tool is usable
- the caller must have all listed permissions
- defaults to
Script Execution
For execution.type = script, define exactly one of:
script: inline JavaScript or TypeScriptscript_file: a relative path under the manifest directory
Use entrypoint to select the exported function the runtime should call.
Skill Manifest Shape
Every skill manifest must include:
skill_idnamedescription
Each skill must define exactly one instruction source:
instructioninstruction_file
Optional fields include:
versionexecution_model_tiercompletion_verificationtoolsscript_toolstool_definitionsrequires_skillsrequires_labelsrequires_permissions
Example:
skill_id: browser_examples
name: Browser Examples
description: Browser-based tools for authenticated workflows.
execution_model_tier: large
tools:
- read_text_file
script_tools:
- send_media_attachment
tool_definitions:
- name: capture_example
description: Open a page and return the current URL.
input_schema:
type: object
properties:
url:
type: string
required: [url]
execution:
type: script
script_file: scripts/capture_example.mts
entrypoint: run
instruction_file: instructions.md
Field behavior:
tools- declares referenced external or built-in tool ids for the selected skill
- these tools are exposed to the LLM tool list
- these tools are also callable from
flo.callTool(...)
execution_model_tier- optionally requests the execution-stage model tier for that skill
- allowed values:
nano,small,medium,large,frontier - applies only to the main execution stage, not task selection, gate, or task summary
- when multiple selected skills request different tiers, Flo uses the highest requested tier
- the requested tier is resolved through the runtime’s configured tier-to-model mapping
- when omitted, Flo uses the configured execution-stage model
script_tools- declares referenced external or built-in tool ids for the selected skill
- these tools are callable from
flo.callTool(...) - these tools are not exposed to the LLM tool list
tool_definitions- declares inline tool manifests owned by the skill
- inline tools are available to the selected skill without being repeated in
toolsorscript_tools
requires_permissions- defaults to
[] - declares permission ids required before the skill is visible or selectable
- the caller must have all listed permissions
- defaults to
Authoring rules:
- use
toolswhen the model should be able to call the tool directly - use
script_toolswhen only your script should call the tool - do not repeat an inline tool from
tool_definitionsin eithertoolsorscript_tools script_toolsonly changes LLM visibility; it does not create a separate security boundary from the selected skill’s scripts
MCP Tool References
Runtime MCP tools use the same tools and script_tools fields as built-in and manifest-backed
tools. Their ids have the form mcp__<server_id>__<tool_name>:
tools:
- mcp__github__search_issues
script_tools:
- mcp__github__get_issue
The runtime administrator must register the named MCP server separately. Skill manifests contain only tool ids; server addresses and vault credential bindings are not part of a skill bundle.
Completion Verification
Skills may override the default completion check with an optional boolean:
skill_id: example
name: Example
description: Handle a request
instruction: Complete the requested work.
completion_verification: false
A failed final tool call still requires completion verification, even when this setting is false. The agent may recover from the failure; if completion cannot be confirmed within the recovery limit, the task fails.
Set completion_verification: true to request verification, false to skip it, or omit
the field to inherit the default. If multiple active skills specify overrides, explicit
true takes precedence over false. Required helper skills participate in the same rule.
The setting applies to normal successful model-backed execution; it does not add verification
to direct tool calls. An enabled verifier that cannot complete causes the turn to fail rather
than report success. Disabling verification does not allow empty interactive replies: the runtime
attempts one continuation from existing tool results, then reports failure if no visible reply or
attachment is produced.
Visibility Summary
Flo has separate visibility rules for normal execution and explicit /call usage.
During normal execution:
- the LLM sees globally available tools plus tools listed in the selected skills’
tools - the selected skills’ scripts can call globally available tools, tools listed in the selected skills’
toolsandscript_tools, and any tool whose manifest setsdirect_call: true - inline tools from
tool_definitionsare available to the owning skill without being repeated elsewhere
During /call execution:
/calllists tools whose manifests setdirect_call: true- the external-app direct-call API uses the same
direct_call: truegate - running
/call <tool_id>preserves the current selected-skill context for nested tool access - the called tool may always call itself
- the called tool may also call globally available tools and tools listed in that tool manifest’s own
script_tools - helper tools still remain hidden unless they are global, reachable through the selected skill set, or declared in the direct-call tool’s own
script_tools
Permission Catalogs
Permissions are bundle-scoped. A skill bundle may include at most one *.permissions.yaml file.
Use the catalog file to define the permission ids that skills and tools reference from requires_permissions.
Example:
catalog_id: admin
version: v1
groups:
- group_id: admin
name: Admin
description: Administrative actions
permissions:
- permission_id: admin.roles.write
name: Manage roles
description: Create, update, and delete roles
- permission_id: admin.permissions.read
name: View permission catalog
Catalog fields:
catalog_id- optional stable identifier for the catalog
version- optional catalog version string
groups- required list of permission groups
groups[].group_id- required unique group id
groups[].name- required display name
groups[].description- optional description
groups[].permissions- list of permissions in the group
groups[].permissions[].permission_id- required unique permission id used by
requires_permissions
- required unique permission id used by
groups[].permissions[].name- required display name
groups[].permissions[].description- optional description
Authoring rules:
- define each permission id once in the bundle catalog
- use exact permission ids in
requires_permissions - permission ids, group ids, and names must be non-empty
- leading or trailing whitespace is rejected
- duplicate
group_id,permission_id, orrequires_permissionsentries are rejected - if a bundle includes more than one
*.permissions.yamlfile, loading fails
Runtime behavior:
- a skill with
requires_permissionsis hidden unless the caller has all listed permissions - a tool with
requires_permissionsis unavailable unless the caller has all listed permissions - profile permission assignment is validated against the active bundle slot’s catalog
- if a profile is granted a permission id that is not defined in the active catalog, the update is rejected
Runtime Prompt Manifest Shape
Runtime prompt manifests are bundle-scoped resources. They are not owned by a skill.
Flo loads at most one *.prompts.yaml file from a bundle. If more than one is present, bundle loading fails.
Required fields:
execution.preamble
Optional fields:
system_prompt_resolver
Example:
execution:
preamble: |
You are a slot-scoped runtime.
Keep responses concrete and concise.
system_prompt_resolver:
script_file: prompts/resolve-system-prompt.mts
entrypoint: resolveSystemPrompt
timeout_ms: 3000
Field behavior:
execution.preamble- required non-empty string
- prepended to the runtime’s configured execution-stage prompt
- if the runtime execution prompt is empty, the preamble is used by itself
system_prompt_resolver- optional dynamic session prompt script
script_fileis resolved relative to the*.prompts.yamlfilescript_filemust be a relative.mtsor.mjspath and cannot use..entrypointmust name the exported resolver functiontimeout_msmust be greater than zero- resolver failures fail the task with
system_prompt_resolution_failed
The resolver receives the current session_id and sanitized profile fields:
{
"session_id": "wecom:group:chat-1:alice",
"profile": {
"profile_id": "profile-1",
"profile_kind": "user",
"permissions": ["returns.manage"],
"display_name": "Alice"
}
}
display_name is omitted when unavailable. The resolver may return a string, null,
or undefined. null, undefined, and blank strings mean no dynamic prompt. A
non-blank string is appended after the normal stage preamble for gate and execution
stages only; task-summary and context-summary prompts do not receive it.
Authoring rules:
- use this manifest for bundle-wide execution guidance that should apply regardless of which skills are selected
- keep it focused on durable execution behavior, not one-off task content
- unknown top-level fields are rejected
- resolver scripts are runtime prompt infrastructure, not user-visible tools
Schedule Manifest Shape
Schedule manifests are bundle-scoped resources. They are not owned by a skill.
Required fields:
schedule_idprofile_channelprofile_external_user_iduser_message- exactly one of
triggerorcron_expression timezone
Optional fields:
contexttarget_agent_overriderequired_labelsselected_skill_idsskip_skill_selectionenabledconcurrency_policycron_expression
Example:
schedule_id: morning_digest
profile_channel: wecom
profile_external_user_id: alice
user_message: Send the morning digest.
selected_skill_ids:
- skill.digest
trigger:
kind: recurring
times_of_day:
- hour: 9
minute: 0
timezone: America/Toronto
enabled: true
concurrency_policy: forbid
Behavior:
- Flo reconciles schedule manifests when a skill bundle is pushed and when
backend-servicestarts with persisted bundle slots. - Flo resolves
profile_channelplusprofile_external_user_idthrough the runtime alias table. - If the alias does not exist, Flo auto-creates a normal non-admin user profile and records the alias.
- If a schedule manifest is removed, Flo deletes the managed schedule but does not delete the profile or alias it created.
- Managed schedules are read-only in the admin UI except for
Run now. concurrency_policydefaults toallowfor compatibility. Useforbidto skip a trigger while an earlier run is queued, blocked, running, or suspended.- Succeeded, failed, and cancelled runs are terminal and do not prevent the next trigger.
For a non-overlapping five-minute email poller:
schedule_id: email_analysis
profile_channel: wecom
profile_external_user_id: alice
user_message: Analyze new emails.
trigger:
kind: recurring
minutes_interval: 5
timezone: America/Toronto
concurrency_policy: forbid
If an earlier email-analysis run is still active at the next five-minute tick, Flo records the tick as triggered, skips dispatch, and advances the regular cadence. Run now applies the same policy but does not change the cadence when it is skipped.
Schedule Trigger Shape
When using trigger, the supported shape is:
trigger:
kind: recurring
Recurring triggers support these fields:
minutes_interval- optional integer in
1..=59 - runs every N minutes
- optional integer in
hours_interval- optional integer in
1..=23 - runs every N hours
- optional integer in
times_of_day- optional list of wall-clock times
- each item has
hourin0..=23andminutein0..=59
days_of_week- optional list of weekdays using
0..=6 0is Sunday,6is Saturday
- optional list of weekdays using
Validation rules:
- define exactly one of
minutes_interval,hours_interval, ortimes_of_day days_of_weekis a filter and may be combined with any one of the cadence options abovetimes_of_dayentries must not contain duplicates- in v1, all
times_of_dayentries must share the sameminute days_of_weekmust not contain duplicates
Examples:
Every 15 minutes:
trigger:
kind: recurring
minutes_interval: 15
Every 6 hours:
trigger:
kind: recurring
hours_interval: 6
At 09:00 and 17:00 every weekday:
trigger:
kind: recurring
times_of_day:
- hour: 9
minute: 0
- hour: 17
minute: 0
days_of_week: [1, 2, 3, 4, 5]
Use cron_expression instead of trigger when you need a schedule shape that is not covered by the recurring trigger fields.
State and Vault Declarations
Use state when your script needs durable non-secret data:
state:
- name: session_counter
key_prefix: counter.session.
scope_kind: session
- name: shared_counter
key_prefix: counter.shared.
scope_kind: shared
scope_id: service
Use vault when your script needs secrets:
vault:
- key: api_token
scope_kinds: [profile, shared]
The runtime still requires the script to fetch secrets explicitly through flo.vault.get(...).
Import Rules
The script runtime supports:
- local static ESM imports
- relative
.mjs,.mts, and related local module paths
The runtime rejects:
- bare specifiers
- package-style imports
- dynamic
import() ..traversal for author-facing asset imports
Next: TypeScript Runtime
Scheduled suspension timeout
A schedule manifest can optionally cap how long each suspension lasts:
concurrency_policy: forbid
suspension_timeout:
seconds: 1800
resume_prompt: "Finish with available information and report unresolved items."
The duration must be an integer from 1 to 31,536,000 seconds and the prompt must be non-empty. A script may request a shorter wait. Suspended runs continue to count as active for overlap prevention. A scheduled run receives one timeout-driven resume; suspending again fails the run so later occurrences can proceed. This setting does not limit active execution time. Changes apply to future runs.