bare-agent 0.34.0 → 0.36.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.
@@ -17,10 +17,19 @@ export type OpenAIOptions = {
17
17
  /**
18
18
  * - BA-18: request/idle timeout in ms. Bounds a silent or
19
19
  * never-answering socket on inactivity so `generate()` rejects with a retryable `TimeoutError`
20
- * (`code: 'ETIMEDOUT'`) instead of hanging until the OS TCP timeout (~2h). `0`/`Infinity`
21
- * disables it (pre-BA-18 behaviour). Overridable per call via `generate(..., { timeoutMs })`.
20
+ * (`code: 'ETIMEDOUT'`, `context.bound: 'idle'`) instead of hanging until the OS TCP timeout (~2h).
21
+ * `0`/`Infinity` disables it (pre-BA-18 behaviour). Overridable per call via `generate(..., { timeoutMs })`.
22
22
  */
23
23
  timeoutMs?: number | undefined;
24
+ /**
25
+ * - BA-19: TOTAL call-duration deadline in ms, beside `timeoutMs`.
26
+ * The idle bound resets on any socket activity, so a response that trickles a byte forever never
27
+ * trips it and hangs for hours. This is an absolute, non-resetting ceiling; on trip, `generate()`
28
+ * rejects with a TERMINAL `TimeoutError` (`code: 'EDEADLINE'`, `context.bound: 'deadline'`,
29
+ * `retryable: false`). DISABLED by default; `0`/`Infinity` disable. Overridable per call via
30
+ * `generate(..., { deadlineMs })`.
31
+ */
32
+ deadlineMs?: number | undefined;
24
33
  };
25
34
  /**
26
35
  * @typedef {object} OpenAIOptions
@@ -34,8 +43,14 @@ export type OpenAIOptions = {
34
43
  * debugging only.
35
44
  * @property {number} [timeoutMs=600000] - BA-18: request/idle timeout in ms. Bounds a silent or
36
45
  * never-answering socket on inactivity so `generate()` rejects with a retryable `TimeoutError`
37
- * (`code: 'ETIMEDOUT'`) instead of hanging until the OS TCP timeout (~2h). `0`/`Infinity`
38
- * disables it (pre-BA-18 behaviour). Overridable per call via `generate(..., { timeoutMs })`.
46
+ * (`code: 'ETIMEDOUT'`, `context.bound: 'idle'`) instead of hanging until the OS TCP timeout (~2h).
47
+ * `0`/`Infinity` disables it (pre-BA-18 behaviour). Overridable per call via `generate(..., { timeoutMs })`.
48
+ * @property {number} [deadlineMs=0] - BA-19: TOTAL call-duration deadline in ms, beside `timeoutMs`.
49
+ * The idle bound resets on any socket activity, so a response that trickles a byte forever never
50
+ * trips it and hangs for hours. This is an absolute, non-resetting ceiling; on trip, `generate()`
51
+ * rejects with a TERMINAL `TimeoutError` (`code: 'EDEADLINE'`, `context.bound: 'deadline'`,
52
+ * `retryable: false`). DISABLED by default; `0`/`Infinity` disable. Overridable per call via
53
+ * `generate(..., { deadlineMs })`.
39
54
  */
40
55
  export class OpenAIProvider {
41
56
  /**
@@ -47,11 +62,12 @@ export class OpenAIProvider {
47
62
  baseUrl: string;
48
63
  exposeErrorBody: boolean;
49
64
  timeoutMs: number | undefined;
65
+ deadlineMs: number | undefined;
50
66
  /**
51
67
  * Generate a response from the OpenAI API.
52
68
  * @param {Message[]} messages - Conversation messages.
53
69
  * @param {ToolDef[]} [tools=[]] - Tool definitions.
54
- * @param {Record<string, any>} [options={}] - Options (temperature, maxTokens, timeoutMs — a per-call override of the constructor's `timeoutMs`; see BA-18).
70
+ * @param {Record<string, any>} [options={}] - Options (temperature, maxTokens, timeoutMs — a per-call override of the constructor's `timeoutMs`, see BA-18; deadlineMs — a per-call override of the constructor's `deadlineMs`, see BA-19).
55
71
  * @returns {Promise<GenerateResult>}
56
72
  * @throws {Error} `[OpenAIProvider] ...` — on HTTP errors (4xx/5xx) or invalid JSON response.
57
73
  */
@@ -73,8 +89,9 @@ export class OpenAIProvider {
73
89
  * @param {string} path
74
90
  * @param {Record<string, any>} body
75
91
  * @param {number} [timeoutMs=0] - Idle-socket timeout (ms); 0 disables. See BA-18 / provider-http.
92
+ * @param {number} [deadlineMs=0] - Total call-duration deadline (ms); 0 disables. See BA-19 / provider-http.
76
93
  * @returns {Promise<any>}
77
94
  */
78
- _request(path: string, body: Record<string, any>, timeoutMs?: number): Promise<any>;
95
+ _request(path: string, body: Record<string, any>, timeoutMs?: number, deadlineMs?: number): Promise<any>;
79
96
  _warnedInsecure: boolean | undefined;
80
97
  }
@@ -5,7 +5,7 @@ const http = require('http');
5
5
  const { ProviderError } = require('./errors');
6
6
  const { requestWithTemperatureFallback } = require('./provider-temperature');
7
7
  const { normalizeStopReason } = require('./provider-stop-reason');
8
- const { resolveTimeoutMs, applyRequestTimeout } = require('./provider-http');
8
+ const { resolveTimeoutMs, applyRequestBounds } = require('./provider-http');
9
9
 
10
10
  /** @typedef {import('../types').Message} Message */
11
11
  /** @typedef {import('../types').ToolDef} ToolDef */
@@ -30,8 +30,14 @@ function isLoopbackHost(hostname) {
30
30
  * debugging only.
31
31
  * @property {number} [timeoutMs=600000] - BA-18: request/idle timeout in ms. Bounds a silent or
32
32
  * never-answering socket on inactivity so `generate()` rejects with a retryable `TimeoutError`
33
- * (`code: 'ETIMEDOUT'`) instead of hanging until the OS TCP timeout (~2h). `0`/`Infinity`
34
- * disables it (pre-BA-18 behaviour). Overridable per call via `generate(..., { timeoutMs })`.
33
+ * (`code: 'ETIMEDOUT'`, `context.bound: 'idle'`) instead of hanging until the OS TCP timeout (~2h).
34
+ * `0`/`Infinity` disables it (pre-BA-18 behaviour). Overridable per call via `generate(..., { timeoutMs })`.
35
+ * @property {number} [deadlineMs=0] - BA-19: TOTAL call-duration deadline in ms, beside `timeoutMs`.
36
+ * The idle bound resets on any socket activity, so a response that trickles a byte forever never
37
+ * trips it and hangs for hours. This is an absolute, non-resetting ceiling; on trip, `generate()`
38
+ * rejects with a TERMINAL `TimeoutError` (`code: 'EDEADLINE'`, `context.bound: 'deadline'`,
39
+ * `retryable: false`). DISABLED by default; `0`/`Infinity` disable. Overridable per call via
40
+ * `generate(..., { deadlineMs })`.
35
41
  */
36
42
 
37
43
  class OpenAIProvider {
@@ -45,13 +51,15 @@ class OpenAIProvider {
45
51
  this.exposeErrorBody = options.exposeErrorBody === true;
46
52
  // BA-18: request/idle timeout (ms). Resolved at call time (default 600000; 0/Infinity disable).
47
53
  this.timeoutMs = options.timeoutMs;
54
+ // BA-19: total call-duration deadline (ms). Resolved at call time (default 0 = disabled).
55
+ this.deadlineMs = options.deadlineMs;
48
56
  }
49
57
 
50
58
  /**
51
59
  * Generate a response from the OpenAI API.
52
60
  * @param {Message[]} messages - Conversation messages.
53
61
  * @param {ToolDef[]} [tools=[]] - Tool definitions.
54
- * @param {Record<string, any>} [options={}] - Options (temperature, maxTokens, timeoutMs — a per-call override of the constructor's `timeoutMs`; see BA-18).
62
+ * @param {Record<string, any>} [options={}] - Options (temperature, maxTokens, timeoutMs — a per-call override of the constructor's `timeoutMs`, see BA-18; deadlineMs — a per-call override of the constructor's `deadlineMs`, see BA-19).
55
63
  * @returns {Promise<GenerateResult>}
56
64
  * @throws {Error} `[OpenAIProvider] ...` — on HTTP errors (4xx/5xx) or invalid JSON response.
57
65
  */
@@ -73,8 +81,9 @@ class OpenAIProvider {
73
81
  // BA-10: newer models (o1/gpt-5-class) reject a non-default `temperature` with a 400 — drop it and
74
82
  // retry once. `temperatureDropped` flows back so an upstream receipt can report the effective value.
75
83
  const timeoutMs = resolveTimeoutMs(this.timeoutMs, options.timeoutMs);
84
+ const deadlineMs = resolveTimeoutMs(this.deadlineMs, options.deadlineMs, 0, 'deadlineMs');
76
85
  const { data, temperatureDropped } = await requestWithTemperatureFallback({
77
- request: () => this._request('/chat/completions', body, timeoutMs),
86
+ request: () => this._request('/chat/completions', body, timeoutMs, deadlineMs),
78
87
  hadTemperature: () => body.temperature != null,
79
88
  stripTemperature: () => { delete body.temperature; },
80
89
  warnOnce: () => this._warnTemperatureDropped(),
@@ -133,9 +142,10 @@ class OpenAIProvider {
133
142
  * @param {string} path
134
143
  * @param {Record<string, any>} body
135
144
  * @param {number} [timeoutMs=0] - Idle-socket timeout (ms); 0 disables. See BA-18 / provider-http.
145
+ * @param {number} [deadlineMs=0] - Total call-duration deadline (ms); 0 disables. See BA-19 / provider-http.
136
146
  * @returns {Promise<any>}
137
147
  */
138
- _request(path, body, timeoutMs = 0) {
148
+ _request(path, body, timeoutMs = 0, deadlineMs = 0) {
139
149
  return new Promise((resolve, reject) => {
140
150
  const url = new URL(this.baseUrl + path);
141
151
  const transport = url.protocol === 'https:' ? https : http;
@@ -177,7 +187,7 @@ class OpenAIProvider {
177
187
  }
178
188
  });
179
189
  });
180
- applyRequestTimeout(req, timeoutMs, 'OpenAIProvider');
190
+ applyRequestBounds(req, { timeoutMs, deadlineMs }, 'OpenAIProvider');
181
191
  req.on('error', reject);
182
192
  req.write(payload);
183
193
  req.end();