Skip to content

Latest commit

 

History

History
620 lines (473 loc) · 29.2 KB

File metadata and controls

620 lines (473 loc) · 29.2 KB

Modules Reference

A source-backed reference for every module in the src/ directory. Each section lists its purpose, key files, public exports, and relationships.

See also: Architecture, Loop System, API Reference.

Source Tree Overview

src/
├── index.ts                 # Server plugin entry point (V2 setup)
├── tui.tsx                  # TUI plugin entry point (V2 setup)
├── config.ts                # Agent/command configuration handler
├── setup.ts                 # Config loading, skill installation
├── types.ts                 # Core type definitions (PluginConfig, etc.)
├── version.ts               # VERSION constant generated from package.json
│
├── agents/                  # AI agent definitions
├── client/                  # ForgeClient port + V2 host adapter
├── host/                    # Host-neutral core + V2 composition adapter
├── hooks/                   # Plugin event/lifecycle hooks
├── loop/                    # Core loop state machine & runtime
├── services/                # Business logic services
├── sandbox/                 # msb sandbox management
├── storage/                 # SQLite persistence layer
├── tools/                   # Plugin tools callable by AI agents
├── tui/                     # TUI-specific components
├── utils/                   # Shared utility modules (~40 files)
└── workspace/               # Git worktree / workspace management

Entry Points

src/index.ts — Server Plugin Entry

The server plugin entry. The default export is the OpenCode 2.x module (id + setup) built with define.

Public API (src/index.ts):

Export Type Description
setupForgeV2(ctx) Function OpenCode 2.x setup entry, exported for the module and tests
createParentSessionLookup(options) Function Resolves parent sessions across worktrees
createSessionDirectoryLookup(options) Function Resolves session directory across worktrees
PluginConfig Interface Complete plugin configuration
CompactionConfig Interface Session compaction settings
VERSION Constant Plugin version string

Source: src/index.ts

src/tui.tsx — TUI Plugin Entry

The TUI plugin entry, providing the sidebar widget and dialog system. It talks to the server plugin through the V2 plugin RPC port.

  • Exports { id: 'oc-forge', setup: setupForgeTuiV2 }
  • Registers commands: Execute plan, Open web dashboard, Build sandbox template, and Toggle sandbox
  • Provides the loop sidebar, session-rotation following, and the missing-build-context toast

Source: src/tui.tsx, src/tui/v2.tsx


host/ — Core and V2 Composition

Host-neutral core plus the thin adapter that maps the OpenCode V2 plugin context onto it.

Files

File Purpose
forge-core.ts createForgeCore() — shared services, handlers, tools, cleanup, sandbox resolution, lookups
v2.ts setupForgeV2(ctx) — V2 setup: client, registrations, event pump, cleanup
v2-events.ts Normalizes V2 events into Forge's event shape; busy, idle, and retry derive only from session.execution.* and session.retry.scheduled
forge-rpc.ts FORGE_RPC contract: the TUI methods (executePlan, loopDefaults, autoApproveState/autoApproveSet, loops, loopSidebar, sessionPlan, loopRestart, hostSandboxState/hostSandboxSet, worktrees, version) and the toast/sessionDelete/loopsChanged/autoApproveChanged/hostSandboxChanged events the V2 server emits and the V2 TUI consumes
v2-hooks.ts Registers the core handlers through V2's hook API
v2-tools.ts Registers the shared Forge tools on V2
v2-config.ts Resolves and registers agents and commands on V2

Public API

createForgeCore(config: PluginConfig, host: ForgeHostInput): Promise<ForgeCore>
buildArchitectReminder(): string
createParentSessionLookup(options): ...
createSessionDirectoryLookup(options): ...
setupForgeV2(ctx: Plugin.Context): Promise<() => Promise<void>>

Source: src/host/forge-core.ts, src/host/v2.ts


client/ — ForgeClient Port and Adapter

The port every Forge service depends on, plus the V2 adapter.

Files

File Purpose
port.ts ForgeClient interface
v2-adapter.ts Adapter over the V2 plugin context
v2-workspaces.ts V2 worktree/location workspace implementation
errors.ts Shared error classification and unavailableError()

Source: src/client/port.ts


agents/ — AI Agent Definitions

Defines roles and system prompts for each AI agent used in the forge pipeline.

Files

File Purpose
index.ts buildAgents() factory, barrel exports
types.ts AgentRole, AgentDefinition, AgentConfig types
code.ts Code execution agent
architect.ts Read-only planning/design agent
auditor.ts Code review agent + auditor-loop variant
architect-auto.ts Autonomous architect agent
feature-splitter.ts Splits a PRD into features for a group

Public API

buildAgents(): Record<AgentRole, AgentDefinition>

type AgentRole = 'code' | 'architect' | 'auditor' | 'auditor-loop' | 'architect-auto' | 'feature-splitter'

Source: src/agents/index.ts


hooks/ — Plugin Event Hooks

Translates OpenCode events into loop actions and manages lifecycle side-effects.

Files

File Purpose
index.ts Barrel exports
session.ts Session lifecycle (message, compacting)
loop.ts LoopEventHandler adapter (events → Loop runtime)
host-side-effects.ts Termination side-effects (teardown, toast, log)
watchdog.ts Stall detection and recovery
plan-approval.ts Plan approval dedup/event gating + tool execute before/after hooks
plan-capture.ts Marked-plan capture from streaming assistant message parts
forge-session-attach.ts Auto-attach loops on session.created and chat.message events
loop-permission.ts Patches subagent permission rulesets on session.created for active-loop sessions
sandbox-tools.ts Sandbox tool before/after redirection hooks
sandbox-message.ts Tells the agent its tool calls run in a container
group-orchestrator.ts Advances queued features when a group loop terminates
tool-hook-types.ts Shared tool before/after hook types

Public API (barrel exports from hooks/index.ts)

createSessionHooks(): SessionHooks          // Session lifecycle hooks (session.ts)
createLoopEventHandler(): LoopEventHandler  // Loop event handling adapter (loop.ts)
createToolExecuteBeforeHook()              // Pre-tool execution hook (plan-approval.ts)
createToolExecuteAfterHook()               // Post-tool execution hook (plan-approval.ts)
createPlanApprovalEventHook()              // Plan approval event hook (plan-approval.ts)

Additional hooks available via direct imports (not re-exported by the barrel):

  • createSandboxToolBeforeHook() / createSandboxToolAfterHook() — sandbox tool redirection (sandbox-tools.ts)
  • createSandboxMessageHook() — container note injection (sandbox-message.ts)
  • createForgeSessionAttachHook() / createForgeSessionMessageAttachHook() — auto-attach loops on session events (forge-session-attach.ts)
  • createLoopPermissionPatcher() — patch subagent permissions on session.created (loop-permission.ts)
  • createPlanCaptureEventHook() — plan marker extraction from streaming parts (plan-capture.ts)
  • createGroupOrchestratorEventHook() — group scheduling on loop termination (group-orchestrator.ts)

Source: src/hooks/index.ts


loop/ — Core Loop State Machine

The heart of Forge. Implements autonomous iterative development with phases: coding → auditing → final_auditing → post_action.

Files

File Purpose
index.ts Public API barrel (all re-exports)
runtime.ts createLoop() factory, Loop interface
service.ts DB-backed LoopService (createLoopService)
state.ts Discriminated union LoopState (4 phases: coding, auditing, final_auditing, post_action), row↔state converters
transitions.ts Pure nextTransition() table — no side effects; includes 'post-action-complete' event and handlePostActionEvent
termination.ts TerminationReason union, terminationStatusFor()
prompts.ts Prompt builders for each loop phase, including buildPostActionPrompt()
post-action-config.ts ResolvedPostActionConfig interface and resolvePostActionConfig() resolver
section-summary.ts Parse audit output markers
idle-gate.ts Session busy detection and timeout tracking
in-flight-guard.ts Single-flight guard for concurrent loop start attempts
restartability.ts getRestartability() — decides whether a non-completed loop can restart, blocked, or requires force
token-usage.ts Extract and normalize per-message usage from session output
name-uniqueness.ts Reserve a unique loop identity before any side effects
session-output.ts Fetch session output for loop display

Key Types

type Phase = 'coding' | 'auditing' | 'final_auditing' | 'post_action'

type LoopState =
  | CodingState
  | AuditingState
  | FinalAuditingState
  | PostActionState

type TerminationReason =
  | { kind: 'completed' }
  | { kind: 'cancelled' }
  | { kind: 'user_aborted' }
  | { kind: 'shutdown' }
  | { kind: 'max_iterations' }
  | { kind: 'stall_timeout' }
  | { kind: 'missing_worktree_dir' }
  | { kind: 'session_creation_failed' }
  | { kind: 'audit_retry_exhausted' }
  | { kind: 'final_audit_retry_exhausted' }
  | { kind: 'coding_no_assistant' }
  | { kind: 'worktree_failed'; message: string }
  | { kind: 'error_max_retries'; message: string }

Public API

// Runtime
createLoop(deps: LoopRuntimeDeps): Loop
isWorkspaceNotFoundError(error): boolean

// State
loopRowToState(row, largeFields?): LoopState
loopStateToRow(state, projectId): LoopRow
MAX_RETRIES: number

// Transitions
nextTransition(state, event): Transition

// Prompts
buildContinuationPrompt(state, auditFindings?): string
buildAuditPrompt(state): string
buildSectionInitialPrompt(state, sectionIndex?): string
buildSectionAuditPrompt(state, sectionIndex?): string
buildSectionContinuationPrompt(state, sectionIndex?): string
buildFinalAuditPrompt(state): string
buildPostActionPrompt(state, opts): string

// Termination
terminationStatusFor(reason: TerminationReason): TerminationStatus
terminationReasonToString(reason): string
parseTerminationReasonString(str): TerminationReason

// Section summary
parseSectionSummary(text): { startLine, endLine, summary }
SECTION_SUMMARY_START_MARKER: string
SECTION_SUMMARY_END_MARKER: string

// Idle gate
sessionsAwaitingBusy: Map<string, ...>
AWAITING_BUSY_TIMEOUT_MS: number
markPromptSent(sessionId, loopName): void
clearPromptPending(sessionId): void
isAwaitingBusy(sessionId): boolean
isAwaitingBusyExpired(sessionId): boolean

// Name uniqueness
generateUniqueName(baseName: string, existingNames: readonly string[]): string

// Session output
fetchSessionOutput(client, sessionId, directory, logger?, options?): LoopSessionOutput | null

All external consumers import through the barrel: src/loop/index.ts

Source: src/loop/index.ts


services/ — Business Logic Services

Higher-level orchestration services coordinating between hooks, loop runtime, and storage.

Files

File Purpose
execution.ts Unified command bus for plan execution (createForgeExecutionService())
session-loop-resolver.ts Resolve which loop owns a given session
unified-sandbox-resolver.ts Loop-first sandbox resolution shared by the shell wrapper and tool hooks
deterministic-decomposer.ts Slice a plan into milestones (section_plans rows) deterministically — called once at loop start by execution.ts, not a runtime loop phase
section-bootstrap.ts Build the initial milestone rows for a loop
plan-capture.ts The single write path into a session-scoped plans row (writeSessionPlanContent), marked-plan capture from messages, and resolveSessionPlanOfRecord — the one implementation of "stored plan wins, chat capture is the fallback"
group-orchestrator.ts Feature-group scheduling and per-feature loop launch
group-scheduler.ts Ordering and concurrency cap for a group's features
tui-rpc-service.ts Server-side TUI RPC service: loop list/sidebar, session plan, loop restart (rejected when the loop was restarted after the dialog read it, checked under the shared loop lock), host-sandbox desired/applied state, and the worktree list
worktree-log.ts Log worktree completions

Key Interfaces

type ForgeExecutionSurface = 'tool' | 'approval-hook' | 'api' | 'tui'

interface ForgeExecutionRequestContext {
  surface: ForgeExecutionSurface
  projectId: string
  directory: string
  sourceSessionId?: string
  requestId?: string
}

type PlanSource =
  | { kind: 'inline'; planText: string }
  | { kind: 'stored'; sessionId: string }
  | { kind: 'loop-state'; loopName: string }

Source: src/services/execution.ts


sandbox/ — msb Sandboxing

Drives the msb CLI to provision isolated sandboxes for loop execution.

Files

File Purpose
msb.ts SandboxRuntime facade over the msb CLI (create/exec/remove/list, availability probe)
process.ts Child-process runner (runCommand) shared by the sandbox helpers
template.ts Image build/save/load helper (docker build/docker save/msb load)
config-warnings.ts Warnings for legacy Docker-era sandbox config keys
manager.ts SandboxManager lifecycle management (start/stop/ensureRunning/isLive, orphan cleanup)
reconcile.ts Sandbox reconciliation with loop states
context.ts SandboxContext, isSandboxEnabled()
path.ts Sandbox path utilities
exec-fs.ts Filesystem operations through msb exec
shell-shim.ts Generated shim routing the native shell tool through msb exec
session-controller.ts Per-project host sandbox selection and ownership
loop-settings.ts Per-loop sandbox overrides: validation, workspace extra carrier, and the effective-resource resolver shared by creation and the TUI
env-probe.ts Bounded environment probe for the container note

SandboxRuntime Interface

interface SandboxRuntime {
  checkAvailable(): Promise<MsbAvailability>
  templateExists(ref: string): Promise<boolean>
  loadTemplate(tarPath: string, ref: string): Promise<void>
  createSandbox(name: string, workspaces: SandboxWorkspace[], opts: CreateSandboxOpts): Promise<void>
  removeSandbox(name: string): Promise<void>
  exec(name: string, command: string, opts?: SandboxExecOpts): Promise<CommandResult>
  getSandboxState(name: string): Promise<SandboxState>
  sandboxContainerName(worktreeName: string): string
  listSandboxesByPrefix(prefix: string): Promise<string[]>
  refreshSandboxSecrets(name: string, secrets: SandboxSecretConfig[]): Promise<boolean>
  readSandboxSettings(name: string): Promise<SandboxRuntimeSettings | null>
  resizeSandbox(name: string, resources: Pick<SandboxResources, 'cpus' | 'memory'>): Promise<void>
}

SandboxManager Interface

interface SandboxManager {
  runtime: SandboxRuntime
  start(worktreeName: string, projectDir: string, startedAt?: string, overrides?: SandboxOverrides): Promise<{ containerName: string }>
  stop(worktreeName: string): Promise<void>
  getActive(worktreeName: string): ActiveSandbox | null
  isActive(worktreeName: string): boolean
  isLive(worktreeName: string): Promise<boolean>
  cleanupOrphans(preserveWorktrees?: string[]): Promise<number>
  restore(worktreeName: string, projectDir: string, startedAt: string): Promise<void>
  ensureRunning(worktreeName: string, projectDir: string, startedAt?: string): Promise<string>
  applyOverrides(worktreeName: string, projectDir: string): Promise<ApplyOverridesOutcome>
}

applyOverrides converges an existing sandbox to its effective settings (persisted overrides over config) and returns whether it was unchanged, resized (a CPU or memory change restarts the sandbox in place, keeping its disks), or recreated (a LAN-access change recreates it, because msb fixes network policy at create time). It rejects when the current settings cannot be read, so a LAN restriction is never assumed to hold.

SandboxManagerConfig no longer carries a dataDir field — its only reader was the deleted per-sandbox env-file writer. The overlapping-workspace drop rule is a single shared implementation used by both the mount plan and the workspace builder, so a mount conflict resolves identically on either path. removeSandbox also removes both of the sandbox's named volumes in one bulk msb volume rm — <container>-docker-data, which backs /var/lib/docker for the in-VM Docker Engine, and <container>-cache-data, which backs /opt/forge/cache. Because named volumes survive msb rm, stop routes through the same removal path even when the sandbox is already gone, so a container destroyed out of band still has its disks reclaimed.

Source: src/sandbox/msb.ts, src/sandbox/manager.ts


storage/ — SQLite Persistence Layer

All data persistence via bun:sqlite. Organized as:

Database

Export Description
initializeDatabase(dataDir, options) Creates SQLite DB with migrations
closeDatabase(db: Database) Closes database connections
resolveDataDir() Platform-appropriate data directory
resolveLogPath() Default log file path

Repositories

Each created via createXxxRepo(db) factory with project-scoped queries:

Repository Table(s) Key Types
LoopsRepo loops, loop_large_fields LoopRow, LoopLargeFields
PlansRepo plans PlanRow
ReviewFindingsRepo review_findings ReviewFindingRow
SectionPlansRepo section_plans SectionPlanRow — one row per milestone (decomposed plan section). See Loop System.
LoopTransitionsRepo loop_transitions LoopTransitionRow — append-only phase-transition log per loop
PlanAmendmentsRepo plan_amendments PlanAmendmentRow — append-only plan-amendment audit trail
LoopSessionUsageRepo loop_session_usage LoopSessionUsageRow, LoopUsageAggregate
FeatureGroupsRepo feature_groups FeatureGroupsRepo — grouped-execution state
LoopAttemptsRepo loop_attempts LoopAttemptsRepo — durable audit-attempt history
SessionSandboxPreferencesRepo tui_preferences (JSON keys session-sandbox.desired / .applied / .controller) SessionSandboxPreferencesRepo — host-session sandbox desired/applied state
SessionAutoApproveRepo tui_preferences (TTL-scoped JSON keys session-auto-approve.<sessionId>) SessionAutoApproveRepo — per-session auto-approve state

Migrations

Migrations are registered explicitly, in execution order, in the migrations array (ids 100–149; inline migrations are valid, so not every id ships a .sql file) and tracked in a migrations table.

See storage/migrations/README.md for migration details.

Source: src/storage/index.ts


tools/ — Plugin Tools

Implements tools callable by AI agents during conversations.

Tools Created by createTools(ctx)

Tool File Description
review-write review.ts Store a code review finding (file, line, severity, description)
review-read review.ts Retrieve review findings, filter by file or regex pattern
review-delete review.ts Delete a review finding by file and line
plan-write plan-authoring.ts Architect agents create or overwrite the stored session plan; returns a structure report. Denied while the session owns a running loop.
plan-edit plan-authoring.ts Architect agents edit the stored session plan by exact string replacement (oldString/newString/replaceAll); returns a structure report.
plan-read plan-kv.ts Retrieve plans with pagination and pattern search
section-read section-read.ts Retrieve the current, specified, or (with pending_suffix) ordered pending sections of an active loop's plan; titles are display labels, section content is the executable requirement
plan-adjust plan-adjust.ts Auditor-only, section-audit-only: revise the section under audit (currentSection) and/or destructively replace the pending suffix (sections) of the active loop plan's executable section instructions; the stored master plan row is unchanged. Logged as a plan amendment
execute-plan loop.ts Execute a plan using an iterative development loop, or mode: new-session for a fresh standalone session. Args: title required; plan, loopName, hostSessionId, mode optional.
execute-goal loop.ts Execute a non-empty goal in a dedicated session inside a managed worktree. Args: goal required; title, loopName, maxIterations, hostSessionId optional.
loop-status loop.ts List active/recent loops, show cumulative usage for detailed status, or restart loops with restart/force arguments
loop-cancel loop.ts Cancel an active loop by worktree name

tool.ts is the local tool() helper every definition uses; its schema is zod 4, and the V2 tool registrar converts each definition's args to JSON Schema.

ToolContext

All tool implementations receive a shared context:

interface ToolContext {
  projectId: string
  directory: string
  config: PluginConfig
  logger: Logger
  db: Database
  dataDir: string
  loopHandler: LoopEventHandler
  loop: Loop
  client: ForgeClient
  cleanup: () => Promise<void>
  sandboxManager: SandboxManager | null
  plansRepo: PlansRepo
  reviewFindingsRepo: ReviewFindingsRepo
  loopsRepo: LoopsRepo
  sectionPlansRepo: SectionPlansRepo
  loopSessionUsageRepo?: LoopSessionUsageRepo
  featureGroupsRepo: FeatureGroupsRepo
  groupOrchestrator: GroupOrchestrator
  pendingTeardowns: PendingTeardownRegistry
  resolveActiveLoopForSession: (sessionID) => Promise<ResolvedLoop | null>
}

Source: src/tools/index.ts, src/tools/types.ts


workspace/ — Git Worktree / Workspace Management

Creates and manages git worktrees for isolated loop execution, registered through the V2 worktree inventory.

Files

File Purpose
forge-adapter.ts createForgeWorkspaceAdapter() — the host-neutral worktree adapter
forge-worktree.ts bindSessionToWorkspace(), createBuiltinWorktreeWorkspace(), workspace permission rules
forge-naming.ts Worktree/branch naming and forge-worktree directory detection
forge-workspace-metadata.ts <worktree>/.forge/workspace.json read/write/list
pending-teardown.ts Registry of pending teardown contexts for commit message building
worktree-commit.ts Teardown commit building
worktree-opencode-config.ts Writes opencode.jsonc into a fresh worktree
classify-stale.ts Decision function for stale forge workspace handling
remove-with-context.ts Workspace removal with teardown context
sweep-stale.ts Opportunistic same-project sweep of stale forge workspaces during loop teardown

Source: src/workspace/forge-adapter.ts


utils/ — Shared Utilities

Cross-cutting helpers (~40 files) organized by concern:

Group Files Purpose
Logging logger.ts File logger with rotation (10MB max)
Caching lru-cache.ts Generic LRU cache
Paths opencode-paths.ts, shipped-paths.ts, resolve-project-root.ts Data/tool-output/tmp paths, bundled-asset root, project root
Plan plan-execution.ts, plan-structure.ts, marked-plan-parser.ts, markdown-fences.ts Plan parsing, structure reports, marker extraction
Sections section-capture.ts, section-summary.ts Section extraction/summary parsing
Loop loop-helpers.ts, loop-format.ts, loop-session.ts, loop-registry.ts, loop-permission-options.ts, loop-permission-warnings.ts Loop model/format/session/permission helpers
Sessions audit-session.ts, audit-snapshot.ts, coder-decisions.ts, session-ancestry.ts, session-titles.ts Session naming, ancestry, audit history
TUI tui-execution-preferences.ts, tui-execution-context-cache.ts, tui-models.ts TUI preferences and models
Workspace worktree-cleanup.ts, git-service.ts Worktree cleanup and git operations
Sandbox sandbox-ready.ts Sandbox readiness probe
Misc partial-match.ts, model-fallback.ts, busy-guard.ts, format.ts, duration.ts, is-record.ts, toast.ts, architect-auto-output.ts, feature-list-parser.ts, review-format.ts, cli-flags.ts, bundled-sync.ts Various helpers

constants/ — Permission Rulesets

Security rules for loop and audit sessions.

buildLoopPermissionRuleset(options?): PermissionRule[]  // Allow-all, then configured rules, then review/plan/loop structural denies
buildAuditSessionPermissionRuleset(options?): PermissionRule[] // Allow-all, configured rules, then structural denies for the direct mutation tools edit/write plus the shared plan/loop denies
resolveLoopPermissionOptions(config?): LoopPermissionRulesetOptions // Local resolver: configured loop.permissions deny rules

Only deny entries are honoured; Forge-managed permissions and blanket denies of Forge-required permissions (FORGE_REQUIRED_PERMISSIONS) are rejected with a warning.

Source: src/constants/loop.ts

Workspace-aware resolution (merges portable extra.permissionRules persisted in a workspace's metadata) lives in resolveLoopPermissionOptionsForWorkspace.

Source: src/utils/loop-permission-options.ts


Architectural Patterns

Factory Pattern

Every major component uses a factory function pattern with dependency injection:

setupForgeV2(ctx)                 // Server plugin entry
createForgeCore(config, host)      // Host-neutral core
createLoop(deps)                  // Loop runtime
createLoopService(...)            // State management
createSandboxManager(runtime, config, logger, git?) // Sandbox
createTools(ctx)                  // Tool registry
createForgeWorkspaceAdapter(deps) // Workspace
createMsbRuntime(logger)          // Sandbox
createLogger(config)              // Logging

Dependencies are injected via parameter objects, not global singletons. The one deliberate exception is process-shared state (processShared in src/utils/process-shared.ts): OpenCode loads a separate module graph per location in one process, so state that must be one per process (host sandbox controllers, idle-gate markers, the loop registry) lives on globalThis under registered symbols, and every module copy resolves the same value.

Barrel Exports

Five modules use barrel index.ts files:

  • src/hooks/index.ts
  • src/storage/index.ts
  • src/loop/index.ts
  • src/agents/index.ts
  • src/tools/index.ts

Other modules do NOT have barrel files (utils, sandbox, services, workspace).

Repository Pattern

All data access goes through typed repo interfaces:

  • LoopsRepo, PlansRepo, ReviewFindingsRepo, SectionPlansRepo, LoopSessionUsageRepo, FeatureGroupsRepo
  • Each created via createXxxRepo(db) with project-scoped queries.
  • Rows are mapped to domain objects via loopRowToState() etc.

State Machine Pattern

The loop follows a strict phase-based state machine:

  • States: coding, auditing, final_auditing, post_action
  • Transitions managed by nextTransition() in transitions.ts
  • Each phase has dedicated prompt builders and session rotation logic
  • State changes are persisted to SQLite after every mutation

Notification Pattern

A LoopChangeNotifier callback is threaded through all state mutation calls. It fires on insert, delete, terminate, rotate, phase, iteration, status, session, sandbox, workspace, audit-result, error, reconcile events.

Plugin Hook Pattern

setup(ctx) registers the core handlers through V2's hook API:

  • tool.transform — register the Forge tools and wrap the built-in shell tool for sandbox routing
  • tool.hook('execute.before') / tool.hook('execute.after') — pre/post tool execution
  • shell.hook('create.before') — sandbox shell routing
  • session.hook('prompt') — session message handling
  • session.hook('context') — system context injection and the architect reminder
  • session.hook('compaction') — context compaction
  • agent.transform / command.transform — agent/command configuration injection
  • event.subscribe — event dispatching