Skip to content

exposes-as-agent / exposed-commands is parse-only — no runtime synthesis or input routing (app→agent baking unwired) #178

@pawellisowski

Description

@pawellisowski

Summary

The app→agent exposure surface is spec-complete and parse-only, but runtime-absent. An app can declare exposes-as-agent: true + an exposed-commands: block, the manifest parses it, the app-spec describes the registry synthesizing an agent from it — but no code path ever reads exposed_commands, so a workflow exposed as an agent cannot actually be invoked as one, and a caller cannot route per-command inputs into the nested app's chain.

This blocks a high-value capability ("build a multi-node workflow, then bake it into a single reusable agent that other workflows call, still setting its inputs"). Filing per the verified-gap rule, not speculation — every claim below is cited.

Verified evidence (AWARE 0.51, this repo)

  1. exposed_commands is dead code. cli/src/manifest/app.rs:21-23:

    #[allow(dead_code)]
    #[serde(rename = "exposed-commands", default)]
    pub exposed_commands: Option<Value>,

    A grep across all of cli/src/ finds exposed_commands referenced only at this declaration — no orchestrator, invoker, registry, or compiler code reads it.

  2. No agent-manifest synthesis. The app-spec (10-core/app-spec.md ~832-874) describes the registry/orchestrator synthesizing an agent manifest from exposed-commands at install/compile, after which the registry "treats it like an agent" callable via agent: <ns>/<app>. No such synthesis exists in cli/src/runtime/orchestrator.rs or cli/src/runtime/invoker.rs.

  3. No per-command input routing. RuntimeContext.inputs is a single flat map. Nothing maps a caller's per-command inputs into a nested app's inputs:, validates against the declared exposed-commands.<cmd>.inputs types, or namespaces them per command. So the declared inputs: (e.g. welded-to-tc's tc-project-id, or a hypothetical phase) are documentation only at the agent boundary.

  4. No CLI verb. AppCommand (cli/src/commands/app.rs) has 12 variants — list, show, install, uninstall, validate, export, run, explain, compile, inspect, stop, logs — none bake/expose/publish/convert an app into an agent. publish exists only under AgentCommand (hand-written agents).

  5. Zero end-to-end usage. 30-apps/_examples/welded-to-tc.flo:13-30 is the only app that sets exposes-as-agent: true (declares a start command with inputs: tc-project-id, tc-folder-id + a stream output). Nothing anywhere invokes an exposed app as an agent — only doc examples.

Observed vs expected

  • Observed: exposes-as-agent: true + exposed-commands: parse cleanly and have no runtime effect. An app so declared cannot be called as an agent by another app, and its declared command inputs are inert.
  • Expected (per app-spec): at install/compile, an agent manifest is synthesized from exposed-commands; the app becomes callable as agent: <ns>/<app>, command: <cmd>; the caller's command inputs route into the nested app's inputs: and are type-validated.

Repro

# welded-to-tc declares exposes-as-agent: true + exposed-commands.start
aware app install ./30-apps/_examples/welded-to-tc.flo
# There is no synthesized agent to call, and no verb to produce one:
aware agent list            # welded-to-tc does not appear as a callable agent
# No app→agent verb exists (no bake/expose/publish under `aware app`).

Suggested scope (the wiring this asks for)

  1. At compile/install, when exposes-as-agent: true, synthesize an agent manifest from exposed-commands (commands, lifecycle, inputs/outputs schema) and register it so agent: <ns>/<app> resolves.
  2. In the orchestrator/invoker, when a node calls an app-backed agent, dispatch into the nested app's node chain and route the caller's per-command inputs into that app's inputs: map (with type validation against the declared command inputs).
  3. Drop #[allow(dead_code)] once exposed_commands is consumed.
  4. (Optional) an end-to-end example/test: app B calls welded-to-tc (or a phase-parameterized app) as an agent and sets its inputs.

Why it matters / cross-ref

This is the substrate prerequisite for a "bake a working workflow into a reusable, input-controllable agent" feature in floless.app (designed, gated on this — UI deliberately not built against dead code). Related: #101 (floless.app capability audit). Until this lands, the working path to set an app's inputs is a direct aware app run <app> --input k=v, which functions correctly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions