Note: this body was rewritten after the original claim was falsified. The comments below preserve how it got here — the first version asserted personas could not be launched non-interactively at all, which is false. Read this body, not the opening comment.
The gap
A persona can be launched three ways today, and only one of them takes a persona.
| Surface |
Takes a persona? |
agentworkforce agent <persona> (interactive TTY) |
yes |
Agent Relay spawn action |
no — cli + free-text task + target_node |
| Relayflow steps |
no — { name, cli, role, preset } (see relayflows/workflows/ci/fix-mcp-spawn.yaml) |
So anything placed on a fleet node, or run as a workflow step, is a hand-written prompt rather than a persona.
Why that loses more than convenience
A persona is not a prompt. nango-integrations declares agentsMdContent, skills (each with a source that gets installed), mcpServers, harness, model, and harnessSettings. Pasting its text into a task string carries one of those. The spawned agent has no skills installed and no MCP server — it works from memory of a workflow the persona exists to enforce.
And it fails silently. Nothing errors. The agent runs, sounds correct, and is missing its tools. A wrong path that works badly is worse than one that fails.
The practical consequence: every remote agent is a fresh, unversioned approximation of a capability that already exists in a reviewed file, and improvements to that file never reach the fleet.
The capability already exists — this is a caller problem
@agentworkforce/deploy exports a real programmatic entrypoint:
export async function deploy(opts: DeployOptions, resolvers?: DeployResolvers): Promise<DeployResult>
plus preflightPersona, the mode launchers (devLauncher / sandboxLauncher / cloudLauncher), ModeLaunchHandle / ModeLaunchInput / ModeLauncher, and createBufferedIO. It validates declared persona inputs up front with typed errors rather than failing inside a child process.
@agentworkforce/local-surface already composes that into Agent Relay: defineWorkforcePersonaNode(options): FleetNodeDefinition, whose launchPersonaRunner calls deploy() with mode: 'dev', detach: true, bridged: true, noPrompt: true (local-surface/src/index.ts:145). The bridged flag exists specifically so the persona survives a non-interactive parent whose stdin ends.
So non-interactive, in-process persona launch is built, deliberate, and already wired to @agent-relay/fleet. What is missing is that spawn and relayflow steps do not reach for it.
Constraint — SDK surfaces only
Do not implement this by shelling out to agentworkforce agent. pear/src/main/broker.ts:2991 does exactly that, and it is not the pattern to copy. Shelling out:
- makes a CLI binary a runtime dependency of every calling host — unacceptable for an arbitrary fleet node or cloud sandbox;
- couples two release trains across a process boundary with no type checking (
relayfile mount recently rejected --workspace / --local-dir, flags its own help text advertised);
- forces stdout scraping for state — pear parses readiness out of log lines with
personaHarnessReadyFromOutput;
- drives a TTY-oriented command from a daemon.
Notably pear already depends on @agentworkforce/deploy and shells out anyway. The library is there; the caller reaches past it.
What done looks like
- Agent Relay
spawn accepts a persona (name or path) as an alternative to cli + task, resolved through the same registry agentworkforce uses (built-in / cwd / personal / configured dirs). harness and model come from the persona spec. A task supplied alongside layers the concrete assignment over the persona's standing instructions.
- Relayflow steps accept a persona in place of
cli + role, with the same resolution.
- Both go through
@agentworkforce/deploy / local-surface in process. No agentworkforce binary required on the node, no output parsing.
- Launch is deduped and verified. Borrow the discipline from pear even though not its mechanism: coalesce concurrent launches of the same persona (
personaSpawnRequestKey keys on project + persona; a fleet needs the node-scoped equivalent), and confirm broker registration and harness readiness before reporting success, releasing the agent if either fails. A launched process is not a running persona.
- Discoverability. Nothing today points a caller from
spawn toward defineWorkforcePersonaNode. Whatever ships, make the working path findable from the wrong one.
Cross-repo
The persona resolution and SDK live here (workforce). The spawn action lives in relay. Relayflow step schema lives in relayflows. Expect changes in more than one repo; do not merge anything — the merge gate is the principal's.
The gap
A persona can be launched three ways today, and only one of them takes a persona.
agentworkforce agent <persona>(interactive TTY)spawnactioncli+ free-texttask+target_node{ name, cli, role, preset }(seerelayflows/workflows/ci/fix-mcp-spawn.yaml)So anything placed on a fleet node, or run as a workflow step, is a hand-written prompt rather than a persona.
Why that loses more than convenience
A persona is not a prompt.
nango-integrationsdeclaresagentsMdContent,skills(each with a source that gets installed),mcpServers,harness,model, andharnessSettings. Pasting its text into ataskstring carries one of those. The spawned agent has no skills installed and no MCP server — it works from memory of a workflow the persona exists to enforce.And it fails silently. Nothing errors. The agent runs, sounds correct, and is missing its tools. A wrong path that works badly is worse than one that fails.
The practical consequence: every remote agent is a fresh, unversioned approximation of a capability that already exists in a reviewed file, and improvements to that file never reach the fleet.
The capability already exists — this is a caller problem
@agentworkforce/deployexports a real programmatic entrypoint:plus
preflightPersona, the mode launchers (devLauncher/sandboxLauncher/cloudLauncher),ModeLaunchHandle/ModeLaunchInput/ModeLauncher, andcreateBufferedIO. It validates declared persona inputs up front with typed errors rather than failing inside a child process.@agentworkforce/local-surfacealready composes that into Agent Relay:defineWorkforcePersonaNode(options): FleetNodeDefinition, whoselaunchPersonaRunnercallsdeploy()withmode: 'dev',detach: true,bridged: true,noPrompt: true(local-surface/src/index.ts:145). Thebridgedflag exists specifically so the persona survives a non-interactive parent whose stdin ends.So non-interactive, in-process persona launch is built, deliberate, and already wired to
@agent-relay/fleet. What is missing is thatspawnand relayflow steps do not reach for it.Constraint — SDK surfaces only
Do not implement this by shelling out to
agentworkforce agent.pear/src/main/broker.ts:2991does exactly that, and it is not the pattern to copy. Shelling out:relayfile mountrecently rejected--workspace/--local-dir, flags its own help text advertised);personaHarnessReadyFromOutput;Notably pear already depends on
@agentworkforce/deployand shells out anyway. The library is there; the caller reaches past it.What done looks like
spawnaccepts a persona (name or path) as an alternative tocli+task, resolved through the same registryagentworkforceuses (built-in / cwd / personal / configured dirs).harnessandmodelcome from the persona spec. Atasksupplied alongside layers the concrete assignment over the persona's standing instructions.cli+role, with the same resolution.@agentworkforce/deploy/local-surfacein process. Noagentworkforcebinary required on the node, no output parsing.personaSpawnRequestKeykeys on project + persona; a fleet needs the node-scoped equivalent), and confirm broker registration and harness readiness before reporting success, releasing the agent if either fails. A launched process is not a running persona.spawntowarddefineWorkforcePersonaNode. Whatever ships, make the working path findable from the wrong one.Cross-repo
The persona resolution and SDK live here (
workforce). Thespawnaction lives inrelay. Relayflow step schema lives inrelayflows. Expect changes in more than one repo; do not merge anything — the merge gate is the principal's.