kiokuko-dsh 0.2.25 → 0.2.27
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/context/replay-model-memory.d.ts +2 -2
- package/dist/context/replay-model-memory.d.ts.map +1 -1
- package/dist/context/replay-model-memory.js +70 -16
- package/dist/context/replay-model-memory.js.map +1 -1
- package/dist/dsh/host-adapter/lifecycle.d.ts +20 -18
- package/dist/dsh/host-adapter/lifecycle.d.ts.map +1 -1
- package/dist/dsh/host-adapter/lifecycle.js +298 -102
- package/dist/dsh/host-adapter/lifecycle.js.map +1 -1
- package/dist/dsh/host-adapter.d.ts +47 -47
- package/dist/dsh/host-adapter.d.ts.map +1 -1
- package/dist/dsh/host-adapter.js +1213 -592
- package/dist/dsh/host-adapter.js.map +1 -1
- package/dist/dsh/index.d.ts +42 -41
- package/dist/dsh/index.d.ts.map +1 -1
- package/dist/dsh/index.js +166 -92
- package/dist/dsh/index.js.map +1 -1
- package/dist/dsh/lifecycle-memory.d.ts +11 -7
- package/dist/dsh/lifecycle-memory.d.ts.map +1 -1
- package/dist/dsh/lifecycle-memory.js +599 -172
- package/dist/dsh/lifecycle-memory.js.map +1 -1
- package/dist/dsh/models/vendor/THIRD_PARTY_NOTICES.txt +1 -1
- package/dist/dsh/models/vendor/opencode-sdk.js +1829 -978
- package/dist/dsh/models/vendor/opencode-sdk.js.map +4 -4
- package/dist/dsh/task-completion.d.ts +7 -7
- package/dist/dsh/task-completion.d.ts.map +1 -1
- package/dist/dsh/task-completion.js +554 -194
- package/dist/dsh/task-completion.js.map +1 -1
- package/dist/dsh/task-state-host.d.ts +10 -10
- package/dist/dsh/task-state-host.d.ts.map +1 -1
- package/dist/dsh/task-state-host.js +337 -145
- package/dist/dsh/task-state-host.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/locale/en.json +1 -1
- package/dist/locale/ja.json +1 -1
- package/dist/locale/ko.json +1 -1
- package/dist/locale/zh-CN.json +1 -1
- package/dist/memory/replay-reflection.d.ts +94 -0
- package/dist/memory/replay-reflection.d.ts.map +1 -0
- package/dist/memory/replay-reflection.js +149 -0
- package/dist/memory/replay-reflection.js.map +1 -0
- package/dist/memory/replay-review.d.ts +18 -12
- package/dist/memory/replay-review.d.ts.map +1 -1
- package/dist/memory/replay-review.js +107 -46
- package/dist/memory/replay-review.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,33 +1,39 @@
|
|
|
1
|
-
import { readTaskSnapshot } from
|
|
2
|
-
import { dshTurnBoundarySeq } from
|
|
3
|
-
import { withImmediateTransaction } from
|
|
4
|
-
import { LedgerStore } from
|
|
5
|
-
import { terminalizeLedgerRunInTransaction } from
|
|
6
|
-
import { KiokukoError } from
|
|
7
|
-
import { saveTaskCompletionReceipt, taskCompletionBlocksClose } from
|
|
8
|
-
import { readCodingWorkflow } from
|
|
9
|
-
import { stopCodingWorkflow, synchronizeCodingWorkflow } from
|
|
10
|
-
import { saveSessionNotice } from
|
|
11
|
-
import { canonicalContentHash } from
|
|
12
|
-
import { DshRunLifecycle } from
|
|
1
|
+
import { readTaskSnapshot, } from "../task-state.js";
|
|
2
|
+
import { dshTurnBoundarySeq } from "../session-memory-finalizer.js";
|
|
3
|
+
import { withImmediateTransaction } from "../../db/transaction.js";
|
|
4
|
+
import { LedgerStore } from "../../ledger/store.js";
|
|
5
|
+
import { terminalizeLedgerRunInTransaction } from "../../enno-oduno/store.js";
|
|
6
|
+
import { KiokukoError } from "../../errors.js";
|
|
7
|
+
import { saveTaskCompletionReceipt, taskCompletionBlocksClose, } from "../task-completion.js";
|
|
8
|
+
import { readCodingWorkflow } from "../coding-workflow/store.js";
|
|
9
|
+
import { stopCodingWorkflow, synchronizeCodingWorkflow, } from "../coding-workflow/service.js";
|
|
10
|
+
import { saveSessionNotice } from "../plugin-records.js";
|
|
11
|
+
import { canonicalContentHash } from "../../serialization/validate.js";
|
|
12
|
+
import { DshRunLifecycle, } from "../session-bridge.js";
|
|
13
13
|
function currentCloseAuthority(database, runId, authority) {
|
|
14
14
|
if (!authority)
|
|
15
|
-
return database
|
|
15
|
+
return (database
|
|
16
|
+
.prepare("SELECT task_managed FROM ledger_runs WHERE run_id=?")
|
|
17
|
+
.get(runId)?.task_managed !== 1);
|
|
16
18
|
if (authority.taskId !== runId)
|
|
17
19
|
return false;
|
|
18
20
|
try {
|
|
19
21
|
const current = readTaskSnapshot(database, authority);
|
|
20
|
-
return current.revision === authority.revision &&
|
|
22
|
+
return (current.revision === authority.revision &&
|
|
23
|
+
current.generation === authority.generation &&
|
|
24
|
+
!current.pendingStop);
|
|
21
25
|
}
|
|
22
26
|
catch {
|
|
23
27
|
return false;
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
|
-
function closeAuthority(item) {
|
|
30
|
+
function closeAuthority(item) {
|
|
31
|
+
return item.authority ? { authority: item.authority } : {};
|
|
32
|
+
}
|
|
27
33
|
export function createLifecycle(deps) {
|
|
28
|
-
const { runtime, sessions, sessionMirror, autoReview, answerReview, ennoController, ennoMemory, executionSupport, gate, turnState, currentSession, currentForAgentEvent, stateForRun, deliverCompletionReport, reviewBinding, clearToolRun, sessionEventSource } = deps;
|
|
34
|
+
const { runtime, sessions, sessionMirror, autoReview, answerReview, ennoController, ennoMemory, executionSupport, gate, turnState, currentSession, currentForAgentEvent, stateForRun, deliverCompletionReport, reviewBinding, clearToolRun, sessionEventSource, } = deps;
|
|
29
35
|
const getSelection = deps.getSelection;
|
|
30
|
-
const completionReady = async (item) => runtime.withDatabase(database => {
|
|
36
|
+
const completionReady = async (item) => runtime.withDatabase((database) => {
|
|
31
37
|
if (!currentCloseAuthority(database, item.runId, item.authority))
|
|
32
38
|
return false;
|
|
33
39
|
try {
|
|
@@ -35,17 +41,28 @@ export function createLifecycle(deps) {
|
|
|
35
41
|
const assessment = saveTaskCompletionReceipt(database, item.runId);
|
|
36
42
|
if (!taskCompletionBlocksClose(assessment))
|
|
37
43
|
return true;
|
|
38
|
-
const unresolved = assessment.criteria.filter(criterion => criterion.state !==
|
|
39
|
-
const text = `完了条件の検証待ちです。未確認: ${unresolved
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
const unresolved = assessment.criteria.filter((criterion) => criterion.state !== "satisfied");
|
|
45
|
+
const text = `完了条件の検証待ちです。未確認: ${unresolved
|
|
46
|
+
.map((criterion) => criterion.description)
|
|
47
|
+
.join(" / ")
|
|
48
|
+
.slice(0, 4000)}。task_completion status で根拠を確認し、検証方法を登録してください。`;
|
|
49
|
+
saveSessionNotice(database, {
|
|
50
|
+
id: `completion-wait:${canonicalContentHash({ runId: item.runId, unresolved })}`,
|
|
51
|
+
runId: item.runId,
|
|
52
|
+
sessionId: item.sessionId,
|
|
53
|
+
rootPath: item.cwd,
|
|
54
|
+
kind: "status",
|
|
55
|
+
text,
|
|
56
|
+
anchorSeq: 0,
|
|
57
|
+
});
|
|
42
58
|
return false;
|
|
43
59
|
}
|
|
44
60
|
catch {
|
|
45
61
|
// A missing or corrupt new completion assessment never grants an opted-in
|
|
46
62
|
// enforce run a verified terminal state. Legacy/shadow close as before.
|
|
47
|
-
return database
|
|
48
|
-
.
|
|
63
|
+
return (database
|
|
64
|
+
.prepare("SELECT mode FROM dsh_completion_runs WHERE run_id = ?")
|
|
65
|
+
.get(item.runId)?.mode !== "enforce");
|
|
49
66
|
}
|
|
50
67
|
});
|
|
51
68
|
const resolveIdleClose = async (agentId, sessionId, nativeSession, nativeAgent) => {
|
|
@@ -53,34 +70,58 @@ export function createLifecycle(deps) {
|
|
|
53
70
|
return undefined;
|
|
54
71
|
await deliverCompletionReport(nativeSession);
|
|
55
72
|
const item = currentForAgentEvent(agentId, sessionId, undefined, nativeSession, nativeAgent);
|
|
56
|
-
if (item === undefined ||
|
|
73
|
+
if (item === undefined ||
|
|
74
|
+
item.closed ||
|
|
75
|
+
executionSupport.paused(item.sessionId))
|
|
57
76
|
return undefined;
|
|
58
77
|
if (nativeAgent && answerReview.hold(nativeAgent))
|
|
59
78
|
return undefined;
|
|
60
79
|
const selection = getSelection(item.runId)?.value;
|
|
61
80
|
if (selection?.discussion)
|
|
62
81
|
return undefined;
|
|
63
|
-
if (selection &&
|
|
82
|
+
if (selection &&
|
|
83
|
+
((selection.status !== "ready" &&
|
|
84
|
+
item.prepared.intake.profile.taskType !== "chat") ||
|
|
85
|
+
(item.failed && selection.mode === "normal")))
|
|
64
86
|
return undefined;
|
|
65
87
|
const state = await runtime.withDatabase((database) => stateForRun(database, item));
|
|
66
|
-
const coding = await runtime.withDatabase(db => readCodingWorkflow(db, item.runId));
|
|
67
|
-
if (coding?.state ===
|
|
68
|
-
return {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
const coding = await runtime.withDatabase((db) => readCodingWorkflow(db, item.runId));
|
|
89
|
+
if (coding?.state === "cancelled" || coding?.state === "superseded")
|
|
90
|
+
return {
|
|
91
|
+
...closeAuthority(item),
|
|
92
|
+
runId: item.runId,
|
|
93
|
+
status: "cancelled",
|
|
94
|
+
terminalTurn: item.turn,
|
|
95
|
+
};
|
|
96
|
+
if (state.status === "cancelled")
|
|
97
|
+
return {
|
|
98
|
+
...closeAuthority(item),
|
|
99
|
+
runId: item.runId,
|
|
100
|
+
status: "cancelled",
|
|
101
|
+
};
|
|
102
|
+
if (state.status === "blocked" || state.nextAction === "report_blocker")
|
|
103
|
+
return {
|
|
104
|
+
...closeAuthority(item),
|
|
105
|
+
runId: item.runId,
|
|
106
|
+
status: "failed",
|
|
107
|
+
terminalTurn: item.turn,
|
|
108
|
+
};
|
|
73
109
|
// A chat run spans the quiet time between user messages. Enno's
|
|
74
110
|
// inapplicable `complete` means that no orchestration is required for this
|
|
75
111
|
// turn; it does not mean that the persistent conversation has ended.
|
|
76
|
-
if (item.prepared.intake.profile.taskType ===
|
|
112
|
+
if (item.prepared.intake.profile.taskType === "chat")
|
|
77
113
|
return undefined;
|
|
78
|
-
if (state.status ===
|
|
79
|
-
if (!await completionReady(item))
|
|
114
|
+
if (state.status === "completed" || state.nextAction === "complete") {
|
|
115
|
+
if (!(await completionReady(item)))
|
|
80
116
|
return undefined;
|
|
81
117
|
// A provider failure while wording the final response cannot undo
|
|
82
118
|
// already verified completion. The durable host report covers it.
|
|
83
|
-
return {
|
|
119
|
+
return {
|
|
120
|
+
...closeAuthority(item),
|
|
121
|
+
runId: item.runId,
|
|
122
|
+
status: "completed",
|
|
123
|
+
terminalTurn: item.turn,
|
|
124
|
+
};
|
|
84
125
|
}
|
|
85
126
|
// Idle means the driver has no active work, not that Enno reached a
|
|
86
127
|
// terminal state. Keep only genuinely resumable active states open.
|
|
@@ -95,65 +136,102 @@ export function createLifecycle(deps) {
|
|
|
95
136
|
const resolveSessionClose = async (sessionId, nativeSession) => {
|
|
96
137
|
answerReview.cancel(sessionId);
|
|
97
138
|
const item = currentSession(sessionId);
|
|
98
|
-
if (item === undefined ||
|
|
139
|
+
if (item === undefined ||
|
|
140
|
+
item.closed ||
|
|
141
|
+
item.nativeSession !== nativeSession)
|
|
99
142
|
return undefined;
|
|
100
|
-
if (executionSupport.paused(item.sessionId) ||
|
|
101
|
-
|
|
143
|
+
if (executionSupport.paused(item.sessionId) ||
|
|
144
|
+
(getSelection(item.runId)?.value.status !== undefined &&
|
|
145
|
+
getSelection(item.runId)?.value.status !== "ready")) {
|
|
146
|
+
const end = await sessionMirror.latestEvent(sessionId, "turn/end", Number.MAX_SAFE_INTEGER);
|
|
102
147
|
if (end)
|
|
103
|
-
autoReview.notify(reviewBinding(item, nativeSession), end.seq,
|
|
148
|
+
autoReview.notify(reviewBinding(item, nativeSession), end.seq, "boundary");
|
|
104
149
|
return undefined;
|
|
105
150
|
}
|
|
106
151
|
const state = await runtime.withDatabase((database) => stateForRun(database, item));
|
|
107
|
-
if (state.status ===
|
|
108
|
-
if (!await completionReady(item))
|
|
152
|
+
if (state.status === "completed") {
|
|
153
|
+
if (!(await completionReady(item)))
|
|
109
154
|
return undefined;
|
|
110
155
|
await deliverCompletionReport(nativeSession);
|
|
111
|
-
return {
|
|
156
|
+
return {
|
|
157
|
+
...closeAuthority(item),
|
|
158
|
+
runId: item.runId,
|
|
159
|
+
status: "completed",
|
|
160
|
+
terminalTurn: item.turn,
|
|
161
|
+
};
|
|
112
162
|
}
|
|
113
163
|
if (item.failed)
|
|
114
|
-
return {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
164
|
+
return {
|
|
165
|
+
...closeAuthority(item),
|
|
166
|
+
runId: item.runId,
|
|
167
|
+
status: "failed",
|
|
168
|
+
terminalTurn: item.turn,
|
|
169
|
+
};
|
|
170
|
+
if (state.status === "cancelled")
|
|
171
|
+
return {
|
|
172
|
+
...closeAuthority(item),
|
|
173
|
+
runId: item.runId,
|
|
174
|
+
status: "cancelled",
|
|
175
|
+
};
|
|
176
|
+
if (state.status === "blocked" || state.nextAction === "report_blocker")
|
|
177
|
+
return {
|
|
178
|
+
...closeAuthority(item),
|
|
179
|
+
runId: item.runId,
|
|
180
|
+
status: "failed",
|
|
181
|
+
terminalTurn: item.turn,
|
|
182
|
+
};
|
|
183
|
+
if (item.prepared.intake.profile.taskType === "chat" ||
|
|
184
|
+
state.nextAction === "complete") {
|
|
185
|
+
if (item.prepared.intake.profile.taskType !== "chat" &&
|
|
186
|
+
!(await completionReady(item)))
|
|
121
187
|
return undefined;
|
|
122
|
-
return {
|
|
188
|
+
return {
|
|
189
|
+
...closeAuthority(item),
|
|
190
|
+
runId: item.runId,
|
|
191
|
+
status: "completed",
|
|
192
|
+
terminalTurn: item.turn,
|
|
193
|
+
};
|
|
123
194
|
}
|
|
124
|
-
return { ...closeAuthority(item), runId: item.runId, status:
|
|
195
|
+
return { ...closeAuthority(item), runId: item.runId, status: "cancelled" };
|
|
125
196
|
};
|
|
126
197
|
const closeRun = async (input) => {
|
|
127
198
|
let committedAuthority;
|
|
128
199
|
const committed = await runtime.withDatabase((database) => withImmediateTransaction(database, () => {
|
|
129
200
|
if (!currentCloseAuthority(database, input.runId, input.authority))
|
|
130
201
|
return false;
|
|
131
|
-
if (input.status ===
|
|
132
|
-
stopCodingWorkflow(database, input.runId,
|
|
202
|
+
if (input.status === "cancelled")
|
|
203
|
+
stopCodingWorkflow(database, input.runId, "cancelled");
|
|
133
204
|
let completionBlocked;
|
|
134
205
|
try {
|
|
135
206
|
completionBlocked = taskCompletionBlocksClose(saveTaskCompletionReceipt(database, input.runId));
|
|
136
207
|
}
|
|
137
208
|
catch {
|
|
138
|
-
completionBlocked =
|
|
209
|
+
completionBlocked =
|
|
210
|
+
database
|
|
211
|
+
.prepare("SELECT mode FROM dsh_completion_runs WHERE run_id=?")
|
|
212
|
+
.get(input.runId)?.mode === "enforce";
|
|
139
213
|
}
|
|
140
|
-
if (input.status ===
|
|
141
|
-
throw new KiokukoError(
|
|
214
|
+
if (input.status === "completed" && completionBlocked)
|
|
215
|
+
throw new KiokukoError("CONFLICT", "Required task completion evidence is unresolved");
|
|
142
216
|
const store = new LedgerStore(database);
|
|
143
217
|
const before = store.readRun(input.runId);
|
|
144
218
|
if (before === undefined)
|
|
145
|
-
throw new KiokukoError(
|
|
146
|
-
if (before.status ===
|
|
219
|
+
throw new KiokukoError("INTEGRITY_ERROR", "Run close target does not exist");
|
|
220
|
+
if (before.status === "intake" || before.status === "active") {
|
|
147
221
|
terminalizeLedgerRunInTransaction(database, input.runId, input.status);
|
|
148
222
|
}
|
|
149
223
|
else if (before.status !== input.status) {
|
|
150
|
-
throw new KiokukoError(
|
|
224
|
+
throw new KiokukoError("CONFLICT", "Run close status is immutable");
|
|
151
225
|
}
|
|
152
226
|
// Native completion and authority have been checked independently.
|
|
153
227
|
// A missing optional memory range cannot change the observed task outcome.
|
|
154
228
|
if (input.authority) {
|
|
155
229
|
const closed = readTaskSnapshot(database, input.authority);
|
|
156
|
-
committedAuthority = {
|
|
230
|
+
committedAuthority = {
|
|
231
|
+
...input.authority,
|
|
232
|
+
revision: closed.revision,
|
|
233
|
+
generation: closed.generation,
|
|
234
|
+
};
|
|
157
235
|
}
|
|
158
236
|
return true;
|
|
159
237
|
}));
|
|
@@ -162,31 +240,50 @@ export function createLifecycle(deps) {
|
|
|
162
240
|
const reviewFinishes = [];
|
|
163
241
|
// The DB callback may resume after another client reopened this same run.
|
|
164
242
|
// Run-wide caches must be retired atomically with a fresh generation check.
|
|
165
|
-
await runtime.withDatabase(database => {
|
|
243
|
+
await runtime.withDatabase((database) => {
|
|
166
244
|
if (!currentCloseAuthority(database, input.runId, committedAuthority))
|
|
167
245
|
return;
|
|
168
246
|
try {
|
|
169
247
|
autoReview.worker.abort(input.runId);
|
|
170
248
|
}
|
|
171
|
-
catch {
|
|
172
|
-
|
|
173
|
-
|
|
249
|
+
catch {
|
|
250
|
+
/* Optional memory cleanup cannot retain a task owner. */
|
|
251
|
+
}
|
|
252
|
+
const items = turnState
|
|
253
|
+
.allTurns()
|
|
254
|
+
.filter((candidate) => candidate.runId === input.runId &&
|
|
255
|
+
(!input.authority ||
|
|
256
|
+
(candidate.authority?.sessionId === input.authority.sessionId &&
|
|
257
|
+
candidate.authority.generation === input.authority.generation)));
|
|
174
258
|
const committedSessionId = items[0]?.sessionId;
|
|
175
259
|
if (committedSessionId)
|
|
176
260
|
try {
|
|
177
|
-
deps.onRunClosed?.(input.runId, input.status, committedSessionId
|
|
261
|
+
deps.onRunClosed?.(input.runId, input.status, committedSessionId, committedAuthority &&
|
|
262
|
+
readTaskSnapshot(database, committedAuthority).state === "Done"
|
|
263
|
+
? committedAuthority
|
|
264
|
+
: undefined);
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
/* optional memory observation */
|
|
178
268
|
}
|
|
179
|
-
catch { /* optional memory observation */ }
|
|
180
269
|
for (const item of items) {
|
|
181
|
-
ennoController.retire({
|
|
182
|
-
...(item.
|
|
270
|
+
ennoController.retire({
|
|
271
|
+
...(item.nativeAgent === undefined
|
|
272
|
+
? {}
|
|
273
|
+
: { nativeAgent: item.nativeAgent }),
|
|
274
|
+
...(item.nativeSession === undefined
|
|
275
|
+
? {}
|
|
276
|
+
: { nativeSession: item.nativeSession }),
|
|
277
|
+
});
|
|
183
278
|
if (item.nativeAgent)
|
|
184
279
|
reviewFinishes.push(answerReview.finish(item.nativeAgent));
|
|
185
280
|
item.closed = true;
|
|
186
281
|
try {
|
|
187
282
|
ennoMemory.clear(item.runId);
|
|
188
283
|
}
|
|
189
|
-
catch {
|
|
284
|
+
catch {
|
|
285
|
+
/* Optional memory cleanup cannot retain a task owner. */
|
|
286
|
+
}
|
|
190
287
|
executionSupport.clear(item.sessionId);
|
|
191
288
|
gate.clearTurn(item.sessionId, item.turn);
|
|
192
289
|
}
|
|
@@ -196,9 +293,72 @@ export function createLifecycle(deps) {
|
|
|
196
293
|
await Promise.all(reviewFinishes);
|
|
197
294
|
};
|
|
198
295
|
const runLifecycle = new DshRunLifecycle({ closeRun });
|
|
296
|
+
const completeTask = async (task) => {
|
|
297
|
+
const authority = await runtime.withDatabase((database) => {
|
|
298
|
+
const current = readTaskSnapshot(database, task);
|
|
299
|
+
if (current.state !== "Done" ||
|
|
300
|
+
task.state !== "Done" ||
|
|
301
|
+
current.pendingStop ||
|
|
302
|
+
current.revision !== task.revision ||
|
|
303
|
+
current.generation !== task.generation)
|
|
304
|
+
return undefined;
|
|
305
|
+
const item = currentSession(task.sessionId);
|
|
306
|
+
const agent = item?.nativeAgent;
|
|
307
|
+
if (!item ||
|
|
308
|
+
item.closed ||
|
|
309
|
+
item.runId !== task.taskId ||
|
|
310
|
+
item.sessionId !== task.sessionId ||
|
|
311
|
+
item.repositoryRoot !== task.repositoryRoot ||
|
|
312
|
+
!agent ||
|
|
313
|
+
agent.status !== "idle" ||
|
|
314
|
+
agent.session !== item.nativeSession ||
|
|
315
|
+
sessions?.get(task.sessionId) !== item.nativeSession ||
|
|
316
|
+
currentForAgentEvent(item.agentId, item.sessionId, undefined, item.nativeSession, agent) !== item)
|
|
317
|
+
return undefined;
|
|
318
|
+
const matches = (candidate) => {
|
|
319
|
+
const prior = candidate.authority;
|
|
320
|
+
return (candidate.runId === task.taskId &&
|
|
321
|
+
candidate.sessionId === task.sessionId &&
|
|
322
|
+
candidate.repositoryRoot === task.repositoryRoot &&
|
|
323
|
+
candidate.nativeAgent === agent &&
|
|
324
|
+
candidate.nativeSession === item.nativeSession &&
|
|
325
|
+
!!prior &&
|
|
326
|
+
prior.taskId === task.taskId &&
|
|
327
|
+
prior.sessionId === task.sessionId &&
|
|
328
|
+
prior.repositoryRoot === task.repositoryRoot &&
|
|
329
|
+
((prior.revision === task.revision &&
|
|
330
|
+
prior.generation === task.generation) ||
|
|
331
|
+
(prior.revision + 1 === task.revision &&
|
|
332
|
+
prior.generation + 1 === task.generation)));
|
|
333
|
+
};
|
|
334
|
+
if (!matches(item))
|
|
335
|
+
return undefined;
|
|
336
|
+
const next = {
|
|
337
|
+
taskId: task.taskId,
|
|
338
|
+
sessionId: task.sessionId,
|
|
339
|
+
repositoryRoot: task.repositoryRoot,
|
|
340
|
+
revision: task.revision,
|
|
341
|
+
generation: task.generation,
|
|
342
|
+
};
|
|
343
|
+
// Explicit completion advances the task lease before the final native answer.
|
|
344
|
+
// Reconcile only its exact projection; closeRun independently rechecks it.
|
|
345
|
+
for (const candidate of turnState.allTurns())
|
|
346
|
+
if (matches(candidate))
|
|
347
|
+
candidate.authority = next;
|
|
348
|
+
return next;
|
|
349
|
+
});
|
|
350
|
+
if (authority)
|
|
351
|
+
await runLifecycle.closeTurn({
|
|
352
|
+
runId: task.taskId,
|
|
353
|
+
status: "completed",
|
|
354
|
+
authority,
|
|
355
|
+
});
|
|
356
|
+
};
|
|
199
357
|
const optionalSourceBoundary = (item) => {
|
|
200
358
|
try {
|
|
201
|
-
return {
|
|
359
|
+
return {
|
|
360
|
+
sourceEndSeq: dshTurnBoundarySeq(sessionEventSource(item.nativeSession), item.turn, "end"),
|
|
361
|
+
};
|
|
202
362
|
}
|
|
203
363
|
catch {
|
|
204
364
|
return {}; /* Incomplete source evidence stays unavailable to memory. */
|
|
@@ -212,7 +372,9 @@ export function createLifecycle(deps) {
|
|
|
212
372
|
try {
|
|
213
373
|
await sessionMirror.checkpointAfterNativeFlush(item.nativeSession);
|
|
214
374
|
}
|
|
215
|
-
catch {
|
|
375
|
+
catch {
|
|
376
|
+
/* Optional mirror cannot veto native task closure. */
|
|
377
|
+
}
|
|
216
378
|
return optionalSourceBoundary(item);
|
|
217
379
|
}
|
|
218
380
|
catch {
|
|
@@ -220,71 +382,97 @@ export function createLifecycle(deps) {
|
|
|
220
382
|
}
|
|
221
383
|
};
|
|
222
384
|
const retireSupersededRun = async (item, status) => {
|
|
223
|
-
if (status ===
|
|
224
|
-
status =
|
|
225
|
-
if (status !==
|
|
226
|
-
await runLifecycle.closeTurn({
|
|
385
|
+
if (status === "completed" && !(await completionReady(item)))
|
|
386
|
+
status = "cancelled";
|
|
387
|
+
if (status !== "completed") {
|
|
388
|
+
await runLifecycle.closeTurn({
|
|
389
|
+
...closeAuthority(item),
|
|
390
|
+
runId: item.runId,
|
|
391
|
+
status,
|
|
392
|
+
...(status === "failed" ? await failedBoundary(item) : {}),
|
|
393
|
+
});
|
|
227
394
|
return;
|
|
228
395
|
}
|
|
229
396
|
if (item.nativeSession === undefined || sessions?.flush === undefined) {
|
|
230
|
-
throw new KiokukoError(
|
|
397
|
+
throw new KiokukoError("CONFLICT", "Completed DSH run requires its exact native session checkpoint");
|
|
231
398
|
}
|
|
232
399
|
await sessions.flush(item.nativeSession);
|
|
233
400
|
try {
|
|
234
401
|
await sessionMirror.checkpointAfterNativeFlush(item.nativeSession);
|
|
235
402
|
}
|
|
236
|
-
catch {
|
|
403
|
+
catch {
|
|
404
|
+
/* Optional mirror cannot veto native task closure. */
|
|
405
|
+
}
|
|
237
406
|
await runLifecycle.closeTurn({
|
|
238
|
-
...closeAuthority(item),
|
|
407
|
+
...closeAuthority(item),
|
|
408
|
+
runId: item.runId,
|
|
239
409
|
status,
|
|
240
410
|
...optionalSourceBoundary(item),
|
|
241
411
|
});
|
|
242
412
|
};
|
|
243
413
|
const closeRemainingRuns = async (pausedSessions) => {
|
|
244
414
|
const failures = [];
|
|
245
|
-
const remainingRuns = [
|
|
415
|
+
const remainingRuns = [
|
|
416
|
+
...new Map(turnState
|
|
417
|
+
.allTurns()
|
|
246
418
|
.filter((item) => !item.closed && !pausedSessions.has(item.sessionId))
|
|
247
|
-
.map((item) => [item.runId, item])).values()
|
|
419
|
+
.map((item) => [item.runId, item])).values(),
|
|
420
|
+
];
|
|
248
421
|
for (const item of remainingRuns) {
|
|
249
422
|
try {
|
|
250
423
|
const selected = getSelection(item.runId)?.value;
|
|
251
424
|
if (selected) {
|
|
252
|
-
const state = await runtime.withDatabase(db => stateForRun(db, item));
|
|
253
|
-
if (selected.status !==
|
|
254
|
-
|
|
255
|
-
|
|
425
|
+
const state = await runtime.withDatabase((db) => stateForRun(db, item));
|
|
426
|
+
if (selected.status !== "ready" ||
|
|
427
|
+
(selected.mode === "enno" &&
|
|
428
|
+
!["completed", "blocked", "cancelled"].includes(state.status ?? "")) ||
|
|
429
|
+
(selected.mode === "normal" &&
|
|
430
|
+
(item.failed ||
|
|
431
|
+
item.nativeAgent?.status ===
|
|
432
|
+
"running")))
|
|
256
433
|
continue;
|
|
257
434
|
}
|
|
258
435
|
let status;
|
|
259
|
-
if (item.prepared.intake.profile.taskType ===
|
|
260
|
-
status = item.failed ?
|
|
436
|
+
if (item.prepared.intake.profile.taskType === "chat")
|
|
437
|
+
status = item.failed ? "failed" : "cancelled";
|
|
261
438
|
else {
|
|
262
439
|
const state = await runtime.withDatabase((database) => stateForRun(database, item));
|
|
263
|
-
status =
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
440
|
+
status =
|
|
441
|
+
state.status === "completed" || state.nextAction === "complete"
|
|
442
|
+
? "completed"
|
|
443
|
+
: item.failed ||
|
|
444
|
+
state.status === "blocked" ||
|
|
445
|
+
state.nextAction === "report_blocker"
|
|
446
|
+
? "failed"
|
|
447
|
+
: "cancelled";
|
|
268
448
|
}
|
|
269
|
-
if (status ===
|
|
270
|
-
if (!await completionReady(item))
|
|
449
|
+
if (status === "completed") {
|
|
450
|
+
if (!(await completionReady(item)))
|
|
271
451
|
continue;
|
|
272
452
|
await deliverCompletionReport(item.nativeSession);
|
|
273
|
-
if (item.nativeSession === undefined ||
|
|
274
|
-
|
|
453
|
+
if (item.nativeSession === undefined ||
|
|
454
|
+
sessions?.flush === undefined) {
|
|
455
|
+
throw new KiokukoError("CONFLICT", "Completed DSH run requires its exact native session checkpoint");
|
|
275
456
|
}
|
|
276
457
|
await sessions.flush(item.nativeSession);
|
|
277
458
|
try {
|
|
278
459
|
await sessionMirror.checkpointAfterNativeFlush(item.nativeSession);
|
|
279
460
|
}
|
|
280
|
-
catch {
|
|
461
|
+
catch {
|
|
462
|
+
/* Optional mirror cannot veto native task closure. */
|
|
463
|
+
}
|
|
281
464
|
}
|
|
282
465
|
await runLifecycle.closeTurn({
|
|
283
|
-
...closeAuthority(item),
|
|
466
|
+
...closeAuthority(item),
|
|
467
|
+
runId: item.runId,
|
|
284
468
|
status,
|
|
285
|
-
...(status !==
|
|
286
|
-
|
|
287
|
-
|
|
469
|
+
...(status !== "completed"
|
|
470
|
+
? status === "failed"
|
|
471
|
+
? await failedBoundary(item)
|
|
472
|
+
: {}
|
|
473
|
+
: {
|
|
474
|
+
...optionalSourceBoundary(item),
|
|
475
|
+
}),
|
|
288
476
|
});
|
|
289
477
|
}
|
|
290
478
|
catch (error) {
|
|
@@ -293,6 +481,14 @@ export function createLifecycle(deps) {
|
|
|
293
481
|
}
|
|
294
482
|
return failures;
|
|
295
483
|
};
|
|
296
|
-
return {
|
|
484
|
+
return {
|
|
485
|
+
runLifecycle,
|
|
486
|
+
completeTask,
|
|
487
|
+
resolveIdleClose,
|
|
488
|
+
resolveSessionRunId,
|
|
489
|
+
resolveSessionClose,
|
|
490
|
+
retireSupersededRun,
|
|
491
|
+
closeRemainingRuns,
|
|
492
|
+
};
|
|
297
493
|
}
|
|
298
494
|
//# sourceMappingURL=lifecycle.js.map
|