@compilr-dev/sdk 0.23.1 → 0.23.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.
@@ -272,6 +272,28 @@ export declare class TeamAgent {
272
272
  * `thought_signature`) that do not transfer across a rebuild.
273
273
  */
274
274
  private stashForRebuild;
275
+ /**
276
+ * A turn is in flight: the host has called `run()`/`stream()` and is awaiting it.
277
+ *
278
+ * ⚠️ HOSTS MUST BRACKET EVERY RUN, IN A `finally`. `TeamAgent` does not own the run call —
279
+ * the host holds the unwrapped `Agent` — so it cannot observe this for itself.
280
+ */
281
+ private turnStartedAt;
282
+ /**
283
+ * How long a turn may be in flight before this stops believing the host.
284
+ *
285
+ * ⚠️ A FLAG THAT ONLY EVER GETS SET IS WORSE THAN THE BUG IT GUARDS. A host that throws past
286
+ * its `endTurn()` would block every edit to that agent for the life of the process, with no
287
+ * way back short of a restart. Going stale bounds that to one window; the edit that slips
288
+ * through after it is the same edit that would have been allowed before this guard existed.
289
+ */
290
+ private static readonly TURN_STALE_AFTER_MS;
291
+ /** Mark the start of a turn. Pair with `endTurn()` in a `finally`. */
292
+ beginTurn(): void;
293
+ /** Mark the end of a turn, however it ended — completed, aborted or thrown. */
294
+ endTurn(): void;
295
+ /** Whether a turn is in flight and recent enough to still be believed. */
296
+ get isTurnInFlight(): boolean;
275
297
  /**
276
298
  * Apply an edit to this agent in place.
277
299
  *
@@ -337,6 +337,36 @@ export class TeamAgent {
337
337
  this.storedState = state;
338
338
  this._agent = null;
339
339
  }
340
+ /**
341
+ * A turn is in flight: the host has called `run()`/`stream()` and is awaiting it.
342
+ *
343
+ * ⚠️ HOSTS MUST BRACKET EVERY RUN, IN A `finally`. `TeamAgent` does not own the run call —
344
+ * the host holds the unwrapped `Agent` — so it cannot observe this for itself.
345
+ */
346
+ turnStartedAt = null;
347
+ /**
348
+ * How long a turn may be in flight before this stops believing the host.
349
+ *
350
+ * ⚠️ A FLAG THAT ONLY EVER GETS SET IS WORSE THAN THE BUG IT GUARDS. A host that throws past
351
+ * its `endTurn()` would block every edit to that agent for the life of the process, with no
352
+ * way back short of a restart. Going stale bounds that to one window; the edit that slips
353
+ * through after it is the same edit that would have been allowed before this guard existed.
354
+ */
355
+ static TURN_STALE_AFTER_MS = 10 * 60 * 1000;
356
+ /** Mark the start of a turn. Pair with `endTurn()` in a `finally`. */
357
+ beginTurn() {
358
+ this.turnStartedAt = Date.now();
359
+ }
360
+ /** Mark the end of a turn, however it ended — completed, aborted or thrown. */
361
+ endTurn() {
362
+ this.turnStartedAt = null;
363
+ }
364
+ /** Whether a turn is in flight and recent enough to still be believed. */
365
+ get isTurnInFlight() {
366
+ if (this.turnStartedAt === null)
367
+ return false;
368
+ return Date.now() - this.turnStartedAt < TeamAgent.TURN_STALE_AFTER_MS;
369
+ }
340
370
  /**
341
371
  * Apply an edit to this agent in place.
342
372
  *
@@ -360,6 +390,25 @@ export class TeamAgent {
360
390
  when it is not. Its `rebuilt` contribution is folded in below.
361
391
  */
362
392
  const tierChanged = patch.modelTier !== undefined && patch.modelTier !== this._modelTier;
393
+ /*
394
+ ⚠️ REFUSED MID-TURN, BEFORE ANYTHING IS MUTATED. A rebuild snapshots the history with
395
+ `serialize()` and drops the instance — but the in-flight `run()` holds its own reference
396
+ and finishes anyway, appending to an agent nobody will read again. Measured: the user
397
+ watches a reply stream in, and the rebuilt agent is handed the snapshot from BEFORE it,
398
+ with no memory of having said it.
399
+
400
+ Not a broken conversation — `setHistory` repairs tool pairing, so nothing 400s. It is
401
+ worse than that: the agent silently forgets a turn the user just watched, and the next
402
+ thing it says is built on a history the user can see is wrong.
403
+
404
+ Only rebuilding edits are refused. A rename or a new face is read from the roster at
405
+ render time and cannot tear anything, so those still apply mid-turn.
406
+ */
407
+ if ((needsRebuild || tierChanged) && this.isTurnInFlight) {
408
+ throw new Error(`Agent '${this.id}' is mid-reply. Changing its prompt, model, tools or skills rebuilds ` +
409
+ `it and would drop the turn it is in the middle of — wait for the reply to finish, ` +
410
+ `or stop it, then save.`);
411
+ }
363
412
  if (patch.modelTier !== undefined)
364
413
  this.setModelTier(patch.modelTier);
365
414
  if (patch.displayName !== undefined)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/sdk",
3
- "version": "0.23.1",
3
+ "version": "0.23.2",
4
4
  "description": "Universal agent runtime for building AI-powered applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",