@botbotgo/agent-harness 0.0.85 → 0.0.87

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.
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.84";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.86";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.84";
1
+ export const AGENT_HARNESS_VERSION = "0.0.86";
@@ -15,6 +15,7 @@ export declare class SqlitePersistence implements RuntimePersistence {
15
15
  private threadDir;
16
16
  private runDir;
17
17
  private execute;
18
+ private executeTransaction;
18
19
  private selectOne;
19
20
  private selectAll;
20
21
  private mapThreadSummary;
@@ -29,6 +30,18 @@ export declare class SqlitePersistence implements RuntimePersistence {
29
30
  status: RunState;
30
31
  createdAt: string;
31
32
  }): Promise<void>;
33
+ bootstrapRun(input: {
34
+ threadId: string;
35
+ agentId: string;
36
+ runId: string;
37
+ status: RunState;
38
+ createdAt: string;
39
+ executionMode: string;
40
+ adapterKind?: string;
41
+ userMessage: TranscriptMessage;
42
+ runRequest: PersistedRunRequest;
43
+ createThread: boolean;
44
+ }): Promise<void>;
32
45
  createRun(input: {
33
46
  threadId: string;
34
47
  runId: string;
@@ -263,6 +263,31 @@ export class SqlitePersistence {
263
263
  }
264
264
  await client.execute(sql);
265
265
  }
266
+ async executeTransaction(steps) {
267
+ await this.ensureInitialized();
268
+ const client = await this.getClient();
269
+ await client.execute("BEGIN IMMEDIATE");
270
+ try {
271
+ for (const step of steps) {
272
+ if (step.args) {
273
+ await client.execute(step.sql, step.args);
274
+ }
275
+ else {
276
+ await client.execute(step.sql);
277
+ }
278
+ }
279
+ await client.execute("COMMIT");
280
+ }
281
+ catch (error) {
282
+ try {
283
+ await client.execute("ROLLBACK");
284
+ }
285
+ catch {
286
+ // Ignore rollback failures and preserve the original error.
287
+ }
288
+ throw error;
289
+ }
290
+ }
266
291
  async selectOne(sql, args) {
267
292
  await this.ensureInitialized();
268
293
  const client = await this.getClient();
@@ -347,6 +372,61 @@ export class SqlitePersistence {
347
372
  (thread_id, workspace_id, entry_agent_id, status, latest_run_id, created_at, updated_at)
348
373
  VALUES (?, ?, ?, ?, ?, ?, ?)`, [input.threadId, "default", input.agentId, input.status, input.runId, input.createdAt, input.createdAt]);
349
374
  }
375
+ async bootstrapRun(input) {
376
+ await mkdir(this.threadDir(input.threadId), { recursive: true });
377
+ await mkdir(path.join(this.runDir(input.threadId, input.runId), "events"), { recursive: true });
378
+ const steps = [];
379
+ if (input.createThread) {
380
+ steps.push({
381
+ sql: `INSERT OR REPLACE INTO threads
382
+ (thread_id, workspace_id, entry_agent_id, status, latest_run_id, created_at, updated_at)
383
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
384
+ args: [input.threadId, "default", input.agentId, input.status, input.runId, input.createdAt, input.createdAt],
385
+ });
386
+ }
387
+ steps.push({
388
+ sql: `INSERT OR REPLACE INTO runs
389
+ (run_id, thread_id, agent_id, execution_mode, adapter_kind, created_at, updated_at, state, previous_state, state_entered_at, last_transition_at, resumable, checkpoint_ref)
390
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
391
+ args: [
392
+ input.runId,
393
+ input.threadId,
394
+ input.agentId,
395
+ input.executionMode,
396
+ input.adapterKind ?? input.executionMode ?? null,
397
+ input.createdAt,
398
+ input.createdAt,
399
+ "running",
400
+ null,
401
+ input.createdAt,
402
+ input.createdAt,
403
+ 0,
404
+ null,
405
+ ],
406
+ }, {
407
+ sql: `INSERT OR REPLACE INTO run_control
408
+ (run_id, cancel_requested, cancel_reason, cancel_requested_at, heartbeat_at, worker_id, worker_started_at)
409
+ VALUES (?, 0, NULL, NULL, NULL, NULL, NULL)`,
410
+ args: [input.runId],
411
+ }, {
412
+ sql: `INSERT INTO thread_messages
413
+ (thread_id, role, content_json, run_id, created_at)
414
+ VALUES (?, ?, ?, ?, ?)`,
415
+ args: [
416
+ input.threadId,
417
+ input.userMessage.role,
418
+ JSON.stringify(input.userMessage.content),
419
+ input.userMessage.runId,
420
+ input.userMessage.createdAt,
421
+ ],
422
+ }, {
423
+ sql: `INSERT OR REPLACE INTO run_requests
424
+ (run_id, thread_id, request_json, saved_at)
425
+ VALUES (?, ?, ?, ?)`,
426
+ args: [input.runId, input.threadId, JSON.stringify(input.runRequest), input.runRequest.savedAt],
427
+ });
428
+ await this.executeTransaction(steps);
429
+ }
350
430
  async createRun(input) {
351
431
  await mkdir(path.join(this.runDir(input.threadId, input.runId), "events"), { recursive: true });
352
432
  await this.execute(`INSERT OR REPLACE INTO runs
@@ -75,6 +75,18 @@ export type ApprovalFilter = {
75
75
  };
76
76
  export interface RuntimePersistence {
77
77
  initialize(): Promise<void>;
78
+ bootstrapRun?(input: {
79
+ threadId: string;
80
+ agentId: string;
81
+ runId: string;
82
+ status: RunState;
83
+ createdAt: string;
84
+ executionMode: string;
85
+ adapterKind?: string;
86
+ userMessage: TranscriptMessage;
87
+ runRequest: PersistedRunRequest;
88
+ createThread: boolean;
89
+ }): Promise<void>;
78
90
  createThread(input: {
79
91
  threadId: string;
80
92
  agentId: string;
@@ -437,36 +437,54 @@ export class AgentHarnessRuntime {
437
437
  this.backgroundTasks.delete(task);
438
438
  });
439
439
  }
440
- async ensureThreadStarted(selectedAgentId, binding, input, existingThreadId) {
440
+ async ensureThreadStarted(selectedAgentId, binding, input, runRequest, existingThreadId) {
441
441
  const threadId = existingThreadId ?? createPersistentId();
442
442
  const runId = createPersistentId();
443
443
  const createdAt = new Date().toISOString();
444
444
  const isNewThread = !existingThreadId;
445
- if (isNewThread) {
446
- await this.persistence.createThread({
445
+ const userMessage = {
446
+ role: "user",
447
+ content: normalizeMessageContent(input),
448
+ runId,
449
+ createdAt,
450
+ };
451
+ if (typeof this.persistence.bootstrapRun === "function") {
452
+ await this.persistence.bootstrapRun({
447
453
  threadId,
448
- agentId: selectedAgentId,
454
+ agentId: binding.agent.id,
449
455
  runId,
450
456
  status: "running",
451
457
  createdAt,
452
- });
453
- }
454
- await Promise.all([
455
- this.persistence.appendThreadMessage(threadId, {
456
- role: "user",
457
- content: normalizeMessageContent(input),
458
- runId,
459
- createdAt,
460
- }),
461
- this.persistence.createRun({
462
- threadId,
463
- runId,
464
- agentId: binding.agent.id,
465
458
  executionMode: getBindingAdapterKind(binding),
466
459
  adapterKind: getBindingAdapterKind(binding),
467
- createdAt,
468
- }),
469
- ]);
460
+ userMessage,
461
+ runRequest,
462
+ createThread: isNewThread,
463
+ });
464
+ }
465
+ else {
466
+ if (isNewThread) {
467
+ await this.persistence.createThread({
468
+ threadId,
469
+ agentId: selectedAgentId,
470
+ runId,
471
+ status: "running",
472
+ createdAt,
473
+ });
474
+ }
475
+ await Promise.all([
476
+ this.persistence.appendThreadMessage(threadId, userMessage),
477
+ this.persistence.createRun({
478
+ threadId,
479
+ runId,
480
+ agentId: binding.agent.id,
481
+ executionMode: getBindingAdapterKind(binding),
482
+ adapterKind: getBindingAdapterKind(binding),
483
+ createdAt,
484
+ }),
485
+ this.persistence.saveRunRequest(threadId, runId, runRequest),
486
+ ]);
487
+ }
470
488
  return { threadId, runId, createdAt, isNewThread };
471
489
  }
472
490
  async loadPriorHistory(threadId, runId) {
@@ -1046,9 +1064,9 @@ export class AgentHarnessRuntime {
1046
1064
  if (!policyDecision.allowed) {
1047
1065
  throw new Error(`Policy evaluation blocked agent ${selectedAgentId}: ${policyDecision.reasons.join(", ")}`);
1048
1066
  }
1049
- const { threadId, runId, isNewThread } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, options.threadId);
1050
1067
  const priority = this.normalizeRunPriority(options.priority);
1051
- const runRequestPromise = this.persistence.saveRunRequest(threadId, runId, this.buildPersistedRunRequest(options.input, invocation, priority));
1068
+ const runRequest = this.buildPersistedRunRequest(options.input, invocation, priority);
1069
+ const { threadId, runId, isNewThread } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, runRequest, options.threadId);
1052
1070
  const runCreatedEventPromise = this.emitRunCreated(threadId, runId, {
1053
1071
  agentId: binding.agent.id,
1054
1072
  requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
@@ -1056,7 +1074,6 @@ export class AgentHarnessRuntime {
1056
1074
  executionMode: getBindingAdapterKind(binding),
1057
1075
  });
1058
1076
  const releaseRunSlotPromise = this.acquireRunSlot(threadId, runId, "running", priority);
1059
- await runRequestPromise;
1060
1077
  await runCreatedEventPromise;
1061
1078
  const releaseRunSlot = await releaseRunSlotPromise;
1062
1079
  try {
@@ -1093,10 +1110,10 @@ export class AgentHarnessRuntime {
1093
1110
  }
1094
1111
  let emitted = false;
1095
1112
  let streamActivityObserved = false;
1096
- const { threadId, runId, isNewThread } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, options.threadId);
1097
1113
  const priority = this.normalizeRunPriority(options.priority);
1114
+ const runRequest = this.buildPersistedRunRequest(options.input, invocation, priority);
1115
+ const { threadId, runId, isNewThread } = await this.ensureThreadStarted(selectedAgentId, binding, options.input, runRequest, options.threadId);
1098
1116
  const priorHistoryPromise = Promise.resolve(isNewThread ? [] : undefined).then((historyHint) => historyHint ?? this.loadPriorHistory(threadId, runId));
1099
- const runRequestPromise = this.persistence.saveRunRequest(threadId, runId, this.buildPersistedRunRequest(options.input, invocation, priority));
1100
1117
  const runCreatedEventPromise = this.emitRunCreated(threadId, runId, {
1101
1118
  agentId: selectedAgentId,
1102
1119
  requestedAgentId: options.agentId ?? AUTO_AGENT_ID,
@@ -1112,7 +1129,6 @@ export class AgentHarnessRuntime {
1112
1129
  const [priorHistory, acquiredReleaseRunSlot] = await Promise.all([
1113
1130
  priorHistoryPromise,
1114
1131
  releaseRunSlotPromise,
1115
- runRequestPromise,
1116
1132
  ]).then(([loadedPriorHistory, resolvedReleaseRunSlot]) => [loadedPriorHistory, resolvedReleaseRunSlot]);
1117
1133
  releaseRunSlot = acquiredReleaseRunSlot;
1118
1134
  let assistantOutput = "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.85",
3
+ "version": "0.0.87",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",