acpreact 1.1.1 → 1.1.2

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 (3) hide show
  1. package/core.js +27 -20
  2. package/index.js +2 -2
  3. package/package.json +1 -1
package/core.js CHANGED
@@ -6,14 +6,12 @@ import { FallbackEngine } from './fallback.js';
6
6
 
7
7
  const DEFAULT_TIMEOUT_MS = 120_000;
8
8
 
9
- function attachOutputs(err, output, errorOutput) {
10
- err.output = output;
11
- err.stderr = errorOutput;
12
- return err;
13
- }
9
+ function attachOutputs(err, output, errorOutput) { err.output = output; err.stderr = errorOutput; return err; }
14
10
 
15
11
  function spawnService(entry, prompt, options, callbacks) {
12
+ const abortSignal = options?._abortSignal;
16
13
  return new Promise((resolve, reject) => {
14
+ if (abortSignal?.aborted) return reject(new Error('Aborted'));
17
15
  const binary = entry.config?.binary || entry.name;
18
16
  const args = entry.config?.buildArgs
19
17
  ? entry.config.buildArgs(prompt, options)
@@ -23,17 +21,21 @@ function spawnService(entry, prompt, options, callbacks) {
23
21
  child.stdin.end();
24
22
  child.stdout.on('data', (d) => { const c = d.toString(); output += c; callbacks?.onOutput?.(c); });
25
23
  child.stderr.on('data', (d) => { const c = d.toString(); errorOutput += c; callbacks?.onStderr?.(c); });
24
+ const timeoutMs = options?.timeout ?? DEFAULT_TIMEOUT_MS;
26
25
  const timer = setTimeout(() => {
27
26
  child.kill();
28
- reject(attachOutputs(new Error(`Timeout after ${DEFAULT_TIMEOUT_MS}ms`), output, errorOutput));
29
- }, options?.timeout ?? DEFAULT_TIMEOUT_MS);
27
+ reject(attachOutputs(new Error(`Timeout after ${timeoutMs}ms`), output, errorOutput));
28
+ }, timeoutMs);
29
+ const onAbort = () => { child.kill(); clearTimeout(timer); reject(attachOutputs(new Error('Aborted'), output, errorOutput)); };
30
+ abortSignal?.addEventListener('abort', onAbort, { once: true });
30
31
  child.on('close', (code) => {
31
32
  clearTimeout(timer);
33
+ abortSignal?.removeEventListener('abort', onAbort);
32
34
  if (code !== 0 && code !== null && !output)
33
35
  return reject(attachOutputs(new Error(`${binary} exited with code ${code}: ${errorOutput}`), output, errorOutput));
34
36
  resolve({ rawOutput: output, stderr: errorOutput, code });
35
37
  });
36
- child.on('error', (err) => { clearTimeout(timer); reject(attachOutputs(err, output, errorOutput)); });
38
+ child.on('error', (err) => { clearTimeout(timer); abortSignal?.removeEventListener('abort', onAbort); reject(attachOutputs(err, output, errorOutput)); });
37
39
  });
38
40
  }
39
41
 
@@ -55,9 +57,13 @@ class ACPProtocol extends EventEmitter {
55
57
  }
56
58
  }
57
59
  this.fallback = new FallbackEngine([]);
58
- this.fallback.on('rate-limited', (e) => this.emit('rate-limited', e));
60
+ this.fallback.on('rate-limited', (e) => {
61
+ this.registry.markRateLimited(e.name, e.profileId, e.cooldownMs);
62
+ this.emit('rate-limited', e);
63
+ });
59
64
  this.fallback.on('fallback', (e) => this.emit('fallback', e));
60
65
  this.fallback.on('success', (e) => this.emit('success', e));
66
+ this._abortController = null;
61
67
  }
62
68
 
63
69
  generateRequestId() { return ++this.messageId; }
@@ -109,12 +115,7 @@ class ACPProtocol extends EventEmitter {
109
115
 
110
116
  createJsonRpcResponse(id, result) { return { jsonrpc: '2.0', id, result }; }
111
117
 
112
- createJsonRpcError(id, error) {
113
- return {
114
- jsonrpc: '2.0', id,
115
- error: { code: error?.code ?? -32000, message: error instanceof Error ? error.message : String(error) },
116
- };
117
- }
118
+ createJsonRpcError(id, error) { return { jsonrpc: '2.0', id, error: { code: error?.code ?? -32000, message: error instanceof Error ? error.message : String(error) } }; }
118
119
 
119
120
  validateToolCall(toolName) {
120
121
  if (!this.toolWhitelist.has(toolName)) {
@@ -149,16 +150,22 @@ class ACPProtocol extends EventEmitter {
149
150
  if (options.services) {
150
151
  stack = createServiceStack(options.services);
151
152
  } else if (options.cli) {
152
- stack = [{ name: options.cli, profileId: '__default__', config: {} }];
153
+ stack = [{ name: options.cli, profileId: '__default__', config: { cli: options.cli } }];
153
154
  } else {
154
155
  stack = this.registry.getAll().length > 0
155
156
  ? this.registry.getAvailable()
156
- : [{ name: 'kilo', profileId: '__default__', config: {} }];
157
+ : [{ name: 'kilo', profileId: '__default__', config: { cli: 'kilo' } }];
157
158
  }
158
159
 
159
- this.fallback._stack = stack;
160
+ this._abortController = new AbortController();
161
+ const runOptions = { ...options, _abortSignal: this._abortController.signal };
162
+ const engine = new FallbackEngine(stack);
163
+ engine.on('rate-limited', (e) => this.fallback.emit('rate-limited', e));
164
+ engine.on('fallback', (e) => this.fallback.emit('fallback', e));
165
+ engine.on('success', (e) => this.fallback.emit('success', e));
160
166
 
161
- const { rawOutput } = await this.fallback.run(spawnService, fullPrompt, options);
167
+ const { rawOutput } = await engine.run(spawnService, fullPrompt, runOptions);
168
+ this._abortController = null;
162
169
 
163
170
  try {
164
171
  const toolCalls = parseToolCalls(rawOutput);
@@ -176,7 +183,7 @@ class ACPProtocol extends EventEmitter {
176
183
  }
177
184
  }
178
185
 
179
- stop() {}
186
+ stop() { if (this._abortController) { this._abortController.abort(); this._abortController = null; } }
180
187
  }
181
188
 
182
189
  export { ACPProtocol };
package/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { ACPProtocol } from './core.js';
2
- import { ServiceRegistry, isRateLimited, createServiceStack } from './services.js';
2
+ import { ServiceRegistry, isRateLimited, createServiceStack, buildArgs, DEFAULT_COOLDOWN_MS } from './services.js';
3
3
  import { FallbackEngine } from './fallback.js';
4
4
 
5
- export { ACPProtocol, ServiceRegistry, FallbackEngine, isRateLimited, createServiceStack };
5
+ export { ACPProtocol, ServiceRegistry, FallbackEngine, isRateLimited, createServiceStack, buildArgs, DEFAULT_COOLDOWN_MS };
6
6
 
7
7
  /*
8
8
  * acpreact - ACP SDK for registering tools with multi-service fallback
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acpreact",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "description": "ACP SDK for monitoring and reacting to chat conversations",