dev-sessions 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -0
- package/dist/backends/claude-tmux.d.ts +19 -0
- package/dist/backends/claude-tmux.js +162 -0
- package/dist/backends/claude-tmux.js.map +1 -0
- package/dist/backends/codex-appserver.d.ts +71 -0
- package/dist/backends/codex-appserver.js +839 -0
- package/dist/backends/codex-appserver.js.map +1 -0
- package/dist/champion-ids.d.ts +11 -0
- package/dist/champion-ids.js +51 -0
- package/dist/champion-ids.js.map +1 -0
- package/dist/cli.d.ts +33 -0
- package/dist/cli.js +307 -0
- package/dist/cli.js.map +1 -0
- package/dist/gateway/client.d.ts +31 -0
- package/dist/gateway/client.js +146 -0
- package/dist/gateway/client.js.map +1 -0
- package/dist/gateway/server.d.ts +27 -0
- package/dist/gateway/server.js +409 -0
- package/dist/gateway/server.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/session-manager.d.ts +36 -0
- package/dist/session-manager.js +407 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/session-store.d.ts +15 -0
- package/dist/session-store.js +143 -0
- package/dist/session-store.js.map +1 -0
- package/dist/transcript/claude-parser.d.ts +17 -0
- package/dist/transcript/claude-parser.js +203 -0
- package/dist/transcript/claude-parser.js.map +1 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +37 -0
- package/skill/SKILL.md +141 -0
package/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# dev-sessions
|
|
2
|
+
|
|
3
|
+
A CLI tool for coding agents to spawn, manage, and communicate with other coding agent sessions. Built for agent-to-agent delegation — no MCP overhead, just a CLI that agents call via Bash.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
Coding agents (Claude Code, Codex) are increasingly capable of orchestrating parallel work. But the tooling for agent-to-agent communication is either over-engineered (MCP servers, HTTP gateways, SSH tunnels) or too primitive (raw tmux commands).
|
|
8
|
+
|
|
9
|
+
`dev-sessions` provides a clean CLI interface that lets agents:
|
|
10
|
+
- **Spawn** new coding agent sessions (Claude Code or Codex)
|
|
11
|
+
- **Send** tasks and messages to those sessions
|
|
12
|
+
- **Wait** for turns to complete (transcript-aware, not terminal scraping)
|
|
13
|
+
- **Read** structured responses (clean assistant text, not ANSI noise)
|
|
14
|
+
- **Check status** (idle, working, waiting for input)
|
|
15
|
+
|
|
16
|
+
## Architecture
|
|
17
|
+
|
|
18
|
+
### Claude Code Sessions
|
|
19
|
+
- **Backend**: tmux (human-attachable via `tmux attach -t dev-<id>`)
|
|
20
|
+
- **Session ID**: Pre-assigned UUID via `claude --session-id <uuid>`
|
|
21
|
+
- **Transcript**: `~/.claude/projects/<encoded-path>/<uuid>.jsonl`
|
|
22
|
+
- **Message delivery**: tmux send-keys (base64 encoded for safety)
|
|
23
|
+
- **Turn detection**: Watches for `system` entries in JSONL transcript (definitive turn-completion signal)
|
|
24
|
+
|
|
25
|
+
### Codex Sessions
|
|
26
|
+
- **Backend**: Persistent `codex app-server` daemon (JSON-RPC 2.0 over WebSocket)
|
|
27
|
+
- **Session ID**: Thread ID from `thread/start` response
|
|
28
|
+
- **Conversation continuity**: Multiple sends share the same thread — full conversation history preserved
|
|
29
|
+
- **Message delivery**: `turn/start` JSON-RPC call
|
|
30
|
+
- **Turn detection**: `turn/completed` notification (streaming, no polling)
|
|
31
|
+
- **Daemon lifecycle**: Auto-started on first `create --cli codex`, auto-stopped when last Codex session is killed
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Create a session (defaults: --cli claude, --mode yolo)
|
|
37
|
+
dev-sessions create --description "refactor auth module"
|
|
38
|
+
# => fizz-top
|
|
39
|
+
|
|
40
|
+
# Send a task
|
|
41
|
+
dev-sessions send fizz-top "Implement JWT auth. See AUTH-SPEC.md for details."
|
|
42
|
+
dev-sessions send fizz-top --file BRIEFING.md
|
|
43
|
+
|
|
44
|
+
# Wait for the agent to finish its turn
|
|
45
|
+
dev-sessions wait fizz-top --timeout 300
|
|
46
|
+
|
|
47
|
+
# Get the agent's response (clean text, not terminal noise)
|
|
48
|
+
dev-sessions last-message fizz-top
|
|
49
|
+
|
|
50
|
+
# Check what's happening
|
|
51
|
+
dev-sessions status fizz-top # idle | working | waiting_for_input
|
|
52
|
+
dev-sessions list # all active sessions
|
|
53
|
+
|
|
54
|
+
# Synchronous delegation (one-liner)
|
|
55
|
+
sid=$(dev-sessions create -q) && dev-sessions send $sid "run tests and fix failures" && dev-sessions wait $sid && dev-sessions last-message $sid
|
|
56
|
+
|
|
57
|
+
# Fan-out
|
|
58
|
+
s1=$(dev-sessions create -q --description "frontend")
|
|
59
|
+
s2=$(dev-sessions create -q --description "backend")
|
|
60
|
+
dev-sessions send $s1 "build React form per SPEC.md"
|
|
61
|
+
dev-sessions send $s2 "add /api/users endpoint per SPEC.md"
|
|
62
|
+
dev-sessions wait $s1
|
|
63
|
+
dev-sessions wait $s2
|
|
64
|
+
dev-sessions last-message $s1
|
|
65
|
+
dev-sessions last-message $s2
|
|
66
|
+
|
|
67
|
+
# Codex session (persistent app-server, conversation continuity)
|
|
68
|
+
sid=$(dev-sessions create --cli codex -q)
|
|
69
|
+
dev-sessions send $sid "hello"
|
|
70
|
+
dev-sessions send $sid "what did I just say?" # has context from first message
|
|
71
|
+
dev-sessions last-message $sid # "You said hello"
|
|
72
|
+
|
|
73
|
+
# Clean up
|
|
74
|
+
dev-sessions kill fizz-top
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Installation
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm install -g dev-sessions
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Or clone and link:
|
|
84
|
+
```bash
|
|
85
|
+
git clone <repo-url>
|
|
86
|
+
cd dev-sessions
|
|
87
|
+
npm install && npm run build && npm link
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Skill Installation (Optional)
|
|
91
|
+
|
|
92
|
+
Install the `/dev-sessions` skill for Claude Code and/or Codex:
|
|
93
|
+
```bash
|
|
94
|
+
dev-sessions install-skill --global # Auto-detect available tools
|
|
95
|
+
dev-sessions install-skill --global --claude # Claude Code only
|
|
96
|
+
dev-sessions install-skill --global --codex # Codex CLI only
|
|
97
|
+
dev-sessions install-skill --local # Current directory only
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The skill teaches agents best practices for task delegation, polling strategies, and fan-out patterns.
|
|
101
|
+
|
|
102
|
+
## Modes
|
|
103
|
+
|
|
104
|
+
| Mode | Flag | Description |
|
|
105
|
+
|------|------|-------------|
|
|
106
|
+
| `yolo` | `--mode yolo` | Runs CLI with permission bypass flags (default) |
|
|
107
|
+
| `native` | `--mode native` | Runs CLI normally (will prompt for permissions) |
|
|
108
|
+
| `docker` | `--mode docker` | Runs via `clauded` Docker wrapper (Claude Code only) |
|
|
109
|
+
|
|
110
|
+
## Docker Integration
|
|
111
|
+
|
|
112
|
+
When running inside a Docker container (detected via `IS_SANDBOX=1`), the CLI automatically routes commands through an HTTP gateway relay on the host.
|
|
113
|
+
|
|
114
|
+
**Setup:**
|
|
115
|
+
1. Start the gateway on the host: `dev-sessions gateway --port 6767`
|
|
116
|
+
2. Inside Docker, the CLI detects `IS_SANDBOX=1` and uses `DEV_SESSIONS_GATEWAY_URL` (default `http://host.docker.internal:6767`)
|
|
117
|
+
3. `HOST_PATH` maps the container workspace to the host filesystem
|
|
118
|
+
|
|
119
|
+
This is an optional integration for users of [claude-ting](https://github.com/anthropics/claude-ting) Docker workflows. The CLI works natively on the host without any gateway.
|
|
120
|
+
|
|
121
|
+
## Commands
|
|
122
|
+
|
|
123
|
+
| Command | Description |
|
|
124
|
+
|---------|-------------|
|
|
125
|
+
| `create [options]` | Spawn a new agent session (`--cli claude\|codex`, `--mode yolo\|native\|docker`, `-q` for quiet) |
|
|
126
|
+
| `send <id> <msg>` | Send a message to a session (`--file` to send file contents) |
|
|
127
|
+
| `wait <id>` | Block until current turn completes (`--timeout` in seconds) |
|
|
128
|
+
| `last-message <id>` | Get last assistant message(s) from transcript (`--count N`) |
|
|
129
|
+
| `status <id>` | Get session status: `idle`, `working`, or `waiting_for_input` |
|
|
130
|
+
| `list` | List all active sessions |
|
|
131
|
+
| `kill <id>` | Terminate a session and clean up |
|
|
132
|
+
| `gateway` | Start the Docker relay gateway HTTP server (`--port`) |
|
|
133
|
+
| `install-skill` | Install the /dev-sessions skill (`--global\|--local`, `--claude\|--codex`) |
|
|
134
|
+
|
|
135
|
+
## Session Lifecycle
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
create → send → [wait / poll status] → last-message → kill
|
|
139
|
+
↑ |
|
|
140
|
+
└────────────────────────────────────┘
|
|
141
|
+
(send follow-up)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Development
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npm install
|
|
148
|
+
npm run build
|
|
149
|
+
npm test # unit + integration tests (83 tests)
|
|
150
|
+
npm run test:integration # integration tests only
|
|
151
|
+
npm link # for local CLI testing
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
MIT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SessionMode } from '../types';
|
|
2
|
+
export declare class ClaudeTmuxBackend {
|
|
3
|
+
private readonly timeoutMs;
|
|
4
|
+
constructor(timeoutMs?: number);
|
|
5
|
+
createSession(tmuxSessionName: string, workspacePath: string, mode: SessionMode, sessionUuid: string): Promise<void>;
|
|
6
|
+
sendMessage(tmuxSessionName: string, message: string): Promise<void>;
|
|
7
|
+
submitInput(tmuxSessionName: string): Promise<void>;
|
|
8
|
+
killSession(tmuxSessionName: string): Promise<void>;
|
|
9
|
+
listSessions(): Promise<string[]>;
|
|
10
|
+
sessionExists(tmuxSessionName: string): Promise<boolean>;
|
|
11
|
+
isClaudeRunning(tmuxSessionName: string): Promise<boolean>;
|
|
12
|
+
isCliRunning(tmuxSessionName: string, commandPatterns?: RegExp[]): Promise<boolean>;
|
|
13
|
+
private getPaneCommands;
|
|
14
|
+
private isUserCliProcess;
|
|
15
|
+
private buildStartupCommand;
|
|
16
|
+
private execTmux;
|
|
17
|
+
private execCommand;
|
|
18
|
+
private sleep;
|
|
19
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ClaudeTmuxBackend = void 0;
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const node_util_1 = require("node:util");
|
|
10
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
11
|
+
const SHELL_COMMAND_PATTERN = /(^|\s|\/)-?(bash|zsh|sh|fish)(\s|$)/;
|
|
12
|
+
const CONTROL_COMMAND_PATTERN = /(^|\s|\/)(tmux|login)(\s|$)/;
|
|
13
|
+
function shellEscape(value) {
|
|
14
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
15
|
+
}
|
|
16
|
+
class ClaudeTmuxBackend {
|
|
17
|
+
timeoutMs;
|
|
18
|
+
constructor(timeoutMs = 15_000) {
|
|
19
|
+
this.timeoutMs = timeoutMs;
|
|
20
|
+
}
|
|
21
|
+
async createSession(tmuxSessionName, workspacePath, mode, sessionUuid) {
|
|
22
|
+
const startupCommand = this.buildStartupCommand(workspacePath, mode, sessionUuid);
|
|
23
|
+
await this.execTmux([
|
|
24
|
+
'new-session',
|
|
25
|
+
'-d',
|
|
26
|
+
'-s',
|
|
27
|
+
tmuxSessionName,
|
|
28
|
+
'-n',
|
|
29
|
+
tmuxSessionName,
|
|
30
|
+
'bash',
|
|
31
|
+
'-lc',
|
|
32
|
+
startupCommand
|
|
33
|
+
]);
|
|
34
|
+
if (mode === 'docker') {
|
|
35
|
+
await this.sleep(5000);
|
|
36
|
+
await this.execTmux(['send-keys', '-t', tmuxSessionName, 'C-m']);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async sendMessage(tmuxSessionName, message) {
|
|
40
|
+
if (!(await this.isClaudeRunning(tmuxSessionName))) {
|
|
41
|
+
throw new Error('Claude is not running in this tmux session - refusing to send message');
|
|
42
|
+
}
|
|
43
|
+
const encodedMessage = Buffer.from(message, 'utf8').toString('base64');
|
|
44
|
+
const sessionTarget = shellEscape(tmuxSessionName);
|
|
45
|
+
const encoded = shellEscape(encodedMessage);
|
|
46
|
+
const script = [
|
|
47
|
+
`decoded=$( (printf '%s' ${encoded} | base64 --decode 2>/dev/null) || (printf '%s' ${encoded} | base64 -D) )`,
|
|
48
|
+
`tmux send-keys -l -t ${sessionTarget} "$decoded"`
|
|
49
|
+
].join('\n');
|
|
50
|
+
await this.execCommand('bash', ['-lc', script], 30_000);
|
|
51
|
+
// Small gaps mirror the original SSH gateway timing and improve submit reliability.
|
|
52
|
+
await this.sleep(75);
|
|
53
|
+
// Keep Enter presses as separate tmux commands to match the original gateway behavior.
|
|
54
|
+
await this.execTmux(['send-keys', '-t', tmuxSessionName, 'C-m']);
|
|
55
|
+
await this.sleep(150);
|
|
56
|
+
await this.execTmux(['send-keys', '-t', tmuxSessionName, 'C-m']);
|
|
57
|
+
}
|
|
58
|
+
async submitInput(tmuxSessionName) {
|
|
59
|
+
if (!(await this.isClaudeRunning(tmuxSessionName))) {
|
|
60
|
+
throw new Error('Claude is not running in this tmux session - refusing to submit input');
|
|
61
|
+
}
|
|
62
|
+
await this.execTmux(['send-keys', '-t', tmuxSessionName, 'C-m']);
|
|
63
|
+
}
|
|
64
|
+
async killSession(tmuxSessionName) {
|
|
65
|
+
await this.execTmux(['kill-session', '-t', tmuxSessionName]);
|
|
66
|
+
}
|
|
67
|
+
async listSessions() {
|
|
68
|
+
try {
|
|
69
|
+
const output = await this.execTmux(['list-sessions', '-F', '#{session_name}']);
|
|
70
|
+
return output
|
|
71
|
+
.split('\n')
|
|
72
|
+
.map((line) => line.trim())
|
|
73
|
+
.filter((line) => line.length > 0);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async sessionExists(tmuxSessionName) {
|
|
80
|
+
try {
|
|
81
|
+
await this.execTmux(['has-session', '-t', tmuxSessionName]);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async isClaudeRunning(tmuxSessionName) {
|
|
89
|
+
return this.isCliRunning(tmuxSessionName, [
|
|
90
|
+
/(^|\s|\/)(claude|clauded)(\s|$)/,
|
|
91
|
+
/docker.*ubuntu-dev/
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
async isCliRunning(tmuxSessionName, commandPatterns = []) {
|
|
95
|
+
try {
|
|
96
|
+
const paneCommands = await this.getPaneCommands(tmuxSessionName);
|
|
97
|
+
if (commandPatterns.length > 0) {
|
|
98
|
+
return paneCommands.some((command) => commandPatterns.some((pattern) => pattern.test(command)));
|
|
99
|
+
}
|
|
100
|
+
return paneCommands.some((command) => this.isUserCliProcess(command));
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async getPaneCommands(tmuxSessionName) {
|
|
107
|
+
const paneOutput = await this.execTmux(['list-panes', '-t', tmuxSessionName, '-F', '#{pane_tty}']);
|
|
108
|
+
const paneTtys = paneOutput
|
|
109
|
+
.split('\n')
|
|
110
|
+
.map((line) => line.trim())
|
|
111
|
+
.filter((line) => line.length > 0);
|
|
112
|
+
const commands = [];
|
|
113
|
+
for (const paneTty of paneTtys) {
|
|
114
|
+
const ttyName = node_path_1.default.basename(paneTty);
|
|
115
|
+
let psOutput = '';
|
|
116
|
+
try {
|
|
117
|
+
psOutput = await this.execCommand('ps', ['-t', ttyName, '-o', 'command=']);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const ttyCommands = psOutput
|
|
123
|
+
.split('\n')
|
|
124
|
+
.map((line) => line.trim())
|
|
125
|
+
.filter((line) => line.length > 0);
|
|
126
|
+
commands.push(...ttyCommands);
|
|
127
|
+
}
|
|
128
|
+
return commands;
|
|
129
|
+
}
|
|
130
|
+
isUserCliProcess(command) {
|
|
131
|
+
if (command.length === 0) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
return !SHELL_COMMAND_PATTERN.test(command) && !CONTROL_COMMAND_PATTERN.test(command);
|
|
135
|
+
}
|
|
136
|
+
buildStartupCommand(workspacePath, mode, sessionUuid) {
|
|
137
|
+
const binary = mode === 'docker' ? 'clauded' : 'claude';
|
|
138
|
+
const commandParts = [`${binary} --session-id ${shellEscape(sessionUuid)}`];
|
|
139
|
+
if (mode === 'yolo') {
|
|
140
|
+
commandParts.push('--dangerously-skip-permissions');
|
|
141
|
+
}
|
|
142
|
+
return `cd ${shellEscape(workspacePath)} && ${commandParts.join(' ')}`;
|
|
143
|
+
}
|
|
144
|
+
async execTmux(args) {
|
|
145
|
+
return this.execCommand('tmux', args);
|
|
146
|
+
}
|
|
147
|
+
async execCommand(command, args, timeoutMs = this.timeoutMs) {
|
|
148
|
+
const { stdout } = await execFileAsync(command, args, {
|
|
149
|
+
encoding: 'utf8',
|
|
150
|
+
timeout: timeoutMs,
|
|
151
|
+
maxBuffer: 1024 * 1024 * 4
|
|
152
|
+
});
|
|
153
|
+
return stdout;
|
|
154
|
+
}
|
|
155
|
+
async sleep(ms) {
|
|
156
|
+
await new Promise((resolve) => {
|
|
157
|
+
setTimeout(resolve, ms);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.ClaudeTmuxBackend = ClaudeTmuxBackend;
|
|
162
|
+
//# sourceMappingURL=claude-tmux.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-tmux.js","sourceRoot":"","sources":["../../src/backends/claude-tmux.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { AgentTurnStatus } from '../types';
|
|
2
|
+
type TurnCompletionStatus = 'completed' | 'failed' | 'interrupted';
|
|
3
|
+
export interface CodexAppServerInfo {
|
|
4
|
+
pid: number;
|
|
5
|
+
port: number;
|
|
6
|
+
url: string;
|
|
7
|
+
}
|
|
8
|
+
export interface CodexSessionCreateResult {
|
|
9
|
+
threadId: string;
|
|
10
|
+
model: string;
|
|
11
|
+
appServerPid: number;
|
|
12
|
+
appServerPort: number;
|
|
13
|
+
}
|
|
14
|
+
export interface CodexTurnWaitResult {
|
|
15
|
+
completed: boolean;
|
|
16
|
+
timedOut: boolean;
|
|
17
|
+
elapsedMs: number;
|
|
18
|
+
status: TurnCompletionStatus;
|
|
19
|
+
errorMessage?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface CodexSendMessageOptions {
|
|
22
|
+
workspacePath: string;
|
|
23
|
+
model?: string;
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface CodexTurnSendResult extends CodexTurnWaitResult {
|
|
27
|
+
threadId: string;
|
|
28
|
+
assistantMessage: string;
|
|
29
|
+
appServerPid: number;
|
|
30
|
+
appServerPort: number;
|
|
31
|
+
}
|
|
32
|
+
export interface CodexRpcClient {
|
|
33
|
+
readonly currentTurnText: string;
|
|
34
|
+
readonly lastTurnStatus?: TurnCompletionStatus;
|
|
35
|
+
readonly lastTurnError?: string;
|
|
36
|
+
connectAndInitialize(): Promise<void>;
|
|
37
|
+
request(method: string, params?: unknown): Promise<unknown>;
|
|
38
|
+
waitForTurnCompletion(timeoutMs: number): Promise<CodexTurnWaitResult>;
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export interface CodexAppServerDaemonManager {
|
|
42
|
+
ensureServer(): Promise<CodexAppServerInfo>;
|
|
43
|
+
getServer(): Promise<CodexAppServerInfo | undefined>;
|
|
44
|
+
isServerRunning(pid?: number, port?: number): Promise<boolean>;
|
|
45
|
+
resetServer(server?: CodexAppServerInfo): Promise<void>;
|
|
46
|
+
stopServer(): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
export interface CodexAppServerBackendDependencies {
|
|
49
|
+
clientFactory?: (url: string) => CodexRpcClient;
|
|
50
|
+
daemonManager?: CodexAppServerDaemonManager;
|
|
51
|
+
}
|
|
52
|
+
export declare class CodexAppServerBackend {
|
|
53
|
+
private readonly sessionState;
|
|
54
|
+
private readonly daemonManager;
|
|
55
|
+
private readonly clientFactory;
|
|
56
|
+
constructor(dependencies?: CodexAppServerBackendDependencies);
|
|
57
|
+
createSession(championId: string, workspacePath: string, model?: string): Promise<CodexSessionCreateResult>;
|
|
58
|
+
sendMessage(championId: string, threadId: string, message: string, options?: CodexSendMessageOptions): Promise<CodexTurnSendResult>;
|
|
59
|
+
waitForTurn(championId: string, timeoutMs?: number): Promise<CodexTurnWaitResult>;
|
|
60
|
+
getLastAssistantMessages(championId: string, count: number): string[];
|
|
61
|
+
getSessionStatus(championId: string): AgentTurnStatus;
|
|
62
|
+
killSession(championId: string, pid?: number, threadId?: string, port?: number): Promise<void>;
|
|
63
|
+
stopAppServer(): Promise<void>;
|
|
64
|
+
sessionExists(_championId: string, pid?: number, port?: number): Promise<boolean>;
|
|
65
|
+
private ensureSessionState;
|
|
66
|
+
private withConnectedClient;
|
|
67
|
+
private withConnectedClientToServer;
|
|
68
|
+
private shouldResetDaemonAfterConnectionFailure;
|
|
69
|
+
private isResumeNotFoundError;
|
|
70
|
+
}
|
|
71
|
+
export {};
|