@botbotgo/agent-harness 0.0.290 → 0.0.291
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/README.md +20 -20
- package/README.zh.md +14 -14
- package/dist/acp.d.ts +5 -5
- package/dist/acp.js +3 -3
- package/dist/api.d.ts +20 -21
- package/dist/api.js +38 -50
- package/dist/cli.js +47 -43
- package/dist/config/agents/orchestra.yaml +3 -3
- package/dist/config/knowledge/knowledge-runtime.yaml +4 -4
- package/dist/config/runtime/runtime-memory.yaml +7 -7
- package/dist/config/runtime/workspace.yaml +7 -7
- package/dist/contracts/core.d.ts +1 -1
- package/dist/contracts/runtime.d.ts +35 -40
- package/dist/contracts/workspace.d.ts +2 -2
- package/dist/flow/build-flow-graph.js +20 -33
- package/dist/flow/export-sequence-mermaid.js +4 -4
- package/dist/flow/types.d.ts +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/init-project.js +10 -10
- package/dist/knowledge/module.js +37 -45
- package/dist/mcp.d.ts +9 -9
- package/dist/mcp.js +6 -6
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/persistence/file-store.d.ts +69 -69
- package/dist/persistence/file-store.js +224 -221
- package/dist/persistence/sqlite-request-context-store.d.ts +22 -0
- package/dist/persistence/sqlite-request-context-store.js +64 -0
- package/dist/persistence/sqlite-request-queue-store.d.ts +41 -0
- package/dist/persistence/sqlite-request-queue-store.js +120 -0
- package/dist/persistence/sqlite-store.d.ts +72 -72
- package/dist/persistence/sqlite-store.js +361 -361
- package/dist/persistence/types.d.ts +84 -84
- package/dist/protocol/a2a/http.js +79 -74
- package/dist/protocol/ag-ui/http.d.ts +7 -7
- package/dist/protocol/ag-ui/http.js +20 -20
- package/dist/resource/resource-impl.js +1 -1
- package/dist/runtime/adapter/compat/deepagent-compat.d.ts +2 -2
- package/dist/runtime/adapter/flow/invocation-flow.d.ts +6 -5
- package/dist/runtime/adapter/flow/invocation-flow.js +6 -5
- package/dist/runtime/adapter/flow/stream-runtime.d.ts +3 -3
- package/dist/runtime/adapter/flow/stream-runtime.js +5 -4
- package/dist/runtime/adapter/invocation-result.d.ts +6 -5
- package/dist/runtime/adapter/invocation-result.js +5 -4
- package/dist/runtime/adapter/middleware-assembly.js +3 -2
- package/dist/runtime/adapter/tool/tool-hitl.js +1 -1
- package/dist/runtime/adapter/upstream-configurable-keys.d.ts +2 -0
- package/dist/runtime/adapter/upstream-configurable-keys.js +2 -0
- package/dist/runtime/agent-runtime-adapter.d.ts +11 -8
- package/dist/runtime/agent-runtime-adapter.js +36 -32
- package/dist/runtime/harness/events/events.d.ts +8 -8
- package/dist/runtime/harness/events/events.js +25 -19
- package/dist/runtime/harness/events/listener-runtime.d.ts +5 -4
- package/dist/runtime/harness/events/listener-runtime.js +7 -3
- package/dist/runtime/harness/events/runtime-event-operations.d.ts +7 -7
- package/dist/runtime/harness/events/runtime-event-operations.js +5 -5
- package/dist/runtime/harness/events/streaming.d.ts +8 -7
- package/dist/runtime/harness/events/streaming.js +20 -19
- package/dist/runtime/harness/events/timeline.js +6 -6
- package/dist/runtime/harness/index.d.ts +1 -1
- package/dist/runtime/harness/index.js +1 -1
- package/dist/runtime/harness/run/helpers.d.ts +14 -11
- package/dist/runtime/harness/run/helpers.js +10 -7
- package/dist/runtime/harness/run/inspection.d.ts +3 -2
- package/dist/runtime/harness/run/inspection.js +7 -7
- package/dist/runtime/harness/run/operator-overview.d.ts +2 -2
- package/dist/runtime/harness/run/operator-overview.js +18 -17
- package/dist/runtime/harness/run/queue-diagnostics.js +6 -6
- package/dist/runtime/harness/run/recovery.d.ts +15 -15
- package/dist/runtime/harness/run/recovery.js +53 -50
- package/dist/runtime/harness/run/resources.d.ts +2 -2
- package/dist/runtime/harness/run/resources.js +8 -8
- package/dist/runtime/harness/run/resume.d.ts +3 -3
- package/dist/runtime/harness/run/resume.js +4 -4
- package/dist/runtime/harness/run/routing.d.ts +4 -4
- package/dist/runtime/harness/run/routing.js +8 -8
- package/dist/runtime/harness/run/run-lifecycle.d.ts +12 -12
- package/dist/runtime/harness/run/run-lifecycle.js +26 -26
- package/dist/runtime/harness/run/run-operations.d.ts +45 -45
- package/dist/runtime/harness/run/run-operations.js +79 -78
- package/dist/runtime/harness/run/run-queue.d.ts +8 -8
- package/dist/runtime/harness/run/run-queue.js +16 -16
- package/dist/runtime/harness/run/run-slot-acquisition.d.ts +32 -32
- package/dist/runtime/harness/run/run-slot-acquisition.js +41 -41
- package/dist/runtime/harness/run/{thread-records.d.ts → session-records.d.ts} +6 -13
- package/dist/runtime/harness/run/{thread-records.js → session-records.js} +14 -60
- package/dist/runtime/harness/run/start-run.d.ts +36 -36
- package/dist/runtime/harness/run/start-run.js +55 -36
- package/dist/runtime/harness/run/startup-runtime.d.ts +9 -9
- package/dist/runtime/harness/run/startup-runtime.js +22 -20
- package/dist/runtime/harness/run/stream-run.d.ts +18 -18
- package/dist/runtime/harness/run/stream-run.js +52 -52
- package/dist/runtime/harness/runtime-defaults.d.ts +2 -2
- package/dist/runtime/harness/runtime-defaults.js +7 -7
- package/dist/runtime/harness/system/health-monitor.d.ts +3 -3
- package/dist/runtime/harness/system/health-monitor.js +18 -18
- package/dist/runtime/harness/system/mem0-ingestion-sync.d.ts +6 -6
- package/dist/runtime/harness/system/mem0-ingestion-sync.js +36 -27
- package/dist/runtime/harness/system/runtime-memory-candidates.js +2 -2
- package/dist/runtime/harness/system/runtime-memory-manager.d.ts +13 -13
- package/dist/runtime/harness/system/runtime-memory-manager.js +41 -38
- package/dist/runtime/harness/system/runtime-memory-policy.d.ts +1 -1
- package/dist/runtime/harness/system/runtime-memory-policy.js +1 -1
- package/dist/runtime/harness/system/runtime-memory-records.d.ts +4 -2
- package/dist/runtime/harness/system/runtime-memory-records.js +21 -8
- package/dist/runtime/harness/system/runtime-memory-sync.d.ts +6 -6
- package/dist/runtime/harness/system/runtime-memory-sync.js +47 -44
- package/dist/runtime/harness/system/{thread-memory-sync.d.ts → session-memory-sync.d.ts} +7 -7
- package/dist/runtime/harness/system/{thread-memory-sync.js → session-memory-sync.js} +28 -28
- package/dist/runtime/harness.d.ts +33 -45
- package/dist/runtime/harness.js +273 -291
- package/dist/runtime/maintenance/checkpoint-maintenance.js +2 -2
- package/dist/runtime/maintenance/file-checkpoint-saver.d.ts +1 -1
- package/dist/runtime/maintenance/file-checkpoint-saver.js +6 -6
- package/dist/runtime/maintenance/runtime-record-maintenance.d.ts +1 -1
- package/dist/runtime/maintenance/runtime-record-maintenance.js +33 -33
- package/dist/runtime/maintenance/sqlite-checkpoint-saver.d.ts +1 -1
- package/dist/runtime/maintenance/sqlite-checkpoint-saver.js +30 -10
- package/dist/runtime/support/harness-support.d.ts +2 -2
- package/dist/runtime/support/harness-support.js +7 -7
- package/dist/runtime/support/runtime-adapter-options.d.ts +2 -2
- package/dist/runtime/support/runtime-adapter-options.js +3 -3
- package/dist/runtime/support/runtime-factories.d.ts +2 -2
- package/dist/runtime/support/runtime-factories.js +10 -10
- package/dist/workspace/agent-binding-compiler.js +3 -3
- package/dist/workspace/object-loader.js +1 -1
- package/dist/workspace/support/workspace-ref-utils.d.ts +4 -3
- package/dist/workspace/support/workspace-ref-utils.js +5 -4
- package/package.json +1 -1
- package/dist/persistence/sqlite-run-context-store.d.ts +0 -22
- package/dist/persistence/sqlite-run-context-store.js +0 -64
- package/dist/persistence/sqlite-run-queue-store.d.ts +0 -41
- package/dist/persistence/sqlite-run-queue-store.js +0 -120
|
@@ -6,30 +6,30 @@ function nowIso() {
|
|
|
6
6
|
return new Date(Date.now()).toISOString();
|
|
7
7
|
}
|
|
8
8
|
export class FilePersistence {
|
|
9
|
-
|
|
10
|
-
constructor(
|
|
11
|
-
this.
|
|
9
|
+
runtimeRoot;
|
|
10
|
+
constructor(runtimeRoot) {
|
|
11
|
+
this.runtimeRoot = runtimeRoot;
|
|
12
12
|
}
|
|
13
13
|
artifactsRoot() {
|
|
14
|
-
return resolveRuntimeArtifactsRoot(this.
|
|
14
|
+
return resolveRuntimeArtifactsRoot(this.runtimeRoot);
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
return path.join(this.artifactsRoot(), "indexes", "sessions", `${
|
|
16
|
+
sessionIndexPath(sessionId) {
|
|
17
|
+
return path.join(this.artifactsRoot(), "indexes", "sessions", `${sessionId}.json`);
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
return path.join(this.artifactsRoot(), "indexes", "requests", `${
|
|
19
|
+
requestIndexPath(requestId) {
|
|
20
|
+
return path.join(this.artifactsRoot(), "indexes", "requests", `${requestId}.json`);
|
|
21
21
|
}
|
|
22
22
|
approvalIndexPath(approvalId) {
|
|
23
23
|
return path.join(this.artifactsRoot(), "indexes", "approvals", `${approvalId}.json`);
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
return path.join(this.artifactsRoot(), "indexes", "requests-queue", `${
|
|
25
|
+
requestQueuePath(requestId) {
|
|
26
|
+
return path.join(this.artifactsRoot(), "indexes", "requests-queue", `${requestId}.json`);
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
return path.join(this.artifactsRoot(), "indexes", "requests-control", `${
|
|
28
|
+
requestControlPath(requestId) {
|
|
29
|
+
return path.join(this.artifactsRoot(), "indexes", "requests-control", `${requestId}.json`);
|
|
30
30
|
}
|
|
31
|
-
traceItemsPath(
|
|
32
|
-
return path.join(this.
|
|
31
|
+
traceItemsPath(sessionId, requestId) {
|
|
32
|
+
return path.join(this.requestDir(sessionId, requestId), "trace-items.ndjson");
|
|
33
33
|
}
|
|
34
34
|
async initialize() {
|
|
35
35
|
await Promise.all([
|
|
@@ -41,44 +41,44 @@ export class FilePersistence {
|
|
|
41
41
|
"sessions",
|
|
42
42
|
].map((segment) => ensureDir(path.join(this.artifactsRoot(), segment))));
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
return path.join(this.artifactsRoot(), "sessions",
|
|
44
|
+
sessionDir(sessionId) {
|
|
45
|
+
return path.join(this.artifactsRoot(), "sessions", sessionId);
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
return path.join(this.
|
|
47
|
+
requestDir(sessionId, requestId) {
|
|
48
|
+
return path.join(this.sessionDir(sessionId), "requests", requestId);
|
|
49
49
|
}
|
|
50
|
-
async
|
|
50
|
+
async createSession(input) {
|
|
51
51
|
const meta = {
|
|
52
|
-
|
|
52
|
+
sessionId: input.sessionId,
|
|
53
53
|
workspaceId: "default",
|
|
54
54
|
entryAgentId: input.agentId,
|
|
55
55
|
createdAt: input.createdAt,
|
|
56
56
|
updatedAt: input.createdAt,
|
|
57
57
|
status: input.status,
|
|
58
|
-
|
|
58
|
+
latestRequestId: input.requestId,
|
|
59
59
|
};
|
|
60
|
-
await writeJson(path.join(this.
|
|
61
|
-
await writeJson(path.join(this.
|
|
62
|
-
|
|
60
|
+
await writeJson(path.join(this.sessionDir(input.sessionId), "meta.json"), meta);
|
|
61
|
+
await writeJson(path.join(this.sessionDir(input.sessionId), "messages.json"), {
|
|
62
|
+
sessionId: input.sessionId,
|
|
63
63
|
items: [],
|
|
64
64
|
});
|
|
65
|
-
await writeJson(this.
|
|
66
|
-
|
|
65
|
+
await writeJson(this.sessionIndexPath(input.sessionId), {
|
|
66
|
+
sessionId: input.sessionId,
|
|
67
67
|
status: input.status,
|
|
68
|
-
|
|
68
|
+
latestRequestId: input.requestId,
|
|
69
69
|
updatedAt: input.createdAt,
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
|
-
async
|
|
73
|
-
const
|
|
74
|
-
await ensureDir(path.join(
|
|
75
|
-
await ensureDir(path.join(
|
|
76
|
-
const
|
|
72
|
+
async createRequest(input) {
|
|
73
|
+
const requestDir = this.requestDir(input.sessionId, input.requestId);
|
|
74
|
+
await ensureDir(path.join(requestDir, "events"));
|
|
75
|
+
await ensureDir(path.join(requestDir, "approvals"));
|
|
76
|
+
const sessionMeta = await this.getSessionMeta(input.sessionId);
|
|
77
77
|
const meta = {
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
requestId: input.requestId,
|
|
79
|
+
sessionId: input.sessionId,
|
|
80
80
|
agentId: input.agentId,
|
|
81
|
-
|
|
81
|
+
parentRequestId: sessionMeta?.latestRequestId ?? null,
|
|
82
82
|
executionMode: input.executionMode,
|
|
83
83
|
adapterKind: input.adapterKind ?? input.executionMode,
|
|
84
84
|
createdAt: input.createdAt,
|
|
@@ -102,35 +102,35 @@ export class FilePersistence {
|
|
|
102
102
|
traceItems: [],
|
|
103
103
|
};
|
|
104
104
|
await Promise.all([
|
|
105
|
-
writeJson(path.join(
|
|
106
|
-
writeJson(path.join(
|
|
107
|
-
writeJson(path.join(
|
|
108
|
-
writeFile(this.traceItemsPath(input.
|
|
109
|
-
writeJson(path.join(
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
writeJson(path.join(requestDir, "meta.json"), meta),
|
|
106
|
+
writeJson(path.join(requestDir, "lifecycle.json"), lifecycle),
|
|
107
|
+
writeJson(path.join(requestDir, "inspection.json"), inspection),
|
|
108
|
+
writeFile(this.traceItemsPath(input.sessionId, input.requestId), "", "utf8"),
|
|
109
|
+
writeJson(path.join(requestDir, "checkpoint-ref.json"), {
|
|
110
|
+
sessionId: input.sessionId,
|
|
111
|
+
requestId: input.requestId,
|
|
112
112
|
checkpointRef: null,
|
|
113
113
|
savedAt: input.createdAt,
|
|
114
114
|
kind: "langgraph-checkpoint",
|
|
115
115
|
}),
|
|
116
|
-
writeJson(path.join(
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
writeJson(path.join(requestDir, "artifacts.json"), {
|
|
117
|
+
sessionId: input.sessionId,
|
|
118
|
+
requestId: input.requestId,
|
|
119
119
|
items: [],
|
|
120
120
|
}),
|
|
121
|
-
writeJson(path.join(
|
|
121
|
+
writeJson(path.join(requestDir, "error.json"), {
|
|
122
122
|
hasError: false,
|
|
123
123
|
lastError: null,
|
|
124
124
|
}),
|
|
125
|
-
writeJson(this.
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
writeJson(this.requestIndexPath(input.requestId), {
|
|
126
|
+
requestId: input.requestId,
|
|
127
|
+
sessionId: input.sessionId,
|
|
128
128
|
state: "running",
|
|
129
129
|
resumable: false,
|
|
130
130
|
updatedAt: input.createdAt,
|
|
131
131
|
}),
|
|
132
|
-
writeJson(this.
|
|
133
|
-
|
|
132
|
+
writeJson(this.requestControlPath(input.requestId), {
|
|
133
|
+
requestId: input.requestId,
|
|
134
134
|
cancelRequested: false,
|
|
135
135
|
cancelReason: null,
|
|
136
136
|
cancelRequestedAt: null,
|
|
@@ -140,13 +140,13 @@ export class FilePersistence {
|
|
|
140
140
|
}),
|
|
141
141
|
]);
|
|
142
142
|
}
|
|
143
|
-
async
|
|
144
|
-
const lifecyclePath = path.join(this.
|
|
145
|
-
const
|
|
146
|
-
const inspectionPath = path.join(this.
|
|
147
|
-
const [lifecycle,
|
|
143
|
+
async setRequestState(sessionId, requestId, state, checkpointRef) {
|
|
144
|
+
const lifecyclePath = path.join(this.requestDir(sessionId, requestId), "lifecycle.json");
|
|
145
|
+
const requestMetaPath = path.join(this.requestDir(sessionId, requestId), "meta.json");
|
|
146
|
+
const inspectionPath = path.join(this.requestDir(sessionId, requestId), "inspection.json");
|
|
147
|
+
const [lifecycle, requestMeta, inspection] = await Promise.all([
|
|
148
148
|
readJson(lifecyclePath),
|
|
149
|
-
readJson(
|
|
149
|
+
readJson(requestMetaPath),
|
|
150
150
|
readJson(inspectionPath),
|
|
151
151
|
]);
|
|
152
152
|
const now = nowIso();
|
|
@@ -160,8 +160,8 @@ export class FilePersistence {
|
|
|
160
160
|
};
|
|
161
161
|
await Promise.all([
|
|
162
162
|
writeJson(lifecyclePath, next),
|
|
163
|
-
writeJson(
|
|
164
|
-
...
|
|
163
|
+
writeJson(requestMetaPath, {
|
|
164
|
+
...requestMeta,
|
|
165
165
|
updatedAt: now,
|
|
166
166
|
}),
|
|
167
167
|
writeJson(inspectionPath, {
|
|
@@ -171,40 +171,40 @@ export class FilePersistence {
|
|
|
171
171
|
}),
|
|
172
172
|
]);
|
|
173
173
|
if (checkpointRef !== undefined) {
|
|
174
|
-
await writeJson(path.join(this.
|
|
175
|
-
|
|
176
|
-
|
|
174
|
+
await writeJson(path.join(this.requestDir(sessionId, requestId), "checkpoint-ref.json"), {
|
|
175
|
+
sessionId,
|
|
176
|
+
requestId,
|
|
177
177
|
checkpointRef,
|
|
178
178
|
savedAt: now,
|
|
179
179
|
kind: "langgraph-checkpoint",
|
|
180
180
|
});
|
|
181
181
|
}
|
|
182
|
-
await writeJson(this.
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
await writeJson(this.requestIndexPath(requestId), {
|
|
183
|
+
requestId,
|
|
184
|
+
sessionId,
|
|
185
185
|
state,
|
|
186
186
|
resumable: next.resumable,
|
|
187
187
|
updatedAt: now,
|
|
188
188
|
});
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
await writeJson(
|
|
195
|
-
await writeJson(this.
|
|
196
|
-
|
|
189
|
+
const sessionMetaPath = path.join(this.sessionDir(sessionId), "meta.json");
|
|
190
|
+
const sessionMeta = await readJson(sessionMetaPath);
|
|
191
|
+
sessionMeta.status = state;
|
|
192
|
+
sessionMeta.updatedAt = now;
|
|
193
|
+
sessionMeta.latestRequestId = requestId;
|
|
194
|
+
await writeJson(sessionMetaPath, sessionMeta);
|
|
195
|
+
await writeJson(this.sessionIndexPath(sessionId), {
|
|
196
|
+
sessionId,
|
|
197
197
|
status: state,
|
|
198
|
-
|
|
198
|
+
latestRequestId: requestId,
|
|
199
199
|
updatedAt: now,
|
|
200
200
|
});
|
|
201
201
|
}
|
|
202
202
|
async appendEvent(event) {
|
|
203
203
|
const sequenceId = String(event.sequence).padStart(6, "0");
|
|
204
|
-
const inspectionPath = path.join(this.
|
|
204
|
+
const inspectionPath = path.join(this.requestDir(event.sessionId, event.requestId), "inspection.json");
|
|
205
205
|
const inspection = await readJson(inspectionPath);
|
|
206
206
|
await Promise.all([
|
|
207
|
-
writeJson(path.join(this.
|
|
207
|
+
writeJson(path.join(this.requestDir(event.sessionId, event.requestId), "events", `${sequenceId}.json`), event),
|
|
208
208
|
writeJson(inspectionPath, {
|
|
209
209
|
...inspection,
|
|
210
210
|
lastActivityAt: event.timestamp,
|
|
@@ -212,21 +212,21 @@ export class FilePersistence {
|
|
|
212
212
|
]);
|
|
213
213
|
}
|
|
214
214
|
async listSessions(filter = {}) {
|
|
215
|
-
const
|
|
216
|
-
if (!(await fileExists(
|
|
215
|
+
const sessionIndexDir = path.join(this.artifactsRoot(), "indexes", "sessions");
|
|
216
|
+
if (!(await fileExists(sessionIndexDir))) {
|
|
217
217
|
return [];
|
|
218
218
|
}
|
|
219
|
-
const entries = await readdir(
|
|
219
|
+
const entries = await readdir(sessionIndexDir);
|
|
220
220
|
const records = await Promise.all(entries.map(async (entry) => {
|
|
221
|
-
const index = await readJson(path.join(
|
|
221
|
+
const index = await readJson(path.join(sessionIndexDir, entry));
|
|
222
222
|
const [meta, runInspection] = await Promise.all([
|
|
223
|
-
readJson(path.join(this.
|
|
224
|
-
readJson(path.join(this.
|
|
223
|
+
readJson(path.join(this.sessionDir(index.sessionId), "meta.json")),
|
|
224
|
+
readJson(path.join(this.requestDir(index.sessionId, index.latestRequestId), "inspection.json")).catch(() => null),
|
|
225
225
|
]);
|
|
226
226
|
return {
|
|
227
227
|
agentId: meta.entryAgentId,
|
|
228
|
-
sessionId: index.
|
|
229
|
-
latestRequestId: index.
|
|
228
|
+
sessionId: index.sessionId,
|
|
229
|
+
latestRequestId: index.latestRequestId,
|
|
230
230
|
createdAt: meta.createdAt,
|
|
231
231
|
updatedAt: index.updatedAt,
|
|
232
232
|
status: index.status,
|
|
@@ -241,9 +241,9 @@ export class FilePersistence {
|
|
|
241
241
|
const sessions = await this.listSessions(filter);
|
|
242
242
|
const summaries = await Promise.all(sessions.map(async (session) => {
|
|
243
243
|
const [meta, messageBundle] = await Promise.all([
|
|
244
|
-
readJson(path.join(this.
|
|
245
|
-
readJson(path.join(this.
|
|
246
|
-
.catch(() => ({
|
|
244
|
+
readJson(path.join(this.sessionDir(session.sessionId), "meta.json")),
|
|
245
|
+
readJson(path.join(this.sessionDir(session.sessionId), "messages.json"))
|
|
246
|
+
.catch(() => ({ sessionId: session.sessionId, items: [] })),
|
|
247
247
|
]);
|
|
248
248
|
const items = messageBundle.items;
|
|
249
249
|
return {
|
|
@@ -256,7 +256,7 @@ export class FilePersistence {
|
|
|
256
256
|
}));
|
|
257
257
|
return summaries.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
258
258
|
}
|
|
259
|
-
async
|
|
259
|
+
async listRequestIndexes() {
|
|
260
260
|
const runIndexDir = path.join(this.artifactsRoot(), "indexes", "requests");
|
|
261
261
|
if (!(await fileExists(runIndexDir))) {
|
|
262
262
|
return [];
|
|
@@ -264,18 +264,18 @@ export class FilePersistence {
|
|
|
264
264
|
const entries = (await readdir(runIndexDir)).sort();
|
|
265
265
|
return Promise.all(entries.map((entry) => readJson(path.join(runIndexDir, entry))));
|
|
266
266
|
}
|
|
267
|
-
async
|
|
268
|
-
const
|
|
267
|
+
async readRequestSummary(sessionId, requestId) {
|
|
268
|
+
const requestDir = this.requestDir(sessionId, requestId);
|
|
269
269
|
const [meta, lifecycle, inspection] = await Promise.all([
|
|
270
|
-
readJson(path.join(
|
|
271
|
-
readJson(path.join(
|
|
272
|
-
readJson(path.join(
|
|
270
|
+
readJson(path.join(requestDir, "meta.json")),
|
|
271
|
+
readJson(path.join(requestDir, "lifecycle.json")),
|
|
272
|
+
readJson(path.join(requestDir, "inspection.json")),
|
|
273
273
|
]);
|
|
274
274
|
return {
|
|
275
|
-
requestId: meta.
|
|
276
|
-
sessionId: meta.
|
|
275
|
+
requestId: meta.requestId,
|
|
276
|
+
sessionId: meta.sessionId,
|
|
277
277
|
agentId: meta.agentId,
|
|
278
|
-
...(meta.
|
|
278
|
+
...(meta.parentRequestId ? { parentRequestId: meta.parentRequestId } : {}),
|
|
279
279
|
executionMode: meta.executionMode,
|
|
280
280
|
adapterKind: meta.adapterKind ?? meta.executionMode,
|
|
281
281
|
createdAt: meta.createdAt,
|
|
@@ -291,70 +291,70 @@ export class FilePersistence {
|
|
|
291
291
|
...(inspection.runtimeSnapshot ? { runtimeSnapshot: inspection.runtimeSnapshot } : {}),
|
|
292
292
|
};
|
|
293
293
|
}
|
|
294
|
-
async
|
|
295
|
-
const indexes = await this.
|
|
296
|
-
const
|
|
297
|
-
return
|
|
298
|
-
.filter((
|
|
299
|
-
if (filter.agentId &&
|
|
294
|
+
async listRequests(filter = {}) {
|
|
295
|
+
const indexes = await this.listRequestIndexes();
|
|
296
|
+
const requests = await Promise.all(indexes.map((record) => this.readRequestSummary(record.sessionId, record.requestId)));
|
|
297
|
+
return requests
|
|
298
|
+
.filter((request) => {
|
|
299
|
+
if (filter.agentId && request.agentId !== filter.agentId) {
|
|
300
300
|
return false;
|
|
301
301
|
}
|
|
302
|
-
if (filter.
|
|
302
|
+
if (filter.sessionId && request.sessionId !== filter.sessionId) {
|
|
303
303
|
return false;
|
|
304
304
|
}
|
|
305
|
-
if (filter.state &&
|
|
305
|
+
if (filter.state && request.state !== filter.state) {
|
|
306
306
|
return false;
|
|
307
307
|
}
|
|
308
308
|
return true;
|
|
309
309
|
})
|
|
310
310
|
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
311
311
|
}
|
|
312
|
-
async
|
|
313
|
-
const indexPath = this.
|
|
312
|
+
async getRequest(requestId) {
|
|
313
|
+
const indexPath = this.requestIndexPath(requestId);
|
|
314
314
|
if (!(await fileExists(indexPath))) {
|
|
315
315
|
return null;
|
|
316
316
|
}
|
|
317
317
|
const index = await readJson(indexPath);
|
|
318
|
-
return this.
|
|
318
|
+
return this.readRequestSummary(index.sessionId, index.requestId);
|
|
319
319
|
}
|
|
320
|
-
async getSession(
|
|
321
|
-
const filePath = this.
|
|
320
|
+
async getSession(sessionId) {
|
|
321
|
+
const filePath = this.sessionIndexPath(sessionId);
|
|
322
322
|
if (!(await fileExists(filePath))) {
|
|
323
323
|
return null;
|
|
324
324
|
}
|
|
325
325
|
const [index, meta] = await Promise.all([
|
|
326
326
|
readJson(filePath),
|
|
327
|
-
readJson(path.join(this.
|
|
327
|
+
readJson(path.join(this.sessionDir(sessionId), "meta.json")),
|
|
328
328
|
]);
|
|
329
|
-
const runInspection = await readJson(path.join(this.
|
|
329
|
+
const runInspection = await readJson(path.join(this.requestDir(sessionId, index.latestRequestId), "inspection.json")).catch(() => null);
|
|
330
330
|
return {
|
|
331
331
|
agentId: meta.entryAgentId,
|
|
332
|
-
sessionId
|
|
333
|
-
latestRequestId: index.
|
|
332
|
+
sessionId,
|
|
333
|
+
latestRequestId: index.latestRequestId,
|
|
334
334
|
createdAt: meta.createdAt,
|
|
335
335
|
updatedAt: index.updatedAt,
|
|
336
336
|
status: index.status,
|
|
337
337
|
currentAgentId: runInspection?.currentAgentId ?? undefined,
|
|
338
338
|
};
|
|
339
339
|
}
|
|
340
|
-
async
|
|
341
|
-
const filePath = path.join(this.
|
|
340
|
+
async getSessionMeta(sessionId) {
|
|
341
|
+
const filePath = path.join(this.sessionDir(sessionId), "meta.json");
|
|
342
342
|
if (!(await fileExists(filePath))) {
|
|
343
343
|
return null;
|
|
344
344
|
}
|
|
345
345
|
return readJson(filePath);
|
|
346
346
|
}
|
|
347
|
-
async
|
|
348
|
-
const
|
|
349
|
-
if (!(await fileExists(
|
|
347
|
+
async listSessionRequests(sessionId) {
|
|
348
|
+
const requestsDir = path.join(this.sessionDir(sessionId), "requests");
|
|
349
|
+
if (!(await fileExists(requestsDir))) {
|
|
350
350
|
return [];
|
|
351
351
|
}
|
|
352
|
-
const
|
|
353
|
-
const
|
|
354
|
-
return
|
|
352
|
+
const requestIds = (await readdir(requestsDir)).sort();
|
|
353
|
+
const requests = await Promise.all(requestIds.map(async (requestId) => this.readRequestSummary(sessionId, requestId)));
|
|
354
|
+
return requests.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
355
355
|
}
|
|
356
|
-
async
|
|
357
|
-
const eventsDir = path.join(this.
|
|
356
|
+
async listRequestEvents(sessionId, requestId) {
|
|
357
|
+
const eventsDir = path.join(this.requestDir(sessionId, requestId), "events");
|
|
358
358
|
if (!(await fileExists(eventsDir))) {
|
|
359
359
|
return [];
|
|
360
360
|
}
|
|
@@ -374,10 +374,10 @@ export class FilePersistence {
|
|
|
374
374
|
if (filter.status && approval.status !== filter.status) {
|
|
375
375
|
return false;
|
|
376
376
|
}
|
|
377
|
-
if (filter.
|
|
377
|
+
if (filter.sessionId && approval.sessionId !== filter.sessionId) {
|
|
378
378
|
return false;
|
|
379
379
|
}
|
|
380
|
-
if (filter.
|
|
380
|
+
if (filter.requestId && approval.requestId !== filter.requestId) {
|
|
381
381
|
return false;
|
|
382
382
|
}
|
|
383
383
|
return true;
|
|
@@ -390,25 +390,25 @@ export class FilePersistence {
|
|
|
390
390
|
}
|
|
391
391
|
return readJson(approvalPath);
|
|
392
392
|
}
|
|
393
|
-
async
|
|
394
|
-
const approvalsDir = path.join(this.
|
|
393
|
+
async getRequestApprovals(sessionId, requestId) {
|
|
394
|
+
const approvalsDir = path.join(this.requestDir(sessionId, requestId), "approvals");
|
|
395
395
|
if (!(await fileExists(approvalsDir))) {
|
|
396
396
|
return [];
|
|
397
397
|
}
|
|
398
398
|
const entries = (await readdir(approvalsDir)).sort();
|
|
399
399
|
return Promise.all(entries.map((entry) => readJson(path.join(approvalsDir, entry))));
|
|
400
400
|
}
|
|
401
|
-
async
|
|
402
|
-
return readJson(path.join(this.
|
|
401
|
+
async getRequestMeta(sessionId, requestId) {
|
|
402
|
+
return readJson(path.join(this.requestDir(sessionId, requestId), "meta.json"));
|
|
403
403
|
}
|
|
404
|
-
async
|
|
405
|
-
return readJson(path.join(this.
|
|
404
|
+
async getRequestLifecycle(sessionId, requestId) {
|
|
405
|
+
return readJson(path.join(this.requestDir(sessionId, requestId), "lifecycle.json"));
|
|
406
406
|
}
|
|
407
|
-
async
|
|
408
|
-
const inspection = await readJson(path.join(this.
|
|
407
|
+
async getRequestInspection(sessionId, requestId) {
|
|
408
|
+
const inspection = await readJson(path.join(this.requestDir(sessionId, requestId), "inspection.json"));
|
|
409
409
|
return {
|
|
410
410
|
...inspection,
|
|
411
|
-
traceItems: (await this.
|
|
411
|
+
traceItems: (await this.listRequestTraceItems(sessionId, requestId))
|
|
412
412
|
?? (Array.isArray(inspection.traceItems)
|
|
413
413
|
? inspection.traceItems
|
|
414
414
|
: Array.isArray(inspection.upstreamEvents)
|
|
@@ -416,8 +416,8 @@ export class FilePersistence {
|
|
|
416
416
|
: []),
|
|
417
417
|
};
|
|
418
418
|
}
|
|
419
|
-
async
|
|
420
|
-
const inspectionPath = path.join(this.
|
|
419
|
+
async updateRequestInspection(sessionId, requestId, patch) {
|
|
420
|
+
const inspectionPath = path.join(this.requestDir(sessionId, requestId), "inspection.json");
|
|
421
421
|
const current = await readJson(inspectionPath);
|
|
422
422
|
await writeJson(inspectionPath, {
|
|
423
423
|
...current,
|
|
@@ -429,11 +429,11 @@ export class FilePersistence {
|
|
|
429
429
|
traceItems: Array.isArray(current.traceItems) ? current.traceItems : [],
|
|
430
430
|
});
|
|
431
431
|
}
|
|
432
|
-
async
|
|
433
|
-
await appendFile(this.traceItemsPath(
|
|
432
|
+
async appendRequestTraceItem(sessionId, requestId, item) {
|
|
433
|
+
await appendFile(this.traceItemsPath(sessionId, requestId), `${JSON.stringify(item)}\n`, "utf8");
|
|
434
434
|
}
|
|
435
|
-
async
|
|
436
|
-
const traceItemsPath = this.traceItemsPath(
|
|
435
|
+
async listRequestTraceItems(sessionId, requestId) {
|
|
436
|
+
const traceItemsPath = this.traceItemsPath(sessionId, requestId);
|
|
437
437
|
if (await fileExists(traceItemsPath)) {
|
|
438
438
|
const contents = await readFile(traceItemsPath, "utf8");
|
|
439
439
|
return contents
|
|
@@ -442,69 +442,69 @@ export class FilePersistence {
|
|
|
442
442
|
.filter((line) => line.length > 0)
|
|
443
443
|
.map((line) => JSON.parse(line));
|
|
444
444
|
}
|
|
445
|
-
const inspection = await readJson(path.join(this.
|
|
445
|
+
const inspection = await readJson(path.join(this.requestDir(sessionId, requestId), "inspection.json"));
|
|
446
446
|
return Array.isArray(inspection.traceItems)
|
|
447
447
|
? inspection.traceItems
|
|
448
448
|
: Array.isArray(inspection.upstreamEvents)
|
|
449
449
|
? inspection.upstreamEvents
|
|
450
450
|
: [];
|
|
451
451
|
}
|
|
452
|
-
async
|
|
453
|
-
const
|
|
454
|
-
const
|
|
455
|
-
if (!(await fileExists(
|
|
452
|
+
async deleteSession(sessionId) {
|
|
453
|
+
const sessionDir = this.sessionDir(sessionId);
|
|
454
|
+
const sessionIndexPath = this.sessionIndexPath(sessionId);
|
|
455
|
+
if (!(await fileExists(sessionDir)) && !(await fileExists(sessionIndexPath))) {
|
|
456
456
|
return false;
|
|
457
457
|
}
|
|
458
458
|
const [runIndexes, approvals] = await Promise.all([
|
|
459
|
-
this.
|
|
459
|
+
this.listRequestIndexes(),
|
|
460
460
|
this.listApprovals(),
|
|
461
461
|
]);
|
|
462
462
|
await Promise.all([
|
|
463
463
|
...runIndexes
|
|
464
|
-
.filter((record) => record.
|
|
465
|
-
.map((record) => rm(this.
|
|
464
|
+
.filter((record) => record.sessionId === sessionId)
|
|
465
|
+
.map((record) => rm(this.requestIndexPath(record.requestId), { force: true })),
|
|
466
466
|
...approvals
|
|
467
|
-
.filter((record) => record.sessionId ===
|
|
467
|
+
.filter((record) => record.sessionId === sessionId)
|
|
468
468
|
.map((record) => rm(this.approvalIndexPath(record.approvalId), { force: true })),
|
|
469
469
|
...runIndexes
|
|
470
|
-
.filter((record) => record.
|
|
471
|
-
.flatMap((record) => [rm(this.
|
|
472
|
-
rm(
|
|
473
|
-
rm(
|
|
470
|
+
.filter((record) => record.sessionId === sessionId)
|
|
471
|
+
.flatMap((record) => [rm(this.requestQueuePath(record.requestId), { force: true }), rm(this.requestControlPath(record.requestId), { force: true })]),
|
|
472
|
+
rm(sessionIndexPath, { force: true }),
|
|
473
|
+
rm(sessionDir, { recursive: true, force: true }),
|
|
474
474
|
]);
|
|
475
475
|
return true;
|
|
476
476
|
}
|
|
477
|
-
async
|
|
478
|
-
await writeJson(path.join(this.
|
|
477
|
+
async saveRequestInput(sessionId, requestId, request) {
|
|
478
|
+
await writeJson(path.join(this.requestDir(sessionId, requestId), "request.json"), request);
|
|
479
479
|
}
|
|
480
|
-
async
|
|
481
|
-
const requestPath = path.join(this.
|
|
480
|
+
async getRequestInput(sessionId, requestId) {
|
|
481
|
+
const requestPath = path.join(this.requestDir(sessionId, requestId), "request.json");
|
|
482
482
|
if (!(await fileExists(requestPath))) {
|
|
483
483
|
return null;
|
|
484
484
|
}
|
|
485
485
|
return readJson(requestPath);
|
|
486
486
|
}
|
|
487
|
-
async
|
|
488
|
-
const requestPath = path.join(this.
|
|
487
|
+
async clearRequestInput(sessionId, requestId) {
|
|
488
|
+
const requestPath = path.join(this.requestDir(sessionId, requestId), "request.json");
|
|
489
489
|
if (await fileExists(requestPath)) {
|
|
490
490
|
await rm(requestPath, { force: true });
|
|
491
491
|
}
|
|
492
492
|
}
|
|
493
493
|
async createApproval(record) {
|
|
494
|
-
const sessionId = record.sessionId
|
|
495
|
-
const requestId = record.requestId
|
|
494
|
+
const sessionId = record.sessionId;
|
|
495
|
+
const requestId = record.requestId;
|
|
496
496
|
const normalized = {
|
|
497
497
|
...record,
|
|
498
498
|
sessionId,
|
|
499
499
|
requestId,
|
|
500
500
|
};
|
|
501
501
|
await Promise.all([
|
|
502
|
-
writeJson(path.join(this.
|
|
502
|
+
writeJson(path.join(this.requestDir(sessionId, requestId), "approvals", `${record.approvalId}.json`), normalized),
|
|
503
503
|
writeJson(this.approvalIndexPath(record.approvalId), normalized),
|
|
504
504
|
]);
|
|
505
505
|
}
|
|
506
|
-
async resolveApproval(
|
|
507
|
-
const approvalPath = path.join(this.
|
|
506
|
+
async resolveApproval(sessionId, requestId, approvalId, status) {
|
|
507
|
+
const approvalPath = path.join(this.requestDir(sessionId, requestId), "approvals", `${approvalId}.json`);
|
|
508
508
|
const current = await readJson(approvalPath);
|
|
509
509
|
const updated = {
|
|
510
510
|
...current,
|
|
@@ -517,62 +517,65 @@ export class FilePersistence {
|
|
|
517
517
|
]);
|
|
518
518
|
return updated;
|
|
519
519
|
}
|
|
520
|
-
async
|
|
521
|
-
const
|
|
522
|
-
await writeJson(path.join(
|
|
523
|
-
const artifactsPath = path.join(
|
|
520
|
+
async createRequestArtifact(sessionId, requestId, artifact, content) {
|
|
521
|
+
const requestDir = this.requestDir(sessionId, requestId);
|
|
522
|
+
await writeJson(path.join(requestDir, artifact.path), content);
|
|
523
|
+
const artifactsPath = path.join(requestDir, "artifacts.json");
|
|
524
524
|
const current = await readJson(artifactsPath);
|
|
525
525
|
current.items.push(artifact);
|
|
526
526
|
await writeJson(artifactsPath, current);
|
|
527
527
|
return artifact;
|
|
528
528
|
}
|
|
529
|
-
async
|
|
530
|
-
return readJson(path.join(this.
|
|
529
|
+
async listRequestArtifacts(sessionId, requestId) {
|
|
530
|
+
return readJson(path.join(this.requestDir(sessionId, requestId), "artifacts.json"));
|
|
531
531
|
}
|
|
532
|
-
async
|
|
533
|
-
const filePath = path.join(this.
|
|
532
|
+
async readRequestArtifact(sessionId, requestId, artifactPath) {
|
|
533
|
+
const filePath = path.join(this.requestDir(sessionId, requestId), artifactPath);
|
|
534
534
|
if (!(await fileExists(filePath))) {
|
|
535
535
|
return null;
|
|
536
536
|
}
|
|
537
537
|
return readJson(filePath);
|
|
538
538
|
}
|
|
539
|
-
async
|
|
540
|
-
const messagesPath = path.join(this.
|
|
539
|
+
async appendSessionMessage(sessionId, message) {
|
|
540
|
+
const messagesPath = path.join(this.sessionDir(sessionId), "messages.json");
|
|
541
541
|
const current = (await fileExists(messagesPath))
|
|
542
542
|
? await readJson(messagesPath)
|
|
543
|
-
: {
|
|
543
|
+
: { sessionId, items: [] };
|
|
544
544
|
current.items.push(message);
|
|
545
|
-
await writeJson(messagesPath,
|
|
545
|
+
await writeJson(messagesPath, {
|
|
546
|
+
sessionId: current.sessionId ?? sessionId,
|
|
547
|
+
items: current.items,
|
|
548
|
+
});
|
|
546
549
|
}
|
|
547
|
-
async
|
|
548
|
-
const messagesPath = path.join(this.
|
|
550
|
+
async listSessionMessages(sessionId, limit = 12) {
|
|
551
|
+
const messagesPath = path.join(this.sessionDir(sessionId), "messages.json");
|
|
549
552
|
if (!(await fileExists(messagesPath))) {
|
|
550
553
|
return [];
|
|
551
554
|
}
|
|
552
555
|
const current = await readJson(messagesPath);
|
|
553
556
|
return current.items.slice(-limit);
|
|
554
557
|
}
|
|
555
|
-
async
|
|
556
|
-
await writeJson(path.join(this.
|
|
558
|
+
async saveRequestRecoveryIntent(sessionId, requestId, intent) {
|
|
559
|
+
await writeJson(path.join(this.requestDir(sessionId, requestId), "recovery-intent.json"), intent);
|
|
557
560
|
}
|
|
558
|
-
async
|
|
559
|
-
const intentPath = path.join(this.
|
|
561
|
+
async getRequestRecoveryIntent(sessionId, requestId) {
|
|
562
|
+
const intentPath = path.join(this.requestDir(sessionId, requestId), "recovery-intent.json");
|
|
560
563
|
if (!(await fileExists(intentPath))) {
|
|
561
564
|
return null;
|
|
562
565
|
}
|
|
563
566
|
return readJson(intentPath);
|
|
564
567
|
}
|
|
565
|
-
async
|
|
566
|
-
const intentPath = path.join(this.
|
|
568
|
+
async clearRequestRecoveryIntent(sessionId, requestId) {
|
|
569
|
+
const intentPath = path.join(this.requestDir(sessionId, requestId), "recovery-intent.json");
|
|
567
570
|
if (!(await fileExists(intentPath))) {
|
|
568
571
|
return;
|
|
569
572
|
}
|
|
570
573
|
await rm(intentPath, { force: true });
|
|
571
574
|
}
|
|
572
|
-
async
|
|
573
|
-
const current = (await this.
|
|
574
|
-
|
|
575
|
-
|
|
575
|
+
async enqueueRequest(input) {
|
|
576
|
+
const current = (await this.getQueuedRequest(input.requestId)) ?? {
|
|
577
|
+
requestId: input.requestId,
|
|
578
|
+
sessionId: input.sessionId,
|
|
576
579
|
priority: input.priority ?? 0,
|
|
577
580
|
queueKey: input.queueKey ?? null,
|
|
578
581
|
enqueuedAt: nowIso(),
|
|
@@ -583,9 +586,9 @@ export class FilePersistence {
|
|
|
583
586
|
attemptCount: 0,
|
|
584
587
|
lastError: null,
|
|
585
588
|
};
|
|
586
|
-
await writeJson(this.
|
|
589
|
+
await writeJson(this.requestQueuePath(input.requestId), {
|
|
587
590
|
...current,
|
|
588
|
-
|
|
591
|
+
sessionId: input.sessionId,
|
|
589
592
|
priority: input.priority ?? current.priority,
|
|
590
593
|
queueKey: input.queueKey ?? current.queueKey,
|
|
591
594
|
availableAt: input.availableAt ?? current.availableAt,
|
|
@@ -595,31 +598,31 @@ export class FilePersistence {
|
|
|
595
598
|
lastError: null,
|
|
596
599
|
});
|
|
597
600
|
}
|
|
598
|
-
async
|
|
599
|
-
const filePath = this.
|
|
601
|
+
async getQueuedRequest(requestId) {
|
|
602
|
+
const filePath = this.requestQueuePath(requestId);
|
|
600
603
|
if (!(await fileExists(filePath))) {
|
|
601
604
|
return null;
|
|
602
605
|
}
|
|
603
606
|
return readJson(filePath);
|
|
604
607
|
}
|
|
605
|
-
async
|
|
606
|
-
const current = await this.
|
|
608
|
+
async claimQueuedRequest(input) {
|
|
609
|
+
const current = await this.getQueuedRequest(input.requestId);
|
|
607
610
|
if (!current) {
|
|
608
|
-
throw new Error(`Missing queued
|
|
611
|
+
throw new Error(`Missing queued request ${input.requestId}`);
|
|
609
612
|
}
|
|
610
613
|
const claimedAt = input.claimedAt ?? nowIso();
|
|
611
614
|
const next = {
|
|
612
615
|
...current,
|
|
613
|
-
|
|
616
|
+
sessionId: input.sessionId,
|
|
614
617
|
claimedBy: input.workerId,
|
|
615
618
|
claimedAt,
|
|
616
619
|
leaseExpiresAt: input.leaseExpiresAt,
|
|
617
620
|
attemptCount: current.attemptCount + 1,
|
|
618
621
|
};
|
|
619
|
-
await writeJson(this.
|
|
620
|
-
await writeJson(this.
|
|
621
|
-
...(await this.
|
|
622
|
-
|
|
622
|
+
await writeJson(this.requestQueuePath(input.requestId), next);
|
|
623
|
+
await writeJson(this.requestControlPath(input.requestId), {
|
|
624
|
+
...(await this.getRequestControl(input.requestId) ?? {
|
|
625
|
+
requestId: input.requestId,
|
|
623
626
|
cancelRequested: false,
|
|
624
627
|
cancelReason: null,
|
|
625
628
|
cancelRequestedAt: null,
|
|
@@ -633,19 +636,19 @@ export class FilePersistence {
|
|
|
633
636
|
});
|
|
634
637
|
return next;
|
|
635
638
|
}
|
|
636
|
-
async
|
|
637
|
-
const current = await this.
|
|
639
|
+
async renewRequestLease(input) {
|
|
640
|
+
const current = await this.getQueuedRequest(input.requestId);
|
|
638
641
|
if (current) {
|
|
639
|
-
await writeJson(this.
|
|
642
|
+
await writeJson(this.requestQueuePath(input.requestId), {
|
|
640
643
|
...current,
|
|
641
644
|
claimedBy: input.workerId,
|
|
642
645
|
claimedAt: current.claimedAt ?? input.heartbeatAt ?? nowIso(),
|
|
643
646
|
leaseExpiresAt: input.leaseExpiresAt,
|
|
644
647
|
});
|
|
645
648
|
}
|
|
646
|
-
await writeJson(this.
|
|
647
|
-
...(await this.
|
|
648
|
-
|
|
649
|
+
await writeJson(this.requestControlPath(input.requestId), {
|
|
650
|
+
...(await this.getRequestControl(input.requestId) ?? {
|
|
651
|
+
requestId: input.requestId,
|
|
649
652
|
cancelRequested: false,
|
|
650
653
|
cancelReason: null,
|
|
651
654
|
cancelRequestedAt: null,
|
|
@@ -655,23 +658,23 @@ export class FilePersistence {
|
|
|
655
658
|
}),
|
|
656
659
|
heartbeatAt: input.heartbeatAt ?? nowIso(),
|
|
657
660
|
workerId: input.workerId,
|
|
658
|
-
workerStartedAt: (await this.
|
|
661
|
+
workerStartedAt: (await this.getRequestControl(input.requestId))?.workerStartedAt ?? input.heartbeatAt ?? nowIso(),
|
|
659
662
|
});
|
|
660
663
|
}
|
|
661
|
-
async
|
|
662
|
-
await rm(this.
|
|
663
|
-
const current = await this.
|
|
664
|
+
async releaseRequestClaim(requestId) {
|
|
665
|
+
await rm(this.requestQueuePath(requestId), { force: true });
|
|
666
|
+
const current = await this.getRequestControl(requestId);
|
|
664
667
|
if (!current) {
|
|
665
668
|
return;
|
|
666
669
|
}
|
|
667
|
-
await writeJson(this.
|
|
670
|
+
await writeJson(this.requestControlPath(requestId), {
|
|
668
671
|
...current,
|
|
669
672
|
heartbeatAt: null,
|
|
670
673
|
workerId: null,
|
|
671
674
|
workerStartedAt: null,
|
|
672
675
|
});
|
|
673
676
|
}
|
|
674
|
-
async
|
|
677
|
+
async listExpiredClaimedRequests(cutoffIso) {
|
|
675
678
|
const queueDir = path.join(this.artifactsRoot(), "indexes", "requests-queue");
|
|
676
679
|
if (!(await fileExists(queueDir))) {
|
|
677
680
|
return [];
|
|
@@ -680,17 +683,17 @@ export class FilePersistence {
|
|
|
680
683
|
const records = await Promise.all(entries.map((entry) => readJson(path.join(queueDir, entry))));
|
|
681
684
|
return records.filter((record) => record.claimedBy && record.leaseExpiresAt && record.leaseExpiresAt <= cutoffIso);
|
|
682
685
|
}
|
|
683
|
-
async
|
|
684
|
-
const filePath = this.
|
|
686
|
+
async getRequestControl(requestId) {
|
|
687
|
+
const filePath = this.requestControlPath(requestId);
|
|
685
688
|
if (!(await fileExists(filePath))) {
|
|
686
689
|
return null;
|
|
687
690
|
}
|
|
688
691
|
return readJson(filePath);
|
|
689
692
|
}
|
|
690
|
-
async
|
|
691
|
-
const current = await this.
|
|
693
|
+
async requestRequestCancel(requestId, reason) {
|
|
694
|
+
const current = await this.getRequestControl(requestId);
|
|
692
695
|
if (!current) {
|
|
693
|
-
throw new Error(`Missing
|
|
696
|
+
throw new Error(`Missing request control for ${requestId}`);
|
|
694
697
|
}
|
|
695
698
|
const updated = {
|
|
696
699
|
...current,
|
|
@@ -698,15 +701,15 @@ export class FilePersistence {
|
|
|
698
701
|
cancelReason: reason ?? null,
|
|
699
702
|
cancelRequestedAt: nowIso(),
|
|
700
703
|
};
|
|
701
|
-
await writeJson(this.
|
|
704
|
+
await writeJson(this.requestControlPath(requestId), updated);
|
|
702
705
|
return updated;
|
|
703
706
|
}
|
|
704
|
-
async
|
|
705
|
-
const current = await this.
|
|
707
|
+
async clearRequestCancel(requestId) {
|
|
708
|
+
const current = await this.getRequestControl(requestId);
|
|
706
709
|
if (!current) {
|
|
707
710
|
return;
|
|
708
711
|
}
|
|
709
|
-
await writeJson(this.
|
|
712
|
+
await writeJson(this.requestControlPath(requestId), {
|
|
710
713
|
...current,
|
|
711
714
|
cancelRequested: false,
|
|
712
715
|
cancelReason: null,
|