@zq-silk/yui 0.13.7 → 0.13.8
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/ARCHITECTURE.md +40 -4
- package/README.md +19 -1
- package/dist/cli/updatePorts.js +4 -4
- package/dist/cli.js +1 -1
- package/dist/commands/taskCommands.js +1 -12
- package/dist/commands/taskContextCommand.js +1 -1
- package/dist/commands/taskExecutionCommands.js +0 -5
- package/dist/commands/taskOverviewCommand.js +5 -1
- package/dist/commands/taskRoleRuntimeStatus.js +0 -13
- package/dist/context/sessionBootstrapManifest.js +26 -23
- package/dist/controller/controller.js +0 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +84 -78
- package/dist/executor/agentExecutor.js +14 -28
- package/dist/executor/fileRoleLaunchPlanner.js +2 -3
- package/dist/lifecycle/exactRunTerminalization.js +1 -3
- package/dist/output/rolePresentation.js +0 -1
- package/dist/repository/taskWorkspacePreparer.js +0 -1
- package/dist/role/role.js +12 -20
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/providerRuntimeIdentity.js +8 -17
- package/dist/scheduler/activeRoleRunDelivery.js +2 -10
- package/dist/scheduler/activeTaskProgress.js +1 -4
- package/dist/scheduler/leaderWakeupProcessor.js +0 -5
- package/dist/scheduler/roleRunStall.js +3 -3
- package/dist/storage/migration/productionRegistry.js +143 -0
- package/dist/storage/taskStore.js +2 -2
- package/dist/web/webSnapshot.js +3 -0
- package/i18n/README.zh-CN.md +11 -3
- package/package.json +1 -1
- package/skills/yui-runtime/SKILL.md +5 -4
|
@@ -9,7 +9,7 @@ import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
|
9
9
|
import { buildTaskWakeEnvelope } from "../context/wakeNotification.js";
|
|
10
10
|
import { createTaskWake, fallbackWakeCursor, latestTaskWake } from "../scheduler/taskWake.js";
|
|
11
11
|
import { answerInputRequest } from "../input/inputRequest.js";
|
|
12
|
-
import { activeRoleAgentBinding
|
|
12
|
+
import { activeRoleAgentBinding } from "../role/role.js";
|
|
13
13
|
import { effectiveLaunchWithTaskMainWorkspace, effectiveLaunchSnapshotsCompatible, effectiveLaunchSnapshotsCompatibleForTaskSession, resolveEffectiveLaunch, validateEffectiveLaunchSnapshot } from "../executor/effectiveLaunch.js";
|
|
14
14
|
import { SYSTEM_OPERATOR_ROLE } from "../role/systemRoles.js";
|
|
15
15
|
import { failAgentRun, markAgentRunDelivered, markAgentRunPushed, reopenRunForProviderRetry, clearProviderRetryOnProgress, agentRunDeliveryReceiptId, withProviderRetry } from "../run/agentRun.js";
|
|
@@ -101,7 +101,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
101
101
|
outcome = this.observeRuntimePromptAccepted(input, adapterId, now);
|
|
102
102
|
break;
|
|
103
103
|
case "turn.completed": {
|
|
104
|
-
const
|
|
104
|
+
const classification = this.classifyRuntimeTurnCompleted({
|
|
105
105
|
taskId,
|
|
106
106
|
roleName: input.fence.roleName,
|
|
107
107
|
agentId: input.fence.agentId,
|
|
@@ -110,10 +110,8 @@ export class FileSchedulerStoreAdapter {
|
|
|
110
110
|
nativeSessionId: input.fence.nativeSessionId,
|
|
111
111
|
turnId: input.fence.nativeTurnId,
|
|
112
112
|
attemptId: input.fence.receiptId,
|
|
113
|
-
runId: input.fence.runId
|
|
114
|
-
|
|
115
|
-
};
|
|
116
|
-
const classification = this.classifyRuntimeTurnCompleted(completed);
|
|
113
|
+
runId: input.fence.runId
|
|
114
|
+
});
|
|
117
115
|
if (classification !== "apply")
|
|
118
116
|
return classification;
|
|
119
117
|
if (this.runtimeTurnHasActiveNativeSubagents(input)) {
|
|
@@ -123,39 +121,24 @@ export class FileSchedulerStoreAdapter {
|
|
|
123
121
|
outcome = this.validateCanonicalRunObservation(input, now);
|
|
124
122
|
break;
|
|
125
123
|
}
|
|
126
|
-
|
|
127
|
-
outcome = result.disposition === "obsolete" ? "obsolete" : "applied";
|
|
124
|
+
outcome = this.foldProviderTurnBoundary(input, adapterId, "completed", input.payload.summary ?? "Agent turn completed without a workflow outcome.", now);
|
|
128
125
|
break;
|
|
129
126
|
}
|
|
130
127
|
case "turn.failed": {
|
|
131
128
|
const failureEvidence = input.payload.failure;
|
|
132
129
|
if (failureEvidence.runTerminal !== true) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const boundary = this.observeRuntimeTurnCompleted({
|
|
146
|
-
taskId,
|
|
147
|
-
roleName: input.fence.roleName,
|
|
148
|
-
agentId: input.fence.agentId,
|
|
149
|
-
adapterId,
|
|
150
|
-
launchId: input.fence.launchId,
|
|
151
|
-
nativeSessionId: input.fence.nativeSessionId,
|
|
152
|
-
turnId: input.fence.nativeTurnId,
|
|
153
|
-
attemptId: input.fence.receiptId,
|
|
154
|
-
runId: input.fence.runId,
|
|
155
|
-
summary: input.payload.summary ?? `Provider Turn failed: ${failureEvidence.code}.`,
|
|
156
|
-
providerStatus: "failed"
|
|
157
|
-
}, now);
|
|
158
|
-
outcome = boundary.disposition === "obsolete" ? "obsolete" : "applied";
|
|
130
|
+
outcome = this.foldProviderTurnBoundary(input, adapterId, "failed", input.payload.summary ?? `Provider Turn failed: ${failureEvidence.code}.`, now);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
const active = this.store.getActiveAgentRun(taskId, input.fence.roleName);
|
|
134
|
+
const recordedTurn = this.store.getTaskRoleSessionSet(taskId, input.fence.roleName)?.providerBinding?.turn;
|
|
135
|
+
if (input.fence.runId !== undefined
|
|
136
|
+
&& active?.id !== input.fence.runId
|
|
137
|
+
&& recordedTurn !== null
|
|
138
|
+
&& recordedTurn !== undefined
|
|
139
|
+
&& recordedTurn.runId === input.fence.runId
|
|
140
|
+
&& recordedTurn.turnId === input.fence.nativeTurnId) {
|
|
141
|
+
outcome = this.foldProviderTurnBoundary(input, adapterId, "failed", input.payload.summary ?? `Provider Turn failed: ${failureEvidence.code}.`, now);
|
|
159
142
|
break;
|
|
160
143
|
}
|
|
161
144
|
const failure = {
|
|
@@ -194,7 +177,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
194
177
|
case "operation.completed":
|
|
195
178
|
case "operation.failed":
|
|
196
179
|
case "turn.waiting":
|
|
197
|
-
case "turn.cancelled":
|
|
198
180
|
case "activity.observed":
|
|
199
181
|
case "observer.health":
|
|
200
182
|
case "native-work.snapshot":
|
|
@@ -204,6 +186,10 @@ export class FileSchedulerStoreAdapter {
|
|
|
204
186
|
case "input.delivery-unknown":
|
|
205
187
|
outcome = this.validateCanonicalRunObservation(input, now);
|
|
206
188
|
break;
|
|
189
|
+
case "turn.cancelled": {
|
|
190
|
+
outcome = this.foldProviderTurnBoundary(input, adapterId, "cancelled", input.payload.summary ?? "Provider Turn was cancelled.", now);
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
207
193
|
case "conversation.observed":
|
|
208
194
|
case "activation.started":
|
|
209
195
|
case "activation.ended":
|
|
@@ -292,6 +278,26 @@ export class FileSchedulerStoreAdapter {
|
|
|
292
278
|
const projection = projectRuntimeTaskEvents(input.fence, run.createdAt, this.store.listEvents(taskId));
|
|
293
279
|
return Object.values(projection.operations).some(({ kind }) => kind === "subagent");
|
|
294
280
|
}
|
|
281
|
+
foldProviderTurnBoundary(input, adapterId, providerStatus, summary, now) {
|
|
282
|
+
const completed = {
|
|
283
|
+
taskId: input.fence.taskId,
|
|
284
|
+
roleName: input.fence.roleName,
|
|
285
|
+
agentId: input.fence.agentId,
|
|
286
|
+
adapterId,
|
|
287
|
+
launchId: input.fence.launchId,
|
|
288
|
+
nativeSessionId: input.fence.nativeSessionId,
|
|
289
|
+
turnId: input.fence.nativeTurnId,
|
|
290
|
+
attemptId: input.fence.receiptId,
|
|
291
|
+
runId: input.fence.runId,
|
|
292
|
+
summary,
|
|
293
|
+
providerStatus
|
|
294
|
+
};
|
|
295
|
+
const classification = this.classifyRuntimeTurnCompleted(completed);
|
|
296
|
+
if (classification !== "apply")
|
|
297
|
+
return classification;
|
|
298
|
+
const result = this.observeRuntimeTurnCompleted(completed, now);
|
|
299
|
+
return result.disposition === "obsolete" ? "obsolete" : "applied";
|
|
300
|
+
}
|
|
295
301
|
validateCanonicalSessionObservation(input, now) {
|
|
296
302
|
return this.store.transaction((store) => {
|
|
297
303
|
const run = store.getAgentRun(input.fence.taskId, input.fence.runId);
|
|
@@ -381,7 +387,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
381
387
|
&& active.status === "active"
|
|
382
388
|
&& active.deliveredAt !== undefined
|
|
383
389
|
&& role !== null) {
|
|
384
|
-
store.saveRole(input.fence.taskId, updateRoleStatus(role, "detached", now));
|
|
385
390
|
if (role.name === "leader") {
|
|
386
391
|
const message = [
|
|
387
392
|
`Leader native Session became unavailable while Run ${active.id} remains active.`,
|
|
@@ -827,10 +832,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
827
832
|
return existing && !recovered ? "already-recorded" : "recorded";
|
|
828
833
|
});
|
|
829
834
|
}
|
|
830
|
-
hasInFlightTurn(taskId, roleName) {
|
|
831
|
-
const sessions = this.store.getTaskRoleSessionSet(taskId, roleName);
|
|
832
|
-
return sessions !== null && sessions.inFlight !== null;
|
|
833
|
-
}
|
|
834
835
|
beginAgentHostProviderTurn(input) {
|
|
835
836
|
this.store.transaction((store) => {
|
|
836
837
|
const sessions = store.getTaskRoleSessionSet(input.taskId, input.roleName);
|
|
@@ -844,7 +845,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
844
845
|
if (sessions === null || sessions === undefined
|
|
845
846
|
|| binding === null || binding === undefined
|
|
846
847
|
|| sessions.inFlight?.runId !== input.runId
|
|
847
|
-
|| binding.runId !== input.runId
|
|
848
848
|
|| session?.launchId !== input.launchId
|
|
849
849
|
|| session.nativeSessionId !== input.nativeSessionId
|
|
850
850
|
|| currentProviderConversation(binding).conversationId !== input.nativeSessionId
|
|
@@ -854,7 +854,8 @@ export class FileSchedulerStoreAdapter {
|
|
|
854
854
|
|| !processingOwnsExactInitialDelivery(mailbox, input.taskId, input.runId, input.attemptId)) {
|
|
855
855
|
throw new AgentHostProviderTurnFenceError("Agent Host Provider Turn carries a stale durable writer fence. Release and reacquire Provider authority before retrying input.");
|
|
856
856
|
}
|
|
857
|
-
const exactReplay = binding.turn?.
|
|
857
|
+
const exactReplay = binding.turn?.runId === input.runId
|
|
858
|
+
&& binding.turn.attemptId === input.attemptId
|
|
858
859
|
&& binding.turn.authorityEpoch === input.authorityEpoch
|
|
859
860
|
&& binding.turn.status === "submitting";
|
|
860
861
|
if (!exactReplay && binding.turn !== null
|
|
@@ -863,6 +864,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
863
864
|
throw new AgentHostProviderTurnFenceError("Provider Conversation already has an unsettled Turn.");
|
|
864
865
|
}
|
|
865
866
|
store.saveTaskRoleSessionSet(updateTaskRoleProviderRuntime(sessions, beginProviderTurn(binding, {
|
|
867
|
+
runId: input.runId,
|
|
866
868
|
attemptId: input.attemptId,
|
|
867
869
|
authorityEpoch: input.authorityEpoch,
|
|
868
870
|
submittedAt: input.now.toISOString()
|
|
@@ -875,7 +877,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
875
877
|
const binding = sessions?.providerBinding;
|
|
876
878
|
if (sessions === null || sessions === undefined
|
|
877
879
|
|| binding === null || binding === undefined
|
|
878
|
-
|| binding.runId !== input.runId
|
|
880
|
+
|| binding.turn?.runId !== input.runId
|
|
879
881
|
|| binding.turn?.attemptId !== input.attemptId) {
|
|
880
882
|
throw new Error("Agent Host Provider Turn submission is no longer current.");
|
|
881
883
|
}
|
|
@@ -893,7 +895,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
893
895
|
const session = sessions?.sessions[input.agentId];
|
|
894
896
|
const binding = sessions?.providerBinding;
|
|
895
897
|
if (binding === null || binding === undefined
|
|
896
|
-
|| binding.runId !== input.runId
|
|
897
898
|
|| session?.launchId !== input.launchId
|
|
898
899
|
|| session.nativeSessionId !== input.nativeSessionId
|
|
899
900
|
|| currentProviderConversation(binding).conversationId !== input.nativeSessionId)
|
|
@@ -1022,7 +1023,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
1022
1023
|
const binding = sessions?.providerBinding;
|
|
1023
1024
|
if (sessions === null || sessions === undefined
|
|
1024
1025
|
|| binding === null || binding === undefined
|
|
1025
|
-
|| binding.runId !== input.executionRef.id
|
|
1026
1026
|
|| binding.authority.owner !== "controller") {
|
|
1027
1027
|
throw new Error("Controller does not own the Provider writer authority.");
|
|
1028
1028
|
}
|
|
@@ -1034,6 +1034,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1034
1034
|
throw new Error("Provider input claim carries a stale Conversation/Activation fence.");
|
|
1035
1035
|
}
|
|
1036
1036
|
const next = beginProviderTurn(binding, {
|
|
1037
|
+
runId: input.executionRef.id,
|
|
1037
1038
|
attemptId: input.attemptId,
|
|
1038
1039
|
authorityEpoch: binding.authority.epoch,
|
|
1039
1040
|
submittedAt: input.now.toISOString()
|
|
@@ -1282,7 +1283,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
1282
1283
|
store.saveAgentRun(input.run);
|
|
1283
1284
|
store.saveActiveAgentRun(input.run);
|
|
1284
1285
|
store.saveWorkMailbox(claimed);
|
|
1285
|
-
store.saveRole(input.task.id, updateRoleStatus(role, "running", input.now));
|
|
1286
1286
|
bindTaskRoleRunInFlight(store, role, input.run, input.now);
|
|
1287
1287
|
store.saveEvent(input.task.id, createTaskEvent(store.nextEventId(input.task.id), input.task.id, "run.dispatched", runLaunchEventPayload(input.run), input.now));
|
|
1288
1288
|
if (input.wakeId !== undefined && input.wakeFromCursor !== undefined) {
|
|
@@ -1368,9 +1368,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
1368
1368
|
markTaskRoleRunPushedInFlight(store, role, deliveryRun, input.now);
|
|
1369
1369
|
}
|
|
1370
1370
|
}
|
|
1371
|
-
if (role.status !== "running") {
|
|
1372
|
-
store.saveRole(input.task.id, updateRoleStatus(role, "running", input.now));
|
|
1373
|
-
}
|
|
1374
1371
|
if (input.session !== null && input.session.nativeSessionId !== undefined) {
|
|
1375
1372
|
const existing = store.getRoleSession(input.task.id, input.role.name);
|
|
1376
1373
|
const reservation = matchingPreparedRuntimeReservation(store, input);
|
|
@@ -1494,10 +1491,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
1494
1491
|
routeRoleEvent(store, deliveryFailureEvent, input.roleName, terminal.purpose === "review" ? "review-failed" : "role-run-failed", input.now);
|
|
1495
1492
|
}
|
|
1496
1493
|
else {
|
|
1497
|
-
const failedRole = store.getRole(input.taskId, input.roleName);
|
|
1498
|
-
if (failedRole !== null) {
|
|
1499
|
-
store.saveRole(input.taskId, updateRoleStatus(failedRole, "failed", input.now));
|
|
1500
|
-
}
|
|
1501
1494
|
store.saveLeaderFailure(recordLeaderFailure(input.taskId, session?.nativeSessionId ?? "(unregistered)", summary, input.now, store.getLeaderFailure(input.taskId)));
|
|
1502
1495
|
routeRoleEvent(store, deliveryFailureEvent, input.roleName, "leader-run-failed", input.now);
|
|
1503
1496
|
}
|
|
@@ -1534,7 +1527,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
1534
1527
|
// Consume this failed delivery attempt. Releasing it would immediately
|
|
1535
1528
|
// redispatch the same wake and manufacture another Leader Session.
|
|
1536
1529
|
store.saveWorkMailbox(completeProcessing(mailbox, mailbox.processing.batchId));
|
|
1537
|
-
store.saveRole(input.task.id, updateRoleStatus(role, "failed", input.now));
|
|
1538
1530
|
breakTaskSessionIfPresent(store, input.task.id, role.name, active.effective.agentId, input.now);
|
|
1539
1531
|
const failure = recordLeaderFailure(input.task.id, input.failure.nativeSessionId, input.failure.message, input.now, store.getLeaderFailure(input.task.id));
|
|
1540
1532
|
store.saveLeaderFailure(failure);
|
|
@@ -1599,7 +1591,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
1599
1591
|
}, input.now);
|
|
1600
1592
|
if (terminal.disposition !== "applied")
|
|
1601
1593
|
return "state-changed";
|
|
1602
|
-
store.saveRole(task.id, updateRoleStatus(role, "exited", input.now));
|
|
1603
1594
|
stopTaskSessionIfPresent(store, task.id, role.name, currentRun.effective.agentId, input.now);
|
|
1604
1595
|
queueLeaderWakeup(store, task.id, wakeReason("review-failed"), input.now);
|
|
1605
1596
|
return "failed";
|
|
@@ -1639,7 +1630,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
1639
1630
|
}
|
|
1640
1631
|
}
|
|
1641
1632
|
const leaderFailed = role.name === "leader";
|
|
1642
|
-
store.saveRole(task.id, updateRoleStatus(role, leaderFailed ? "failed" : "exited", input.now));
|
|
1643
1633
|
stopTaskSessionIfPresent(store, task.id, role.name, currentRun.effective.agentId, input.now);
|
|
1644
1634
|
if (leaderFailed) {
|
|
1645
1635
|
const failure = recordLeaderFailure(task.id, input.session?.nativeSessionId ?? "(unregistered)", input.summary, input.now, store.getLeaderFailure(task.id));
|
|
@@ -1860,6 +1850,19 @@ export class FileSchedulerStoreAdapter {
|
|
|
1860
1850
|
catch {
|
|
1861
1851
|
return "obsolete";
|
|
1862
1852
|
}
|
|
1853
|
+
const providerTurn = sessions?.providerBinding?.turn;
|
|
1854
|
+
const canonicalTurnId = sessions === null || sessions === undefined
|
|
1855
|
+
? input.turnId
|
|
1856
|
+
: canonicalStructuredProviderTurnId(sessions, input.turnId, input.attemptId);
|
|
1857
|
+
const recordedProviderTurn = input.runId !== undefined
|
|
1858
|
+
&& providerTurn?.runId === input.runId
|
|
1859
|
+
&& providerTurn.turnId === canonicalTurnId;
|
|
1860
|
+
if (recordedProviderTurn) {
|
|
1861
|
+
const run = this.store.getAgentRun(input.taskId, input.runId);
|
|
1862
|
+
return run === null
|
|
1863
|
+
? "obsolete"
|
|
1864
|
+
: run.pushedAt === undefined ? "deferred" : "apply";
|
|
1865
|
+
}
|
|
1863
1866
|
// A normal explicit CLI yield may precede its native Hook; that Hook still
|
|
1864
1867
|
// owns the turn fact. A forced cleanup boundary or stopped process instead
|
|
1865
1868
|
// makes the old generation obsolete.
|
|
@@ -1893,6 +1896,13 @@ export class FileSchedulerStoreAdapter {
|
|
|
1893
1896
|
let sessions = store.getTaskRoleSessionSet(input.taskId, input.roleName)
|
|
1894
1897
|
?? createRoleSessionSet({ scope: "task", taskId: input.taskId, roleName: input.roleName }, input.agentId, now);
|
|
1895
1898
|
const canonicalTurnId = canonicalStructuredProviderTurnId(sessions, input.turnId, input.attemptId);
|
|
1899
|
+
const providerTurn = sessions.providerBinding?.turn;
|
|
1900
|
+
const recordedProviderTurn = input.runId !== undefined
|
|
1901
|
+
&& providerTurn?.runId === input.runId
|
|
1902
|
+
&& providerTurn.turnId === canonicalTurnId;
|
|
1903
|
+
const observedRun = input.runId === undefined
|
|
1904
|
+
? null
|
|
1905
|
+
: store.getAgentRun(input.taskId, input.runId);
|
|
1896
1906
|
const existing = sessions.sessions[input.agentId];
|
|
1897
1907
|
const owner = {
|
|
1898
1908
|
scope: "task",
|
|
@@ -1912,7 +1922,8 @@ export class FileSchedulerStoreAdapter {
|
|
|
1912
1922
|
&& hasRecentTurnId(effectiveExisting.recentCompletedTurnIds, canonicalTurnId)) {
|
|
1913
1923
|
return { session: effectiveExisting, duplicate: true };
|
|
1914
1924
|
}
|
|
1915
|
-
if (
|
|
1925
|
+
if (!recordedProviderTurn
|
|
1926
|
+
&& sessions.inFlight === null
|
|
1916
1927
|
&& existing !== undefined
|
|
1917
1928
|
&& (hasRuntimeCleanupObligation(store.getWorkMailbox(runtimeLifecycleTarget(owner)))
|
|
1918
1929
|
|| isObsoleteTerminalRuntimeRun(store, input))) {
|
|
@@ -1920,7 +1931,8 @@ export class FileSchedulerStoreAdapter {
|
|
|
1920
1931
|
}
|
|
1921
1932
|
// Classification is only an optimization. Revalidate the Run inside the
|
|
1922
1933
|
// authoritative transaction before a Hook may claim a native identity.
|
|
1923
|
-
if (
|
|
1934
|
+
if (!recordedProviderTurn
|
|
1935
|
+
&& sessions.inFlight !== null
|
|
1924
1936
|
&& (input.runId === undefined
|
|
1925
1937
|
|| sessions.inFlight.runId !== input.runId)) {
|
|
1926
1938
|
if (effectiveExisting === undefined) {
|
|
@@ -1928,7 +1940,9 @@ export class FileSchedulerStoreAdapter {
|
|
|
1928
1940
|
}
|
|
1929
1941
|
return { session: effectiveExisting, duplicate: false };
|
|
1930
1942
|
}
|
|
1931
|
-
if (
|
|
1943
|
+
if (recordedProviderTurn
|
|
1944
|
+
? observedRun?.pushedAt === undefined
|
|
1945
|
+
: sessions.inFlight !== null && sessions.inFlight.pushedAt === undefined) {
|
|
1932
1946
|
throw new Error("Runtime turn completion requires an independently committed transport receipt.");
|
|
1933
1947
|
}
|
|
1934
1948
|
const idleStatus = effectiveExisting?.status === "stopped"
|
|
@@ -2016,6 +2030,11 @@ export class FileSchedulerStoreAdapter {
|
|
|
2016
2030
|
recordObsoleteRuntimeEvent(store, input, "run-missing", now);
|
|
2017
2031
|
return { disposition: "obsolete", runId: input.runId };
|
|
2018
2032
|
}
|
|
2033
|
+
const sessions = store.getTaskRoleSessionSet(input.taskId, input.roleName);
|
|
2034
|
+
if (sessions?.providerBinding?.turn?.runId === input.runId
|
|
2035
|
+
&& sessions.providerBinding.turn.turnId === input.nativeTurnId) {
|
|
2036
|
+
store.saveTaskRoleSessionSet(settleStructuredProviderTurn(sessions, input.nativeTurnId, "failed", now));
|
|
2037
|
+
}
|
|
2019
2038
|
const summary = runtimeTurnFailureSummary(input);
|
|
2020
2039
|
// Issue 04: a classified transient Provider failure keeps the exact Run
|
|
2021
2040
|
// and Native Session and is retried in place instead of terminalizing.
|
|
@@ -2045,10 +2064,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
2045
2064
|
roleName: input.roleName
|
|
2046
2065
|
}), RUNTIME_CLEANUP_REQUIRED_REASON, now, [{ type: "task", id: input.taskId }]);
|
|
2047
2066
|
const terminal = result.run;
|
|
2048
|
-
const role = store.getRole(input.taskId, input.roleName);
|
|
2049
|
-
if (role !== null) {
|
|
2050
|
-
store.saveRole(input.taskId, updateRoleStatus(role, input.roleName === "leader" ? "failed" : "idle", now));
|
|
2051
|
-
}
|
|
2052
2067
|
const failureEvent = createTaskEvent(store.nextEventId(input.taskId), input.taskId, "runtime.turn-failed", {
|
|
2053
2068
|
eventId: input.eventId,
|
|
2054
2069
|
runId: input.runId,
|
|
@@ -2555,7 +2570,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
2555
2570
|
const sessions = store.getTaskRoleSessionSet(taskId, input.fence.roleName);
|
|
2556
2571
|
const run = store.getAgentRun(taskId, input.fence.runId);
|
|
2557
2572
|
if (sessions === null || sessions.providerBinding === null || run === null
|
|
2558
|
-
|| sessions.providerBinding.runId !== input.fence.runId
|
|
2559
2573
|
|| input.fence.conversationId === undefined) {
|
|
2560
2574
|
recordCanonicalObservationObsolete(store, input, "provider-binding-missing", now);
|
|
2561
2575
|
return "obsolete";
|
|
@@ -2747,10 +2761,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
2747
2761
|
return "obsolete";
|
|
2748
2762
|
}
|
|
2749
2763
|
store.saveTaskRoleSessionSet(recordStructuredProviderAcceptance(updateRoleAgentSessionStatus(sessions, input.fence.agentId, "running", now), input, now));
|
|
2750
|
-
const role = store.getRole(input.fence.taskId, input.fence.roleName);
|
|
2751
|
-
if (role !== null && role.status !== "running") {
|
|
2752
|
-
store.saveRole(input.fence.taskId, updateRoleStatus(role, "running", now));
|
|
2753
|
-
}
|
|
2754
2764
|
store.saveEvent(input.fence.taskId, createTaskEvent(store.nextEventId(input.fence.taskId), input.fence.taskId, "run.input-delivered", {
|
|
2755
2765
|
attemptId: inputDelivery.attemptId,
|
|
2756
2766
|
runId: active.id,
|
|
@@ -3339,10 +3349,6 @@ function finalizeProviderRetryDeadline(store, run, summary, reason, now) {
|
|
|
3339
3349
|
elapsedMs: String(Math.max(0, now.getTime() - Date.parse(retry.firstFailureAt)))
|
|
3340
3350
|
}, now);
|
|
3341
3351
|
store.saveEvent(run.taskId, exhaustionEvent);
|
|
3342
|
-
const role = store.getRole(run.taskId, run.roleName);
|
|
3343
|
-
if (role !== null) {
|
|
3344
|
-
store.saveRole(run.taskId, updateRoleStatus(role, run.roleName === "leader" ? "failed" : "idle", now));
|
|
3345
|
-
}
|
|
3346
3352
|
if (run.roleName === "leader") {
|
|
3347
3353
|
store.saveLeaderFailure(recordLeaderFailure(run.taskId, retry.nativeSessionId ?? "(unproven)", summary, now, store.getLeaderFailure(run.taskId)));
|
|
3348
3354
|
}
|
|
@@ -3527,8 +3533,7 @@ function mapRole(store, role) {
|
|
|
3527
3533
|
...(binding.config.effort === undefined ? {} : { effort: binding.config.effort }),
|
|
3528
3534
|
effective,
|
|
3529
3535
|
workspace: role.workspace,
|
|
3530
|
-
...(workspace === undefined ? {} : { managedWorkspace: workspace })
|
|
3531
|
-
status: role.status
|
|
3536
|
+
...(workspace === undefined ? {} : { managedWorkspace: workspace })
|
|
3532
3537
|
};
|
|
3533
3538
|
}
|
|
3534
3539
|
function taskSessionEffective(store, taskId, roleName, agentId, existing) {
|
|
@@ -3822,7 +3827,9 @@ function recordStructuredProviderAcceptance(sessions, input, now) {
|
|
|
3822
3827
|
const turnId = input.fence.nativeTurnId;
|
|
3823
3828
|
if (current === null || attemptId === undefined || turnId === undefined)
|
|
3824
3829
|
return sessions;
|
|
3825
|
-
if (current.turn
|
|
3830
|
+
if (current.turn === null || current.turn.runId !== input.fence.runId)
|
|
3831
|
+
return sessions;
|
|
3832
|
+
if (current.turn.attemptId === attemptId
|
|
3826
3833
|
&& ["accepted", "running", "completed", "failed", "cancelled"].includes(current.turn.status))
|
|
3827
3834
|
return sessions;
|
|
3828
3835
|
const binding = acceptProviderTurn(current, {
|
|
@@ -3855,7 +3862,6 @@ function bindOrSupersedeProviderRuntime(sessions, input, now, replacementBasis)
|
|
|
3855
3862
|
return bindTaskRoleProviderRuntime(sessions, createProviderRuntimeBinding({
|
|
3856
3863
|
providerNamespace: input.fence.driverId,
|
|
3857
3864
|
accountScope: input.fence.agentId,
|
|
3858
|
-
runId: sessions.inFlight.runId,
|
|
3859
3865
|
conversationId,
|
|
3860
3866
|
activationId,
|
|
3861
3867
|
startedAt: sessions.inFlight.preparedAt
|
|
@@ -3893,7 +3899,7 @@ function rejectSubmittingProviderTurn(store, target, delivery, now, reason) {
|
|
|
3893
3899
|
const binding = sessions?.providerBinding;
|
|
3894
3900
|
if (sessions === null || sessions === undefined
|
|
3895
3901
|
|| binding === null || binding === undefined
|
|
3896
|
-
|| binding.runId !== delivery.executionRef.id
|
|
3902
|
+
|| binding.turn?.runId !== delivery.executionRef.id
|
|
3897
3903
|
|| binding.turn?.attemptId !== delivery.attemptId
|
|
3898
3904
|
|| (binding.turn.status !== "submitting"
|
|
3899
3905
|
&& binding.turn.status !== "delivery-unknown"))
|
|
@@ -3931,7 +3937,7 @@ function markSubmittingProviderTurnUnknown(store, target, delivery, now, reason)
|
|
|
3931
3937
|
const binding = sessions?.providerBinding;
|
|
3932
3938
|
if (sessions === null || sessions === undefined
|
|
3933
3939
|
|| binding === null || binding === undefined
|
|
3934
|
-
|| binding.runId !== delivery.executionRef.id
|
|
3940
|
+
|| binding.turn?.runId !== delivery.executionRef.id
|
|
3935
3941
|
|| binding.turn?.attemptId !== delivery.attemptId
|
|
3936
3942
|
|| binding.turn.status !== "submitting")
|
|
3937
3943
|
return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { hasRecentTurnId, rememberRecentTurnId, validatePendingTurnCompletion, validateRecentTurnIds } from "./turnCompletion.js";
|
|
3
3
|
import { effectiveLaunchSnapshotsCompatible, effectiveLaunchSnapshotsCompatibleForTaskSession, validateEffectiveLaunchSnapshot } from "./effectiveLaunch.js";
|
|
4
|
-
import { currentProviderActivation, endProviderActivation,
|
|
4
|
+
import { currentProviderActivation, endProviderActivation, validateProviderRuntimeBinding } from "../runtime/providerRuntimeIdentity.js";
|
|
5
5
|
import { builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
|
|
6
6
|
export function createRoleSessionSet(owner, activeAgentId, now) {
|
|
7
7
|
const base = {
|
|
@@ -14,7 +14,7 @@ export function createRoleSessionSet(owner, activeAgentId, now) {
|
|
|
14
14
|
? { ...base, schemaVersion: 3 }
|
|
15
15
|
: {
|
|
16
16
|
...base,
|
|
17
|
-
schemaVersion:
|
|
17
|
+
schemaVersion: 7,
|
|
18
18
|
inFlight: null,
|
|
19
19
|
providerBinding: null
|
|
20
20
|
};
|
|
@@ -298,15 +298,14 @@ export function bindTaskRoleRun(set, fence, preparedAt, mode) {
|
|
|
298
298
|
throw new Error("Task Role session set already has an in-flight Run.");
|
|
299
299
|
}
|
|
300
300
|
const timestamp = requireDate(preparedAt, "Task Role Run preparedAt");
|
|
301
|
-
// A fresh Session follows physical cleanup and does not inherit
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
301
|
+
// A fresh Session follows physical cleanup and does not inherit runtime
|
|
302
|
+
// evidence from the disposable old Conversation. A resumed Session keeps
|
|
303
|
+
// that evidence unchanged here: claiming a durable Run is a workflow
|
|
304
|
+
// mutation, not a Provider operation. AgentHost binds the Conversation to
|
|
305
|
+
// this Run only when it can actually submit the next serialized Turn.
|
|
305
306
|
const providerBinding = mode === "new"
|
|
306
307
|
? null
|
|
307
|
-
: set.providerBinding
|
|
308
|
-
? rebindProviderRuntimeRun(set.providerBinding, normalized.runId)
|
|
309
|
-
: null;
|
|
308
|
+
: set.providerBinding;
|
|
310
309
|
const updated = {
|
|
311
310
|
...set,
|
|
312
311
|
inFlight: { ...normalized, preparedAt: timestamp },
|
|
@@ -340,9 +339,6 @@ export function prepareTaskRoleRunRedispatch(set, fence, preparedAt) {
|
|
|
340
339
|
export function bindTaskRoleProviderRuntime(set, binding, updatedAt) {
|
|
341
340
|
validateRoleSessionSet(set);
|
|
342
341
|
const normalized = validateProviderRuntimeBinding(binding);
|
|
343
|
-
if (set.inFlight === null || set.inFlight.runId !== normalized.runId) {
|
|
344
|
-
throw new Error("Provider Runtime Binding does not match the in-flight Run.");
|
|
345
|
-
}
|
|
346
342
|
if (set.providerBinding !== null) {
|
|
347
343
|
if (JSON.stringify(set.providerBinding) === JSON.stringify(normalized))
|
|
348
344
|
return set;
|
|
@@ -358,7 +354,6 @@ export function updateTaskRoleProviderRuntime(set, binding, updatedAt) {
|
|
|
358
354
|
validateRoleSessionSet(set);
|
|
359
355
|
const normalized = validateProviderRuntimeBinding(binding);
|
|
360
356
|
if (set.providerBinding === null
|
|
361
|
-
|| normalized.runId !== set.providerBinding.runId
|
|
362
357
|
|| normalized.providerNamespace !== set.providerBinding.providerNamespace
|
|
363
358
|
|| normalized.accountScope !== set.providerBinding.accountScope) {
|
|
364
359
|
throw new Error("Provider Runtime Binding identity cannot change in place.");
|
|
@@ -525,21 +520,15 @@ export function clearTaskRoleRun(set, fence, clearedAt) {
|
|
|
525
520
|
return validateRoleSessionSet(updated);
|
|
526
521
|
}
|
|
527
522
|
/**
|
|
528
|
-
* Applies an authoritative application-level Run terminal fact to
|
|
529
|
-
*
|
|
530
|
-
*
|
|
523
|
+
* Applies an authoritative application-level Run terminal fact to its exact
|
|
524
|
+
* delivery receipt. Provider lifecycle remains independent: only the matching
|
|
525
|
+
* native Turn boundary may move the Session from running to ready. AgentHost
|
|
526
|
+
* serializes a retained next delivery while that Turn is still active.
|
|
531
527
|
*/
|
|
532
528
|
export function terminalizeTaskRoleRunSession(set, fence, terminalAt) {
|
|
533
529
|
validateRoleSessionSet(set);
|
|
534
530
|
const inFlight = set.inFlight;
|
|
535
|
-
|
|
536
|
-
? set
|
|
537
|
-
: clearTaskRoleRun(set, fence, terminalAt);
|
|
538
|
-
const session = updated.sessions[updated.activeAgentId];
|
|
539
|
-
if (session?.status === "running") {
|
|
540
|
-
updated = updateRoleAgentSessionStatus(updated, updated.activeAgentId, "ready", terminalAt);
|
|
541
|
-
}
|
|
542
|
-
return updated;
|
|
531
|
+
return inFlight === null ? set : clearTaskRoleRun(set, fence, terminalAt);
|
|
543
532
|
}
|
|
544
533
|
export function settleTaskRoleCompletion(set, expected, settledAt) {
|
|
545
534
|
validateRoleSessionSet(set);
|
|
@@ -597,7 +586,7 @@ export function validateRoleSessionSet(set) {
|
|
|
597
586
|
}
|
|
598
587
|
}
|
|
599
588
|
else {
|
|
600
|
-
if (set.schemaVersion !==
|
|
589
|
+
if (set.schemaVersion !== 7) {
|
|
601
590
|
throw new Error("Task Role session set schema version is invalid.");
|
|
602
591
|
}
|
|
603
592
|
if (!Object.hasOwn(set, "inFlight")
|
|
@@ -637,9 +626,6 @@ export function validateRoleSessionSet(set) {
|
|
|
637
626
|
throw new Error("Task Role in-flight Run Agent must be active.");
|
|
638
627
|
}
|
|
639
628
|
if (providerBinding !== null) {
|
|
640
|
-
if (inFlight !== null && providerBinding.runId !== inFlight.runId) {
|
|
641
|
-
throw new Error("Provider Runtime Binding must match the in-flight Run.");
|
|
642
|
-
}
|
|
643
629
|
const session = taskSet.sessions[inFlight?.agentId ?? set.activeAgentId];
|
|
644
630
|
if (session === undefined) {
|
|
645
631
|
throw new Error("Provider Runtime Binding has no active Role Agent session.");
|
|
@@ -608,8 +608,7 @@ export class FileRoleLaunchPlanner {
|
|
|
608
608
|
role: {
|
|
609
609
|
name: role.name,
|
|
610
610
|
workspace: effectiveWorkspace,
|
|
611
|
-
...(agentWorkspace === effectiveWorkspace ? {} : { cwd: agentWorkspace })
|
|
612
|
-
...(owner.scope === "task" ? { status: role.status } : {})
|
|
611
|
+
...(agentWorkspace === effectiveWorkspace ? {} : { cwd: agentWorkspace })
|
|
613
612
|
},
|
|
614
613
|
launch: ordinaryConversationLaunch,
|
|
615
614
|
session,
|
|
@@ -659,7 +658,7 @@ export class FileRoleLaunchPlanner {
|
|
|
659
658
|
}
|
|
660
659
|
#providerOwnedTurnForLaunch(taskId, roleName, runId) {
|
|
661
660
|
const binding = this.store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding;
|
|
662
|
-
if (binding === null || binding === undefined || binding.runId !== runId)
|
|
661
|
+
if (binding === null || binding === undefined || binding.turn?.runId !== runId)
|
|
663
662
|
return undefined;
|
|
664
663
|
const turn = binding.turn;
|
|
665
664
|
if (turn === null || !["accepted", "running"].includes(turn.status))
|
|
@@ -2,7 +2,6 @@ import { isDeepStrictEqual } from "node:util";
|
|
|
2
2
|
import { completeProcessing } from "../coordination/workMailbox.js";
|
|
3
3
|
import { settleExactWorkExecution } from "../coordination/workMailboxQueue.js";
|
|
4
4
|
import { terminalizeTaskRoleRunSession } from "../executor/agentExecutor.js";
|
|
5
|
-
import { updateRoleStatus } from "../role/role.js";
|
|
6
5
|
import { finishReviewRound, updateReviewExecutionGroup } from "../review/reviewRound.js";
|
|
7
6
|
import { reconcileReviewFindingsAfterReview } from "../review/reviewFindingLedger.js";
|
|
8
7
|
import { agentRunDeliveryReceiptId, failAgentRun, withYieldReceipt, yieldAgentRun } from "../run/agentRun.js";
|
|
@@ -265,7 +264,7 @@ export function retireExactActiveAgentRun(store, input, now) {
|
|
|
265
264
|
const session = sessions?.sessions[input.agentId];
|
|
266
265
|
const providerBinding = sessions?.providerBinding;
|
|
267
266
|
const providerTurn = providerBinding?.turn;
|
|
268
|
-
const providerSettled =
|
|
267
|
+
const providerSettled = providerTurn?.runId === current.id
|
|
269
268
|
&& (providerTurn?.status === "completed"
|
|
270
269
|
|| providerTurn?.status === "failed"
|
|
271
270
|
|| providerTurn?.status === "cancelled"
|
|
@@ -450,7 +449,6 @@ export function terminalizeExactTaskRun(store, input, now) {
|
|
|
450
449
|
else {
|
|
451
450
|
store.clearActiveAgentRun(input.taskId, input.roleName);
|
|
452
451
|
}
|
|
453
|
-
store.saveRole(input.taskId, updateRoleStatus(role, "idle", now));
|
|
454
452
|
if (sessions !== null) {
|
|
455
453
|
store.saveTaskRoleSessionSet(terminalizeTaskRoleRunSession(sessions, {
|
|
456
454
|
agentId: input.agentId,
|
|
@@ -23,7 +23,6 @@ export function renderRoleDetails(title, role, input) {
|
|
|
23
23
|
const overview = [
|
|
24
24
|
` Kind ${input.kind}`,
|
|
25
25
|
` Active Agent ${role.activeAgentId}`,
|
|
26
|
-
...("status" in role ? [` Status ${role.status}`] : []),
|
|
27
26
|
` Workspace ${role.workspace}`,
|
|
28
27
|
` Desired launch r${role.launchRevision}; Profile intent=${role.defaultAccess}`,
|
|
29
28
|
` Effective launch ${effective === undefined
|
|
@@ -2764,7 +2764,6 @@ function retireWorkspaceBoundSession(store, taskId, roleName, now) {
|
|
|
2764
2764
|
}
|
|
2765
2765
|
function canCorrectActiveWorkItemRoleWorkspaceHint(store, taskId, role, item, workspace) {
|
|
2766
2766
|
if (role.taskId !== taskId
|
|
2767
|
-
|| role.status !== "running"
|
|
2768
2767
|
|| item.taskId !== taskId
|
|
2769
2768
|
|| item.assignee !== role.name
|
|
2770
2769
|
|| ["completed", "failed", "retired"].includes(item.status)
|