@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 +2 -1
- package/dist/api.js +3 -0
- package/dist/contracts/types.d.ts +5 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/persistence/file-store.d.ts +32 -4
- package/dist/persistence/file-store.js +197 -6
- package/dist/persistence/sqlite-store.d.ts +32 -4
- package/dist/persistence/sqlite-store.js +174 -9
- package/dist/persistence/types.d.ts +64 -3
- package/dist/runtime/agent-runtime-adapter.d.ts +5 -0
- package/dist/runtime/agent-runtime-adapter.js +93 -19
- package/dist/runtime/harness.d.ts +9 -1
- package/dist/runtime/harness.js +267 -65
- package/dist/runtime/health-monitor.js +1 -1
- package/dist/runtime/runtime-record-maintenance.js +2 -0
- package/dist/workspace/object-loader.js +133 -7
- package/dist/workspace/support/workspace-ref-utils.d.ts +3 -0
- package/dist/workspace/support/workspace-ref-utils.js +30 -1
- package/package.json +2 -2
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.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.81";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
}
|