@enderfga/claw-orchestrator 3.0.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/LICENSE +21 -0
- package/README.md +218 -0
- package/assets/banner.jpg +0 -0
- package/configs/council-reviewer-prompt.md +82 -0
- package/configs/council-system-prompt.md +141 -0
- package/dist/bin/cli.d.ts +13 -0
- package/dist/bin/cli.js +460 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/src/base-oneshot-session.d.ts +87 -0
- package/dist/src/base-oneshot-session.js +228 -0
- package/dist/src/base-oneshot-session.js.map +1 -0
- package/dist/src/circuit-breaker.d.ts +21 -0
- package/dist/src/circuit-breaker.js +49 -0
- package/dist/src/circuit-breaker.js.map +1 -0
- package/dist/src/consensus.d.ts +20 -0
- package/dist/src/consensus.js +52 -0
- package/dist/src/consensus.js.map +1 -0
- package/dist/src/constants.d.ts +129 -0
- package/dist/src/constants.js +138 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/council.d.ts +67 -0
- package/dist/src/council.js +914 -0
- package/dist/src/council.js.map +1 -0
- package/dist/src/embedded-server.d.ts +25 -0
- package/dist/src/embedded-server.js +360 -0
- package/dist/src/embedded-server.js.map +1 -0
- package/dist/src/inbox-manager.d.ts +38 -0
- package/dist/src/inbox-manager.js +111 -0
- package/dist/src/inbox-manager.js.map +1 -0
- package/dist/src/index.d.ts +63 -0
- package/dist/src/index.js +973 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/logger.d.ts +16 -0
- package/dist/src/logger.js +44 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/models.d.ts +69 -0
- package/dist/src/models.js +299 -0
- package/dist/src/models.js.map +1 -0
- package/dist/src/openai-compat.d.ts +224 -0
- package/dist/src/openai-compat.js +756 -0
- package/dist/src/openai-compat.js.map +1 -0
- package/dist/src/persistent-codex-app-session.d.ts +108 -0
- package/dist/src/persistent-codex-app-session.js +465 -0
- package/dist/src/persistent-codex-app-session.js.map +1 -0
- package/dist/src/persistent-codex-session.d.ts +37 -0
- package/dist/src/persistent-codex-session.js +208 -0
- package/dist/src/persistent-codex-session.js.map +1 -0
- package/dist/src/persistent-cursor-session.d.ts +21 -0
- package/dist/src/persistent-cursor-session.js +241 -0
- package/dist/src/persistent-cursor-session.js.map +1 -0
- package/dist/src/persistent-custom-session.d.ts +78 -0
- package/dist/src/persistent-custom-session.js +938 -0
- package/dist/src/persistent-custom-session.js.map +1 -0
- package/dist/src/persistent-gemini-session.d.ts +21 -0
- package/dist/src/persistent-gemini-session.js +216 -0
- package/dist/src/persistent-gemini-session.js.map +1 -0
- package/dist/src/persistent-session.d.ts +80 -0
- package/dist/src/persistent-session.js +745 -0
- package/dist/src/persistent-session.js.map +1 -0
- package/dist/src/proxy/anthropic-adapter.d.ts +136 -0
- package/dist/src/proxy/anthropic-adapter.js +392 -0
- package/dist/src/proxy/anthropic-adapter.js.map +1 -0
- package/dist/src/proxy/handler.d.ts +39 -0
- package/dist/src/proxy/handler.js +365 -0
- package/dist/src/proxy/handler.js.map +1 -0
- package/dist/src/proxy/schema-cleaner.d.ts +11 -0
- package/dist/src/proxy/schema-cleaner.js +34 -0
- package/dist/src/proxy/schema-cleaner.js.map +1 -0
- package/dist/src/proxy/thought-cache.d.ts +19 -0
- package/dist/src/proxy/thought-cache.js +53 -0
- package/dist/src/proxy/thought-cache.js.map +1 -0
- package/dist/src/session-manager.d.ts +317 -0
- package/dist/src/session-manager.js +1528 -0
- package/dist/src/session-manager.js.map +1 -0
- package/dist/src/types.d.ts +513 -0
- package/dist/src/types.js +8 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/validation.d.ts +31 -0
- package/dist/src/validation.js +104 -0
- package/dist/src/validation.js.map +1 -0
- package/openclaw.plugin.json +122 -0
- package/package.json +84 -0
- package/skills/SKILL.md +184 -0
- package/skills/references/claude-cli-tracking.md +25 -0
- package/skills/references/cli.md +187 -0
- package/skills/references/council.md +210 -0
- package/skills/references/getting-started.md +133 -0
- package/skills/references/inbox.md +81 -0
- package/skills/references/multi-engine.md +382 -0
- package/skills/references/openai-compat.md +203 -0
- package/skills/references/sessions.md +191 -0
- package/skills/references/tools.md +418 -0
- package/skills/references/ultra.md +126 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent Codex Session — wraps OpenAI `codex` CLI
|
|
3
|
+
*
|
|
4
|
+
* Unlike Claude Code, Codex does not maintain a persistent subprocess with
|
|
5
|
+
* streaming JSON I/O. Each send() spawns a new `codex` process in
|
|
6
|
+
* `--sandbox workspace-write` mode (the modern replacement for the deprecated
|
|
7
|
+
* `--full-auto` flag) with `--json` to get line-delimited JSON events.
|
|
8
|
+
*
|
|
9
|
+
* The "session" is persistent in the sense that:
|
|
10
|
+
* - Working directory (cwd) carries accumulated code changes across sends
|
|
11
|
+
* - Stats, history, and cost are tracked continuously
|
|
12
|
+
* - The `thread_id` from the first send is captured and reused via
|
|
13
|
+
* `codex exec resume <id>` for subsequent sends, giving the model real
|
|
14
|
+
* conversation continuity (Codex 0.119+).
|
|
15
|
+
*/
|
|
16
|
+
import type { SessionConfig, SessionSendOptions, TurnResult } from './types.js';
|
|
17
|
+
import { BaseOneShotSession } from './base-oneshot-session.js';
|
|
18
|
+
export declare class PersistentCodexSession extends BaseOneShotSession {
|
|
19
|
+
/**
|
|
20
|
+
* Captured from the first `thread.started` event. Each subsequent send()
|
|
21
|
+
* issues `codex exec resume <id>` so the model sees prior turns.
|
|
22
|
+
*/
|
|
23
|
+
private codexThreadId?;
|
|
24
|
+
constructor(config: SessionConfig, codexBin?: string);
|
|
25
|
+
/** Expose the captured thread ID for the codex_resume tool and stats overlay. */
|
|
26
|
+
get threadId(): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Build the Codex spawn args for this turn.
|
|
29
|
+
*
|
|
30
|
+
* First turn: `codex exec [--sandbox W] --skip-git-repo-check --json --model M -C cwd <msg>`
|
|
31
|
+
* Resume turns: `codex exec resume <thread_id> [--sandbox W] --skip-git-repo-check --json --model M -C cwd <msg>`
|
|
32
|
+
*/
|
|
33
|
+
private _buildArgs;
|
|
34
|
+
protected _run(message: string, options: SessionSendOptions): Promise<TurnResult>;
|
|
35
|
+
/** Override getStats to expose the captured thread ID. */
|
|
36
|
+
getStats(): ReturnType<BaseOneShotSession['getStats']>;
|
|
37
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent Codex Session — wraps OpenAI `codex` CLI
|
|
3
|
+
*
|
|
4
|
+
* Unlike Claude Code, Codex does not maintain a persistent subprocess with
|
|
5
|
+
* streaming JSON I/O. Each send() spawns a new `codex` process in
|
|
6
|
+
* `--sandbox workspace-write` mode (the modern replacement for the deprecated
|
|
7
|
+
* `--full-auto` flag) with `--json` to get line-delimited JSON events.
|
|
8
|
+
*
|
|
9
|
+
* The "session" is persistent in the sense that:
|
|
10
|
+
* - Working directory (cwd) carries accumulated code changes across sends
|
|
11
|
+
* - Stats, history, and cost are tracked continuously
|
|
12
|
+
* - The `thread_id` from the first send is captured and reused via
|
|
13
|
+
* `codex exec resume <id>` for subsequent sends, giving the model real
|
|
14
|
+
* conversation continuity (Codex 0.119+).
|
|
15
|
+
*/
|
|
16
|
+
import { spawn } from 'node:child_process';
|
|
17
|
+
import { SESSION_EVENT } from './constants.js';
|
|
18
|
+
import { BaseOneShotSession } from './base-oneshot-session.js';
|
|
19
|
+
// ─── PersistentCodexSession ─────────────────────────────────────────────────
|
|
20
|
+
export class PersistentCodexSession extends BaseOneShotSession {
|
|
21
|
+
/**
|
|
22
|
+
* Captured from the first `thread.started` event. Each subsequent send()
|
|
23
|
+
* issues `codex exec resume <id>` so the model sees prior turns.
|
|
24
|
+
*/
|
|
25
|
+
codexThreadId;
|
|
26
|
+
constructor(config, codexBin) {
|
|
27
|
+
super(config, codexBin || process.env.CODEX_BIN || 'codex', {
|
|
28
|
+
enginePrefix: 'codex',
|
|
29
|
+
defaultModel: 'gpt-5.5',
|
|
30
|
+
supportsCachedTokens: true,
|
|
31
|
+
engineDisplayName: 'Codex',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/** Expose the captured thread ID for the codex_resume tool and stats overlay. */
|
|
35
|
+
get threadId() {
|
|
36
|
+
return this.codexThreadId;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build the Codex spawn args for this turn.
|
|
40
|
+
*
|
|
41
|
+
* First turn: `codex exec [--sandbox W] --skip-git-repo-check --json --model M -C cwd <msg>`
|
|
42
|
+
* Resume turns: `codex exec resume <thread_id> [--sandbox W] --skip-git-repo-check --json --model M -C cwd <msg>`
|
|
43
|
+
*/
|
|
44
|
+
_buildArgs(message) {
|
|
45
|
+
const args = ['exec'];
|
|
46
|
+
const isResume = !!this.codexThreadId;
|
|
47
|
+
if (isResume) {
|
|
48
|
+
// `codex exec resume` rejects --sandbox and -C; the sandbox policy and
|
|
49
|
+
// cwd are inherited from the original session (verified empirically
|
|
50
|
+
// against codex 0.128.0 — passing --sandbox here errors with
|
|
51
|
+
// "unexpected argument").
|
|
52
|
+
args.push('resume', this.codexThreadId, '--skip-git-repo-check', '--json');
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const sandbox = this.options.sandboxMode || 'workspace-write';
|
|
56
|
+
args.push('--sandbox', sandbox, '--skip-git-repo-check', '--json');
|
|
57
|
+
if (this.options.cwd)
|
|
58
|
+
args.push('-C', this.options.cwd);
|
|
59
|
+
}
|
|
60
|
+
if (this.options.model)
|
|
61
|
+
args.push('--model', this.options.model);
|
|
62
|
+
args.push(message);
|
|
63
|
+
return args;
|
|
64
|
+
}
|
|
65
|
+
_run(message, options) {
|
|
66
|
+
const args = this._buildArgs(message);
|
|
67
|
+
const timeout = options.timeout || 300_000;
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
let stdoutBuf = '';
|
|
70
|
+
let stderr = '';
|
|
71
|
+
let assistantText = '';
|
|
72
|
+
let lastUsage;
|
|
73
|
+
let settled = false;
|
|
74
|
+
const proc = spawn(this.engineBin, args, {
|
|
75
|
+
cwd: this.options.cwd,
|
|
76
|
+
env: { ...process.env },
|
|
77
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
78
|
+
});
|
|
79
|
+
this.currentProc = proc;
|
|
80
|
+
const timer = setTimeout(() => {
|
|
81
|
+
if (!settled) {
|
|
82
|
+
settled = true;
|
|
83
|
+
proc.kill('SIGTERM');
|
|
84
|
+
reject(new Error('Timeout waiting for Codex response'));
|
|
85
|
+
}
|
|
86
|
+
}, timeout);
|
|
87
|
+
const handleEvent = (line) => {
|
|
88
|
+
const trimmed = line.trim();
|
|
89
|
+
if (!trimmed)
|
|
90
|
+
return;
|
|
91
|
+
let event;
|
|
92
|
+
try {
|
|
93
|
+
event = JSON.parse(trimmed);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Not JSON — log it (could be a stray Codex banner or warning).
|
|
97
|
+
this.emit(SESSION_EVENT.LOG, `[codex-stdout] ${trimmed}`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const ev = event;
|
|
101
|
+
switch (ev.type) {
|
|
102
|
+
case 'thread.started': {
|
|
103
|
+
const t = event;
|
|
104
|
+
if (t.thread_id && !this.codexThreadId) {
|
|
105
|
+
this.codexThreadId = t.thread_id;
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case 'item.completed': {
|
|
110
|
+
const it = event;
|
|
111
|
+
if (it.item?.type === 'agent_message' && typeof it.item.text === 'string') {
|
|
112
|
+
const chunk = it.item.text;
|
|
113
|
+
assistantText += chunk;
|
|
114
|
+
try {
|
|
115
|
+
options.callbacks?.onText?.(chunk);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// User callback errors are not fatal.
|
|
119
|
+
}
|
|
120
|
+
this.emit(SESSION_EVENT.TEXT, chunk);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Tool-call items (command, apply_patch, mcp_tool_call) — surface as tool events.
|
|
124
|
+
try {
|
|
125
|
+
options.callbacks?.onToolUse?.(event);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Same swallow rule.
|
|
129
|
+
}
|
|
130
|
+
this.emit(SESSION_EVENT.LOG, `[codex-tool] ${trimmed}`);
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case 'turn.completed': {
|
|
135
|
+
const tc = event;
|
|
136
|
+
if (tc.usage)
|
|
137
|
+
lastUsage = tc.usage;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
default:
|
|
141
|
+
// Unhandled event types still go to the log so users can debug.
|
|
142
|
+
this.emit(SESSION_EVENT.LOG, `[codex-event] ${trimmed}`);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
proc.stdout?.on('data', (data) => {
|
|
146
|
+
stdoutBuf += data.toString();
|
|
147
|
+
let nl;
|
|
148
|
+
while ((nl = stdoutBuf.indexOf('\n')) >= 0) {
|
|
149
|
+
const line = stdoutBuf.slice(0, nl);
|
|
150
|
+
stdoutBuf = stdoutBuf.slice(nl + 1);
|
|
151
|
+
handleEvent(line);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
proc.stderr?.on('data', (data) => {
|
|
155
|
+
stderr += data.toString();
|
|
156
|
+
this.emit(SESSION_EVENT.LOG, `[codex-stderr] ${data.toString()}`);
|
|
157
|
+
});
|
|
158
|
+
proc.on('close', (code) => {
|
|
159
|
+
clearTimeout(timer);
|
|
160
|
+
this.currentProc = null;
|
|
161
|
+
if (settled)
|
|
162
|
+
return;
|
|
163
|
+
settled = true;
|
|
164
|
+
// Drain any final partial line as an event attempt.
|
|
165
|
+
if (stdoutBuf.trim())
|
|
166
|
+
handleEvent(stdoutBuf);
|
|
167
|
+
this._recordTurnComplete();
|
|
168
|
+
// Real usage from `turn.completed`. Falls back to zero rather than
|
|
169
|
+
// estimated tokens — better to have an honest "0" than a guess that
|
|
170
|
+
// misleads cost reporting.
|
|
171
|
+
if (lastUsage) {
|
|
172
|
+
this._stats.tokensIn += lastUsage.input_tokens ?? 0;
|
|
173
|
+
this._stats.tokensOut += (lastUsage.output_tokens ?? 0) + (lastUsage.reasoning_output_tokens ?? 0);
|
|
174
|
+
this._stats.cachedTokens += lastUsage.cached_input_tokens ?? 0;
|
|
175
|
+
}
|
|
176
|
+
this._updateCost();
|
|
177
|
+
this._addHistory({ text: assistantText, code });
|
|
178
|
+
const event = {
|
|
179
|
+
type: 'result',
|
|
180
|
+
result: assistantText,
|
|
181
|
+
stop_reason: code === 0 ? 'end_turn' : 'error',
|
|
182
|
+
session_id: this.codexThreadId,
|
|
183
|
+
};
|
|
184
|
+
this.emit(SESSION_EVENT.RESULT, event);
|
|
185
|
+
this.emit(SESSION_EVENT.TURN_COMPLETE, event);
|
|
186
|
+
if (code !== 0) {
|
|
187
|
+
reject(new Error(stderr || `Codex exited with code ${code}`));
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
resolve({ text: assistantText, event });
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
proc.on('error', (err) => {
|
|
194
|
+
clearTimeout(timer);
|
|
195
|
+
if (!settled) {
|
|
196
|
+
settled = true;
|
|
197
|
+
reject(err);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/** Override getStats to expose the captured thread ID. */
|
|
203
|
+
getStats() {
|
|
204
|
+
const base = super.getStats();
|
|
205
|
+
return { ...base, codexThreadId: this.codexThreadId };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=persistent-codex-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistent-codex-session.js","sourceRoot":"","sources":["../../src/persistent-codex-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent Cursor Session — wraps `cursor-agent` CLI
|
|
3
|
+
*
|
|
4
|
+
* Like Codex/Gemini, each send() spawns a new `cursor-agent` process in
|
|
5
|
+
* headless print mode. Cursor CLI supports `--output-format stream-json`
|
|
6
|
+
* which provides NDJSON events similar to Gemini's stream protocol.
|
|
7
|
+
*
|
|
8
|
+
* The "session" is persistent in the same sense as Codex:
|
|
9
|
+
* - Working directory carries accumulated code changes across sends
|
|
10
|
+
* - Stats, history, and cost are tracked continuously
|
|
11
|
+
* - Consistent lifecycle semantics (start/stop/pause/resume)
|
|
12
|
+
*/
|
|
13
|
+
import type { SessionConfig, SessionSendOptions, TurnResult } from './types.js';
|
|
14
|
+
import { BaseOneShotSession } from './base-oneshot-session.js';
|
|
15
|
+
export declare class PersistentCursorSession extends BaseOneShotSession {
|
|
16
|
+
private _currentRl;
|
|
17
|
+
constructor(config: SessionConfig, cursorBin?: string);
|
|
18
|
+
protected _cleanupProc(): void;
|
|
19
|
+
protected _run(message: string, options: SessionSendOptions): Promise<TurnResult>;
|
|
20
|
+
private _handleStreamEvent;
|
|
21
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent Cursor Session — wraps `cursor-agent` CLI
|
|
3
|
+
*
|
|
4
|
+
* Like Codex/Gemini, each send() spawns a new `cursor-agent` process in
|
|
5
|
+
* headless print mode. Cursor CLI supports `--output-format stream-json`
|
|
6
|
+
* which provides NDJSON events similar to Gemini's stream protocol.
|
|
7
|
+
*
|
|
8
|
+
* The "session" is persistent in the same sense as Codex:
|
|
9
|
+
* - Working directory carries accumulated code changes across sends
|
|
10
|
+
* - Stats, history, and cost are tracked continuously
|
|
11
|
+
* - Consistent lifecycle semantics (start/stop/pause/resume)
|
|
12
|
+
*/
|
|
13
|
+
import { spawn } from 'node:child_process';
|
|
14
|
+
import * as readline from 'node:readline';
|
|
15
|
+
import { estimateTokens } from './models.js';
|
|
16
|
+
import { SESSION_EVENT } from './constants.js';
|
|
17
|
+
import { BaseOneShotSession } from './base-oneshot-session.js';
|
|
18
|
+
// ─── PersistentCursorSession ────────────────────────────────────────────────
|
|
19
|
+
export class PersistentCursorSession extends BaseOneShotSession {
|
|
20
|
+
_currentRl = null;
|
|
21
|
+
constructor(config, cursorBin) {
|
|
22
|
+
super(config, cursorBin || process.env.CURSOR_BIN || 'agent', {
|
|
23
|
+
enginePrefix: 'cursor',
|
|
24
|
+
defaultModel: 'claude-sonnet-4-6',
|
|
25
|
+
defaultModelDisplay: 'cursor-default',
|
|
26
|
+
supportsCachedTokens: true,
|
|
27
|
+
engineDisplayName: 'Cursor',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
_cleanupProc() {
|
|
31
|
+
if (this._currentRl) {
|
|
32
|
+
this._currentRl.close();
|
|
33
|
+
this._currentRl = null;
|
|
34
|
+
}
|
|
35
|
+
if (this.currentProc) {
|
|
36
|
+
this.currentProc.stdin?.end();
|
|
37
|
+
this.currentProc.stdout?.destroy();
|
|
38
|
+
this.currentProc.stderr?.destroy();
|
|
39
|
+
}
|
|
40
|
+
super._cleanupProc();
|
|
41
|
+
}
|
|
42
|
+
_run(message, options) {
|
|
43
|
+
// agent -p <prompt> --force --trust --output-format stream-json
|
|
44
|
+
const args = ['-p', message, '--force', '--trust', '--output-format', 'stream-json'];
|
|
45
|
+
if (this.options.model)
|
|
46
|
+
args.push('--model', this.options.model);
|
|
47
|
+
// Workspace directory (prefer --workspace over cwd for explicit path)
|
|
48
|
+
if (this.options.cwd)
|
|
49
|
+
args.push('--workspace', this.options.cwd);
|
|
50
|
+
const timeout = options.timeout || 300_000;
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const resultText = { value: '' };
|
|
53
|
+
let stderr = '';
|
|
54
|
+
let settled = false;
|
|
55
|
+
let gotUsageFromEvents = false;
|
|
56
|
+
const proc = spawn(this.engineBin, args, {
|
|
57
|
+
cwd: this.options.cwd,
|
|
58
|
+
env: { ...process.env },
|
|
59
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
60
|
+
});
|
|
61
|
+
this.currentProc = proc;
|
|
62
|
+
const timer = setTimeout(() => {
|
|
63
|
+
if (!settled) {
|
|
64
|
+
settled = true;
|
|
65
|
+
proc.kill('SIGTERM');
|
|
66
|
+
reject(new Error('Timeout waiting for Cursor response'));
|
|
67
|
+
}
|
|
68
|
+
}, timeout);
|
|
69
|
+
// Parse stream-json output line by line
|
|
70
|
+
const rl = readline.createInterface({ input: proc.stdout, crlfDelay: Infinity });
|
|
71
|
+
this._currentRl = rl;
|
|
72
|
+
rl.on('line', (line) => {
|
|
73
|
+
if (!line.trim())
|
|
74
|
+
return;
|
|
75
|
+
try {
|
|
76
|
+
const event = JSON.parse(line);
|
|
77
|
+
this._handleStreamEvent(event, options, resultText, () => {
|
|
78
|
+
gotUsageFromEvents = true;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Non-JSON line — treat as plain text
|
|
83
|
+
resultText.value += line + '\n';
|
|
84
|
+
try {
|
|
85
|
+
options.callbacks?.onText?.(line + '\n');
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// User callback error
|
|
89
|
+
}
|
|
90
|
+
this.emit(SESSION_EVENT.TEXT, line + '\n');
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
proc.stderr?.on('data', (data) => {
|
|
94
|
+
const sanitized = data
|
|
95
|
+
.toString()
|
|
96
|
+
.replace(/CURSOR_API_KEY=[^\s]+/g, 'CURSOR_API_KEY=***')
|
|
97
|
+
.replace(/Bearer [a-zA-Z0-9_-]+/g, 'Bearer ***');
|
|
98
|
+
stderr += sanitized;
|
|
99
|
+
this.emit(SESSION_EVENT.LOG, `[cursor-stderr] ${sanitized}`);
|
|
100
|
+
});
|
|
101
|
+
proc.on('close', (code) => {
|
|
102
|
+
clearTimeout(timer);
|
|
103
|
+
this.currentProc = null;
|
|
104
|
+
if (this._currentRl) {
|
|
105
|
+
this._currentRl.close();
|
|
106
|
+
this._currentRl = null;
|
|
107
|
+
}
|
|
108
|
+
if (settled)
|
|
109
|
+
return;
|
|
110
|
+
settled = true;
|
|
111
|
+
this._recordTurnComplete();
|
|
112
|
+
// Fallback: estimate tokens if stream events didn't provide usage
|
|
113
|
+
if (!gotUsageFromEvents && resultText.value.length > 0) {
|
|
114
|
+
this._stats.tokensIn += estimateTokens(message);
|
|
115
|
+
this._stats.tokensOut += estimateTokens(resultText.value);
|
|
116
|
+
this._updateCost();
|
|
117
|
+
}
|
|
118
|
+
this._addHistory({ text: resultText.value, code });
|
|
119
|
+
const event = {
|
|
120
|
+
type: 'result',
|
|
121
|
+
result: resultText.value,
|
|
122
|
+
stop_reason: code === 0 ? 'end_turn' : 'error',
|
|
123
|
+
};
|
|
124
|
+
this.emit(SESSION_EVENT.RESULT, event);
|
|
125
|
+
this.emit(SESSION_EVENT.TURN_COMPLETE, event);
|
|
126
|
+
if (code !== 0) {
|
|
127
|
+
reject(new Error(stderr || `Cursor exited with code ${code}`));
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
resolve({ text: resultText.value, event });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
proc.on('error', (err) => {
|
|
134
|
+
clearTimeout(timer);
|
|
135
|
+
if (!settled) {
|
|
136
|
+
settled = true;
|
|
137
|
+
reject(err);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// ─── Stream Event Handling ────────────────────────────────────────────
|
|
143
|
+
_handleStreamEvent(event, options, resultText, markUsageReceived) {
|
|
144
|
+
const type = event.type;
|
|
145
|
+
switch (type) {
|
|
146
|
+
case 'system':
|
|
147
|
+
// Init event — extract session_id if available
|
|
148
|
+
if (event.session_id && !this.sessionId?.startsWith('cursor-live-')) {
|
|
149
|
+
this.sessionId = `cursor-live-${event.session_id}`;
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
case 'user':
|
|
153
|
+
// Echo of user prompt — skip
|
|
154
|
+
break;
|
|
155
|
+
case 'assistant': {
|
|
156
|
+
// Cursor format: { type: "assistant", message: { role, content: [{ type, text }] } }
|
|
157
|
+
const msg = event.message;
|
|
158
|
+
if (!msg)
|
|
159
|
+
break;
|
|
160
|
+
const contentArr = msg.content;
|
|
161
|
+
if (contentArr) {
|
|
162
|
+
for (const block of contentArr) {
|
|
163
|
+
if (block.type === 'text' && block.text) {
|
|
164
|
+
resultText.value += block.text;
|
|
165
|
+
try {
|
|
166
|
+
options.callbacks?.onText?.(block.text);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// User callback error
|
|
170
|
+
}
|
|
171
|
+
this.emit(SESSION_EVENT.TEXT, block.text);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
// Also support generic "message" format for forward compatibility
|
|
178
|
+
case 'message': {
|
|
179
|
+
if (event.role === 'user')
|
|
180
|
+
break;
|
|
181
|
+
const text = event.content || '';
|
|
182
|
+
if (text) {
|
|
183
|
+
resultText.value += text;
|
|
184
|
+
try {
|
|
185
|
+
options.callbacks?.onText?.(text);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// User callback error
|
|
189
|
+
}
|
|
190
|
+
this.emit(SESSION_EVENT.TEXT, text);
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
case 'tool_use':
|
|
195
|
+
this._stats.toolCalls++;
|
|
196
|
+
try {
|
|
197
|
+
options.callbacks?.onToolUse?.(event);
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// User callback error
|
|
201
|
+
}
|
|
202
|
+
this.emit(SESSION_EVENT.TOOL_USE, event);
|
|
203
|
+
break;
|
|
204
|
+
case 'tool_result':
|
|
205
|
+
try {
|
|
206
|
+
options.callbacks?.onToolResult?.(event);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// User callback error
|
|
210
|
+
}
|
|
211
|
+
if (event.is_error)
|
|
212
|
+
this._stats.toolErrors++;
|
|
213
|
+
this.emit(SESSION_EVENT.TOOL_RESULT, event);
|
|
214
|
+
break;
|
|
215
|
+
case 'result': {
|
|
216
|
+
// Cursor uses camelCase: inputTokens, outputTokens, cacheReadTokens
|
|
217
|
+
const usage = event.usage;
|
|
218
|
+
if (usage) {
|
|
219
|
+
this._stats.tokensIn += usage.inputTokens || usage.input_tokens || usage.prompt_tokens || 0;
|
|
220
|
+
this._stats.tokensOut += usage.outputTokens || usage.output_tokens || usage.completion_tokens || 0;
|
|
221
|
+
const cached = usage.cacheReadTokens || usage.cached_tokens || 0;
|
|
222
|
+
if (cached)
|
|
223
|
+
this._stats.cachedTokens += cached;
|
|
224
|
+
this._updateCost();
|
|
225
|
+
markUsageReceived();
|
|
226
|
+
}
|
|
227
|
+
// Result text (if not already captured from assistant events)
|
|
228
|
+
const resultStr = event.result;
|
|
229
|
+
if (resultStr && !resultText.value)
|
|
230
|
+
resultText.value = resultStr;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
case 'error':
|
|
234
|
+
this.emit(SESSION_EVENT.LOG, `[cursor-error] ${event.error || JSON.stringify(event)}`);
|
|
235
|
+
break;
|
|
236
|
+
default:
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=persistent-cursor-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistent-cursor-session.js","sourceRoot":"","sources":["../../src/persistent-cursor-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAG1C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,+EAA+E;AAE/E,MAAM,OAAO,uBAAwB,SAAQ,kBAAkB;IACrD,UAAU,GAA8B,IAAI,CAAC;IAErD,YAAY,MAAqB,EAAE,SAAkB;QACnD,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,EAAE;YAC5D,YAAY,EAAE,QAAQ;YACtB,YAAY,EAAE,mBAAmB;YACjC,mBAAmB,EAAE,gBAAgB;YACrC,oBAAoB,EAAE,IAAI;YAC1B,iBAAiB,EAAE,QAAQ;SAC5B,CAAC,CAAC;IACL,CAAC;IAEkB,YAAY;QAC7B,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YACxB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;YAC9B,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YACnC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QACrC,CAAC;QACD,KAAK,CAAC,YAAY,EAAE,CAAC;IACvB,CAAC;IAES,IAAI,CAAC,OAAe,EAAE,OAA2B;QACzD,gEAAgE;QAChE,MAAM,IAAI,GAAa,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,aAAa,CAAC,CAAC;QAE/F,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK;YAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACjE,sEAAsE;QACtE,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG;YAAE,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAEjE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC;QAE3C,OAAO,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACjD,MAAM,UAAU,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;YACjC,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,IAAI,kBAAkB,GAAG,KAAK,CAAC;YAE/B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE;gBACvC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG;gBACrB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;gBACvB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;aAChC,CAAC,CAAC;YACH,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YAExB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,GAAG,IAAI,CAAC;oBACf,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBACrB,MAAM,CAAC,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC,EAAE,OAAO,CAAC,CAAC;YAEZ,wCAAwC;YACxC,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,MAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;YAClF,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;YACrB,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;gBAC7B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;oBAAE,OAAO;gBACzB,IAAI,CAAC;oBACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC;oBAC1D,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE;wBACvD,kBAAkB,GAAG,IAAI,CAAC;oBAC5B,CAAC,CAAC,CAAC;gBACL,CAAC;gBAAC,MAAM,CAAC;oBACP,sCAAsC;oBACtC,UAAU,CAAC,KAAK,IAAI,IAAI,GAAG,IAAI,CAAC;oBAChC,IAAI,CAAC;wBACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;oBAC3C,CAAC;oBAAC,MAAM,CAAC;wBACP,sBAAsB;oBACxB,CAAC;oBACD,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC;gBAC7C,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;gBACvC,MAAM,SAAS,GAAG,IAAI;qBACnB,QAAQ,EAAE;qBACV,OAAO,CAAC,wBAAwB,EAAE,oBAAoB,CAAC;qBACvD,OAAO,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC;gBACnD,MAAM,IAAI,SAAS,CAAC;gBACpB,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,mBAAmB,SAAS,EAAE,CAAC,CAAC;YAC/D,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;gBACxB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;gBACxB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;oBACpB,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;oBACxB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;gBACzB,CAAC;gBAED,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBAEf,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAE3B,kEAAkE;gBAClE,IAAI,CAAC,kBAAkB,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvD,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;oBAChD,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,cAAc,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;oBAC1D,IAAI,CAAC,WAAW,EAAE,CAAC;gBACrB,CAAC;gBAED,IAAI,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBAEnD,MAAM,KAAK,GAAgB;oBACzB,IAAI,EAAE,QAAQ;oBACd,MAAM,EAAE,UAAU,CAAC,KAAK;oBACxB,WAAW,EAAE,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO;iBAC/C,CAAC;gBAEF,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBACvC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;gBAE9C,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;oBACf,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,2BAA2B,IAAI,EAAE,CAAC,CAAC,CAAC;gBACjE,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC7C,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACvB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,GAAG,IAAI,CAAC;oBACf,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,yEAAyE;IAEjE,kBAAkB,CACxB,KAA8B,EAC9B,OAA2B,EAC3B,UAA6B,EAC7B,iBAA6B;QAE7B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAc,CAAC;QAElC,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,QAAQ;gBACX,+CAA+C;gBAC/C,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;oBACpE,IAAI,CAAC,SAAS,GAAG,eAAe,KAAK,CAAC,UAAU,EAAE,CAAC;gBACrD,CAAC;gBACD,MAAM;YAER,KAAK,MAAM;gBACT,6BAA6B;gBAC7B,MAAM;YAER,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,qFAAqF;gBACrF,MAAM,GAAG,GAAG,KAAK,CAAC,OAA8C,CAAC;gBACjE,IAAI,CAAC,GAAG;oBAAE,MAAM;gBAChB,MAAM,UAAU,GAAG,GAAG,CAAC,OAA6D,CAAC;gBACrF,IAAI,UAAU,EAAE,CAAC;oBACf,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;wBAC/B,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;4BACxC,UAAU,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC;4BAC/B,IAAI,CAAC;gCACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;4BAC1C,CAAC;4BAAC,MAAM,CAAC;gCACP,sBAAsB;4BACxB,CAAC;4BACD,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;wBAC5C,CAAC;oBACH,CAAC;gBACH,CAAC;gBACD,MAAM;YACR,CAAC;YAED,kEAAkE;YAClE,KAAK,SAAS,CAAC,CAAC,CAAC;gBACf,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;oBAAE,MAAM;gBACjC,MAAM,IAAI,GAAI,KAAK,CAAC,OAAkB,IAAI,EAAE,CAAC;gBAC7C,IAAI,IAAI,EAAE,CAAC;oBACT,UAAU,CAAC,KAAK,IAAI,IAAI,CAAC;oBACzB,IAAI,CAAC;wBACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;oBACpC,CAAC;oBAAC,MAAM,CAAC;wBACP,sBAAsB;oBACxB,CAAC;oBACD,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACtC,CAAC;gBACD,MAAM;YACR,CAAC;YAED,KAAK,UAAU;gBACb,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACxB,IAAI,CAAC;oBACH,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC;gBACxC,CAAC;gBAAC,MAAM,CAAC;oBACP,sBAAsB;gBACxB,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBACzC,MAAM;YAER,KAAK,aAAa;gBAChB,IAAI,CAAC;oBACH,OAAO,CAAC,SAAS,EAAE,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC;gBAC3C,CAAC;gBAAC,MAAM,CAAC;oBACP,sBAAsB;gBACxB,CAAC;gBACD,IAAI,KAAK,CAAC,QAAQ;oBAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;gBAC7C,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;gBAC5C,MAAM;YAER,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,oEAAoE;gBACpE,MAAM,KAAK,GAAG,KAAK,CAAC,KAA2C,CAAC;gBAChE,IAAI,KAAK,EAAE,CAAC;oBACV,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC;oBAC5F,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,iBAAiB,IAAI,CAAC,CAAC;oBACnG,MAAM,MAAM,GAAG,KAAK,CAAC,eAAe,IAAI,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC;oBACjE,IAAI,MAAM;wBAAE,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC;oBAC/C,IAAI,CAAC,WAAW,EAAE,CAAC;oBACnB,iBAAiB,EAAE,CAAC;gBACtB,CAAC;gBACD,8DAA8D;gBAC9D,MAAM,SAAS,GAAG,KAAK,CAAC,MAA4B,CAAC;gBACrD,IAAI,SAAS,IAAI,CAAC,UAAU,CAAC,KAAK;oBAAE,UAAU,CAAC,KAAK,GAAG,SAAS,CAAC;gBACjE,MAAM;YACR,CAAC;YAED,KAAK,OAAO;gBACV,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,kBAAkB,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACvF,MAAM;YAER;gBACE,MAAM;QACV,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent Custom Session — generic wrapper for any coding agent CLI
|
|
3
|
+
*
|
|
4
|
+
* Supports two operating modes based on CustomEngineConfig.persistent:
|
|
5
|
+
*
|
|
6
|
+
* persistent=true — long-running subprocess with stream-json I/O over
|
|
7
|
+
* stdin/stdout (like Claude Code). Started once, messages
|
|
8
|
+
* sent as JSON lines on stdin.
|
|
9
|
+
*
|
|
10
|
+
* persistent=false — one-shot per send (like Gemini/Codex). Each send()
|
|
11
|
+
* spawns a new process with the message as a CLI argument.
|
|
12
|
+
*
|
|
13
|
+
* The config maps OpenClaw session concepts (permission modes, models, etc.)
|
|
14
|
+
* to the target CLI's flags, so any coding agent with a CLI can be integrated
|
|
15
|
+
* without writing engine-specific code.
|
|
16
|
+
*/
|
|
17
|
+
import { EventEmitter } from 'node:events';
|
|
18
|
+
import { type SessionConfig, type SessionStats, type EffortLevel, type ISession, type SessionSendOptions, type TurnResult, type CostBreakdown } from './types.js';
|
|
19
|
+
export declare class PersistentCustomSession extends EventEmitter implements ISession {
|
|
20
|
+
private options;
|
|
21
|
+
private engineConfig;
|
|
22
|
+
private engineBin;
|
|
23
|
+
private sanitize;
|
|
24
|
+
private proc;
|
|
25
|
+
private _rl;
|
|
26
|
+
private _streamCallbacks;
|
|
27
|
+
private _contextHighFired;
|
|
28
|
+
private currentProc;
|
|
29
|
+
private _currentRl;
|
|
30
|
+
private _isReady;
|
|
31
|
+
private _isPaused;
|
|
32
|
+
private _isBusy;
|
|
33
|
+
private currentRequestId;
|
|
34
|
+
private _startTime;
|
|
35
|
+
private _history;
|
|
36
|
+
sessionId?: string;
|
|
37
|
+
private _stats;
|
|
38
|
+
constructor(config: SessionConfig);
|
|
39
|
+
get pid(): number | undefined;
|
|
40
|
+
get isReady(): boolean;
|
|
41
|
+
get isPaused(): boolean;
|
|
42
|
+
get isBusy(): boolean;
|
|
43
|
+
start(): Promise<this>;
|
|
44
|
+
private _startPersistent;
|
|
45
|
+
private _startOneShot;
|
|
46
|
+
send(message: string | unknown[], options?: SessionSendOptions): Promise<TurnResult | {
|
|
47
|
+
requestId: number;
|
|
48
|
+
sent: boolean;
|
|
49
|
+
}>;
|
|
50
|
+
private _sendPersistent;
|
|
51
|
+
private _sendOneShot;
|
|
52
|
+
private _runOneShot;
|
|
53
|
+
private _handlePersistentEvent;
|
|
54
|
+
private _handleOneShotEvent;
|
|
55
|
+
private _waitForTurnComplete;
|
|
56
|
+
getStats(): SessionStats & {
|
|
57
|
+
sessionId?: string;
|
|
58
|
+
uptime: number;
|
|
59
|
+
};
|
|
60
|
+
getHistory(limit?: number): Array<{
|
|
61
|
+
time: string;
|
|
62
|
+
type: string;
|
|
63
|
+
event: unknown;
|
|
64
|
+
}>;
|
|
65
|
+
compact(summary?: string): Promise<TurnResult | {
|
|
66
|
+
requestId: number;
|
|
67
|
+
sent: boolean;
|
|
68
|
+
}>;
|
|
69
|
+
getEffort(): EffortLevel;
|
|
70
|
+
setEffort(level: EffortLevel): void;
|
|
71
|
+
getCost(): CostBreakdown;
|
|
72
|
+
resolveModel(alias: string): string;
|
|
73
|
+
pause(): void;
|
|
74
|
+
resume(): void;
|
|
75
|
+
stop(): void;
|
|
76
|
+
private _appendPermissionArgs;
|
|
77
|
+
private _updateCost;
|
|
78
|
+
}
|