orchajs 0.23.8 → 0.23.10

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.
@@ -7,6 +7,8 @@ export declare class AmarsiaTelemetryClient {
7
7
  endpoint?: string;
8
8
  });
9
9
  enqueue(fact: AmarsiaRunFact): void;
10
+ /** @internal Tracks telemetry projection work that must settle before drain completes. */
11
+ trackProjection(projection: Promise<void>): void;
10
12
  drain(options?: {
11
13
  timeoutMs?: number;
12
14
  }): Promise<void>;
@@ -10,11 +10,13 @@ const RELEVANT_EVENTS = new Set([
10
10
  const MAX_BATCH_RUNS = 100;
11
11
  const MAX_KEEPALIVE_BYTES = 48 * 1024;
12
12
  const HEARTBEAT_INTERVAL_MS = 30_000;
13
+ const EVALUATION_FLUSH_TIMEOUT_MS = 5_000;
13
14
  export class AmarsiaTelemetryClient {
14
15
  #key;
15
16
  #baseUrl;
16
17
  #queue = new Map();
17
18
  #activeRuns = new Map();
19
+ #pendingProjections = new Set();
18
20
  #timer;
19
21
  #heartbeatTimer;
20
22
  #attempt = 0;
@@ -56,6 +58,13 @@ export class AmarsiaTelemetryClient {
56
58
  const timer = this.#timer;
57
59
  timer.unref?.();
58
60
  }
61
+ /** @internal Tracks telemetry projection work that must settle before drain completes. */
62
+ trackProjection(projection) {
63
+ this.#pendingProjections.add(projection);
64
+ void projection.finally(() => {
65
+ this.#pendingProjections.delete(projection);
66
+ });
67
+ }
59
68
  #scheduleTerminal() {
60
69
  if (this.#timer)
61
70
  clearTimeout(this.#timer);
@@ -81,14 +90,23 @@ export class AmarsiaTelemetryClient {
81
90
  async drain(options = {}) {
82
91
  const timeoutMs = Math.max(0, options.timeoutMs ?? 2_000);
83
92
  const deadline = Date.now() + timeoutMs;
84
- if (this.#timer) {
85
- clearTimeout(this.#timer);
86
- this.#timer = undefined;
87
- }
88
- while (this.#queue.size > 0 || this.#flushPromise) {
93
+ while (this.#pendingProjections.size > 0 ||
94
+ this.#queue.size > 0 ||
95
+ this.#flushPromise) {
89
96
  const remaining = deadline - Date.now();
90
97
  if (remaining <= 0)
91
98
  return;
99
+ if (this.#pendingProjections.size > 0) {
100
+ await Promise.race([
101
+ Promise.allSettled([...this.#pendingProjections]).then(() => undefined),
102
+ new Promise((resolve) => setTimeout(resolve, remaining)),
103
+ ]);
104
+ continue;
105
+ }
106
+ if (this.#timer) {
107
+ clearTimeout(this.#timer);
108
+ this.#timer = undefined;
109
+ }
92
110
  await Promise.race([
93
111
  this.#flush(),
94
112
  new Promise((resolve) => setTimeout(resolve, remaining)),
@@ -102,6 +120,11 @@ export class AmarsiaTelemetryClient {
102
120
  }
103
121
  this.#activeRuns.clear();
104
122
  await this.drain(options);
123
+ if (this.#heartbeatTimer) {
124
+ clearTimeout(this.#heartbeatTimer);
125
+ this.#heartbeatTimer = undefined;
126
+ }
127
+ this.#activeRuns.clear();
105
128
  }
106
129
  async #flush() {
107
130
  if (this.#flushPromise)
@@ -206,11 +229,23 @@ export function withAmarsiaTelemetry(options) {
206
229
  })
207
230
  .catch(() => undefined);
208
231
  pending.set(sessionId, processing);
232
+ client.trackProjection(processing);
209
233
  void processing.finally(() => {
210
234
  if (pending.get(sessionId) === processing) {
211
235
  pending.delete(sessionId);
212
236
  }
213
237
  });
238
+ const hasTerminalRunEvent = events.some((event) => event.type === "run.completed" ||
239
+ event.type === "run.failed" ||
240
+ event.type === "run.paused");
241
+ const hasEvaluationResult = events.some((event) => event.type === "evaluation.completed" ||
242
+ event.type === "evaluation.failed");
243
+ if (hasTerminalRunEvent || hasEvaluationResult) {
244
+ await processing;
245
+ }
246
+ if (hasEvaluationResult) {
247
+ await client.drain({ timeoutMs: EVALUATION_FLUSH_TIMEOUT_MS });
248
+ }
214
249
  },
215
250
  };
216
251
  }
@@ -35,11 +35,13 @@ const RELEVANT_EVENTS = /* @__PURE__ */ new Set([
35
35
  const MAX_BATCH_RUNS = 100;
36
36
  const MAX_KEEPALIVE_BYTES = 48 * 1024;
37
37
  const HEARTBEAT_INTERVAL_MS = 3e4;
38
+ const EVALUATION_FLUSH_TIMEOUT_MS = 5e3;
38
39
  class AmarsiaTelemetryClient {
39
40
  #key;
40
41
  #baseUrl;
41
42
  #queue = /* @__PURE__ */ new Map();
42
43
  #activeRuns = /* @__PURE__ */ new Map();
44
+ #pendingProjections = /* @__PURE__ */ new Set();
43
45
  #timer;
44
46
  #heartbeatTimer;
45
47
  #attempt = 0;
@@ -79,6 +81,13 @@ class AmarsiaTelemetryClient {
79
81
  const timer = this.#timer;
80
82
  timer.unref?.();
81
83
  }
84
+ /** @internal Tracks telemetry projection work that must settle before drain completes. */
85
+ trackProjection(projection) {
86
+ this.#pendingProjections.add(projection);
87
+ void projection.finally(() => {
88
+ this.#pendingProjections.delete(projection);
89
+ });
90
+ }
82
91
  #scheduleTerminal() {
83
92
  if (this.#timer) clearTimeout(this.#timer);
84
93
  this.#timer = setTimeout(() => {
@@ -102,13 +111,20 @@ class AmarsiaTelemetryClient {
102
111
  async drain(options = {}) {
103
112
  const timeoutMs = Math.max(0, options.timeoutMs ?? 2e3);
104
113
  const deadline = Date.now() + timeoutMs;
105
- if (this.#timer) {
106
- clearTimeout(this.#timer);
107
- this.#timer = void 0;
108
- }
109
- while (this.#queue.size > 0 || this.#flushPromise) {
114
+ while (this.#pendingProjections.size > 0 || this.#queue.size > 0 || this.#flushPromise) {
110
115
  const remaining = deadline - Date.now();
111
116
  if (remaining <= 0) return;
117
+ if (this.#pendingProjections.size > 0) {
118
+ await Promise.race([
119
+ Promise.allSettled([...this.#pendingProjections]).then(() => void 0),
120
+ new Promise((resolve) => setTimeout(resolve, remaining))
121
+ ]);
122
+ continue;
123
+ }
124
+ if (this.#timer) {
125
+ clearTimeout(this.#timer);
126
+ this.#timer = void 0;
127
+ }
112
128
  await Promise.race([
113
129
  this.#flush(),
114
130
  new Promise((resolve) => setTimeout(resolve, remaining))
@@ -122,6 +138,11 @@ class AmarsiaTelemetryClient {
122
138
  }
123
139
  this.#activeRuns.clear();
124
140
  await this.drain(options);
141
+ if (this.#heartbeatTimer) {
142
+ clearTimeout(this.#heartbeatTimer);
143
+ this.#heartbeatTimer = void 0;
144
+ }
145
+ this.#activeRuns.clear();
125
146
  }
126
147
  async #flush() {
127
148
  if (this.#flushPromise) return this.#flushPromise;
@@ -223,11 +244,24 @@ function withAmarsiaTelemetry(options) {
223
244
  }
224
245
  }).catch(() => void 0);
225
246
  pending.set(sessionId, processing);
247
+ client.trackProjection(processing);
226
248
  void processing.finally(() => {
227
249
  if (pending.get(sessionId) === processing) {
228
250
  pending.delete(sessionId);
229
251
  }
230
252
  });
253
+ const hasTerminalRunEvent = events.some(
254
+ (event) => event.type === "run.completed" || event.type === "run.failed" || event.type === "run.paused"
255
+ );
256
+ const hasEvaluationResult = events.some(
257
+ (event) => event.type === "evaluation.completed" || event.type === "evaluation.failed"
258
+ );
259
+ if (hasTerminalRunEvent || hasEvaluationResult) {
260
+ await processing;
261
+ }
262
+ if (hasEvaluationResult) {
263
+ await client.drain({ timeoutMs: EVALUATION_FLUSH_TIMEOUT_MS });
264
+ }
231
265
  }
232
266
  };
233
267
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orchajs",
3
- "version": "0.23.8",
3
+ "version": "0.23.10",
4
4
  "description": "A filesystem-convention, stateful agent framework for JavaScript.",
5
5
  "license": "MIT",
6
6
  "repository": {