mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-21 03:04:28 +00:00
WP2: Create AgentSession basic structure + update plan for keep-old-code strategy
This commit is contained in:
parent
3f305502cd
commit
29d96ab25a
4 changed files with 204 additions and 88 deletions
|
|
@ -11,6 +11,22 @@ To resume work on this refactoring:
|
||||||
3. Check the work packages below - find first unchecked item
|
3. Check the work packages below - find first unchecked item
|
||||||
4. Read any files mentioned in that work package before making changes
|
4. Read any files mentioned in that work package before making changes
|
||||||
|
|
||||||
|
## Strategy: Keep Old Code for Reference
|
||||||
|
|
||||||
|
We create new files alongside old ones instead of modifying in place:
|
||||||
|
- `src/modes/print-mode.ts` (new) - old code stays in `main.ts`
|
||||||
|
- `src/modes/rpc-mode.ts` (new) - old code stays in `main.ts`
|
||||||
|
- `src/modes/interactive/interactive-mode.ts` (new) - old code stays in `tui/tui-renderer.ts`
|
||||||
|
- `src/main-new.ts` (new) - old code stays in `main.ts`
|
||||||
|
- `src/cli-new.ts` (new) - old code stays in `cli.ts`
|
||||||
|
|
||||||
|
This allows:
|
||||||
|
- Parallel comparison of old vs new behavior
|
||||||
|
- Gradual migration and testing
|
||||||
|
- Easy rollback if needed
|
||||||
|
|
||||||
|
Final switchover: When everything works, rename files and delete old code.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
@ -244,9 +260,9 @@ export class AgentSession {
|
||||||
1. `npm run check` passes
|
1. `npm run check` passes
|
||||||
2. Class can be instantiated (will test via later integration)
|
2. Class can be instantiated (will test via later integration)
|
||||||
|
|
||||||
- [ ] Create `src/core/agent-session.ts` with basic structure
|
- [x] Create `src/core/agent-session.ts` with basic structure
|
||||||
- [ ] Create `src/core/index.ts` barrel export
|
- [x] Create `src/core/index.ts` barrel export
|
||||||
- [ ] Verify with `npm run check`
|
- [x] Verify with `npm run check`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -1313,16 +1329,18 @@ export { runRpcMode } from "./rpc-mode.js";
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### WP14: Update main.ts to use AgentSession and new modes
|
### WP14: Create main-new.ts using AgentSession and new modes
|
||||||
> Refactor main.ts to use AgentSession and the new mode modules.
|
> Create a new main file that uses AgentSession and the new mode modules.
|
||||||
|
> Old main.ts is kept for reference/comparison.
|
||||||
|
|
||||||
**Files to modify:**
|
**Files to create:**
|
||||||
- `src/main.ts`
|
- `src/main-new.ts` (copy from main.ts, then modify)
|
||||||
|
- `src/cli-new.ts` (copy from cli.ts, point to main-new.ts)
|
||||||
|
|
||||||
**Changes:**
|
**Changes to main-new.ts:**
|
||||||
1. Remove `runSingleShotMode()` function (replaced by print-mode.ts)
|
1. Remove `runSingleShotMode()` function (use print-mode.ts)
|
||||||
2. Remove `runRpcMode()` function (replaced by rpc-mode.ts)
|
2. Remove `runRpcMode()` function (use rpc-mode.ts)
|
||||||
3. Remove `executeRpcBashCommand()` function (replaced by bash-executor.ts)
|
3. Remove `executeRpcBashCommand()` function (use bash-executor.ts)
|
||||||
4. Create `AgentSession` instance after agent setup
|
4. Create `AgentSession` instance after agent setup
|
||||||
5. Pass `AgentSession` to mode functions
|
5. Pass `AgentSession` to mode functions
|
||||||
|
|
||||||
|
|
@ -1348,36 +1366,56 @@ if (mode === "rpc") {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**cli-new.ts:**
|
||||||
|
```typescript
|
||||||
|
#!/usr/bin/env node
|
||||||
|
import { main } from "./main-new.js";
|
||||||
|
main(process.argv.slice(2));
|
||||||
|
```
|
||||||
|
|
||||||
|
**Testing the new implementation:**
|
||||||
|
```bash
|
||||||
|
# Run new implementation directly
|
||||||
|
npx tsx src/cli-new.ts -p "hello"
|
||||||
|
npx tsx src/cli-new.ts --mode json "hello"
|
||||||
|
npx tsx src/cli-new.ts # interactive mode
|
||||||
|
```
|
||||||
|
|
||||||
**Verification:**
|
**Verification:**
|
||||||
1. `npm run check` passes
|
1. `npm run check` passes
|
||||||
2. Manual test: `pi -p "hello"` works
|
2. Manual test: `npx tsx src/cli-new.ts -p "hello"` works
|
||||||
3. Manual test: `pi --mode json "hello"` works
|
3. Manual test: `npx tsx src/cli-new.ts --mode json "hello"` works
|
||||||
4. Manual test: `pi --mode rpc` works
|
4. Manual test: `npx tsx src/cli-new.ts --mode rpc` works
|
||||||
|
|
||||||
- [ ] Remove `runSingleShotMode()` from main.ts
|
- [ ] Copy main.ts to main-new.ts
|
||||||
- [ ] Remove `runRpcMode()` from main.ts
|
- [ ] Remove `runSingleShotMode()` from main-new.ts
|
||||||
- [ ] Remove `executeRpcBashCommand()` from main.ts
|
- [ ] Remove `runRpcMode()` from main-new.ts
|
||||||
|
- [ ] Remove `executeRpcBashCommand()` from main-new.ts
|
||||||
- [ ] Import and use `runPrintMode` from modes
|
- [ ] Import and use `runPrintMode` from modes
|
||||||
- [ ] Import and use `runRpcMode` from modes
|
- [ ] Import and use `runRpcMode` from modes
|
||||||
- [ ] Create `AgentSession` in main()
|
- [ ] Create `AgentSession` in main()
|
||||||
- [ ] Update mode routing to use new functions
|
- [ ] Update mode routing to use new functions
|
||||||
|
- [ ] Create cli-new.ts
|
||||||
- [ ] Verify with `npm run check`
|
- [ ] Verify with `npm run check`
|
||||||
- [ ] Manual test all three modes
|
- [ ] Manual test all three modes via cli-new.ts
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### WP15: Refactor TuiRenderer to use AgentSession
|
### WP15: Create InteractiveMode using AgentSession
|
||||||
> Update TuiRenderer to use AgentSession instead of direct agent/sessionManager access.
|
> Create a new interactive mode class that uses AgentSession.
|
||||||
|
> Old tui-renderer.ts is kept for reference.
|
||||||
|
|
||||||
**Files to modify:**
|
**Files to create:**
|
||||||
- `src/tui/tui-renderer.ts`
|
- `src/modes/interactive/interactive-mode.ts` (based on tui-renderer.ts)
|
||||||
|
|
||||||
**This is the largest change. Strategy:**
|
**This is the largest change. Strategy:**
|
||||||
1. Change constructor to accept `AgentSession` instead of separate agent/sessionManager/settingsManager
|
1. Copy tui-renderer.ts to new location
|
||||||
2. Replace all `this.agent.*` calls with `this.session.agent.*` or appropriate AgentSession methods
|
2. Rename class from `TuiRenderer` to `InteractiveMode`
|
||||||
3. Replace all `this.sessionManager.*` calls with AgentSession methods
|
3. Change constructor to accept `AgentSession` instead of separate agent/sessionManager/settingsManager
|
||||||
4. Replace all `this.settingsManager.*` calls with AgentSession methods where applicable
|
4. Replace all `this.agent.*` calls with `this.session.agent.*` or appropriate AgentSession methods
|
||||||
5. Remove duplicated logic that now lives in AgentSession
|
5. Replace all `this.sessionManager.*` calls with AgentSession methods
|
||||||
|
6. Replace all `this.settingsManager.*` calls with AgentSession methods where applicable
|
||||||
|
7. Remove duplicated logic that now lives in AgentSession
|
||||||
|
|
||||||
**Key replacements:**
|
**Key replacements:**
|
||||||
| Old | New |
|
| Old | New |
|
||||||
|
|
@ -1414,13 +1452,16 @@ constructor(
|
||||||
|
|
||||||
**Verification:**
|
**Verification:**
|
||||||
1. `npm run check` passes
|
1. `npm run check` passes
|
||||||
2. Manual test: Full interactive mode works
|
2. Manual test via cli-new.ts: Full interactive mode works
|
||||||
3. Manual test: All slash commands work
|
3. Manual test: All slash commands work
|
||||||
4. Manual test: All hotkeys work
|
4. Manual test: All hotkeys work
|
||||||
5. Manual test: Bash execution works
|
5. Manual test: Bash execution works
|
||||||
6. Manual test: Model/thinking cycling works
|
6. Manual test: Model/thinking cycling works
|
||||||
|
|
||||||
- [ ] Change TuiRenderer constructor to accept AgentSession
|
- [ ] Create `src/modes/interactive/` directory
|
||||||
|
- [ ] Copy tui-renderer.ts to interactive-mode.ts
|
||||||
|
- [ ] Rename class to `InteractiveMode`
|
||||||
|
- [ ] Change constructor to accept AgentSession
|
||||||
- [ ] Update all agent access to go through session
|
- [ ] Update all agent access to go through session
|
||||||
- [ ] Remove `subscribeToAgent()` method (use session.subscribe)
|
- [ ] Remove `subscribeToAgent()` method (use session.subscribe)
|
||||||
- [ ] Remove `checkAutoCompaction()` method (handled by session)
|
- [ ] Remove `checkAutoCompaction()` method (handled by session)
|
||||||
|
|
@ -1432,21 +1473,25 @@ constructor(
|
||||||
- [ ] Update session switching to use session.switchSession()
|
- [ ] Update session switching to use session.switchSession()
|
||||||
- [ ] Update branch logic to use session.branch()
|
- [ ] Update branch logic to use session.branch()
|
||||||
- [ ] Remove all direct sessionManager access
|
- [ ] Remove all direct sessionManager access
|
||||||
|
- [ ] Update imports to point to `../../tui/` for components (keep old components in place for now)
|
||||||
|
- [ ] Update modes/index.ts to export InteractiveMode
|
||||||
- [ ] Verify with `npm run check`
|
- [ ] Verify with `npm run check`
|
||||||
- [ ] Manual test interactive mode thoroughly
|
- [ ] Manual test interactive mode via cli-new.ts
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### WP16: Update runInteractiveMode to use AgentSession
|
### WP16: Update main-new.ts runInteractiveMode to use InteractiveMode
|
||||||
> Update the runInteractiveMode function in main.ts to create and pass AgentSession.
|
> Update runInteractiveMode in main-new.ts to use the new InteractiveMode class.
|
||||||
|
|
||||||
**Files to modify:**
|
**Files to modify:**
|
||||||
- `src/main.ts`
|
- `src/main-new.ts`
|
||||||
|
|
||||||
**Changes:**
|
**Changes:**
|
||||||
```typescript
|
```typescript
|
||||||
|
import { InteractiveMode } from "./modes/interactive/interactive-mode.js";
|
||||||
|
|
||||||
async function runInteractiveMode(
|
async function runInteractiveMode(
|
||||||
session: AgentSession, // Changed from individual params
|
session: AgentSession,
|
||||||
version: string,
|
version: string,
|
||||||
changelogMarkdown: string | null,
|
changelogMarkdown: string | null,
|
||||||
collapseChangelog: boolean,
|
collapseChangelog: boolean,
|
||||||
|
|
@ -1457,7 +1502,7 @@ async function runInteractiveMode(
|
||||||
initialAttachments?: Attachment[],
|
initialAttachments?: Attachment[],
|
||||||
fdPath: string | null,
|
fdPath: string | null,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const renderer = new TuiRenderer(
|
const mode = new InteractiveMode(
|
||||||
session,
|
session,
|
||||||
version,
|
version,
|
||||||
changelogMarkdown,
|
changelogMarkdown,
|
||||||
|
|
@ -1470,69 +1515,30 @@ async function runInteractiveMode(
|
||||||
|
|
||||||
**Verification:**
|
**Verification:**
|
||||||
1. `npm run check` passes
|
1. `npm run check` passes
|
||||||
2. Manual test: Interactive mode works
|
2. Manual test via cli-new.ts: Interactive mode works
|
||||||
|
|
||||||
- [ ] Update `runInteractiveMode()` signature
|
- [ ] Update `runInteractiveMode()` in main-new.ts
|
||||||
- [ ] Update TuiRenderer instantiation
|
- [ ] Update InteractiveMode instantiation
|
||||||
- [ ] Verify with `npm run check`
|
- [ ] Verify with `npm run check`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### WP17: Rename TuiRenderer to InteractiveMode
|
### WP17: (OPTIONAL) Move TUI components to modes/interactive/
|
||||||
> Rename the class and file to better reflect its purpose.
|
|
||||||
|
|
||||||
**Files to rename/modify:**
|
|
||||||
- `src/tui/tui-renderer.ts` → `src/modes/interactive/interactive-mode.ts`
|
|
||||||
- Update all imports
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. Create `src/modes/interactive/` directory
|
|
||||||
2. Move and rename file
|
|
||||||
3. Rename class from `TuiRenderer` to `InteractiveMode`
|
|
||||||
4. Update imports in main.ts
|
|
||||||
5. Update barrel export in modes/index.ts
|
|
||||||
|
|
||||||
**Verification:**
|
|
||||||
1. `npm run check` passes
|
|
||||||
2. Manual test: Interactive mode works
|
|
||||||
|
|
||||||
- [ ] Create `src/modes/interactive/` directory
|
|
||||||
- [ ] Move `tui/tui-renderer.ts` to `modes/interactive/interactive-mode.ts`
|
|
||||||
- [ ] Rename class to `InteractiveMode`
|
|
||||||
- [ ] Update imports in main.ts
|
|
||||||
- [ ] Update modes/index.ts barrel export
|
|
||||||
- [ ] Verify with `npm run check`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### WP18: Move remaining TUI components
|
|
||||||
> Move TUI-specific components to the interactive mode directory.
|
> Move TUI-specific components to the interactive mode directory.
|
||||||
|
> This is optional cleanup - can be skipped if too disruptive.
|
||||||
|
|
||||||
**Files to move:**
|
**Note:** The old `src/tui/` directory is kept. We just create copies/moves as needed.
|
||||||
|
For now, InteractiveMode can import from `../../tui/` to reuse existing components.
|
||||||
|
|
||||||
|
**Files to potentially move (if doing this WP):**
|
||||||
- `src/tui/assistant-message.ts` → `src/modes/interactive/components/`
|
- `src/tui/assistant-message.ts` → `src/modes/interactive/components/`
|
||||||
- `src/tui/bash-execution.ts` → `src/modes/interactive/components/`
|
- `src/tui/bash-execution.ts` → `src/modes/interactive/components/`
|
||||||
- `src/tui/compaction.ts` → `src/modes/interactive/components/`
|
- etc.
|
||||||
- `src/tui/custom-editor.ts` → `src/modes/interactive/components/`
|
|
||||||
- `src/tui/dynamic-border.ts` → `src/modes/interactive/components/`
|
|
||||||
- `src/tui/footer.ts` → `src/modes/interactive/components/`
|
|
||||||
- `src/tui/model-selector.ts` → `src/modes/interactive/selectors/`
|
|
||||||
- `src/tui/oauth-selector.ts` → `src/modes/interactive/selectors/`
|
|
||||||
- `src/tui/queue-mode-selector.ts` → `src/modes/interactive/selectors/`
|
|
||||||
- `src/tui/session-selector.ts` → `src/modes/interactive/selectors/`
|
|
||||||
- `src/tui/theme-selector.ts` → `src/modes/interactive/selectors/`
|
|
||||||
- `src/tui/thinking-selector.ts` → `src/modes/interactive/selectors/`
|
|
||||||
- `src/tui/tool-execution.ts` → `src/modes/interactive/components/`
|
|
||||||
- `src/tui/user-message.ts` → `src/modes/interactive/components/`
|
|
||||||
- `src/tui/user-message-selector.ts` → `src/modes/interactive/selectors/`
|
|
||||||
|
|
||||||
**Note:** This is optional reorganization. Can be done later or skipped if too disruptive.
|
**Skip this WP for now** - focus on getting the new architecture working first.
|
||||||
|
The component organization can be cleaned up later.
|
||||||
|
|
||||||
- [ ] Create directory structure under `src/modes/interactive/`
|
- [ ] SKIPPED (optional cleanup for later)
|
||||||
- [ ] Move component files
|
|
||||||
- [ ] Move selector files
|
|
||||||
- [ ] Update all imports
|
|
||||||
- [ ] Remove empty `src/tui/` directory
|
|
||||||
- [ ] Verify with `npm run check`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
109
packages/coding-agent/src/core/agent-session.ts
Normal file
109
packages/coding-agent/src/core/agent-session.ts
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
/**
|
||||||
|
* AgentSession - Core abstraction for agent lifecycle and session management.
|
||||||
|
*
|
||||||
|
* This class is shared between all run modes (interactive, print, rpc).
|
||||||
|
* It encapsulates:
|
||||||
|
* - Agent state access
|
||||||
|
* - Event subscription with automatic session persistence
|
||||||
|
* - Model and thinking level management
|
||||||
|
* - Compaction (manual and auto)
|
||||||
|
* - Bash execution
|
||||||
|
* - Session switching and branching
|
||||||
|
*
|
||||||
|
* Modes use this class and add their own I/O layer on top.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Agent, AgentState, AppMessage, ThinkingLevel } from "@mariozechner/pi-agent-core";
|
||||||
|
import type { Model } from "@mariozechner/pi-ai";
|
||||||
|
import type { SessionManager } from "../session-manager.js";
|
||||||
|
import type { SettingsManager } from "../settings-manager.js";
|
||||||
|
import type { FileSlashCommand } from "../slash-commands.js";
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Types
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
export interface AgentSessionConfig {
|
||||||
|
agent: Agent;
|
||||||
|
sessionManager: SessionManager;
|
||||||
|
settingsManager: SettingsManager;
|
||||||
|
/** Models to cycle through with Ctrl+P (from --models flag) */
|
||||||
|
scopedModels?: Array<{ model: Model<any>; thinkingLevel: ThinkingLevel }>;
|
||||||
|
/** File-based slash commands for expansion */
|
||||||
|
fileCommands?: FileSlashCommand[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// AgentSession Class
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
export class AgentSession {
|
||||||
|
readonly agent: Agent;
|
||||||
|
readonly sessionManager: SessionManager;
|
||||||
|
readonly settingsManager: SettingsManager;
|
||||||
|
|
||||||
|
private _scopedModels: Array<{ model: Model<any>; thinkingLevel: ThinkingLevel }>;
|
||||||
|
private _fileCommands: FileSlashCommand[];
|
||||||
|
|
||||||
|
constructor(config: AgentSessionConfig) {
|
||||||
|
this.agent = config.agent;
|
||||||
|
this.sessionManager = config.sessionManager;
|
||||||
|
this.settingsManager = config.settingsManager;
|
||||||
|
this._scopedModels = config.scopedModels ?? [];
|
||||||
|
this._fileCommands = config.fileCommands ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Read-only State Access
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/** Full agent state */
|
||||||
|
get state(): AgentState {
|
||||||
|
return this.agent.state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Current model (may be null if not yet selected) */
|
||||||
|
get model(): Model<any> | null {
|
||||||
|
return this.agent.state.model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Current thinking level */
|
||||||
|
get thinkingLevel(): ThinkingLevel {
|
||||||
|
return this.agent.state.thinkingLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Whether agent is currently streaming a response */
|
||||||
|
get isStreaming(): boolean {
|
||||||
|
return this.agent.state.isStreaming;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** All messages including custom types like BashExecutionMessage */
|
||||||
|
get messages(): AppMessage[] {
|
||||||
|
return this.agent.state.messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Current queue mode */
|
||||||
|
get queueMode(): "all" | "one-at-a-time" {
|
||||||
|
return this.agent.getQueueMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Current session file path */
|
||||||
|
get sessionFile(): string {
|
||||||
|
return this.sessionManager.getSessionFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Current session ID */
|
||||||
|
get sessionId(): string {
|
||||||
|
return this.sessionManager.getSessionId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Scoped models for cycling (from --models flag) */
|
||||||
|
get scopedModels(): ReadonlyArray<{ model: Model<any>; thinkingLevel: ThinkingLevel }> {
|
||||||
|
return this._scopedModels;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** File-based slash commands */
|
||||||
|
get fileCommands(): ReadonlyArray<FileSlashCommand> {
|
||||||
|
return this._fileCommands;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ export interface BashResult {
|
||||||
cancelled: boolean;
|
cancelled: boolean;
|
||||||
/** Whether the output was truncated */
|
/** Whether the output was truncated */
|
||||||
truncated: boolean;
|
truncated: boolean;
|
||||||
/** Path to temp file containing full output (if output exceeded threshold) */
|
/** Path to temp file containing full output (if output exceeded truncation threshold) */
|
||||||
fullOutputPath?: string;
|
fullOutputPath?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,5 @@
|
||||||
* Core modules shared between all run modes.
|
* Core modules shared between all run modes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export { AgentSession, type AgentSessionConfig } from "./agent-session.js";
|
||||||
export { type BashExecutorOptions, type BashResult, executeBash } from "./bash-executor.js";
|
export { type BashExecutorOptions, type BashResult, executeBash } from "./bash-executor.js";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue