Skip to content

refactor: C2b — hosts, runProgram and tools behind the layer contracts - #1313

Closed
gewenyu99 wants to merge 1 commit into
posthog/functional-c2a-tuifrom
posthog/functional-c2b-headless
Closed

gewenyu99 wants to merge 1 commit into
posthog/functional-c2a-tuifrom
posthog/functional-c2b-headless

Conversation

@gewenyu99

@gewenyu99 gewenyu99 commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The contracts in #1369 have no code behind them. On main, one process-wide WizardUI carries the debug output, the prompts and the abort screen for every layer. The CLI runs programs from several places, program steps name TUI screens, tools run as programs, and screens, programs and shared code can all end the process. A program cannot run without a UI, and a change to one host can break the other.

Changes

This PR adds the code behind the #1369 contracts. The CLI calls one of two hosts, each host builds its own session store and calls runProgram, and runProgram calls runAgent. The programs, agent and shared core and the hosts do not compile apart, so they land together, with the test edits this PR needs to stay green.

  • runProgram (src/programs/run-program.ts) runs a registered program on the caller's store: detection through detectProgram when the store has none, readiness, logIn, each host step through workflow.confirmStep, then each runAgent call. Every result lands in the store, and the store ends settled even when the call rejects.
  • SessionStore (src/programs/session/session-store.ts) holds the launch values, detection, the login, the binding the agent resolved and the run's progress. sessionControlTarget serves the control API from it.
  • TUI host: runTui (src/tui/run.ts) and runTuiTool (src/tui/run-tool.ts). WizardStore (src/tui/store.ts) sits over the session store with the TUI-only TuiState (src/tui/tui-state.ts). tuiWorkflow confirms steps from the screens, and a screen ends the run with store.requestExit(code). Each program's screens live in src/tui/programs/<id>, found by getTuiProgram.
  • Headless host: runHeadless (src/headless/run.ts) builds its own SessionStore, prints log lines through LoggingUI, streams the task state, and serves the control API through serveHeadlessControl. It loads no TUI module.
  • Shared host code in src/host: wizardAbort takes its AbortPresenter from the caller, startHostExit resolves each host's exit code, and src/host/control holds ControlClient and RunLedger.
  • Tools in src/tools, apart from programs: addMcpServer, removeMcpServer, runMcpPrompt, runDoctorReport, runProvision, runCliAdd and listSkills. Their screens live in src/tui/tools/<id>, and MCP client installs in src/shared/mcp-clients/install.ts. No program imports @tools.
  • CLI: runWizard, runWizardCI and runWizardHeadless in src/cli/runners start the hosts. underSignals aborts the run on SIGINT or SIGTERM, and exitWith turns the host's code into the exit. In src, only the CLI calls process.exit.
  • Agent: resolveRunConfig (src/agent/runner/switchboard/resolve-run.ts) turns a RunConfig into the ResolvedRunConfig the runner reads.
  • tsdown.config.ts gains the PUBLISH_LIBRARY switch, off. The package still ships the wizard bin only.
  • Deleted: WizardUI, src/ui, src/lib, src/steps, src/commands, the old program steps.ts files, run-agent-legacy.ts, agent-runner.ts, and the architecture test with its allowlist. build: C2d — fence every layer with TypeScript project references #1315 fences the layers with the compiler.

What calls what, on this PR's code (the diagram #1371 adds to the README):

%%{init: {"block": {"padding": 20}}}%%
block-beta
  columns 18
  user["a user at a terminal"]:3 ci["CI or a sandbox"]:3 embedder["your program"]:3 space:3 toolUser["a user or a script"]:3 caller["your code or a test"]:3
  cliBand["src/cli   picks a host, owns the signals and the exit"]:6 space:12
  runWizard["runWizard"]:3 runWizardCI["runWizardCI<br/>runWizardHeadless"]:3 space:6 toolCmd["src/cli<br/>a tool's command"]:3 space:3
  hostBand["src/tui · src/headless   the two hosts"]:6 space:12
  runTui["runTui"]:3 runHeadless["runHeadless"]:3 space:6 runTool["runTuiTool<br/>or an @tools runner"]:3 space:3
  wizardStore["WizardStore<br/>display over the store"]:3 space:15
  programsBand["@programs"]:12 space:6
  tuiStore["SessionStore"]:3 headlessStore["SessionStore"]:3 ownStore["SessionStore"]:3 detect["detectProjectsWithAgent"]:3 space:6
  tuiRun["runProgram"]:3 headlessRun["runProgram"]:3 ownRun["runProgram"]:3 space:9
  agentBand["@agent"]:18
  tuiAgent["runAgent"]:3 headlessAgent["runAgent"]:3 ownAgent["runAgent"]:3 detectAgent["runAgent"]:3 mcpStream["streamMcpPrompt"]:3 callerAgent["runAgent"]:3

  user --> runWizard
  runWizard --> runTui
  runTui --> wizardStore
  wizardStore --> tuiStore
  tuiStore --> tuiRun
  tuiRun --> tuiAgent

  ci --> runWizardCI
  runWizardCI --> runHeadless
  runHeadless --> headlessStore
  headlessStore --> headlessRun
  headlessRun --> headlessAgent

  embedder -- "builds and owns the store" --> ownStore
  ownStore --> ownRun
  ownRun --> ownAgent

  detect --> detectAgent

  toolUser --> toolCmd
  toolCmd --> runTool
  runTool -- "the MCP tutorial only" --> mcpStream

  caller -- "runAgent(config, input)" --> callerAgent

  classDef code fill:#3b82f626,stroke:#3b82f6,stroke-width:2px
  classDef ext fill:none,stroke:#9ca3af,stroke-dasharray:4 3
  classDef band fill:#3b82f640,stroke:none
  class runWizard,runWizardCI,runTui,runHeadless,wizardStore,tuiStore,headlessStore,ownStore,tuiRun,headlessRun,ownRun,tuiAgent,headlessAgent,ownAgent,detect,detectAgent,toolCmd,runTool,mcpStream,callerAgent code
  class user,ci,embedder,toolUser,caller ext
  class cliBand,hostBand,programsBand,agentBand band
Loading
Kind Files Lines Review
Programs, agent and shared modules 180 +5,044/-3,179 read
TUI, headless, host and tools modules 192 +9,246/-832 read
CLI, harness, scripts, docs examples and build config 49 +1,364/-847 read
Old modules, scripts and config deleted 55 +0/-8,531 mechanical
Tests edited to follow the code 94 +3,412/-4,387 read
Tests removed with their code, and the architecture test 11 +0/-2,495 mechanical
Total 581 +19,066/-20,271

Stack: #1311 moves, #1369 contracts, #1313 logic, #1370 tests, #1315 fences, #1371 docs

Test plan

On this tree, which holds #1369:

  • pnpm typecheck: 0 errors.
  • vitest run: 3,260 tests pass.
  • pnpm lint: 0 errors.

Proof on the final code of the stack (#1371), against a local PostHog stack:

  • Headless posthog-integration on a one-file Node app: exit 0, SDK installed.
  • npx tsx --tsconfig tsconfig.json docs/examples/run-agent-quack.ts: replies "quack", outcome success.
  • npx tsx --tsconfig tsconfig.json docs/examples/run-program-quack.ts: replies "quack", outcome success, ends at phase completed.

Created with PostHog Desktop

@github-actions

Copy link
Copy Markdown

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci ai-observability
  • /wizard-ci basic-integration
  • /wizard-ci mcp-analytics
  • /wizard-ci replay-vision
  • /wizard-ci revenue
  • /wizard-ci self-driving
  • /wizard-ci warehouse
  • /wizard-ci warehouse-seeded

Test an individual app:

  • /wizard-ci ai-observability/anthropic
  • /wizard-ci ai-observability/google-adk
  • /wizard-ci ai-observability/groq
Show more apps
  • /wizard-ci ai-observability/manual-capture
  • /wizard-ci ai-observability/openai
  • /wizard-ci ai-observability/openai-agents
  • /wizard-ci ai-observability/opentelemetry
  • /wizard-ci ai-observability/vercel-ai
  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/flutter
  • /wizard-ci basic-integration/javascript-node
  • /wizard-ci basic-integration/javascript-web
  • /wizard-ci basic-integration/laravel
  • /wizard-ci basic-integration/next-js
  • /wizard-ci basic-integration/nuxt
  • /wizard-ci basic-integration/python
  • /wizard-ci basic-integration/rails
  • /wizard-ci basic-integration/react-native
  • /wizard-ci basic-integration/react-router
  • /wizard-ci basic-integration/sveltekit
  • /wizard-ci basic-integration/swift
  • /wizard-ci basic-integration/tanstack-router
  • /wizard-ci basic-integration/tanstack-start
  • /wizard-ci basic-integration/vue
  • /wizard-ci mcp-analytics/custom-dispatcher
  • /wizard-ci mcp-analytics/typescript-sdk
  • /wizard-ci replay-vision/javascript-node
  • /wizard-ci replay-vision/next-js
  • /wizard-ci replay-vision/react-vite
  • /wizard-ci revenue/stripe
  • /wizard-ci self-driving/astro
  • /wizard-ci self-driving/fastapi
  • /wizard-ci self-driving/nuxt
  • /wizard-ci self-driving/react-router
  • /wizard-ci self-driving/sveltekit
  • /wizard-ci warehouse/monorepo-env
  • /wizard-ci warehouse/multi-source-next
  • /wizard-ci warehouse/stripe-node
  • /wizard-ci warehouse/zero-source
  • /wizard-ci warehouse-seeded/next-stripe
  • /wizard-ci warehouse-seeded/next-stripe-declined

Test against a Context Mill branch:

  • /wizard-ci all context-mill:my-branch

Add context-mill:<branch> to any command above to pin the Context Mill branch. It defaults to main.

Results will be posted here when complete.

@gewenyu99 gewenyu99 changed the title refactor(cli): C2b CLI entries, abort as host capability refactor(cli): C2b — CLI owns headless runs and abort Sep 24, 2026
Base automatically changed from posthog/functional-c2a-tui to posthog/functional-c1-move September 27, 2026 15:19
@gewenyu99
gewenyu99 force-pushed the posthog/functional-c2b-headless branch from fd1b0b8 to 25fc53d Compare September 27, 2026 15:24
@gewenyu99
gewenyu99 changed the base branch from posthog/functional-c1-move to posthog/functional-c2a-tui September 27, 2026 15:26
@gewenyu99 gewenyu99 changed the title refactor(cli): C2b — CLI owns headless runs and abort refactor(cli): C2b — folded into C2a (empty) Sep 27, 2026
@gewenyu99
gewenyu99 added this pull request to stack #1372 September 27, 2026 15:28
The hosts call runProgram and return exit codes the CLI applies; tools leave the programs; WizardUI and the global sinks are gone. Test edits this change needs ride with it.

Generated-By: PostHog Desktop
Task-Id: d14e92bb-6ee1-49b5-8502-39cb80079589
polls.signal.removeEventListener('abort', finish);
resolve(readState());
};
const timer = setTimeout(finish, timeoutMs);
Comment thread src/shared/console-log.ts
intro: (message) => console.log(`┌ ${message}`),
outro: (message) => console.log(`└ ${message}`),
log: {
info: (message) => console.log(`│ ${message}`),
@gewenyu99 gewenyu99 closed this Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants