@botbotgo/agent-harness 0.0.80 → 0.0.82

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.
package/dist/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ApprovalRecord, RunRecord, RunOptions, RunSummary, ResumeOptions, RuntimeHealthSnapshot, RuntimeAdapterOptions, ThreadSummary, ThreadRecord, WorkspaceLoadOptions } from "./contracts/types.js";
1
+ import type { ApprovalRecord, CancelOptions, RunRecord, RunOptions, RunSummary, ResumeOptions, RuntimeHealthSnapshot, RuntimeAdapterOptions, ThreadSummary, ThreadRecord, WorkspaceLoadOptions } from "./contracts/types.js";
2
2
  import { AgentHarnessRuntime } from "./runtime/harness.js";
3
3
  import type { InventoryAgentRecord, InventorySkillRecord } from "./runtime/inventory.js";
4
4
  import type { RequirementAssessmentOptions } from "./runtime/skill-requirements.js";
@@ -30,6 +30,7 @@ export declare function describeInventory(runtime: AgentHarnessRuntime, options?
30
30
  agents: InventoryAgentRecord[];
31
31
  };
32
32
  export declare function resolveApproval(runtime: AgentHarnessRuntime, options: ResumeOptions): Promise<import("./contracts/types.js").RunResult>;
33
+ export declare function cancelRun(runtime: AgentHarnessRuntime, options: CancelOptions): Promise<import("./contracts/types.js").RunResult>;
33
34
  export declare function stop(runtime: AgentHarnessRuntime): Promise<void>;
34
35
  export declare function createToolMcpServer(runtime: AgentHarnessRuntime, options: ToolMcpServerOptions): Promise<import("@modelcontextprotocol/sdk/server/mcp.js").McpServer>;
35
36
  export declare function serveToolsOverStdio(runtime: AgentHarnessRuntime, options: ToolMcpServerOptions): Promise<import("@modelcontextprotocol/sdk/server/mcp.js").McpServer>;
package/dist/api.js CHANGED
@@ -46,6 +46,9 @@ export function describeInventory(runtime, options) {
46
46
  export async function resolveApproval(runtime, options) {
47
47
  return runtime.resume(options);
48
48
  }
49
+ export async function cancelRun(runtime, options) {
50
+ return runtime.cancelRun(options);
51
+ }
49
52
  export async function stop(runtime) {
50
53
  return runtime.stop();
51
54
  }
@@ -4,7 +4,7 @@ export type RuntimeCapabilities = {
4
4
  delegation?: boolean;
5
5
  memory?: boolean;
6
6
  };
7
- export type RunState = "queued" | "running" | "waiting_for_approval" | "resuming" | "completed" | "failed";
7
+ export type RunState = "queued" | "claimed" | "running" | "waiting_for_approval" | "resuming" | "cancelling" | "cancelled" | "completed" | "failed";
8
8
  export type ParsedAgentObject = {
9
9
  id: string;
10
10
  executionMode: ExecutionMode;
@@ -440,6 +440,10 @@ export type ResumeOptions = {
440
440
  decision?: "approve" | "edit" | "reject";
441
441
  editedInput?: Record<string, unknown>;
442
442
  };
443
+ export type CancelOptions = {
444
+ runId: string;
445
+ reason?: string;
446
+ };
443
447
  export type RestartConversationOptions = {
444
448
  threadId: string;
445
449
  mode: "restart-in-thread" | "restart-new-thread";
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { AgentHarnessRuntime, createAgentHarness, createToolMcpServer, deleteThread, describeInventory, getApproval, getHealth, getRun, getThread, listAgentSkills, listApprovals, listRuns, listThreads, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
1
+ export { AgentHarnessRuntime, cancelRun, createAgentHarness, createToolMcpServer, deleteThread, describeInventory, getApproval, getHealth, getRun, getThread, listAgentSkills, listApprovals, listRuns, listThreads, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
2
2
  export type { ToolMcpServerOptions } from "./mcp.js";
3
3
  export { tool } from "./tools.js";
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { AgentHarnessRuntime, createAgentHarness, createToolMcpServer, deleteThread, describeInventory, getApproval, getHealth, getRun, getThread, listAgentSkills, listApprovals, listRuns, listThreads, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
1
+ export { AgentHarnessRuntime, cancelRun, createAgentHarness, createToolMcpServer, deleteThread, describeInventory, getApproval, getHealth, getRun, getThread, listAgentSkills, listApprovals, listRuns, listThreads, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
2
2
  export { tool } from "./tools.js";
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.79";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.81";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.79";
1
+ export const AGENT_HARNESS_VERSION = "0.0.81";
@@ -1,5 +1,5 @@
1
1
  import type { ArtifactListing, ArtifactRecord, DelegationRecord, HarnessEvent, InternalApprovalRecord, RunSummary, RunState, ThreadSummary, ThreadRunRecord, TranscriptMessage } from "../contracts/types.js";
2
- import type { PersistedRunRequest, PersistenceLifecycle as Lifecycle, RuntimePersistence, RecoveryIntent, PersistenceRunMeta as RunMeta, PersistenceThreadMeta as ThreadMeta } from "./types.js";
2
+ import type { ApprovalFilter, PersistedRunRequest, PersistedRunControlRecord, PersistedRunQueueRecord, PersistenceLifecycle as Lifecycle, RuntimePersistence, RecoveryIntent, PersistenceRunMeta as RunMeta, RunSummaryFilter, ThreadSummaryFilter, PersistenceThreadMeta as ThreadMeta } from "./types.js";
3
3
  type RunIndexRecord = {
4
4
  runId: string;
5
5
  threadId: string;
@@ -14,6 +14,8 @@ export declare class FilePersistence implements RuntimePersistence {
14
14
  private runIndexPath;
15
15
  private approvalIndexPath;
16
16
  private delegationIndexPath;
17
+ private runQueuePath;
18
+ private runControlPath;
17
19
  initialize(): Promise<void>;
18
20
  threadDir(threadId: string): string;
19
21
  runDir(threadId: string, runId: string): string;
@@ -34,16 +36,16 @@ export declare class FilePersistence implements RuntimePersistence {
34
36
  }): Promise<void>;
35
37
  setRunState(threadId: string, runId: string, state: RunState, checkpointRef?: string | null): Promise<void>;
36
38
  appendEvent(event: HarnessEvent): Promise<void>;
37
- listSessions(): Promise<ThreadSummary[]>;
39
+ listSessions(filter?: ThreadSummaryFilter): Promise<ThreadSummary[]>;
38
40
  listRunIndexes(): Promise<RunIndexRecord[]>;
39
41
  private readRunSummary;
40
- listRuns(): Promise<RunSummary[]>;
42
+ listRuns(filter?: RunSummaryFilter): Promise<RunSummary[]>;
41
43
  getRun(runId: string): Promise<RunSummary | null>;
42
44
  getSession(threadId: string): Promise<ThreadSummary | null>;
43
45
  getThreadMeta(threadId: string): Promise<ThreadMeta | null>;
44
46
  listThreadRuns(threadId: string): Promise<ThreadRunRecord[]>;
45
47
  listRunEvents(threadId: string, runId: string): Promise<HarnessEvent[]>;
46
- listApprovals(): Promise<InternalApprovalRecord[]>;
48
+ listApprovals(filter?: ApprovalFilter): Promise<InternalApprovalRecord[]>;
47
49
  getApproval(approvalId: string): Promise<InternalApprovalRecord | null>;
48
50
  getRunApprovals(threadId: string, runId: string): Promise<InternalApprovalRecord[]>;
49
51
  getRunMeta(threadId: string, runId: string): Promise<RunMeta>;
@@ -64,5 +66,31 @@ export declare class FilePersistence implements RuntimePersistence {
64
66
  saveRecoveryIntent(threadId: string, runId: string, intent: RecoveryIntent): Promise<void>;
65
67
  getRecoveryIntent(threadId: string, runId: string): Promise<RecoveryIntent | null>;
66
68
  clearRecoveryIntent(threadId: string, runId: string): Promise<void>;
69
+ enqueueRun(input: {
70
+ threadId: string;
71
+ runId: string;
72
+ priority?: number;
73
+ queueKey?: string | null;
74
+ availableAt?: string;
75
+ }): Promise<void>;
76
+ getQueuedRun(runId: string): Promise<PersistedRunQueueRecord | null>;
77
+ claimQueuedRun(input: {
78
+ threadId: string;
79
+ runId: string;
80
+ workerId: string;
81
+ claimedAt?: string;
82
+ leaseExpiresAt: string;
83
+ }): Promise<PersistedRunQueueRecord>;
84
+ renewRunLease(input: {
85
+ runId: string;
86
+ workerId: string;
87
+ heartbeatAt?: string;
88
+ leaseExpiresAt: string;
89
+ }): Promise<void>;
90
+ releaseRunClaim(runId: string): Promise<void>;
91
+ listExpiredClaimedRuns(cutoffIso: string): Promise<PersistedRunQueueRecord[]>;
92
+ getRunControl(runId: string): Promise<PersistedRunControlRecord | null>;
93
+ requestRunCancel(runId: string, reason?: string): Promise<PersistedRunControlRecord>;
94
+ clearRunCancel(runId: string): Promise<void>;
67
95
  }
68
96
  export {};
@@ -22,12 +22,20 @@ export class FilePersistence {
22
22
  delegationIndexPath(delegationId) {
23
23
  return path.join(this.runRoot, "indexes", "delegations", `${delegationId}.json`);
24
24
  }
25
+ runQueuePath(runId) {
26
+ return path.join(this.runRoot, "indexes", "queue", `${runId}.json`);
27
+ }
28
+ runControlPath(runId) {
29
+ return path.join(this.runRoot, "indexes", "run-control", `${runId}.json`);
30
+ }
25
31
  async initialize() {
26
32
  await Promise.all([
27
33
  "indexes/threads",
28
34
  "indexes/runs",
29
35
  "indexes/approvals",
30
36
  "indexes/delegations",
37
+ "indexes/queue",
38
+ "indexes/run-control",
31
39
  "threads",
32
40
  ].map((segment) => ensureDir(path.join(this.runRoot, segment))));
33
41
  }
@@ -107,6 +115,15 @@ export class FilePersistence {
107
115
  resumable: false,
108
116
  updatedAt: input.createdAt,
109
117
  }),
118
+ writeJson(this.runControlPath(input.runId), {
119
+ runId: input.runId,
120
+ cancelRequested: false,
121
+ cancelReason: null,
122
+ cancelRequestedAt: null,
123
+ heartbeatAt: null,
124
+ workerId: null,
125
+ workerStartedAt: null,
126
+ }),
110
127
  ]);
111
128
  }
112
129
  async setRunState(threadId, runId, state, checkpointRef) {
@@ -165,7 +182,7 @@ export class FilePersistence {
165
182
  const sequenceId = String(event.sequence).padStart(6, "0");
166
183
  await writeJson(path.join(this.runDir(event.threadId, event.runId), "events", `${sequenceId}.json`), event);
167
184
  }
168
- async listSessions() {
185
+ async listSessions(filter = {}) {
169
186
  const threadIndexDir = path.join(this.runRoot, "indexes", "threads");
170
187
  if (!(await fileExists(threadIndexDir))) {
171
188
  return [];
@@ -183,7 +200,9 @@ export class FilePersistence {
183
200
  status: index.status,
184
201
  };
185
202
  }));
186
- return records.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
203
+ return records
204
+ .filter((record) => !filter.agentId || record.agentId === filter.agentId)
205
+ .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
187
206
  }
188
207
  async listRunIndexes() {
189
208
  const runIndexDir = path.join(this.runRoot, "indexes", "runs");
@@ -212,10 +231,23 @@ export class FilePersistence {
212
231
  resumable: lifecycle.resumable,
213
232
  };
214
233
  }
215
- async listRuns() {
234
+ async listRuns(filter = {}) {
216
235
  const indexes = await this.listRunIndexes();
217
236
  const runs = await Promise.all(indexes.map((record) => this.readRunSummary(record.threadId, record.runId)));
218
- return runs.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
237
+ return runs
238
+ .filter((run) => {
239
+ if (filter.agentId && run.agentId !== filter.agentId) {
240
+ return false;
241
+ }
242
+ if (filter.threadId && run.threadId !== filter.threadId) {
243
+ return false;
244
+ }
245
+ if (filter.state && run.state !== filter.state) {
246
+ return false;
247
+ }
248
+ return true;
249
+ })
250
+ .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
219
251
  }
220
252
  async getRun(runId) {
221
253
  const indexPath = this.runIndexPath(runId);
@@ -267,13 +299,25 @@ export class FilePersistence {
267
299
  const entries = (await readdir(eventsDir)).sort();
268
300
  return Promise.all(entries.map((entry) => readJson(path.join(eventsDir, entry))));
269
301
  }
270
- async listApprovals() {
302
+ async listApprovals(filter = {}) {
271
303
  const approvalsDir = path.join(this.runRoot, "indexes", "approvals");
272
304
  if (!(await fileExists(approvalsDir))) {
273
305
  return [];
274
306
  }
275
307
  const entries = (await readdir(approvalsDir)).sort();
276
- return Promise.all(entries.map((entry) => readJson(path.join(approvalsDir, entry))));
308
+ const approvals = await Promise.all(entries.map((entry) => readJson(path.join(approvalsDir, entry))));
309
+ return approvals.filter((approval) => {
310
+ if (filter.status && approval.status !== filter.status) {
311
+ return false;
312
+ }
313
+ if (filter.threadId && approval.threadId !== filter.threadId) {
314
+ return false;
315
+ }
316
+ if (filter.runId && approval.runId !== filter.runId) {
317
+ return false;
318
+ }
319
+ return true;
320
+ });
277
321
  }
278
322
  async getApproval(approvalId) {
279
323
  const approvalPath = path.join(this.runRoot, "indexes", "approvals", `${approvalId}.json`);
@@ -314,6 +358,9 @@ export class FilePersistence {
314
358
  ...approvals
315
359
  .filter((record) => record.threadId === threadId)
316
360
  .map((record) => rm(this.approvalIndexPath(record.approvalId), { force: true })),
361
+ ...runIndexes
362
+ .filter((record) => record.threadId === threadId)
363
+ .flatMap((record) => [rm(this.runQueuePath(record.runId), { force: true }), rm(this.runControlPath(record.runId), { force: true })]),
317
364
  ...delegations
318
365
  .filter((record) => record.threadId === threadId)
319
366
  .map((record) => rm(this.delegationIndexPath(record.delegationId), { force: true })),
@@ -427,4 +474,148 @@ export class FilePersistence {
427
474
  }
428
475
  await rm(intentPath, { force: true });
429
476
  }
477
+ async enqueueRun(input) {
478
+ const current = (await this.getQueuedRun(input.runId)) ?? {
479
+ runId: input.runId,
480
+ threadId: input.threadId,
481
+ priority: input.priority ?? 0,
482
+ queueKey: input.queueKey ?? null,
483
+ enqueuedAt: nowIso(),
484
+ availableAt: input.availableAt ?? nowIso(),
485
+ claimedBy: null,
486
+ claimedAt: null,
487
+ leaseExpiresAt: null,
488
+ attemptCount: 0,
489
+ lastError: null,
490
+ };
491
+ await writeJson(this.runQueuePath(input.runId), {
492
+ ...current,
493
+ threadId: input.threadId,
494
+ priority: input.priority ?? current.priority,
495
+ queueKey: input.queueKey ?? current.queueKey,
496
+ availableAt: input.availableAt ?? current.availableAt,
497
+ claimedBy: null,
498
+ claimedAt: null,
499
+ leaseExpiresAt: null,
500
+ lastError: null,
501
+ });
502
+ }
503
+ async getQueuedRun(runId) {
504
+ const filePath = this.runQueuePath(runId);
505
+ if (!(await fileExists(filePath))) {
506
+ return null;
507
+ }
508
+ return readJson(filePath);
509
+ }
510
+ async claimQueuedRun(input) {
511
+ const current = await this.getQueuedRun(input.runId);
512
+ if (!current) {
513
+ throw new Error(`Missing queued run ${input.runId}`);
514
+ }
515
+ const claimedAt = input.claimedAt ?? nowIso();
516
+ const next = {
517
+ ...current,
518
+ threadId: input.threadId,
519
+ claimedBy: input.workerId,
520
+ claimedAt,
521
+ leaseExpiresAt: input.leaseExpiresAt,
522
+ attemptCount: current.attemptCount + 1,
523
+ };
524
+ await writeJson(this.runQueuePath(input.runId), next);
525
+ await writeJson(this.runControlPath(input.runId), {
526
+ ...(await this.getRunControl(input.runId) ?? {
527
+ runId: input.runId,
528
+ cancelRequested: false,
529
+ cancelReason: null,
530
+ cancelRequestedAt: null,
531
+ heartbeatAt: null,
532
+ workerId: null,
533
+ workerStartedAt: null,
534
+ }),
535
+ heartbeatAt: claimedAt,
536
+ workerId: input.workerId,
537
+ workerStartedAt: claimedAt,
538
+ });
539
+ return next;
540
+ }
541
+ async renewRunLease(input) {
542
+ const current = await this.getQueuedRun(input.runId);
543
+ if (current) {
544
+ await writeJson(this.runQueuePath(input.runId), {
545
+ ...current,
546
+ claimedBy: input.workerId,
547
+ claimedAt: current.claimedAt ?? input.heartbeatAt ?? nowIso(),
548
+ leaseExpiresAt: input.leaseExpiresAt,
549
+ });
550
+ }
551
+ await writeJson(this.runControlPath(input.runId), {
552
+ ...(await this.getRunControl(input.runId) ?? {
553
+ runId: input.runId,
554
+ cancelRequested: false,
555
+ cancelReason: null,
556
+ cancelRequestedAt: null,
557
+ heartbeatAt: null,
558
+ workerId: null,
559
+ workerStartedAt: null,
560
+ }),
561
+ heartbeatAt: input.heartbeatAt ?? nowIso(),
562
+ workerId: input.workerId,
563
+ workerStartedAt: (await this.getRunControl(input.runId))?.workerStartedAt ?? input.heartbeatAt ?? nowIso(),
564
+ });
565
+ }
566
+ async releaseRunClaim(runId) {
567
+ await rm(this.runQueuePath(runId), { force: true });
568
+ const current = await this.getRunControl(runId);
569
+ if (!current) {
570
+ return;
571
+ }
572
+ await writeJson(this.runControlPath(runId), {
573
+ ...current,
574
+ heartbeatAt: null,
575
+ workerId: null,
576
+ workerStartedAt: null,
577
+ });
578
+ }
579
+ async listExpiredClaimedRuns(cutoffIso) {
580
+ const queueDir = path.join(this.runRoot, "indexes", "queue");
581
+ if (!(await fileExists(queueDir))) {
582
+ return [];
583
+ }
584
+ const entries = (await readdir(queueDir)).sort();
585
+ const records = await Promise.all(entries.map((entry) => readJson(path.join(queueDir, entry))));
586
+ return records.filter((record) => record.claimedBy && record.leaseExpiresAt && record.leaseExpiresAt <= cutoffIso);
587
+ }
588
+ async getRunControl(runId) {
589
+ const filePath = this.runControlPath(runId);
590
+ if (!(await fileExists(filePath))) {
591
+ return null;
592
+ }
593
+ return readJson(filePath);
594
+ }
595
+ async requestRunCancel(runId, reason) {
596
+ const current = await this.getRunControl(runId);
597
+ if (!current) {
598
+ throw new Error(`Missing run control for ${runId}`);
599
+ }
600
+ const updated = {
601
+ ...current,
602
+ cancelRequested: true,
603
+ cancelReason: reason ?? null,
604
+ cancelRequestedAt: nowIso(),
605
+ };
606
+ await writeJson(this.runControlPath(runId), updated);
607
+ return updated;
608
+ }
609
+ async clearRunCancel(runId) {
610
+ const current = await this.getRunControl(runId);
611
+ if (!current) {
612
+ return;
613
+ }
614
+ await writeJson(this.runControlPath(runId), {
615
+ ...current,
616
+ cancelRequested: false,
617
+ cancelReason: null,
618
+ cancelRequestedAt: null,
619
+ });
620
+ }
430
621
  }
@@ -1,5 +1,5 @@
1
1
  import type { ArtifactListing, ArtifactRecord, HarnessEvent, InternalApprovalRecord, RunState, RunSummary, ThreadRunRecord, ThreadSummary, TranscriptMessage } from "../contracts/types.js";
2
- import type { PersistedRunRequest, PersistenceLifecycle, PersistenceRunMeta, PersistenceThreadMeta, RecoveryIntent, RuntimePersistence } from "./types.js";
2
+ import type { PersistedRunRequest, PersistedRunControlRecord, PersistedRunQueueRecord, PersistenceLifecycle, PersistenceRunMeta, PersistenceThreadMeta, RecoveryIntent, RuntimePersistence, ApprovalFilter, RunSummaryFilter, ThreadSummaryFilter } from "./types.js";
3
3
  export declare function listProtectedCheckpointThreadIds(dbPath: string): Promise<Set<string>>;
4
4
  export declare class SqlitePersistence implements RuntimePersistence {
5
5
  private readonly runRoot;
@@ -39,14 +39,14 @@ export declare class SqlitePersistence implements RuntimePersistence {
39
39
  }): Promise<void>;
40
40
  setRunState(threadId: string, runId: string, state: RunState, checkpointRef?: string | null): Promise<void>;
41
41
  appendEvent(event: HarnessEvent): Promise<void>;
42
- listSessions(): Promise<ThreadSummary[]>;
43
- listRuns(): Promise<RunSummary[]>;
42
+ listSessions(filter?: ThreadSummaryFilter): Promise<ThreadSummary[]>;
43
+ listRuns(filter?: RunSummaryFilter): Promise<RunSummary[]>;
44
44
  getRun(runId: string): Promise<RunSummary | null>;
45
45
  getSession(threadId: string): Promise<ThreadSummary | null>;
46
46
  getThreadMeta(threadId: string): Promise<PersistenceThreadMeta | null>;
47
47
  listThreadRuns(threadId: string): Promise<ThreadRunRecord[]>;
48
48
  listRunEvents(threadId: string, runId: string): Promise<HarnessEvent[]>;
49
- listApprovals(): Promise<InternalApprovalRecord[]>;
49
+ listApprovals(filter?: ApprovalFilter): Promise<InternalApprovalRecord[]>;
50
50
  getApproval(approvalId: string): Promise<InternalApprovalRecord | null>;
51
51
  getRunApprovals(threadId: string, runId: string): Promise<InternalApprovalRecord[]>;
52
52
  getRunMeta(threadId: string, runId: string): Promise<PersistenceRunMeta>;
@@ -64,5 +64,33 @@ export declare class SqlitePersistence implements RuntimePersistence {
64
64
  saveRecoveryIntent(threadId: string, runId: string, intent: RecoveryIntent): Promise<void>;
65
65
  getRecoveryIntent(threadId: string, runId: string): Promise<RecoveryIntent | null>;
66
66
  clearRecoveryIntent(threadId: string, runId: string): Promise<void>;
67
+ private mapQueuedRun;
68
+ private mapRunControl;
69
+ enqueueRun(input: {
70
+ threadId: string;
71
+ runId: string;
72
+ priority?: number;
73
+ queueKey?: string | null;
74
+ availableAt?: string;
75
+ }): Promise<void>;
76
+ getQueuedRun(runId: string): Promise<PersistedRunQueueRecord | null>;
77
+ claimQueuedRun(input: {
78
+ threadId: string;
79
+ runId: string;
80
+ workerId: string;
81
+ claimedAt?: string;
82
+ leaseExpiresAt: string;
83
+ }): Promise<PersistedRunQueueRecord>;
84
+ renewRunLease(input: {
85
+ runId: string;
86
+ workerId: string;
87
+ heartbeatAt?: string;
88
+ leaseExpiresAt: string;
89
+ }): Promise<void>;
90
+ releaseRunClaim(runId: string): Promise<void>;
91
+ listExpiredClaimedRuns(cutoffIso: string): Promise<PersistedRunQueueRecord[]>;
92
+ getRunControl(runId: string): Promise<PersistedRunControlRecord | null>;
93
+ requestRunCancel(runId: string, reason?: string): Promise<PersistedRunControlRecord>;
94
+ clearRunCancel(runId: string): Promise<void>;
67
95
  readArtifact(threadId: string, runId: string, artifactPath: string): Promise<unknown>;
68
96
  }