Skip to content

[feat] Add the session skill driver and invocable workflows #1086

Closed

Description

@hsuchifeng

Parent: #818

Goal

Let an agent in an existing coding session run team workflows: it orchestrates, and a deterministic CLI state machine drives the public lease loop. No other runtime is deployed, and nothing outside the engine reads the graph.

Scope

The decisions of 2026-10-24 (comment below) replace the earlier SessionStart and invocable items.

  • invoke: allow | ask | deny, default deny, plus optional alias. invoke limits agents only, and replaces the earlier invocable. deny generates no skill.

    invoke Agent session (any driver; --driver skill always counts) A person's terminal, local driver CI without an agent session, local driver Sub-workflow
    allow Starts Starts Starts Starts
    ask Starts once the person agrees (describe, then --confirmed). Unattended: denied, also with --confirmed Starts Starts Starts
    deny, the default Refused Starts Starts Starts

    A process with an agent session variable is an agent session, in CI too. resume and next --takeover follow the same rule, except for a run the same session started and drives.

  • Discovery through generated skills. pull generates one workflow-<name> skill per workflow an agent may start (allow or ask). It carries the workflow's description and alias and routes to teamai skill get workflow. Generated skills follow the team-skill pipeline, are marked as generated, never enter push, and pull prunes them. No SessionStart workflow list.

  • The skill driver (--driver skill). A file-backed state machine run through teamai workflow. It reaches the engine only through the public CLI (next, report, respond), and returns literal commands for the agent to run. Prompt steps go to child agents, interactive steps stay with the person in the session, and command steps run in detached runners with only the step's requires.env keys. Judging goes through teamai score --next.

  • Workspaces. TeamAI owns the workspace contract and the host owns the place. next --prepare creates a worktree, or adopts one the host made (--adopt), and report commits, pushes and cleans up, through one workspace module shared by every driver.

  • A capability map, not tool names. Each dispatch states what it needs (isolation, parallelism, a person). The agent maps it to the best primitive its host offers. driver.can declares only what the host provides.

  • No nested starts. workflow start fails with nested_workflow inside an attempt (marker in the environment) or a workflow worktree. Both drivers export the marker to every process they launch.

  • Run modes. resolveRunMode: TEAMAI_MODE (attended | unattended) > CI > attended. The skill driver resolves the mode once and keeps it in its state. In unattended, --describe answers the recommended start with --auto-approve, or denied for required approvals, interactive steps, invoke: ask and choices without a default. An unattended resume or takeover with the skill driver decides the same way from the steps the run has left, before it claims anything. Every process TeamAI starts for a step gets TEAMAI_MODE. Judging in the session keeps running; a judge elsewhere does not block the run and is left to score --next.

  • Published runs. A run published from CI or another machine is continued from a session with teamai workflow resume <run> --driver skill (--claim when another install owns it). Claiming is the person's decision.

  • Guides. skill-data/workflow (the orchestrator guide, with references loaded on demand) and skill-data/workflow-local (unattended runs in CI or cloud agents, with a CI reference).

Out of scope

Dependencies

Acceptance criteria

  • Only allow and ask workflows get a generated skill. deny workflows refuse an agent's start with any driver.
  • The skill reads CLI help and the generated reference instead of duplicating flags.
  • The skill driver uses public workflow commands only and never reads the graph.
  • A lease whose workspace mode or requirement the host does not declare is never handed to the session, and stays available to another driver.
  • No agent sees a secret value: command steps get only their requires.env keys.
  • Required approval is never agent-mediated.
  • workflow start inside an attempt or a workflow worktree fails with nested_workflow.
  • An unattended run asks nobody.
  • next, report and respond address a run by id from a workflow worktree and from outside the product repo.

Verification

Unit tests for the transition table, discovery, invoke, places, run modes and claim; a model-based test of the state machine; build, typecheck and lint; real-CLI E2E for a session run, a published-run claim across two checkouts, and an unattended run.

