teleportation-cli 1.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 (54) hide show
  1. package/.claude/hooks/config-loader.mjs +93 -0
  2. package/.claude/hooks/heartbeat.mjs +331 -0
  3. package/.claude/hooks/notification.mjs +35 -0
  4. package/.claude/hooks/permission_request.mjs +307 -0
  5. package/.claude/hooks/post_tool_use.mjs +137 -0
  6. package/.claude/hooks/pre_tool_use.mjs +451 -0
  7. package/.claude/hooks/session-register.mjs +274 -0
  8. package/.claude/hooks/session_end.mjs +256 -0
  9. package/.claude/hooks/session_start.mjs +308 -0
  10. package/.claude/hooks/stop.mjs +277 -0
  11. package/.claude/hooks/user_prompt_submit.mjs +91 -0
  12. package/LICENSE +21 -0
  13. package/README.md +243 -0
  14. package/lib/auth/api-key.js +110 -0
  15. package/lib/auth/credentials.js +341 -0
  16. package/lib/backup/manager.js +461 -0
  17. package/lib/cli/daemon-commands.js +299 -0
  18. package/lib/cli/index.js +303 -0
  19. package/lib/cli/session-commands.js +294 -0
  20. package/lib/cli/snapshot-commands.js +223 -0
  21. package/lib/cli/worktree-commands.js +291 -0
  22. package/lib/config/manager.js +306 -0
  23. package/lib/daemon/lifecycle.js +336 -0
  24. package/lib/daemon/pid-manager.js +160 -0
  25. package/lib/daemon/teleportation-daemon.js +2009 -0
  26. package/lib/handoff/config.js +102 -0
  27. package/lib/handoff/example.js +152 -0
  28. package/lib/handoff/git-handoff.js +351 -0
  29. package/lib/handoff/handoff.js +277 -0
  30. package/lib/handoff/index.js +25 -0
  31. package/lib/handoff/session-state.js +238 -0
  32. package/lib/install/installer.js +555 -0
  33. package/lib/machine-coders/claude-code-adapter.js +329 -0
  34. package/lib/machine-coders/example.js +239 -0
  35. package/lib/machine-coders/gemini-cli-adapter.js +406 -0
  36. package/lib/machine-coders/index.js +103 -0
  37. package/lib/machine-coders/interface.js +168 -0
  38. package/lib/router/classifier.js +251 -0
  39. package/lib/router/example.js +92 -0
  40. package/lib/router/index.js +69 -0
  41. package/lib/router/mech-llms-client.js +277 -0
  42. package/lib/router/models.js +188 -0
  43. package/lib/router/router.js +382 -0
  44. package/lib/session/cleanup.js +100 -0
  45. package/lib/session/metadata.js +258 -0
  46. package/lib/session/mute-checker.js +114 -0
  47. package/lib/session-registry/manager.js +302 -0
  48. package/lib/snapshot/manager.js +390 -0
  49. package/lib/utils/errors.js +166 -0
  50. package/lib/utils/logger.js +148 -0
  51. package/lib/utils/retry.js +155 -0
  52. package/lib/worktree/manager.js +301 -0
  53. package/package.json +66 -0
  54. package/teleportation-cli.cjs +2987 -0
@@ -0,0 +1,406 @@
1
+ /**
2
+ * Gemini CLI Adapter
3
+ *
4
+ * Adapter for Gemini CLI using wrapper-based approval.
5
+ *
6
+ * NOTE: Gemini CLI Hooks v1 (Issue #9070) is proposed but not yet fully
7
+ * implemented in v0.19.x. The hooks in .gemini/hooks/ are ready for when
8
+ * native hooks become available. Until then, we use wrapper mode.
9
+ *
10
+ * Current approach (WRAPPER MODE):
11
+ * - Uses --output-format stream-json for real-time event parsing
12
+ * - Parses tool_use events from stream
13
+ * - Can kill process if tool is denied (tool may have started)
14
+ * - Approval callback invoked when tool_use event is detected
15
+ *
16
+ * Future approach (NATIVE HOOKS - when available):
17
+ * - Configure .gemini/settings.json with hooks.BeforeTool
18
+ * - True pre-execution approval like Claude Code
19
+ * - Hooks in .gemini/hooks/ ready to use
20
+ *
21
+ * Key characteristics:
22
+ * - Supports --yolo for auto-approve mode
23
+ * - 1M token context window (great for large repos)
24
+ *
25
+ * @see .gemini/hooks/ for hook implementations (ready for native hooks)
26
+ */
27
+
28
+ import { spawn } from 'child_process';
29
+ import { promisify } from 'util';
30
+ import { exec } from 'child_process';
31
+ import { createInterface } from 'readline';
32
+ import { MachineCoder, CODER_NAMES } from './interface.js';
33
+
34
+ const execAsync = promisify(exec);
35
+
36
+ /**
37
+ * Check if a command exists on the system
38
+ * @param {string} command
39
+ * @returns {Promise<boolean>}
40
+ */
41
+ async function commandExists(command) {
42
+ try {
43
+ await execAsync(`which ${command}`);
44
+ return true;
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Gemini CLI Adapter
52
+ */
53
+ export class GeminiCliAdapter extends MachineCoder {
54
+ name = CODER_NAMES.GEMINI_CLI;
55
+ displayName = 'Gemini CLI';
56
+
57
+ // Track running processes for stop()
58
+ #runningProcesses = new Map();
59
+
60
+ /**
61
+ * Check if Gemini CLI is available
62
+ * @returns {Promise<boolean>}
63
+ */
64
+ async isAvailable() {
65
+ return commandExists('gemini');
66
+ }
67
+
68
+ /**
69
+ * Get Gemini CLI status
70
+ * @returns {Promise<Object>}
71
+ */
72
+ async getStatus() {
73
+ const available = await this.isAvailable();
74
+
75
+ if (!available) {
76
+ return { available: false };
77
+ }
78
+
79
+ try {
80
+ const { stdout } = await execAsync('gemini --version');
81
+ return {
82
+ available: true,
83
+ version: stdout.trim(),
84
+ models: [
85
+ 'gemini-2.5-flash',
86
+ 'gemini-2.5-flash-lite',
87
+ 'gemini-2.5-pro',
88
+ 'gemini-3-pro-preview',
89
+ ],
90
+ };
91
+ } catch {
92
+ return { available: true };
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Execute a task with Gemini CLI
98
+ *
99
+ * Uses streaming JSON output to parse events in real-time.
100
+ * Can intercept tool_use events for approval.
101
+ *
102
+ * @param {Object} options
103
+ * @returns {Promise<Object>}
104
+ */
105
+ async execute(options) {
106
+ const {
107
+ projectPath,
108
+ sessionId,
109
+ prompt,
110
+ onToolCall,
111
+ onProgress,
112
+ timeoutMs = 600000, // 10 min default
113
+ autoApprove = false,
114
+ model,
115
+ } = options;
116
+
117
+ const executionId = `gemini-${Date.now()}`;
118
+ const args = [];
119
+
120
+ // Add prompt
121
+ args.push('--prompt', prompt);
122
+
123
+ // Streaming JSON output for event parsing
124
+ args.push('--output-format', 'stream-json');
125
+
126
+ // Model selection
127
+ if (model) {
128
+ args.push('--model', model);
129
+ }
130
+
131
+ // Auto-approve mode
132
+ if (autoApprove) {
133
+ args.push('--yolo');
134
+ }
135
+
136
+ return new Promise((resolve, reject) => {
137
+ const startTime = Date.now();
138
+ const toolCalls = [];
139
+ let finalOutput = '';
140
+ let finalStats = {};
141
+ let geminiSessionId = null;
142
+ let processKilled = false; // Flag to prevent race condition after kill
143
+
144
+ const proc = spawn('gemini', args, {
145
+ cwd: projectPath,
146
+ env: {
147
+ ...process.env,
148
+ // Pass session ID for potential future hook integration
149
+ TELEPORTATION_SESSION_ID: sessionId,
150
+ },
151
+ });
152
+
153
+ this.#runningProcesses.set(executionId, proc);
154
+
155
+ // Timeout handling
156
+ const timeout = setTimeout(() => {
157
+ proc.kill('SIGTERM');
158
+ reject(new Error(`Execution timed out after ${timeoutMs}ms`));
159
+ }, timeoutMs);
160
+
161
+ // Parse JSONL stream line by line
162
+ const rl = createInterface({
163
+ input: proc.stdout,
164
+ crlfDelay: Infinity,
165
+ });
166
+
167
+ rl.on('line', async (line) => {
168
+ if (!line.trim()) return;
169
+ if (processKilled) return; // Skip processing if process was killed
170
+
171
+ try {
172
+ const event = JSON.parse(line);
173
+
174
+ // Handle different event types
175
+ switch (event.type) {
176
+ case 'init':
177
+ geminiSessionId = event.session_id;
178
+ onProgress?.({
179
+ type: 'init',
180
+ timestamp: Date.now(),
181
+ data: {
182
+ sessionId: event.session_id,
183
+ model: event.model,
184
+ },
185
+ });
186
+ break;
187
+
188
+ case 'message':
189
+ if (event.role === 'assistant') {
190
+ finalOutput = event.content || finalOutput;
191
+ }
192
+ onProgress?.({
193
+ type: 'message',
194
+ timestamp: Date.now(),
195
+ data: {
196
+ role: event.role,
197
+ content: event.content,
198
+ },
199
+ });
200
+ break;
201
+
202
+ case 'tool_use':
203
+ const toolCall = {
204
+ id: event.tool_id || `tool-${toolCalls.length}`,
205
+ tool: event.tool_name,
206
+ input: event.parameters,
207
+ timestamp: Date.now(),
208
+ };
209
+ toolCalls.push(toolCall);
210
+
211
+ onProgress?.({
212
+ type: 'tool_use',
213
+ timestamp: Date.now(),
214
+ data: toolCall,
215
+ });
216
+
217
+ // Check approval if callback provided and not in auto-approve mode
218
+ if (onToolCall && !autoApprove) {
219
+ try {
220
+ const decision = await onToolCall(toolCall);
221
+
222
+ if (decision === 'deny') {
223
+ // Kill the process - tool was denied
224
+ processKilled = true; // Set flag to prevent race condition
225
+ proc.kill('SIGTERM');
226
+ clearTimeout(timeout);
227
+ this.#runningProcesses.delete(executionId);
228
+
229
+ resolve({
230
+ success: false,
231
+ output: finalOutput,
232
+ error: `Tool '${toolCall.tool}' was denied`,
233
+ toolCalls,
234
+ stats: {
235
+ durationMs: Date.now() - startTime,
236
+ model: finalStats.model,
237
+ },
238
+ executionId,
239
+ geminiSessionId,
240
+ });
241
+ return;
242
+ }
243
+ } catch (err) {
244
+ console.error(`[gemini-adapter] Error checking tool approval: ${err.message}`);
245
+ // Continue on error - fail-safe to allow
246
+ }
247
+ }
248
+ break;
249
+
250
+ case 'tool_result':
251
+ onProgress?.({
252
+ type: 'tool_result',
253
+ timestamp: Date.now(),
254
+ data: {
255
+ toolId: event.tool_id,
256
+ status: event.status,
257
+ output: event.output,
258
+ },
259
+ });
260
+ break;
261
+
262
+ case 'error':
263
+ onProgress?.({
264
+ type: 'error',
265
+ timestamp: Date.now(),
266
+ data: {
267
+ message: event.message || event.error,
268
+ },
269
+ });
270
+ break;
271
+
272
+ case 'result':
273
+ // Final result with stats
274
+ finalStats = {
275
+ tokensUsed: event.stats?.total_tokens,
276
+ inputTokens: event.stats?.input_tokens,
277
+ outputTokens: event.stats?.output_tokens,
278
+ durationMs: event.stats?.duration_ms,
279
+ toolCallCount: event.stats?.tool_calls,
280
+ model: event.stats?.model,
281
+ };
282
+ onProgress?.({
283
+ type: 'done',
284
+ timestamp: Date.now(),
285
+ data: finalStats,
286
+ });
287
+ break;
288
+ }
289
+ } catch (err) {
290
+ // Not valid JSON - might be raw output
291
+ onProgress?.({
292
+ type: 'output',
293
+ timestamp: Date.now(),
294
+ data: { text: line },
295
+ });
296
+ }
297
+ });
298
+
299
+ let stderr = '';
300
+ proc.stderr.on('data', (data) => {
301
+ stderr += data.toString();
302
+ onProgress?.({
303
+ type: 'stderr',
304
+ timestamp: Date.now(),
305
+ data: { text: data.toString() },
306
+ });
307
+ });
308
+
309
+ proc.on('close', (code) => {
310
+ clearTimeout(timeout);
311
+ this.#runningProcesses.delete(executionId);
312
+
313
+ const durationMs = Date.now() - startTime;
314
+
315
+ resolve({
316
+ success: code === 0,
317
+ output: finalOutput,
318
+ error: code !== 0 ? stderr || `Exit code: ${code}` : undefined,
319
+ toolCalls,
320
+ stats: {
321
+ ...finalStats,
322
+ durationMs: finalStats.durationMs || durationMs,
323
+ },
324
+ executionId,
325
+ geminiSessionId,
326
+ });
327
+ });
328
+
329
+ proc.on('error', (err) => {
330
+ clearTimeout(timeout);
331
+ this.#runningProcesses.delete(executionId);
332
+ reject(err);
333
+ });
334
+ });
335
+ }
336
+
337
+ /**
338
+ * Resume a Gemini session with a new prompt
339
+ * @param {string} sessionId - Gemini session ID to resume
340
+ * @param {string} prompt - New prompt
341
+ * @param {Object} options - Additional options
342
+ * @returns {Promise<Object>}
343
+ */
344
+ async resume(sessionId, prompt, options = {}) {
345
+ const {
346
+ projectPath = process.cwd(),
347
+ onToolCall,
348
+ onProgress,
349
+ timeoutMs = 600000,
350
+ autoApprove = false,
351
+ model,
352
+ } = options;
353
+
354
+ const executionId = `gemini-resume-${Date.now()}`;
355
+ const args = ['--resume', sessionId, '--prompt', prompt, '--output-format', 'stream-json'];
356
+
357
+ if (model) {
358
+ args.push('--model', model);
359
+ }
360
+
361
+ if (autoApprove) {
362
+ args.push('--yolo');
363
+ }
364
+
365
+ // Reuse execute logic with resume flag
366
+ return this.execute({
367
+ ...options,
368
+ projectPath,
369
+ prompt: `--resume ${sessionId} ${prompt}`, // Hack: will be overridden by args
370
+ });
371
+ }
372
+
373
+ /**
374
+ * Stop a running execution
375
+ * @param {string} executionId
376
+ * @returns {Promise<boolean>}
377
+ */
378
+ async stop(executionId) {
379
+ const proc = this.#runningProcesses.get(executionId);
380
+ if (proc) {
381
+ proc.kill('SIGTERM');
382
+ this.#runningProcesses.delete(executionId);
383
+ return true;
384
+ }
385
+ return false;
386
+ }
387
+
388
+ /**
389
+ * Get capabilities
390
+ * @returns {Object}
391
+ */
392
+ getCapabilities() {
393
+ return {
394
+ supportsResume: true,
395
+ supportsStreaming: true,
396
+ hasNativeApproval: false, // Native hooks proposed (Issue #9070) but not yet in v0.19.x
397
+ supportsModelSelection: true,
398
+ maxContextTokens: 1000000, // 1M tokens!
399
+ supportedTools: ['Bash', 'Read', 'Edit', 'Write', 'Glob', 'Grep', 'LS', 'WebSearch', 'WebFetch'],
400
+ // Hooks ready in .gemini/hooks/ for when native hooks become available
401
+ pendingNativeHooks: ['BeforeTool', 'AfterTool', 'SessionStart', 'SessionEnd'],
402
+ };
403
+ }
404
+ }
405
+
406
+ export default GeminiCliAdapter;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Machine Coders Module
3
+ *
4
+ * Unified interface for different AI coding assistants:
5
+ * - Claude Code (via hooks)
6
+ * - Gemini CLI (via wrapper + streaming)
7
+ * - Goose CLI (via recipes)
8
+ *
9
+ * @module lib/machine-coders
10
+ */
11
+
12
+ export { MachineCoder, CODER_NAMES } from './interface.js';
13
+ export { ClaudeCodeAdapter } from './claude-code-adapter.js';
14
+ export { GeminiCliAdapter } from './gemini-cli-adapter.js';
15
+ // export { GooseCliAdapter } from './goose-cli-adapter.js'; // TODO: implement
16
+
17
+ /**
18
+ * Get a machine coder adapter by name
19
+ * @param {string} name - 'claude-code' | 'gemini-cli' | 'goose-cli'
20
+ * @returns {MachineCoder}
21
+ */
22
+ export function getMachineCoder(name) {
23
+ switch (name) {
24
+ case 'claude-code':
25
+ const { ClaudeCodeAdapter } = require('./claude-code-adapter.js');
26
+ return new ClaudeCodeAdapter();
27
+ case 'gemini-cli':
28
+ const { GeminiCliAdapter } = require('./gemini-cli-adapter.js');
29
+ return new GeminiCliAdapter();
30
+ // case 'goose-cli':
31
+ // const { GooseCliAdapter } = require('./goose-cli-adapter.js');
32
+ // return new GooseCliAdapter();
33
+ default:
34
+ return null;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Get all available machine coders on this system
40
+ * @returns {Promise<MachineCoder[]>}
41
+ */
42
+ export async function getAvailableCoders() {
43
+ const { ClaudeCodeAdapter } = await import('./claude-code-adapter.js');
44
+ const { GeminiCliAdapter } = await import('./gemini-cli-adapter.js');
45
+
46
+ const coders = [
47
+ new ClaudeCodeAdapter(),
48
+ new GeminiCliAdapter(),
49
+ ];
50
+
51
+ const available = [];
52
+ for (const coder of coders) {
53
+ if (await coder.isAvailable()) {
54
+ available.push(coder);
55
+ }
56
+ }
57
+
58
+ return available;
59
+ }
60
+
61
+ /**
62
+ * Get the best available machine coder for a task
63
+ * Priority: Claude Code > Gemini CLI > Goose CLI
64
+ *
65
+ * @param {Object} [preferences] - Optional preferences
66
+ * @param {string} [preferences.preferred] - Preferred coder name
67
+ * @param {boolean} [preferences.largeContext] - Prefer large context (Gemini)
68
+ * @returns {Promise<MachineCoder|null>}
69
+ */
70
+ export async function getBestCoder(preferences = {}) {
71
+ const available = await getAvailableCoders();
72
+
73
+ if (available.length === 0) {
74
+ return null;
75
+ }
76
+
77
+ // If preferred coder is specified and available, use it
78
+ if (preferences.preferred) {
79
+ const preferred = available.find(c => c.name === preferences.preferred);
80
+ if (preferred) return preferred;
81
+ }
82
+
83
+ // If large context needed, prefer Gemini
84
+ if (preferences.largeContext) {
85
+ const gemini = available.find(c => c.name === 'gemini-cli');
86
+ if (gemini) return gemini;
87
+ }
88
+
89
+ // Default priority: Claude Code first
90
+ const priority = ['claude-code', 'gemini-cli', 'goose-cli'];
91
+ for (const name of priority) {
92
+ const coder = available.find(c => c.name === name);
93
+ if (coder) return coder;
94
+ }
95
+
96
+ return available[0];
97
+ }
98
+
99
+ export default {
100
+ getMachineCoder,
101
+ getAvailableCoders,
102
+ getBestCoder,
103
+ };
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Machine Coder Interface
3
+ *
4
+ * Defines the common interface that all machine coders must implement.
5
+ * This allows Teleportation to work with Claude Code, Gemini CLI, Goose CLI,
6
+ * and future coding assistants in a uniform way.
7
+ */
8
+
9
+ /**
10
+ * Known machine coder names
11
+ */
12
+ export const CODER_NAMES = {
13
+ CLAUDE_CODE: 'claude-code',
14
+ GEMINI_CLI: 'gemini-cli',
15
+ GOOSE_CLI: 'goose-cli',
16
+ };
17
+
18
+ /**
19
+ * Tool call event (when the coder wants to use a tool)
20
+ * @typedef {Object} ToolCall
21
+ * @property {string} id - Unique ID for this tool call
22
+ * @property {string} tool - Tool name (e.g., 'Bash', 'Edit', 'Read')
23
+ * @property {Object} input - Tool input parameters
24
+ * @property {number} timestamp - When the tool was called
25
+ */
26
+
27
+ /**
28
+ * Progress event (streaming updates from the coder)
29
+ * @typedef {Object} ProgressEvent
30
+ * @property {string} type - Event type: 'init' | 'message' | 'tool_use' | 'tool_result' | 'error' | 'done'
31
+ * @property {number} timestamp - Event timestamp
32
+ * @property {Object} [data] - Event-specific data
33
+ */
34
+
35
+ /**
36
+ * Execution options
37
+ * @typedef {Object} ExecuteOptions
38
+ * @property {string} projectPath - Path to the project directory
39
+ * @property {string} sessionId - Teleportation session ID
40
+ * @property {string} prompt - The task/prompt to execute
41
+ * @property {Function} [onToolCall] - Callback for tool approval: (tool: ToolCall) => Promise<'allow' | 'deny' | 'ask'>
42
+ * @property {Function} [onProgress] - Callback for progress events: (event: ProgressEvent) => void
43
+ * @property {number} [timeoutMs] - Execution timeout in milliseconds
44
+ * @property {boolean} [autoApprove] - Skip approval checks (dangerous)
45
+ * @property {string} [model] - Specific model to use (if supported)
46
+ */
47
+
48
+ /**
49
+ * Execution result
50
+ * @typedef {Object} ExecuteResult
51
+ * @property {boolean} success - Whether execution completed successfully
52
+ * @property {string} output - Final output/response
53
+ * @property {string} [error] - Error message if failed
54
+ * @property {ToolCall[]} toolCalls - List of tools that were called
55
+ * @property {Object} [stats] - Execution statistics
56
+ * @property {number} [stats.tokensUsed] - Total tokens used
57
+ * @property {number} [stats.cost] - Estimated cost in USD
58
+ * @property {number} [stats.durationMs] - Execution duration
59
+ * @property {string} [stats.model] - Model that was used
60
+ */
61
+
62
+ /**
63
+ * Coder status
64
+ * @typedef {Object} CoderStatus
65
+ * @property {boolean} available - Whether the coder is available
66
+ * @property {string} [version] - CLI version
67
+ * @property {string[]} [models] - Available models
68
+ * @property {Object} [config] - Current configuration
69
+ */
70
+
71
+ /**
72
+ * Base Machine Coder class
73
+ * All adapters should extend this class.
74
+ */
75
+ export class MachineCoder {
76
+ /**
77
+ * Unique identifier for this coder
78
+ * @type {string}
79
+ */
80
+ name = 'base';
81
+
82
+ /**
83
+ * Human-readable display name
84
+ * @type {string}
85
+ */
86
+ displayName = 'Base Machine Coder';
87
+
88
+ /**
89
+ * Check if this coder is available on the system
90
+ * @returns {Promise<boolean>}
91
+ */
92
+ async isAvailable() {
93
+ throw new Error('Not implemented');
94
+ }
95
+
96
+ /**
97
+ * Get the current status of this coder
98
+ * @returns {Promise<CoderStatus>}
99
+ */
100
+ async getStatus() {
101
+ return {
102
+ available: await this.isAvailable(),
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Execute a task/prompt
108
+ * @param {ExecuteOptions} options
109
+ * @returns {Promise<ExecuteResult>}
110
+ */
111
+ async execute(options) {
112
+ throw new Error('Not implemented');
113
+ }
114
+
115
+ /**
116
+ * Resume a previous session with a new prompt
117
+ * @param {string} sessionId - Session to resume
118
+ * @param {string} prompt - New prompt/task
119
+ * @param {ExecuteOptions} [options] - Additional options
120
+ * @returns {Promise<ExecuteResult>}
121
+ */
122
+ async resume(sessionId, prompt, options = {}) {
123
+ // Default implementation: just execute with the prompt
124
+ // Subclasses can override for proper session resume
125
+ return this.execute({
126
+ ...options,
127
+ sessionId,
128
+ prompt,
129
+ });
130
+ }
131
+
132
+ /**
133
+ * Stop/kill a running execution
134
+ * @param {string} executionId - Execution to stop
135
+ * @returns {Promise<boolean>}
136
+ */
137
+ async stop(executionId) {
138
+ throw new Error('Not implemented');
139
+ }
140
+
141
+ /**
142
+ * Get capabilities of this coder
143
+ * @returns {Object}
144
+ */
145
+ getCapabilities() {
146
+ return {
147
+ // Whether this coder supports session resume
148
+ supportsResume: false,
149
+
150
+ // Whether this coder supports streaming output
151
+ supportsStreaming: false,
152
+
153
+ // Whether this coder has native approval hooks
154
+ hasNativeApproval: false,
155
+
156
+ // Whether this coder supports model selection
157
+ supportsModelSelection: false,
158
+
159
+ // Maximum context size (tokens)
160
+ maxContextTokens: 0,
161
+
162
+ // Supported tools
163
+ supportedTools: [],
164
+ };
165
+ }
166
+ }
167
+
168
+ export default MachineCoder;