Skip to content

draft(workspaces): separate PTY lifecycle from Agent activity - #1038

Draft
luokerenx4 wants to merge 14 commits into
devfrom
codex/session-activity-lifecycle-proposal
Draft

draft(workspaces): separate PTY lifecycle from Agent activity#1038
luokerenx4 wants to merge 14 commits into
devfrom
codex/session-activity-lifecycle-proposal

Conversation

@luokerenx4

@luokerenx4 luokerenx4 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Status

Design/implementation proposal — do not merge yet.

This PR exists so the Session lifecycle model can be reviewed against concrete code, tests, and transport evidence. It does not claim that the proposed state model, persistence boundary, or Workspace-local runtime hooks have been accepted.

The immediate OpenCode startup defect is isolated in #1037. Real configured-effort OpenCode acceptance for this branch depends on that fix, but this PR does not include its commit.

Problem

OpenAlice currently exposes a live PTY as running. OpenCode and Pi intentionally keep their interactive process alive after a turn completes, so process liveness makes a finished Agent look permanently busy. transcript.session.captured only identifies the native conversation and is not a completion signal.

Alternatives considered

  1. Infer idle from output silence. Smallest integration, but TUI redraws, long tools, streaming pauses, focus and resize traffic make quietness unreliable.
  2. Treat process exit as turn completion. Semantically simple, but it breaks the persistent native-TUI Session contract.
  3. Separate PTY lifecycle from native Agent activity. This prototype keeps running/paused for the process and adds transient starting/working/waiting/unavailable/failed/stopped activity sourced from adapter hooks.

The prototype implements option 3 because it avoids timing heuristics while preserving resumable TUI processes. That choice remains open for review.

Prototype

  • Versioned private OSC activity frame scoped to the OpenAlice Session id.
  • Headless terminal consumes the frame without rendering it as terminal content.
  • PersistentSession snapshots activity for attach and reconnect but does not persist it to disk.
  • Workspace-local managed OpenCode and Pi hooks emit native start/settle/error signals when supported.
  • REST, WebSocket, Electron IPC and UI project activity separately from Session process state.
  • UI distinguishes Working from a live TUI that is Ready for user input; unsupported adapters show Live rather than fabricated progress.

Open design questions

  • Keep two axes, or replace the public Session contract with one richer state machine?
  • Require native runtime events, or define a lower-confidence fallback for adapters without them?
  • Keep activity transient, or persist it with explicit staleness semantics?
  • Are Workspace-local managed hooks acceptable, or should adapters wait for non-file runtime registration APIs?
  • Does Pi expose a trustworthy failure event beyond child-process exit?

Verification completed

  • npx tsc --noEmit
  • cd ui && npx tsc -b
  • npx tsc -p apps/desktop/tsconfig.json --noEmit
  • pnpm electron:build (unsigned development build)
  • CSC_IDENTITY_AUTO_DISCOVERY=false pnpm electron:smoke:pty --skip-build — real Electron IPC/PT​​Y attach and CLI socket round trip passed.
  • pnpm docker:smoke — isolated image, HTTP readiness, runtime detection, real Workspace PTY WebSocket, alice CLI round trip, and offboarding passed without AI credentials or a broker.
  • Focused adapter / protocol / terminal / reconnect / UI tests, including late first reply, disconnected output replay, current activity replay, and transcript identity/activity separation.
  • pnpm test: 495 files passed, 1 skipped; 4074 tests passed, 9 skipped.

Still required before any acceptance decision

  • Manual visual/interaction walk of the real Chat route. In-app browser automation rejected the existing localhost tab under its URL security policy, so this PR deliberately does not claim browser acceptance.
  • Decide the open lifecycle semantics above, especially transient vs durable activity and the managed-hook ownership boundary.
  • Re-run a real configured-effort OpenCode turn with fix(workspaces): stop passing run-only flags to OpenCode TUI #1037 present.

@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openalice-demo Ready Ready Preview Aug 9, 2026 11:27am

Request Review

@luokerenx4 luokerenx4 added workflow:parallel Autonomous parallel contribution; leave open for later acceptance theme:reliability Failure recovery, retries, loading, or resilience area:workspace Workspace, Session, templates, or sidebar lifecycle review:deep Requires deliberate human review before merge labels Aug 9, 2026
@luokerenx4

Copy link
Copy Markdown
Contributor Author

Native runtime acceptance update (2026-08-09): added scripts/session-activity-runtime-smoke.ts, an opt-in TUI smoke that uses the runtime native/global login without reading or printing credential material. In a disposable checkout combining this proposal with the isolated argv fix from #1037, both OpenCode and Pi emitted waiting -> working -> waiting for a real turn and their PTY processes remained alive after settling. Running configured-effort OpenCode on #1038 alone exits before plugin load, as expected, so #1037 remains a separate prerequisite rather than being folded into this Draft. The lifecycle design itself remains unapproved and this PR remains Draft/unmerged.

