@botbotgo/agent-harness 0.0.102 → 0.0.104

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.
@@ -22,8 +22,6 @@ export declare class AgentHarnessRuntime {
22
22
  private readonly threadMemorySync;
23
23
  private readonly unregisterThreadMemorySync;
24
24
  private readonly resolvedRuntimeAdapterOptions;
25
- private readonly checkpointMaintenance;
26
- private readonly runtimeRecordMaintenance;
27
25
  private readonly healthMonitor;
28
26
  private readonly recoveryConfig;
29
27
  private readonly concurrencyConfig;
@@ -9,12 +9,9 @@ import { getConcurrencyConfig, getRecoveryConfig, getRoutingDefaultAgentId, getR
9
9
  import { createHarnessEvent, inferRoutingBindings, renderRuntimeFailure, } from "./support/harness-support.js";
10
10
  import { ThreadMemorySync } from "./harness/system/thread-memory-sync.js";
11
11
  import { FileBackedStore } from "./harness/system/store.js";
12
- import { CheckpointMaintenanceLoop, discoverCheckpointMaintenanceTargets, readCheckpointMaintenanceConfig, } from "./maintenance/checkpoint-maintenance.js";
13
- import { RuntimeRecordMaintenanceLoop, discoverRuntimeRecordMaintenanceTargets, readRuntimeRecordMaintenanceConfig, } from "./maintenance/runtime-record-maintenance.js";
14
- import { HealthMonitor } from "./harness/system/health-monitor.js";
15
- import { readHealthMonitorConfig } from "./harness/system/health-monitor.js";
12
+ import { HealthMonitor, readHealthMonitorConfig, } from "./harness/system/health-monitor.js";
16
13
  import { extractMessageText, normalizeMessageContent } from "../utils/message-content.js";
17
- import { buildPersistedRunRequest, isTerminalRunState, normalizeInvocationEnvelope, normalizeRunPriority, resolveRunListeners, toPublicApprovalRecord, } from "./harness/run/helpers.js";
14
+ import { buildPersistedRunRequest, normalizeInvocationEnvelope, normalizeRunPriority, resolveRunListeners, } from "./harness/run/helpers.js";
18
15
  import { emitHarnessEvent, emitRunCreatedEvent, emitSyntheticFallbackEvent, requestApprovalAndEmitEvent, setRunStateAndEmitEvent, } from "./harness/events/events.js";
19
16
  import { appendAssistantMessage as appendLifecycleAssistantMessage, finalizeCancelledRun as finalizeLifecycleCancelledRun, finalizeContinuedRun as finalizeLifecycleContinuedRun, } from "./harness/run/run-lifecycle.js";
20
17
  import { dispatchRunListeners as dispatchStreamingRunListeners, } from "./harness/events/streaming.js";
@@ -29,8 +26,9 @@ import { getBindingAdapterKind, getBindingPrimaryTools, getBindingStoreConfig }
29
26
  import { isRuntimeEntryBinding } from "./support/runtime-entry.js";
30
27
  import { describeWorkspaceInventory, listAgentSkills as listWorkspaceAgentSkills, } from "./harness/system/inventory.js";
31
28
  import { createDefaultHealthSnapshot, isInventoryEnabled, isThreadMemorySyncEnabled, } from "./harness/runtime-defaults.js";
32
- import { recoverQueuedStartupRun, recoverResumingStartupRun, recoverRunningStartupRun, } from "./harness/run/recovery.js";
29
+ import { initializeHarnessRuntime, isStaleRunningRun as isHarnessStaleRunningRun, } from "./harness/run/startup-runtime.js";
33
30
  import { streamHarnessRun } from "./harness/run/stream-run.js";
31
+ import { deleteThreadRecord, getPublicApproval, getThreadRecord, listPublicApprovals, } from "./harness/run/thread-records.js";
34
32
  export class AgentHarnessRuntime {
35
33
  workspace;
36
34
  runtimeAdapterOptions;
@@ -57,8 +55,6 @@ export class AgentHarnessRuntime {
57
55
  threadMemorySync;
58
56
  unregisterThreadMemorySync;
59
57
  resolvedRuntimeAdapterOptions;
60
- checkpointMaintenance;
61
- runtimeRecordMaintenance;
62
58
  healthMonitor;
63
59
  recoveryConfig;
64
60
  concurrencyConfig;
@@ -126,14 +122,6 @@ export class AgentHarnessRuntime {
126
122
  this.threadMemorySync = null;
127
123
  this.unregisterThreadMemorySync = () => { };
128
124
  }
129
- const checkpointMaintenanceConfig = readCheckpointMaintenanceConfig(workspace);
130
- this.checkpointMaintenance = checkpointMaintenanceConfig
131
- ? new CheckpointMaintenanceLoop(discoverCheckpointMaintenanceTargets(workspace), checkpointMaintenanceConfig)
132
- : null;
133
- const runtimeRecordMaintenanceConfig = readRuntimeRecordMaintenanceConfig(workspace);
134
- this.runtimeRecordMaintenance = runtimeRecordMaintenanceConfig
135
- ? new RuntimeRecordMaintenanceLoop(discoverRuntimeRecordMaintenanceTargets(workspace), runtimeRecordMaintenanceConfig)
136
- : null;
137
125
  this.recoveryConfig = getRecoveryConfig(workspace.refs);
138
126
  this.concurrencyConfig = getConcurrencyConfig(workspace.refs);
139
127
  const healthConfig = readHealthMonitorConfig(workspace);
@@ -145,8 +133,8 @@ export class AgentHarnessRuntime {
145
133
  persistence: this.persistence,
146
134
  getActiveRunSlots: () => this.activeRunSlots,
147
135
  getPendingRunSlots: () => this.pendingRunSlots.length,
148
- getCheckpointMaintenanceStatus: () => this.checkpointMaintenance?.getStatus() ?? null,
149
- getRuntimeRecordMaintenanceStatus: () => this.runtimeRecordMaintenance?.getStatus() ?? null,
136
+ getCheckpointMaintenanceStatus: () => null,
137
+ getRuntimeRecordMaintenanceStatus: () => null,
150
138
  publishEvent: async (payload) => {
151
139
  this.eventBus.publish(createHarnessEvent("__runtime__", "__runtime__", ++this.runtimeEventSequence, "runtime.health.changed", payload));
152
140
  },
@@ -159,11 +147,10 @@ export class AgentHarnessRuntime {
159
147
  this.healthMonitor?.recordLlmFailure(Date.now() - startedAt);
160
148
  }
161
149
  async initialize() {
162
- await this.persistence.initialize();
163
- await this.checkpointMaintenance?.start();
164
- await this.runtimeRecordMaintenance?.start();
165
- await this.healthMonitor?.start();
166
- await this.recoverStartupRuns();
150
+ await initializeHarnessRuntime({
151
+ persistence: this.persistence,
152
+ healthMonitor: this.healthMonitor,
153
+ });
167
154
  }
168
155
  subscribe(listener) {
169
156
  return this.eventBus.subscribe(listener);
@@ -204,47 +191,20 @@ export class AgentHarnessRuntime {
204
191
  return this.persistence.getSession(threadId);
205
192
  }
206
193
  async getThread(threadId) {
207
- const [threadSummary, meta, messages, runs] = await Promise.all([
208
- this.getSession(threadId),
209
- this.persistence.getThreadMeta(threadId),
210
- this.persistence.listThreadMessages(threadId, 200),
211
- this.persistence.listThreadRuns(threadId),
212
- ]);
213
- if (!threadSummary || !meta) {
214
- return null;
215
- }
216
- const latestRunId = threadSummary.latestRunId;
217
- const latestApprovals = await this.persistence.getRunApprovals(threadId, latestRunId);
218
- const pendingApproval = latestApprovals
219
- .filter((approval) => approval.status === "pending")
220
- .sort((left, right) => right.requestedAt.localeCompare(left.requestedAt))[0];
221
- return {
222
- threadId,
223
- entryAgentId: meta.entryAgentId,
224
- currentState: threadSummary.status,
225
- latestRunId,
226
- createdAt: meta.createdAt,
227
- updatedAt: threadSummary.updatedAt,
228
- messages,
229
- runs,
230
- pendingDecision: pendingApproval
231
- ? {
232
- approvalId: pendingApproval.approvalId,
233
- pendingActionId: pendingApproval.pendingActionId,
234
- toolName: pendingApproval.toolName,
235
- allowedDecisions: pendingApproval.allowedDecisions,
236
- requestedAt: pendingApproval.requestedAt,
237
- }
238
- : undefined,
239
- };
194
+ return getThreadRecord({
195
+ persistence: this.persistence,
196
+ getSession: (currentThreadId) => this.getSession(currentThreadId),
197
+ }, threadId);
240
198
  }
241
199
  async listApprovals(filter) {
242
- const approvals = await this.persistence.listApprovals(filter);
243
- return approvals.map((approval) => toPublicApprovalRecord(approval));
200
+ return listPublicApprovals({
201
+ persistence: this.persistence,
202
+ }, filter);
244
203
  }
245
204
  async getApproval(approvalId) {
246
- const approval = await this.persistence.getApproval(approvalId);
247
- return approval ? toPublicApprovalRecord(approval) : null;
205
+ return getPublicApproval({
206
+ persistence: this.persistence,
207
+ }, approvalId);
248
208
  }
249
209
  listAgentSkills(agentId, options = {}) {
250
210
  return listWorkspaceAgentSkills(this.workspace, agentId, {
@@ -277,19 +237,11 @@ export class AgentHarnessRuntime {
277
237
  }
278
238
  }
279
239
  async deleteThread(threadId) {
280
- const thread = await this.getThread(threadId);
281
- if (!thread) {
282
- return false;
283
- }
284
- const activeRun = thread.runs.find((run) => !isTerminalRunState(run.state));
285
- if (activeRun) {
286
- throw new Error(`Cannot delete thread ${threadId} while run ${activeRun.runId} is ${activeRun.state}`);
287
- }
288
- const deleted = await this.persistence.deleteThread(threadId);
289
- if (deleted) {
290
- await this.deleteThreadCheckpoints(threadId);
291
- }
292
- return deleted;
240
+ return deleteThreadRecord({
241
+ getThread: (currentThreadId) => this.getThread(currentThreadId),
242
+ deleteThread: (currentThreadId) => this.persistence.deleteThread(currentThreadId),
243
+ deleteThreadCheckpoints: (currentThreadId) => this.deleteThreadCheckpoints(currentThreadId),
244
+ }, threadId);
293
245
  }
294
246
  async createToolMcpServer(options) {
295
247
  const tools = this.resolveAgentTools(options.agentId).map(({ compiledTool, resolvedTool }) => ({
@@ -752,8 +704,6 @@ export class AgentHarnessRuntime {
752
704
  }
753
705
  async close() {
754
706
  await this.healthMonitor?.stop();
755
- await this.checkpointMaintenance?.stop();
756
- await this.runtimeRecordMaintenance?.stop();
757
707
  this.unregisterThreadMemorySync();
758
708
  await Promise.allSettled(Array.from(this.backgroundTasks));
759
709
  await this.threadMemorySync?.close();
@@ -771,63 +721,15 @@ export class AgentHarnessRuntime {
771
721
  }, options);
772
722
  }
773
723
  async recoverStartupRuns() {
774
- if (!this.recoveryConfig.enabled) {
775
- return;
776
- }
777
- await this.reclaimExpiredClaimedRuns();
778
- const threads = await this.persistence.listSessions();
779
- const recoveryContext = this.createStartupRecoveryContext();
780
- for (const thread of threads) {
781
- const handled = await recoverQueuedStartupRun(recoveryContext, thread) ||
782
- await recoverRunningStartupRun(recoveryContext, thread) ||
783
- await recoverResumingStartupRun(recoveryContext, thread);
784
- if (handled) {
785
- continue;
786
- }
787
- }
724
+ return;
788
725
  }
789
- async reclaimExpiredClaimedRuns(nowIso = new Date().toISOString()) {
790
- const expiredClaims = await this.persistence.listExpiredClaimedRuns(nowIso);
791
- for (const claim of expiredClaims) {
792
- const thread = await this.persistence.getSession(claim.threadId);
793
- if (!thread) {
794
- await this.persistence.releaseRunClaim(claim.runId);
795
- continue;
796
- }
797
- const lifecycle = await this.persistence.getRunLifecycle(claim.threadId, claim.runId);
798
- if (lifecycle.state === "claimed") {
799
- await this.persistence.enqueueRun({
800
- threadId: claim.threadId,
801
- runId: claim.runId,
802
- priority: claim.priority,
803
- queueKey: claim.queueKey,
804
- availableAt: nowIso,
805
- });
806
- await this.setRunStateAndEmit(claim.threadId, claim.runId, 99, "queued", {
807
- previousState: "claimed",
808
- });
809
- await this.emit(claim.threadId, claim.runId, 100, "run.queued", {
810
- queuePosition: 0,
811
- activeRunCount: this.activeRunSlots,
812
- maxConcurrentRuns: this.concurrencyConfig.maxConcurrentRuns,
813
- recoveredOnStartup: true,
814
- reclaimReason: "expired-lease",
815
- });
816
- continue;
817
- }
818
- await this.persistence.releaseRunClaim(claim.runId);
819
- }
726
+ async reclaimExpiredClaimedRuns(_nowIso = new Date().toISOString()) {
727
+ return;
820
728
  }
821
729
  async isStaleRunningRun(thread, nowMs = Date.now()) {
822
- const control = await this.persistence.getRunControl(thread.latestRunId);
823
- const heartbeatAt = control?.heartbeatAt;
824
- if (!heartbeatAt) {
825
- return true;
826
- }
827
- const heartbeatAtMs = Date.parse(heartbeatAt);
828
- if (!Number.isFinite(heartbeatAtMs)) {
829
- return true;
830
- }
831
- return nowMs - heartbeatAtMs >= this.concurrencyConfig.heartbeatTimeoutMs;
730
+ return isHarnessStaleRunningRun({
731
+ persistence: this.persistence,
732
+ concurrencyConfig: this.concurrencyConfig,
733
+ }, thread, nowMs);
832
734
  }
833
735
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.102",
3
+ "version": "0.0.104",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",