Implement a reusable agent harness inside PocketAI while keeping the current UI and overall feel largely unchanged.
The main shift should be architectural, not visual:
- keep the current chat panel, approvals, inline diffs, endpoint switching, and session UX
- replace the ad hoc orchestration with a cleaner harness/runtime model
- make the harness reusable across Local PocketAI, Codex Bridge, and future providers
This part is non-negotiable.
- We may reuse or adapt permissively licensed open-source code where the license allows it.
- We may use public behavior, public docs, and our own observations as product requirements.
- We should not copy non-public or restricted source, prompts, tests, strings, schemas, or internal architecture directly.
- If an external tool behavior is desirable, we should restate it as a product requirement and implement it ourselves.
- We should use public documentation and appropriately licensed references for source-level implementation guidance.
- Every migrated subsystem should note its provenance in the PR description:
ported from open-source referenceadapted from existing PocketAInew implementation
Reference priority:
- existing PocketAI code
- permissively licensed open-source references
- public product behavior and docs
- our own design
These are the highest-value areas for delivering a strong coding-agent feel without copying proprietary source:
- Tool registry and permission model
- Agent loop / turn runner / reasoning flow
- Skills layer
- Native IDE bridge, inline diffs, and slash orchestration
- Reliability, memory, compaction, and long-task recovery
PocketAI already has many of the needed harness pieces. The work is mostly to normalize and harden them.
- Tool loop:
src/tool-loop.ts - Tool registry / schemas:
src/tool-definitions.ts - Tool execution and safety checks:
src/tool-executor.ts - Extension orchestration:
src/extension.ts - Chat/session message routing:
src/message-handler.ts - Session persistence:
src/session-manager.ts - Checkpoints and rewind:
src/checkpoints.ts - Inline diffs:
src/inline-diff.ts - Terminal execution:
src/terminal-manager.ts - Permissions and hooks:
src/permissions.ts,src/hooks.ts - Memory/context/compaction:
src/memory-manager.ts,src/workspace-context.ts - Slash command entrypoint:
src/slash-commands.ts
By the end, the harness should provide:
- a first-class tool registry with stable schemas, permission hooks, and rich structured results
- a deterministic turn state machine
- a first-class approval model instead of scattered mode checks
- a canonical tool runtime with shared validation and result envelopes
- a reusable skills layer for higher-level workflows
- better native IDE awareness, including LSP-style code intelligence hooks
- consistent background task handling
- better loop detection and retry behavior
- cleaner provider integration for Local PocketAI and Codex Bridge
- minimal UI churn
Create a dedicated harness layer under src/harness/ and move orchestration into it.
Suggested modules:
src/harness/tools/- canonical tool registry, schemas, discovery, and per-tool adapters
src/harness/types.ts- canonical event, turn, approval, and task types
src/harness/runner.ts- main turn runner / state machine
src/harness/policy.ts- ask/auto/plan approval decisions
src/harness/runtime.ts- executes tool calls through a common contract
src/harness/provider.ts- provider abstraction for model backends
src/harness/events.ts- event emission utilities for UI/state syncing
src/harness/skills/- skill loading, discovery, and invocation
High-level shape:
flowchart LR
UI["VS Code UI / Webviews"] --> Bridge["Extension Bridge"]
Bridge --> Runner["Harness Runner"]
Runner --> Policy["Approval Policy"]
Runner --> Context["Context Builder"]
Runner --> Provider["LLM Provider"]
Runner --> Tools["Tool Registry + Runtime"]
Runner --> Skills["Skills Layer"]
Tools --> IDE["VS Code / FS / Terminal / Git / LSP"]
Skills --> Tools
Provider --> Runner
PocketAI UI should consume harness state/events rather than piecemeal side effects.
Start here, because this is the biggest contributor to perceived intelligence.
Required metadata for each tool:
- stable unique name
- natural-language description written for the model
- JSON-schema-like parameter definition
- execute handler
- permission hook
- risk classification
- structured result formatter
Critical first-wave tools:
read_fileedit_filewrite_filegrepglobrun_command/execute_shell- git inspection tools
- LSP-style IDE tools such as definitions, references, diagnostics
- a tool-discovery helper such as
list_toolsor equivalent registry introspection
Acceptance criteria:
- tools are model-facing assets, not just executor cases
- every tool can explain whether it is safe in ask/auto/plan
- results are structured enough for the next turn to consume without brittle parsing
First write down the lifecycle we want.
Core event types should include:
turn_startedassistant_deltaassistant_message_completedtool_calls_detectedtool_call_pending_approvaltool_call_startedtool_call_completedtool_call_faileddiff_readybackground_task_updatedturn_completedturn_failed
Acceptance criteria:
- one turn has a single source of truth
- transcript updates become an output of the harness, not the orchestration mechanism itself
Refactor src/tool-loop.ts into a harness runner without changing behavior first.
Plan:
- move loop control, retries, and loop detection into
HarnessRunner - keep current tools and current UI behavior intact during the extraction
- preserve both structured and text-based tool paths for now
Acceptance criteria:
- existing chats still work
- current approvals and inline diffs still appear
- no visible UI redesign is required
Convert tools into a shared runtime model instead of having execution logic spread across the loop and executor.
Each tool adapter should expose:
validatepreviewexecuteclassifyRiskformatResult
Preserve existing PocketAI safeguards:
- read-before-edit enforcement
- workspace path restrictions
- permission rules
- checkpoints before file mutations
- lifecycle hooks
- diff preview generation for edit operations
- shell command safety checks and destructive-command warnings
Acceptance criteria:
- all built-in tools run through one runtime path
- tool failures return normalized error objects
Right now, mode behavior is mixed into execution flow. Move it into a policy layer.
The policy layer should answer:
- does this tool require approval?
- can this tool auto-run?
- should this tool be blocked in plan mode?
- should a diff preview be generated before approval?
Acceptance criteria:
Ask,Auto, andPlanwork exactly as they do now from the user’s perspective- mode-specific logic is no longer scattered across loop and executor code
Add a reusable workflow layer on top of raw tools.
Suggested shape:
- bundled built-in skills for common engineering workflows
- workspace-local skills under a dedicated directory such as
.pocketai/skills/ - a
run_skilltool or equivalent harness-native skill invocation path - skill discovery so the agent can find relevant workflows instead of reinventing them
Good first skills:
- implement feature following local style
- refactor function with tests
- debug failing command or stack trace
- add documentation for changed code
Acceptance criteria:
- skills are composable and parameterized
- skills can call tools without bypassing the same safety model
- the model can discover available skills naturally
Keep the current inline diff UI, but stop treating pending edits as incidental tool metadata.
Represent them directly as:
- pending file mutation
- diff preview
- approval status
- applied / rejected / stale state
Acceptance criteria:
- existing inline diff UI remains
- the backing state becomes explicit and easier to reason about
Keep the UI looking the same, but make the harness treat IDE capabilities as first-class operations.
This layer should support:
- open file in editor
- reveal range / selection
- diagnostics and problems access
- definitions / references / symbol discovery
- apply edit with preview
- VS Code command invocation where appropriate
- slash command routing for
/plan,/skills,/doctor,/compact,/tasks, and similar harness-native actions
Acceptance criteria:
- IDE-specific actions do not live as scattered extension helpers
- slash commands become harness-aware rather than ad hoc command branches
The harness should treat commands as tracked tasks, not just tool results.
Add:
- task IDs
- lifecycle state
- stdout/stderr snapshots
- cancellation support
- reconnect-safe status if the webview refreshes
Acceptance criteria:
- long-running tasks survive panel refreshes better
- the model receives consistent command result summaries
The harness should not know whether it is talking to Local PocketAI or Codex Bridge.
Define a provider contract for:
- plain streaming completions
- structured tool support
- reasoning/model metadata
- provider capability flags
Short-term rule:
- Local PocketAI keeps the full current tool harness
- Codex Bridge remains partially capability-gated until it supports approval-compatible tool flows
Acceptance criteria:
- provider-specific branching is isolated
- UI keeps the same endpoint and per-chat model UX
Move these out of scattered orchestration code and into explicit harness stages:
- pre-turn context assembly
- pre-tool safety/context refresh if needed
- post-turn summarization
- periodic compaction
- checkpoint registration
Context sources should include:
- workspace file tree and active editor state
- git status and recent changes
- diagnostics
- project instructions from
.pocketai.md - compatibility readers for
AGENTS.mdorCLAUDE.mdwhen present, treated as user/project guidance rather than proprietary product artifacts
Acceptance criteria:
- less logic living directly in
extension.ts - context behavior remains stable across providers
Before broadening behavior, add a proper test matrix.
Test categories:
- tool approval decisions
- loop detection
- retry handling
- read-before-edit enforcement
- pending diff lifecycle
- background command lifecycle
- provider capability branching
- session restore after refresh
Add fixture-based integration tests for:
- read -> edit -> approve -> continue
- command -> output -> follow-up tool call
- rejection flow
- codex endpoint with reasoning/model switching
- skill invocation and chained tool execution
- IDE bridge operations such as diagnostics and open-file actions
- write a short harness spec
- write a provenance checklist
- decide which open-source modules can be reused directly
- add an experimental feature flag, for example
pocketai.experimentalHarness - define the first-wave tool registry contract
- extract runner, policy, and runtime modules
- keep UI behavior identical
- keep current state shape if possible, with adapters
- move existing tools behind the new registry without changing their user-facing behavior
- finish first-wave tool definitions and structured results
- centralize ask/auto/plan approval behavior
- preserve current safeguards for file edits and commands
- make pending changes explicit
- route inline diff UI through harness state
- add the initial skills layer
- add native IDE and slash-command harness routing
- add LSP-backed code intelligence tools
- unify background tasks
- improve cancellation and reconnect behavior
- isolate Local PocketAI and Codex Bridge behind a common interface
- keep Codex capability-limited where needed
- remove dead orchestration paths
- document the harness
- turn the new path on by default after regression coverage
Risk:
- changing transcript and tool state flow could break the current panel behavior
Mitigation:
- keep the current UI components
- add adapters so the webview can continue consuming familiar state until the harness stabilizes
Risk:
- transcript state and harness state drift
Mitigation:
- keep one canonical harness state object
- generate transcript/webview payloads from it
Risk:
- Codex does not yet support the same approval/diff flow as the local harness
Mitigation:
- capability-gate Codex features
- avoid pretending Codex has parity until the bridge truly supports it
Risk:
- accidental copying from non-public or restricted material
Mitigation:
- do not paste from restricted source material
- do not use non-public repos as implementation references
- treat external tools only as black-box behavior targets when needed
- document provenance on every PR
Risk:
- runtime refactors weaken path, command, or permission boundaries
Mitigation:
- preserve existing guardrails first
- add tests before widening any tool capabilities
Good candidates to preserve or port with minimal changes:
- current tool schemas and descriptions
- checkpoint system
- inline diff manager
- terminal manager
- permission rules
- lifecycle hooks
- session persistence
- existing slash commands where the semantics already fit the new harness
These are already aligned with the kind of harness we want.
These deserve a cleaner pass instead of more patching:
- turn orchestration
- approval policy
- tool execution state model
- background task lifecycle
- skills storage and invocation model
- IDE bridge abstraction
- provider abstraction
- event/state synchronization between extension host and webview
plan.mdin repo root- harness architecture spec
- provenance checklist
- experimental harness flag
- first-wave tool inventory and risk classification
- phased PR series instead of one giant rewrite
The safest first slice is:
- create
src/harness/types.ts,src/harness/policy.ts, andsrc/harness/runner.ts - create a minimal
src/harness/tools/registry.tsthat wraps the current built-in tools - move the current
tool-loop.tslogic into the runner with minimal behavior change - keep existing UI and transcript payloads
- add tests for ask/auto/plan, loop detection, and one edit approval flow
If that slice lands cleanly, the rest of the migration becomes much lower risk.