@luokerenx4

Copy link
Copy Markdown
Contributor Author

Additional Draft-gate findings:

  • Browser acceptance remains unclaimed: localhost interaction was rejected by the in-app browser URL policy, and the Vercel preview redirects to the team login gate.
  • Desktop Package Smoke exposed a real proposal regression on macOS and Windows: Pi headless launch retains --provider openalice-session but no longer loads the extension that registers that provider, so acceptance fails with Unknown provider "openalice-session".

Two focused repair options are recorded in the plan and intentionally left for design review:

  1. Continue loading the shared Pi extension on headless/WebPi and guard activity emission to interactive TTY launches only.
  2. Split process-local provider registration from the interactive activity hook and load each extension only on its owning surface.

No lifecycle implementation change or merge was made in response; the PR remains Draft and unmerged.

@luokerenx4

Copy link
Copy Markdown
Contributor Author

Pi surface trace is now recorded in commit 23f313ee (design only; no implementation change).

Recommended ownership:

  • terminal TUI (node-pty): load a dedicated activity extension; additionally load the provider extension only when OpenAlice access is injected;
  • headless JSON (child_process pipes): provider extension only when injected;
  • WebPi RPC (child_process pipes): provider extension only when injected, with activity remaining owned by the existing RPC state machine.

The recommendation is to split pi-session-provider.ts back to provider-only behavior and add a separate pi-session-activity.ts. This avoids isTTY inference and makes OSC contamination of JSON/RPC stdout impossible by construction.

For failures, the activity extension should retain the final assistant stopReason at agent_end and publish failed vs waiting only at agent_settled; Pi does not expose willRetry to extensions, and agent_settled is the native boundary after retries/compaction/queued continuations finish.

This remains an unapproved Draft recommendation; the PR is still not for merge.

@luokerenx4

Copy link
Copy Markdown
Contributor Author

Baseline update (2026-08-09):

  • Synced the Draft branch with current dev, including the separately merged OpenCode TUI argv fix from fix(workspaces): stop passing run-only flags to OpenCode TUI #1037.
  • Re-ran the focused lifecycle/replay/UI regression set: 9 files, 151 tests passed.
  • Re-ran root TypeScript and UI project typechecks: both passed.
  • No pending Pi provider/activity design was implemented; the PR remains a design proposal and must stay Draft until that ownership decision is accepted.

@luokerenx4

Copy link
Copy Markdown
Contributor Author

Acceptance audit improvement:

The previous UI evidence only tested the activity-to-label helper. Added component-level regressions for all three visible Session listings:

  • Sidebar Session row: live PTY + waiting renders Ready, then working renders Working.
  • Workspace Session library: waiting/working labels and tones render independently while the lifecycle filter remains Running.
  • Workspace overview card: live waiting Session is labelled Ready, not Working.

Focused UI verification: 4 files / 18 tests passed; UI TypeScript project build passed. This strengthens the current proposal without selecting or implementing the pending Pi ownership design.

@luokerenx4

Copy link
Copy Markdown
Contributor Author

Quick Chat delivery audit improvement:

The delivery contract is now pinned across all three ownership boundaries instead of inferred from the terminal layer alone:

  1. /quick-chat route passes the normalized user prompt into SessionFactoryContext.initialPrompt before any renderer attaches.
  2. Adapter tests prove each native CLI receives that seed in its accepted interactive argv position.
  3. PersistentSession tests prove the first reply survives late attach and a later WebSocket reconnect.

Focused verification: 3 files / 61 tests passed; root TypeScript check passed. No pending lifecycle design choice was implemented.

@luokerenx4

Copy link
Copy Markdown
Contributor Author

Added the ordinary Workspace-list activity projection in b4f4d657.

The main /api/workspaces path used by Sidebar polling previously omitted native activity, even though the nested detail route returned it. That forced normal UI views onto the legacy Live fallback. The list and detail routes now share one projection helper: terminal-native activity wins, every WebPi phase is mapped consistently, and records without a live process report stopped. Demo fixtures/handlers now exercise waiting, working, and starting instead of silently covering only the fallback.

Verification:

  • npx tsc --noEmit
  • cd ui && npx tsc -b
  • focused projection/REST/demo set: 5 files, 76 tests
  • full pnpm test: 496 passed, 1 skipped; 4088 tests passed, 9 skipped
  • dev:demo readiness smoke: HTTP 200

The PR remains Draft and unmerged. The Pi provider/activity split and final lifecycle semantics are still intentionally awaiting maintainer design approval.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:workspace Workspace, Session, templates, or sidebar lifecycle review:deep Requires deliberate human review before merge theme:reliability Failure recovery, retries, loading, or resilience workflow:parallel Autonomous parallel contribution; leave open for later acceptance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant