CodeVetter · Repo Unpack · Behavior traces
earendil-works/pi
Three flows dominate. Startup: cli.ts → setupCli → main → auth/package/config short-circuits → parseArgs → runMigrations → SettingsManager → SessionManager (create/open/fork/resume) → createAgentSessionRuntime (settings+resourceLoader+modelRuntime+AgentSession) → theme init → mode dispatch. Prompt turn: session.prompt → Agent.prompt → runAgentLoop → streamAssistantResponse → executeToolCalls → agent_end → persisted + rendered. Persistence: every agent event message_end is appended to the session JSONL by AgentSession's subscriber.
- Startup order in main(): runAuthCommand → Windows self-update quarantine cleanup → cleanupManagedInstall → bootstrap SettingsManager + applyHttpProxySettings/configureHttpDispatcher → handlePackageCommand → handleConfigCommand → parseArgs → --version/--export exits → resolveAppMode → takeOverStdout for non-interactive → validateForkFlags/validateSessionIdFlags → runMigrations.
- Startup continued: startup SettingsManager → optional first-time setup → resolve sessionDir (flag > PI_CODING_AGENT_SESSION_DIR > settings) → createSessionManager → ProjectTrustStore → createRuntime factory → createAgentSessionRuntime → setCapabilityOverrides → initTheme → dispatch to runRpcMode / InteractiveMode.run / runPrintMode.
- createSessionManager branches: --no-session/help/list-models → SessionManager.inMemory; --fork → resolveSessionPath + SessionManager.forkFrom; --session → open or cross-project fork prompt; --resume → selectSession picker; --continue → continueRecent; else SessionManager.create with optional --session-id.
- Runtime creation (createRuntime factory): resolve project trust (cached per-cwd, trustStore, or resolveProjectTrusted prompt for trust-requiring resources) → SettingsManager.create(cwd, agentDir, {projectTrusted}) → createAgentSessionServices (settings, modelRuntime with 15s refresh timeout, resourceLoader) → resolveModelScope → buildSessionOptions → createAgentSessionFromServices.
- Print-mode prompt flow: runPrintMode → rebindSession binds extensions (mode 'print'/'json') and subscribes events → session.prompt(initialMessage) → Agent.prompt → runPromptMessages → runWithLifecycle → runAgentLoop emitting agent_start/turn_start/message events → JSON events streamed via toJsonEvent or final text printed → killTrackedDetachedChildren on SIGTERM/SIGHUP → exit code.
- Single agent turn: runAgentLoop emits agent_start + turn_start + message_start/end for the prompt → runLoop drains steering queue → prepareRequest → streamAssistantResponse (emits message_update deltas) → on done, toolCalls filtered from content → executeToolCalls → tool_results pushed → finishTurn → turn_end → loop repeats until no tool calls, no queued messages → agent_end with newMessages.
- Tool-call safety path: if the assistant message stops with stopReason 'length', failToolCallsFromTruncatedMessage converts every toolCall into an error ToolResultMessage instead of executing possibly-truncated arguments; otherwise prepareToolCall validates args before execution.
- Bash tool execution: createLocalShellOperations.exec → fsAccess check on cwd → spawn(shell, args or stdin-transported command, {detached: !win32}) → trackDetachedChildPid → stream stdout/stderr via onData → timeout/abort → killProcessTree → exit code reported as {exitCode}, null treated as failure.
- Persistence trace: emit(message_end) → AgentSession._handleAgentEvent → sessionManager.appendMessage → appendFileSync on <timestamp>_<id>.jsonl under sessions/<encoded-cwd>/ → entry gets id + parentId forming the session tree → later builds projections via buildSessionProjection for context rebuild.
- Auto-compaction trace: after a turn, AgentSession checks shouldCompact(contextTokens, contextWindow, settings) → spawns compaction under _autoCompactionAbortController → prepareCompaction + compact() calls the LLM to summarize (SUMMARIZATION_SYSTEM_PROMPT, serialized conversation, file-op extraction) → CompactionEntry appended → session context reloaded.
- Session replacement flows (new/fork/switch/import/reload) go through AgentSessionRuntime: tear down current services (emitSessionShutdownEvent), run the createRuntime factory for the new cwd/sessionManager, then rebindSession so the active mode re-subscribes and re-binds extensions.
- RPC session: rpc-entry.ts → main(['--mode','rpc']) → runRpcMode → takeOverStdout → attachJsonlLineReader on stdin → dispatch RpcCommand (prompt/steer/set_model/bash/...) → session events serialized as JSONL responses + extension_ui_request dialogs awaited via pendingExtensionRequests map keyed by UUID.
- Failure path: if the loop throws, Agent.handleRunFailure synthesizes an assistant message with stopReason 'error'|'aborted' and emits the full event sequence so subscribers and the session file still see a well-formed turn end; isRetryableAssistantError drives auto-retry in AgentSession.