@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.
Files changed (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +218 -0
  3. package/assets/banner.jpg +0 -0
  4. package/configs/council-reviewer-prompt.md +82 -0
  5. package/configs/council-system-prompt.md +141 -0
  6. package/dist/bin/cli.d.ts +13 -0
  7. package/dist/bin/cli.js +460 -0
  8. package/dist/bin/cli.js.map +1 -0
  9. package/dist/src/base-oneshot-session.d.ts +87 -0
  10. package/dist/src/base-oneshot-session.js +228 -0
  11. package/dist/src/base-oneshot-session.js.map +1 -0
  12. package/dist/src/circuit-breaker.d.ts +21 -0
  13. package/dist/src/circuit-breaker.js +49 -0
  14. package/dist/src/circuit-breaker.js.map +1 -0
  15. package/dist/src/consensus.d.ts +20 -0
  16. package/dist/src/consensus.js +52 -0
  17. package/dist/src/consensus.js.map +1 -0
  18. package/dist/src/constants.d.ts +129 -0
  19. package/dist/src/constants.js +138 -0
  20. package/dist/src/constants.js.map +1 -0
  21. package/dist/src/council.d.ts +67 -0
  22. package/dist/src/council.js +914 -0
  23. package/dist/src/council.js.map +1 -0
  24. package/dist/src/embedded-server.d.ts +25 -0
  25. package/dist/src/embedded-server.js +360 -0
  26. package/dist/src/embedded-server.js.map +1 -0
  27. package/dist/src/inbox-manager.d.ts +38 -0
  28. package/dist/src/inbox-manager.js +111 -0
  29. package/dist/src/inbox-manager.js.map +1 -0
  30. package/dist/src/index.d.ts +63 -0
  31. package/dist/src/index.js +973 -0
  32. package/dist/src/index.js.map +1 -0
  33. package/dist/src/logger.d.ts +16 -0
  34. package/dist/src/logger.js +44 -0
  35. package/dist/src/logger.js.map +1 -0
  36. package/dist/src/models.d.ts +69 -0
  37. package/dist/src/models.js +299 -0
  38. package/dist/src/models.js.map +1 -0
  39. package/dist/src/openai-compat.d.ts +224 -0
  40. package/dist/src/openai-compat.js +756 -0
  41. package/dist/src/openai-compat.js.map +1 -0
  42. package/dist/src/persistent-codex-app-session.d.ts +108 -0
  43. package/dist/src/persistent-codex-app-session.js +465 -0
  44. package/dist/src/persistent-codex-app-session.js.map +1 -0
  45. package/dist/src/persistent-codex-session.d.ts +37 -0
  46. package/dist/src/persistent-codex-session.js +208 -0
  47. package/dist/src/persistent-codex-session.js.map +1 -0
  48. package/dist/src/persistent-cursor-session.d.ts +21 -0
  49. package/dist/src/persistent-cursor-session.js +241 -0
  50. package/dist/src/persistent-cursor-session.js.map +1 -0
  51. package/dist/src/persistent-custom-session.d.ts +78 -0
  52. package/dist/src/persistent-custom-session.js +938 -0
  53. package/dist/src/persistent-custom-session.js.map +1 -0
  54. package/dist/src/persistent-gemini-session.d.ts +21 -0
  55. package/dist/src/persistent-gemini-session.js +216 -0
  56. package/dist/src/persistent-gemini-session.js.map +1 -0
  57. package/dist/src/persistent-session.d.ts +80 -0
  58. package/dist/src/persistent-session.js +745 -0
  59. package/dist/src/persistent-session.js.map +1 -0
  60. package/dist/src/proxy/anthropic-adapter.d.ts +136 -0
  61. package/dist/src/proxy/anthropic-adapter.js +392 -0
  62. package/dist/src/proxy/anthropic-adapter.js.map +1 -0
  63. package/dist/src/proxy/handler.d.ts +39 -0
  64. package/dist/src/proxy/handler.js +365 -0
  65. package/dist/src/proxy/handler.js.map +1 -0
  66. package/dist/src/proxy/schema-cleaner.d.ts +11 -0
  67. package/dist/src/proxy/schema-cleaner.js +34 -0
  68. package/dist/src/proxy/schema-cleaner.js.map +1 -0
  69. package/dist/src/proxy/thought-cache.d.ts +19 -0
  70. package/dist/src/proxy/thought-cache.js +53 -0
  71. package/dist/src/proxy/thought-cache.js.map +1 -0
  72. package/dist/src/session-manager.d.ts +317 -0
  73. package/dist/src/session-manager.js +1528 -0
  74. package/dist/src/session-manager.js.map +1 -0
  75. package/dist/src/types.d.ts +513 -0
  76. package/dist/src/types.js +8 -0
  77. package/dist/src/types.js.map +1 -0
  78. package/dist/src/validation.d.ts +31 -0
  79. package/dist/src/validation.js +104 -0
  80. package/dist/src/validation.js.map +1 -0
  81. package/openclaw.plugin.json +122 -0
  82. package/package.json +84 -0
  83. package/skills/SKILL.md +184 -0
  84. package/skills/references/claude-cli-tracking.md +25 -0
  85. package/skills/references/cli.md +187 -0
  86. package/skills/references/council.md +210 -0
  87. package/skills/references/getting-started.md +133 -0
  88. package/skills/references/inbox.md +81 -0
  89. package/skills/references/multi-engine.md +382 -0
  90. package/skills/references/openai-compat.md +203 -0
  91. package/skills/references/sessions.md +191 -0
  92. package/skills/references/tools.md +418 -0
  93. 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":"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"}
@@ -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
+ }