Activity

  1. SaulMoro commented on Oct 14, 2026

    @SaulMoro
    Collaborator

    Looking up a run by id regardless of the current directory is in #1087, but the skill driver needs it first. A session started outside the product checkout gets run_not_found, because workflow-commands.ts resolves the run through autoDetectInit. That criterion could move here, or this issue could be blocked by #1087.

  2. SaulMoro commented on Oct 14, 2026

    @SaulMoro
    Collaborator

    Notes from #1078 (#1129) for the skill driver:

    • The driver never needs the graph. Skipped steps, steps that only end the workflow, and steps waiting on a condition are never leased; next settles them before handing out a lease. The session only sees leases.
    • step is a path (ship.check.classify) for steps inside sub-workflows. Treat it as an opaque id.
    • Command leases may carry env, a map of already-rendered strings. Set them for the command; never paste them into the command text. Inputs reach a command only this way.
    • workflow start --inputs '<json>' passes a workflow's declared inputs; an invocable workflow with required inputs needs the session to collect them first.
    • The conformance suite covers skipped steps, rendered env, sub-workflow paths and recovery after a failure; a driver that passes it handles graph runs.

    The engine rules that span files are in docs/dev/workflow-engine.md, added by #1129.

  3. hsuchifeng commented on Oct 15, 2026

    @hsuchifeng
    CollaboratorAuthor

    Move run lookup by id into this issue. The session skill driver must work outside the product checkout, so #1086 should not wait for #1087.

  4. SaulMoro commented on Oct 16, 2026

    @SaulMoro
    Collaborator

    A lease for a prompt step will carry two model fields (#1177): model, the step's own, and defaultModel, the workflow's defaults.model in effect for the step. Each is an alias or a model name, resolved per tool with the member's aliases.

    The driver picks the model, first match wins: the step's model, the model set by the definition deployed for its tool, defaultModel, the tool's default. A default fills a gap and never replaces a model a definition sets for its role. Only the driver can read the deployed definition, so this order cannot be applied before the lease.

    When the first match is the step's model or defaultModel and that model cannot run, the driver fails the step with model_unavailable. That covers an alias with no model for the tool, and a driver that cannot switch models, such as a session that runs every step on its own model. It does not run the tool's default, which would record a result for a model the step did not ask for. It does not release the lease either, since next hands a released step straight back to the same driver. No capability says which models a driver can run, so this lease does not stay available to another driver the way a declined requirement does. How a driver applies a model that only the definition sets does not change.

  5. self-assigned this
    on Oct 19, 2026
  6. SaulMoro commented on Oct 20, 2026

    @SaulMoro
    Collaborator

    Now blocked by #1080 and #1082. The session driver takes its leases from both, so its scope changed with them.

    • Workspaces are a capability, not something to decline. In feat(workflow): add workspace branches #1175, next holds a new or continue lease back from a driver whose can.workspace lacks that mode. A driver that releases a lease gets it back on its next next, so declining it in the driver cannot work. A host that can create worktrees and run agents in parallel, such as Claude Code, can take these leases. The driver then declares the modes and meets the lease's git contract.
    • Open: how the session meets that contract. The local driver does more than git worktree add (worktree.ts in feat(workflow): add workspace branches #1175). It points core.hooksPath at an empty directory, replaces AGENTS.md, CLAUDE.md, .claude and the other agent paths with the run's base commit, gives the worktree the inline pull with prepareCheckout, then commits, pushes and reports changes. A session that creates the worktree with its host's own tool skips the base overlay. A later step then loads hooks and agents that an earlier step committed, which is what [feat] Add workflow workspaces, branches, and session continuation #1080 rules out for a step that holds a token. Either the session reuses the local driver's preparation through a public command, or the skill spells out each step and the conformance suite checks it. The first keeps one implementation. Following the Occam rule, it would extend an existing workflow command, not add a new one.
    • Requirements come from [feat] Enforce workflow requirements and execution limits #1082. Isolation, network, env and budget are checked before the lease there. What this driver declares for them is in my comment on #1082.
  7. SaulMoro commented on Oct 20, 2026

    @SaulMoro
    Collaborator

    Decision on the open question above: the session reuses the local driver's workspace preparation, extending the existing workflow next and workflow report, with no new command.

    • next prepares the worktree for a new or continue lease the way local-driver.ts does in feat(workflow): add workspace branches #1175. It creates the worktree, merges the dependency branches, points core.hooksPath at the empty directory, applies the run's base agent configuration, and runs the inline pull. The lease then names the directory.
    • report commits what the step left, pushes, fills changes, and removes the worktree when cleanup is set.
    • The session runs the step in that directory, for example in a subagent whose working directory is the worktree, and never runs git for the workspace itself.

    Why not let the host create the worktree: the worktree starts from a branch an earlier agent wrote. Hooks committed on it (.githooks, .husky) would run on checkout, merge, commit or push with the member's credentials. Without the base overlay, a later step would also load agents and hooks an earlier step committed. Reusing worktree.ts keeps that protection in one implementation, and the conformance suite checks one code path.

    How the session asks next to prepare the workspace (a field in the --json driver payload or an option) is left to the implementation. A driver that prepares its own workspaces, such as a sandbox driver, must not get it by default.

  8. SaulMoro commented on Oct 20, 2026

    @SaulMoro
    Collaborator
  9. SaulMoro commented on Oct 22, 2026

    @SaulMoro
    Collaborator

    For the skill driver: scorer work from #1085 does not come through workflow next. A driver that can judge asks for it separately.

    $ teamai score --next --json '{"driver":{"kind":"skill","machine":"m1","can":{"judge":{"isolation":"best-effort","tools":["claude"]}}}}'
    # lease: evidence dir, prompt, answerSchema, and a literal submit command
    $ teamai score --lease <ref> --submit --stdin < answer.json

    Two rules for drivers: never list a judge ref in workflow next's running, and settle a judge lease only with --submit. Conformance covers both.

  10. SaulMoro commented on Oct 24, 2026

    @SaulMoro
    Collaborator

    Decisions for the session skill driver, settled before #1304 is finished. They replace the SessionStart and invocable parts of the scope above.

    • The skill driver is its own driver. It reaches the engine only through the public CLI (next, report, respond) and imports nothing from the local driver. Workspace preparation and cleanup move out of local-driver.ts into a shared workspace module that every driver reaches through next --prepare and report. next --prepare can also adopt a directory the host created, such as a host worktree or sandbox: it checks the branch and base and applies the base overlay. TeamAI owns the workspace contract and the host owns the place. A host whose sandbox cannot run the CLI does not declare new or continue.
    • The session agent orchestrates and delegates. A deterministic state machine in the CLI, run through teamai workflow, drives the lease loop (lease, dispatch, await, report, then wait, HITL or done). It keeps its state in a file, so it is idempotent and survives a context compaction, and it returns actions for the agent to carry out. It never reads the graph: the engine keeps dependencies, loops, fan-out, deadlines and budget. Prompt steps go to child agents. Interactive steps stay with the person in the session. Command steps run inside the state machine with only the step's requires.env keys ([feat] Enforce workflow requirements and execution limits #1082), so no agent sees a secret value. A host that cannot scope env leaves env out of can.
    • A capability map, not tool names. Each action states what it needs: isolation, parallelism, or a person. The agent maps it to the most specialized primitive its host offers, in this order: app orchestration tools, team agents, native subagents, inline. driver.can comes from what the host declares.
    • Discovery through generated skills, not SessionStart. pull generates one workflow-<name> skill per advertised workflow. Its description is the workflow's, plus its alias and a lead that asks the agent to prefer the workflow over ad-hoc work. Its body routes to teamai skill get workflow. These skills follow the team-skill pipeline (scope, git exclude, git hooks, base overlay). They are marked as generated, never enter push, and pull prunes them. The SessionStart workflow list goes away.
    • invocable becomes invoke: allow | ask | deny, default deny. With allow, the agent starts the workflow on its own, and the generated skill pre-approves teamai workflow start <name>. With ask, the agent sees the workflow, proposes it and waits for the person's yes. Nothing is pre-approved, and the body says so for hosts without permission prompts. With deny, no skill is generated, and the workflow runs only from CI or as a sub-workflow. The teamai stub pre-approves only the loop and read subcommands (--describe, next, report, respond).
    • No nested starts. workflow start fails with nested_workflow when an attempt marker is in the environment, or when the working directory is a workflow worktree. The local driver and the state machine export that marker to every process they launch. Each child's brief names its attempt. Sub-workflows run as workflow steps. One gap remains: a session child on a read-only step in the member's checkout, which only its brief covers.
    • Skill is the default driver. Everything an agent reads leads to --driver skill. start without --driver still only creates the run, for external drivers. skill-data/workflow holds the orchestrator guide, with references loaded only when an action needs them (for example, a VM or sandbox needs the CLI installed plus init and pull). skill-data/workflow-local covers unattended runs in CI or cloud agents, with a reference for writing CI pipelines that use it.
    • Order. [feat] Enforce workflow requirements and execution limits #1082 stays a blocker. Command steps rely on its scoped env, so feat(workflow): add session discovery and managed public leases #1304 lands after feat(workflow): enforce scoped requirements and execution limits #1303.
  11. SaulMoro commented on Oct 25, 2026

    @SaulMoro
    Collaborator

    FYI, nothing to change here: #1388 (session-close) depends on the skill driver, and #1385 adds an unattended rule for drivers: drain teamai score --next before exiting. Once this lands, the skill driver's skill-data needs that line; #1385 carries it.

  12. SaulMoro commented on Oct 25, 2026

    @SaulMoro
    Collaborator

    Update to my earlier comment: the skill driver's part of run modes now ships with #1304 itself.

    • resolveRunMode in src/launch-context.ts: TEAMAI_MODE > CI > attended.
    • The mode is resolved once and stored in the run state.
    • In unattended, --describe starts with the recommended option and --auto-approve, or answers denied for required approvals, interactive steps and invoke: ask.
    • attemptEnv exports TEAMAI_MODE to every process of an attempt, in both drivers.
    • skill-data/workflow-local/references/ci.md says how CI and TEAMAI_MODE set the mode.

    #1384 extends that resolver and now depends on this issue. No drain line is added to the skill: #1385 adds its session work to the existing done.judging loop.

  13. SaulMoro commented on Oct 25, 2026

    @SaulMoro
    Collaborator

    Correction to my update above: a drain line is added after all, by #1385.
    #1385 adds teamai score --drain and one line to the unattended paragraph of skill-data/workflow/SKILL.md: run teamai score --drain before the session ends.
    Session judging and publishing do not go into done.judging. That loop keeps its shape and stays the run's own pass judging. Nothing else in PR #1304 changes.

  14. added a commit that references this issue on Oct 26, 2026
    fc8a229
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions