@botbotgo/agent-harness 0.0.83 → 0.0.84

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.
@@ -345,6 +345,7 @@ export type RunStartOptions = {
345
345
  agentId?: string;
346
346
  input: MessageContent;
347
347
  threadId?: string;
348
+ priority?: number;
348
349
  invocation?: InvocationEnvelope;
349
350
  listeners?: RunListeners;
350
351
  };
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.82";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.83";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.82";
1
+ export const AGENT_HARNESS_VERSION = "0.0.83";
@@ -27,6 +27,7 @@ export type PersistenceLifecycle = {
27
27
  };
28
28
  export type PersistedRunRequest = {
29
29
  input: MessageContent;
30
+ priority?: number;
30
31
  invocation?: InvocationEnvelope;
31
32
  savedAt: string;
32
33
  };
@@ -29,6 +29,7 @@ export declare class AgentHarnessRuntime {
29
29
  private readonly concurrencyConfig;
30
30
  private readonly workerId;
31
31
  private activeRunSlots;
32
+ private pendingRunInsertionOrder;
32
33
  private readonly pendingRunSlots;
33
34
  private runtimeEventSequence;
34
35
  private toPublicApprovalRecord;
@@ -90,6 +91,9 @@ export declare class AgentHarnessRuntime {
90
91
  private finalizeCancelledRun;
91
92
  private invokeWithHistory;
92
93
  private buildPersistedRunRequest;
94
+ private normalizeRunPriority;
95
+ private resolvePersistedRunPriority;
96
+ private enqueuePendingRunSlot;
93
97
  private executeQueuedRun;
94
98
  private checkpointRefForState;
95
99
  private finalizeContinuedRun;
@@ -46,6 +46,7 @@ export class AgentHarnessRuntime {
46
46
  concurrencyConfig;
47
47
  workerId = `worker-${createPersistentId()}`;
48
48
  activeRunSlots = 0;
49
+ pendingRunInsertionOrder = 0;
49
50
  pendingRunSlots = [];
50
51
  runtimeEventSequence = 0;
51
52
  toPublicApprovalRecord(approval) {
@@ -512,7 +513,7 @@ export class AgentHarnessRuntime {
512
513
  throw error;
513
514
  }
514
515
  }
515
- buildPersistedRunRequest(input, invocation) {
516
+ buildPersistedRunRequest(input, invocation, priority) {
516
517
  const envelope = invocation.invocation ?? {
517
518
  ...(invocation.context ? { context: invocation.context } : {}),
518
519
  ...(invocation.state ? { inputs: invocation.state } : {}),
@@ -520,6 +521,7 @@ export class AgentHarnessRuntime {
520
521
  };
521
522
  return {
522
523
  input: normalizeMessageContent(input),
524
+ priority: Number.isFinite(priority) ? Math.trunc(priority) : undefined,
523
525
  invocation: envelope && Object.keys(envelope).length > 0
524
526
  ? {
525
527
  ...(envelope.context ? { context: envelope.context } : {}),
@@ -531,6 +533,48 @@ export class AgentHarnessRuntime {
531
533
  savedAt: new Date().toISOString(),
532
534
  };
533
535
  }
536
+ normalizeRunPriority(priority) {
537
+ if (!Number.isFinite(priority)) {
538
+ return 0;
539
+ }
540
+ return Math.trunc(priority);
541
+ }
542
+ async resolvePersistedRunPriority(threadId, runId) {
543
+ const persisted = await this.persistence.getRunRequest(threadId, runId);
544
+ return this.normalizeRunPriority(persisted?.priority);
545
+ }
546
+ enqueuePendingRunSlot(entry) {
547
+ const previousPositions = new Map(this.pendingRunSlots
548
+ .filter((candidate) => Boolean(candidate.threadId && candidate.runId))
549
+ .map((candidate, index) => [candidate.runId, index + 1]));
550
+ const queuedEntry = {
551
+ ...entry,
552
+ insertionOrder: this.pendingRunInsertionOrder++,
553
+ };
554
+ this.pendingRunSlots.push(queuedEntry);
555
+ this.pendingRunSlots.sort((left, right) => {
556
+ if (right.priority !== left.priority) {
557
+ return right.priority - left.priority;
558
+ }
559
+ return left.insertionOrder - right.insertionOrder;
560
+ });
561
+ return this.pendingRunSlots.flatMap((candidate, index) => {
562
+ if (!candidate.threadId || !candidate.runId) {
563
+ return [];
564
+ }
565
+ const previousPosition = previousPositions.get(candidate.runId);
566
+ const queuePosition = index + 1;
567
+ if (previousPosition === undefined || previousPosition === queuePosition) {
568
+ return [];
569
+ }
570
+ return [{
571
+ threadId: candidate.threadId,
572
+ runId: candidate.runId,
573
+ priority: candidate.priority,
574
+ queuePosition,
575
+ }];
576
+ });
577
+ }
534
578
  async executeQueuedRun(binding, input, threadId, runId, agentId, options = {}) {
535
579
  const previousState = options.previousState ?? "running";
536
580
  const currentRun = await this.persistence.getRun(runId);
@@ -712,9 +756,9 @@ export class AgentHarnessRuntime {
712
756
  }
713
757
  await listener(value);
714
758
  }
715
- async acquireRunSlot(threadId, runId, activeState = "running") {
759
+ async acquireRunSlot(threadId, runId, activeState = "running", priority = 0) {
716
760
  if (threadId && runId) {
717
- await this.persistence.enqueueRun({ threadId, runId });
761
+ await this.persistence.enqueueRun({ threadId, runId, priority });
718
762
  }
719
763
  let stopHeartbeat = () => undefined;
720
764
  const beginLease = async () => {
@@ -773,8 +817,50 @@ export class AgentHarnessRuntime {
773
817
  void next?.activate();
774
818
  };
775
819
  }
820
+ const activateQueuedRun = async () => {
821
+ const currentRun = runId ? await this.persistence.getRun(runId) : null;
822
+ if (currentRun?.state === "cancelled") {
823
+ return "abort";
824
+ }
825
+ this.activeRunSlots += 1;
826
+ if (threadId && runId) {
827
+ await this.emit(threadId, runId, 4, "run.dequeued", {
828
+ queuePosition: 0,
829
+ activeRunCount: this.activeRunSlots,
830
+ maxConcurrentRuns,
831
+ priority,
832
+ });
833
+ await this.setRunStateAndEmit(threadId, runId, 5, activeState, {
834
+ previousState: "queued",
835
+ });
836
+ await beginLease();
837
+ }
838
+ return "activate";
839
+ };
776
840
  if (threadId && runId) {
777
- const queuePosition = this.pendingRunSlots.length + 1;
841
+ const slotAcquisition = new Promise((resolve, reject) => {
842
+ const displacedEntries = this.enqueuePendingRunSlot({
843
+ threadId,
844
+ runId,
845
+ priority,
846
+ activate: async () => {
847
+ try {
848
+ resolve(await activateQueuedRun());
849
+ }
850
+ catch (error) {
851
+ reject(error);
852
+ }
853
+ },
854
+ abort: () => resolve("abort"),
855
+ });
856
+ void Promise.all(displacedEntries.map((candidate) => this.emit(candidate.threadId, candidate.runId, 3, "run.queued", {
857
+ queuePosition: candidate.queuePosition,
858
+ activeRunCount: this.activeRunSlots,
859
+ maxConcurrentRuns,
860
+ priority: candidate.priority,
861
+ })));
862
+ });
863
+ const queuePosition = this.pendingRunSlots.findIndex((entry) => entry.runId === runId) + 1;
778
864
  await this.setRunStateAndEmit(threadId, runId, 2, "queued", {
779
865
  previousState: activeState,
780
866
  });
@@ -782,37 +868,23 @@ export class AgentHarnessRuntime {
782
868
  queuePosition,
783
869
  activeRunCount: this.activeRunSlots,
784
870
  maxConcurrentRuns,
871
+ priority,
785
872
  });
786
- }
787
- const slotAcquisition = await new Promise((resolve, reject) => {
788
- this.pendingRunSlots.push({ runId, activate: async () => {
789
- try {
790
- const currentRun = runId ? await this.persistence.getRun(runId) : null;
791
- if (currentRun?.state === "cancelled") {
792
- resolve("abort");
793
- return;
794
- }
795
- this.activeRunSlots += 1;
796
- if (threadId && runId) {
797
- await this.emit(threadId, runId, 4, "run.dequeued", {
798
- queuePosition: 0,
799
- activeRunCount: this.activeRunSlots,
800
- maxConcurrentRuns,
801
- });
802
- await this.setRunStateAndEmit(threadId, runId, 5, activeState, {
803
- previousState: "queued",
804
- });
805
- await beginLease();
806
- }
807
- resolve("activate");
808
- }
809
- catch (error) {
810
- reject(error);
811
- }
812
- }, abort: () => resolve("abort") });
813
- });
814
- if (slotAcquisition === "abort") {
815
- return async () => undefined;
873
+ const slotAcquisitionResult = await slotAcquisition;
874
+ if (slotAcquisitionResult === "abort") {
875
+ return async () => undefined;
876
+ }
877
+ let released = false;
878
+ return async () => {
879
+ if (released) {
880
+ return;
881
+ }
882
+ released = true;
883
+ await releaseLease();
884
+ this.activeRunSlots = Math.max(0, this.activeRunSlots - 1);
885
+ const next = this.pendingRunSlots.shift();
886
+ void next?.activate();
887
+ };
816
888
  }
817
889
  let released = false;
818
890
  return async () => {
@@ -923,14 +995,15 @@ export class AgentHarnessRuntime {
923
995
  throw new Error(`Policy evaluation blocked agent ${selectedAgentId}: ${policyDecision.reasons.join(", ")}`);
924
996
  }
925
997
  const { threadId, runId } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, options.threadId);
926
- await this.persistence.saveRunRequest(threadId, runId, this.buildPersistedRunRequest(options.input, invocation));
998
+ const priority = this.normalizeRunPriority(options.priority);
999
+ await this.persistence.saveRunRequest(threadId, runId, this.buildPersistedRunRequest(options.input, invocation, priority));
927
1000
  await this.emitRunCreated(threadId, runId, {
928
1001
  agentId: binding.agent.id,
929
1002
  requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
930
1003
  selectedAgentId,
931
1004
  executionMode: getBindingAdapterKind(binding),
932
1005
  });
933
- const releaseRunSlot = await this.acquireRunSlot(threadId, runId);
1006
+ const releaseRunSlot = await this.acquireRunSlot(threadId, runId, "running", priority);
934
1007
  try {
935
1008
  return await this.executeQueuedRun(binding, options.input, threadId, runId, selectedAgentId, {
936
1009
  context: invocation.context,
@@ -965,7 +1038,8 @@ export class AgentHarnessRuntime {
965
1038
  let emitted = false;
966
1039
  let streamActivityObserved = false;
967
1040
  const { threadId, runId } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, options.threadId);
968
- await this.persistence.saveRunRequest(threadId, runId, this.buildPersistedRunRequest(options.input, invocation));
1041
+ const priority = this.normalizeRunPriority(options.priority);
1042
+ await this.persistence.saveRunRequest(threadId, runId, this.buildPersistedRunRequest(options.input, invocation, priority));
969
1043
  yield { type: "event", event: await this.emitRunCreated(threadId, runId, {
970
1044
  agentId: selectedAgentId,
971
1045
  requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
@@ -973,7 +1047,7 @@ export class AgentHarnessRuntime {
973
1047
  input: options.input,
974
1048
  state: "running",
975
1049
  }) };
976
- const releaseRunSlot = await this.acquireRunSlot(threadId, runId);
1050
+ const releaseRunSlot = await this.acquireRunSlot(threadId, runId, "running", priority);
977
1051
  try {
978
1052
  try {
979
1053
  const priorHistory = await this.loadPriorHistory(threadId, runId);
@@ -1229,7 +1303,7 @@ export class AgentHarnessRuntime {
1229
1303
  return this.finalizeCancelledRun(threadId, runId, thread.status, cancellation.reason);
1230
1304
  }
1231
1305
  await this.persistence.setRunState(threadId, runId, "resuming", `checkpoints/${threadId}/${runId}/cp-1`);
1232
- const releaseRunSlot = await this.acquireRunSlot(threadId, runId, "resuming");
1306
+ const releaseRunSlot = await this.acquireRunSlot(threadId, runId, "resuming", await this.resolvePersistedRunPriority(threadId, runId));
1233
1307
  try {
1234
1308
  await this.persistence.saveRecoveryIntent(threadId, runId, {
1235
1309
  kind: "approval-decision",
@@ -1410,7 +1484,7 @@ export class AgentHarnessRuntime {
1410
1484
  });
1411
1485
  continue;
1412
1486
  }
1413
- const releaseRunSlot = await this.acquireRunSlot(thread.threadId, thread.latestRunId);
1487
+ const releaseRunSlot = await this.acquireRunSlot(thread.threadId, thread.latestRunId, "running", this.normalizeRunPriority(request.priority));
1414
1488
  try {
1415
1489
  await this.executeQueuedRun(binding, request.input, thread.threadId, thread.latestRunId, runMeta.agentId, {
1416
1490
  context: request.invocation?.context,
@@ -1453,7 +1527,7 @@ export class AgentHarnessRuntime {
1453
1527
  await this.persistence.releaseRunClaim(thread.latestRunId);
1454
1528
  continue;
1455
1529
  }
1456
- const releaseRunSlot = await this.acquireRunSlot(thread.threadId, thread.latestRunId, "running");
1530
+ const releaseRunSlot = await this.acquireRunSlot(thread.threadId, thread.latestRunId, "running", this.normalizeRunPriority(request.priority));
1457
1531
  try {
1458
1532
  await this.emit(thread.threadId, thread.latestRunId, 100, "run.resumed", {
1459
1533
  resumeKind: "startup-running-recovery",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.83",
3
+ "version": "0.0.84",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",