@myagentroam/node 0.11.12 → 0.11.13

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.
@@ -44,7 +44,8 @@ export class NodeRuntimeState {
44
44
  queueVersions = new Map();
45
45
  /** Snapshot version of the last FIFO membership or ordering change. */
46
46
  queueOrderVersions = new Map();
47
- pausedQueues = new Set();
47
+ /** Each pause owns its snapshot version so late admissions cannot undo a newer pause. */
48
+ pausedQueues = new Map();
48
49
  /** Bounded tombstones reject stale internal start envelopes after terminal cleanup. */
49
50
  terminalRunIds = new Map();
50
51
  /**
@@ -243,7 +244,7 @@ export class NodeRuntimeState {
243
244
  const queueVersion = this.queueVersions.get(id);
244
245
  const queueOrderVersion = this.queueOrderVersions.get(id);
245
246
  const lease = this.leases.get(id);
246
- const paused = this.pausedQueues.has(id);
247
+ const pauseVersion = this.pausedQueues.get(id);
247
248
  this.sessions.delete(id);
248
249
  this.sessions.set(nativeSessionId, next);
249
250
  if (this.completeHistorySessions.delete(id))
@@ -284,9 +285,9 @@ export class NodeRuntimeState {
284
285
  this.leases.delete(id);
285
286
  this.leases.set(nativeSessionId, lease);
286
287
  }
287
- if (paused) {
288
+ if (pauseVersion !== undefined) {
288
289
  this.pausedQueues.delete(id);
289
- this.pausedQueues.add(nativeSessionId);
290
+ this.pausedQueues.set(nativeSessionId, pauseVersion);
290
291
  }
291
292
  for (const [key, submission] of [...this.messageSubmissions]) {
292
293
  if (!key.startsWith(`${id}\u0000`))
@@ -514,17 +515,27 @@ export class NodeRuntimeState {
514
515
  }
515
516
  consumeSteeredQueueItem(itemId, runId) {
516
517
  const item = this.requireQueueItem(itemId);
517
- const run = this.getRun(runId);
518
+ const activeRun = this.getRun(runId);
519
+ const run = activeRun ?? this.terminalRunIds.get(runId);
518
520
  if (!run || run.sessionId !== item.sessionId || !this.receivingQueueItems.has(itemId))
519
521
  throw new Error('QUEUE_ITEM_INVALID');
520
522
  this.queuedItems.set(item.sessionId, (this.queuedItems.get(item.sessionId) ?? []).filter((id) => id !== itemId));
521
523
  this.queueItems.delete(itemId);
522
524
  this.bumpQueueVersion(item.sessionId);
523
- this.messageSubmissions.set(`${item.sessionId}\u0000${item.clientMessageId}`, {
524
- kind: 'run',
525
- id: run.id
526
- });
527
- this.recentUserMessages.set(run.id, item.content);
525
+ const key = `${item.sessionId}\u0000${item.clientMessageId}`;
526
+ if (activeRun !== undefined) {
527
+ this.messageSubmissions.set(key, { kind: 'run', id: run.id });
528
+ this.recentUserMessages.set(run.id, item.content);
529
+ }
530
+ else {
531
+ this.messageSubmissions.delete(key);
532
+ this.rememberTerminalMessageSubmission(key, run.id);
533
+ }
534
+ }
535
+ rememberTerminalMessageSubmission(key, runId) {
536
+ this.terminalMessageSubmissions.set(key, runId);
537
+ if (this.terminalMessageSubmissions.size > 1_024)
538
+ this.terminalMessageSubmissions.delete(this.terminalMessageSubmissions.keys().next().value);
528
539
  }
529
540
  claimNextQueueItem(sessionId, content) {
530
541
  if (this.pausedQueues.has(sessionId))
@@ -586,8 +597,11 @@ export class NodeRuntimeState {
586
597
  return { run: created.run, turn };
587
598
  }
588
599
  pauseSessionQueue(sessionId) {
589
- this.pausedQueues.add(sessionId);
590
600
  this.bumpQueueVersion(sessionId, false);
601
+ this.pausedQueues.set(sessionId, this.queueVersions.get(sessionId));
602
+ }
603
+ sessionQueuePauseVersion(sessionId) {
604
+ return this.pausedQueues.get(sessionId);
591
605
  }
592
606
  resumeSessionQueue(sessionId) {
593
607
  if (!this.pausedQueues.delete(sessionId))
@@ -1076,9 +1090,7 @@ export class NodeRuntimeState {
1076
1090
  for (const [key, candidate] of this.messageSubmissions)
1077
1091
  if (candidate.kind === 'run' && candidate.id === runId) {
1078
1092
  this.messageSubmissions.delete(key);
1079
- this.terminalMessageSubmissions.set(key, runId);
1080
- if (this.terminalMessageSubmissions.size > 1_024)
1081
- this.terminalMessageSubmissions.delete(this.terminalMessageSubmissions.keys().next().value);
1093
+ this.rememberTerminalMessageSubmission(key, runId);
1082
1094
  }
1083
1095
  for (const [approvalId, approval] of this.approvals)
1084
1096
  if (approval.runId === runId || executionIds.has(approval.runId))
@@ -140,9 +140,9 @@ export class ContinuousRunCoordinator {
140
140
  this.options.runtime.currentExecutionId(active.id) === input.steerTarget.executionId) {
141
141
  const attempt = this.tryNativeSteer(input.submissionId, active, input.onNativeAccepted);
142
142
  if (attempt)
143
- return attempt.then((accepted) => accepted
143
+ return attempt.task.then((accepted) => accepted
144
144
  ? 'ACCEPTED'
145
- : this.suspended || this.options.stopped()
145
+ : !this.nativeSteerPauseCurrent(attempt) || this.suspended || this.options.stopped()
146
146
  ? 'QUEUED'
147
147
  : this.settleFallback(input));
148
148
  }
@@ -164,6 +164,8 @@ export class ContinuousRunCoordinator {
164
164
  (input.steerTarget.executionId === undefined ||
165
165
  this.options.runtime.currentExecutionId(active.id) === input.steerTarget.executionId);
166
166
  const intent = input.steerTarget !== undefined && !steering ? 'SEND' : input.intent;
167
+ if (active?.status === 'CANCELLING' && intent !== 'REPLACE_CURRENT')
168
+ return 'QUEUED';
167
169
  if (steering)
168
170
  this.prepareSteering(input.submissionId);
169
171
  const executionEnded = active !== undefined &&
@@ -255,9 +257,9 @@ export class ContinuousRunCoordinator {
255
257
  if (active) {
256
258
  const attempt = this.tryNativeSteer(itemId, active);
257
259
  if (attempt)
258
- return attempt.then((accepted) => accepted
260
+ return attempt.task.then((accepted) => accepted
259
261
  ? { run: active, delivery: 'ACCEPTED' }
260
- : this.suspended || this.options.stopped()
262
+ : !this.nativeSteerPauseCurrent(attempt) || this.suspended || this.options.stopped()
261
263
  ? { run: active, delivery: 'QUEUED' }
262
264
  : this.executeQueueItemFallback(itemId));
263
265
  }
@@ -266,7 +268,7 @@ export class ContinuousRunCoordinator {
266
268
  tryNativeSteer(itemId, active, onAccepted) {
267
269
  const previous = this.steerInFlight.get(itemId);
268
270
  if (previous)
269
- return previous.task;
271
+ return previous;
270
272
  if (this.suspended || this.options.stopped() || !this.options.steer || !this.canSteer(active))
271
273
  return undefined;
272
274
  const executionId = this.options.runtime.currentExecutionId(active.id);
@@ -275,6 +277,7 @@ export class ContinuousRunCoordinator {
275
277
  this.prepareSteering(itemId);
276
278
  const prepared = this.preparedInputs.get(itemId);
277
279
  const item = this.options.runtime.getQueueItem(itemId);
280
+ const pauseVersion = this.options.runtime.sessionQueuePauseVersion(item.sessionId);
278
281
  const release = this.options.runtime.reserveQueueItem(itemId);
279
282
  let attempt;
280
283
  try {
@@ -292,23 +295,29 @@ export class ContinuousRunCoordinator {
292
295
  .then(async (result) => {
293
296
  if (result.status === 'notAccepted')
294
297
  return false;
298
+ const currentPrepared = this.preparedInputs.get(itemId) ?? prepared;
295
299
  onAccepted?.();
296
300
  this.options.runtime.consumeSteeredQueueItem(itemId, active.id);
297
301
  this.immediateQueueItems.delete(itemId);
298
- this.options.runtime.resumeSessionQueue(item.sessionId);
302
+ if (this.nativeSteerPauseCurrent(admission))
303
+ this.options.runtime.resumeSessionQueue(admission.sessionId);
299
304
  this.options.clearImages(itemId);
300
- if (prepared.goal !== undefined && this.goalCurrent(prepared)) {
305
+ if (currentPrepared.goal !== undefined && this.goalCurrent(currentPrepared)) {
301
306
  const current = this.preparedInputs.get(executionId);
302
307
  if (current !== undefined)
303
- this.preparedInputs.set(executionId, { ...current, goal: prepared.goal });
308
+ this.preparedInputs.set(executionId, { ...current, goal: currentPrepared.goal });
304
309
  }
305
310
  try {
306
- await this.options.afterExecutionSteered?.({ ...prepared, runId: executionId });
311
+ await this.options.afterExecutionSteered?.({
312
+ ...currentPrepared,
313
+ sessionId: admission.sessionId,
314
+ runId: executionId
315
+ });
307
316
  }
308
317
  catch (error) {
309
318
  nodeLog('queue.native_steer.continuation_failed', {
310
319
  level: 'warn',
311
- sessionId: item.sessionId,
320
+ sessionId: admission.sessionId,
312
321
  executionId,
313
322
  code: safeErrorCode(error, 'NATIVE_STEER_CONTINUATION_FAILED')
314
323
  });
@@ -320,7 +329,7 @@ export class ContinuousRunCoordinator {
320
329
  if (result.status === 'accepted')
321
330
  this.options.emitRun(executionId, 'conversation.item', result.item);
322
331
  },
323
- () => this.publish(item.sessionId)
332
+ () => this.publish(admission.sessionId)
324
333
  ]) {
325
334
  try {
326
335
  notify();
@@ -328,7 +337,7 @@ export class ContinuousRunCoordinator {
328
337
  catch {
329
338
  nodeLog('queue.native_steer.notification_failed', {
330
339
  level: 'warn',
331
- sessionId: item.sessionId,
340
+ sessionId: admission.sessionId,
332
341
  executionId
333
342
  });
334
343
  }
@@ -338,10 +347,14 @@ export class ContinuousRunCoordinator {
338
347
  .finally(() => {
339
348
  release();
340
349
  this.steerInFlight.delete(itemId);
341
- this.schedule(item.sessionId);
350
+ this.schedule(admission.sessionId);
342
351
  });
343
- this.steerInFlight.set(itemId, { sessionId: item.sessionId, task });
344
- return task;
352
+ const admission = { sessionId: item.sessionId, pauseVersion, task };
353
+ this.steerInFlight.set(itemId, admission);
354
+ return admission;
355
+ }
356
+ nativeSteerPauseCurrent(admission) {
357
+ return (this.options.runtime.sessionQueuePauseVersion(admission.sessionId) === admission.pauseVersion);
345
358
  }
346
359
  async executeQueueItemFallback(itemId) {
347
360
  const item = this.requirePreparedItem(itemId);
@@ -669,6 +682,9 @@ export class ContinuousRunCoordinator {
669
682
  for (const [id, pending] of this.preparedInputs)
670
683
  if (pending.sessionId === previousId)
671
684
  this.preparedInputs.set(id, { ...pending, sessionId: nextId, externalSessionId: nextId });
685
+ for (const admission of this.steerInFlight.values())
686
+ if (admission.sessionId === previousId)
687
+ admission.sessionId = nextId;
672
688
  const timer = this.scheduledSessions.get(previousId);
673
689
  if (timer !== undefined) {
674
690
  clearTimeout(timer);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/node",
3
- "version": "0.11.12",
3
+ "version": "0.11.13",
4
4
  "description": "MyAgentRoam Node runtime CLI.",
5
5
  "license": "Apache-2.0 AND GPL-2.0-or-later",
6
6
  "type": "module",
@@ -37,10 +37,10 @@
37
37
  "playwright": "1.62.1",
38
38
  "ws": "^8.21.3",
39
39
  "zod": "4.4.3",
40
- "@myagentroam/agent": "0.11.12",
41
- "@myagentroam/marctl": "0.11.0",
42
- "@myagentroam/protocol": "0.11.12",
43
- "@myagentroam/code-navigation": "0.11.0"
40
+ "@myagentroam/agent": "0.11.13",
41
+ "@myagentroam/code-navigation": "0.11.0",
42
+ "@myagentroam/protocol": "0.11.13",
43
+ "@myagentroam/marctl": "0.11.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/ws": "^8.18.1"