@zq-silk/yui 0.13.7 → 0.13.9
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 +23 -4
- package/dist/cli/commandCatalog.js +21 -2
- package/dist/cli/updatePorts.js +4 -4
- package/dist/cli.js +43 -14
- package/dist/commands/agentCommands.js +1 -1
- package/dist/commands/configCommands.js +1 -86
- package/dist/commands/executionAuditCommands.js +17 -16
- package/dist/commands/globalRoleCommands.js +4 -4
- package/dist/commands/sessionCommands.js +2 -6
- package/dist/commands/taskActor.js +1 -2
- package/dist/commands/taskCommands.js +93 -21
- 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 +3 -16
- package/dist/config/configCatalog.js +1 -6
- package/dist/config/yuiConfig.js +0 -79
- package/dist/context/sessionBootstrapManifest.js +26 -23
- package/dist/controller/clientRuntime.js +40 -3
- package/dist/controller/controller.js +29 -54
- package/dist/controller/fileSchedulerStoreAdapter.js +372 -1117
- package/dist/controller/runtime.js +12 -5
- package/dist/controller/runtimeHookRunFence.js +3 -9
- package/dist/controller/runtimeLaunchCoordinator.js +44 -67
- package/dist/controller/structuredProviderObservation.js +18 -5
- package/dist/coordination/workMailbox.js +4 -4
- package/dist/execution/executionHealth.js +1 -1
- package/dist/executor/agentExecutor.js +103 -93
- package/dist/executor/executorRegistry.js +8 -20
- package/dist/executor/fileRoleLaunchPlanner.js +26 -93
- package/dist/executor/turnCompletion.js +5 -5
- package/dist/lifecycle/exactRunTerminalization.js +2 -4
- package/dist/observability/executionAudit.js +40 -94
- package/dist/operator/operatorSessionHistory.js +7 -5
- package/dist/output/rolePresentation.js +0 -1
- package/dist/repository/taskWorkspacePreparer.js +0 -1
- package/dist/role/role.js +13 -21
- package/dist/run/agentRun.js +4 -54
- package/dist/runtime/agentDriver.js +2 -0
- package/dist/runtime/agentError.js +114 -0
- package/dist/runtime/agentHost.js +55 -82
- package/dist/runtime/builtinAgentDrivers.js +21 -9
- package/dist/runtime/builtinAgentErrorMappers.js +150 -0
- package/dist/runtime/exactControlPlane.js +6 -12
- package/dist/runtime/index.js +1 -2
- package/dist/runtime/launchBroker.js +5 -19
- package/dist/runtime/lifecycleReservation.js +20 -4
- package/dist/runtime/providerRuntimeIdentity.js +11 -19
- package/dist/runtime/runtimeBinding.js +0 -27
- package/dist/runtime/runtimeObservation.js +7 -16
- package/dist/runtime/runtimeSessionCandidate.js +3 -10
- package/dist/runtime/sessionLaunchRequest.js +1 -2
- package/dist/runtime/sessionReconciliation.js +2 -2
- package/dist/runtime/structuredProviderHost.js +44 -79
- package/dist/runtime/taskRuntimeIsolation.js +0 -7
- package/dist/runtime/tmuxAdapters.js +6 -49
- package/dist/scheduler/activeRoleRunDelivery.js +220 -178
- package/dist/scheduler/activeTaskProgress.js +1 -4
- package/dist/scheduler/leaderWakeupProcessor.js +123 -88
- package/dist/scheduler/roleRunLiveness.js +4 -1
- package/dist/scheduler/roleRunStall.js +10 -14
- package/dist/scheduler/wakeReason.js +4 -0
- package/dist/storage/migration/productionRegistry.js +475 -0
- package/dist/storage/sqliteSchema.js +54 -2
- package/dist/storage/sqliteStore.js +11 -44
- package/dist/storage/taskStore.js +8 -36
- package/dist/web/webSnapshot.js +3 -0
- package/i18n/README.zh-CN.md +11 -3
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +30 -8
- package/skills/yui-operator/SKILL.md +14 -6
- package/skills/yui-runtime/SKILL.md +11 -8
- package/dist/lifecycle/providerErrorClass.js +0 -152
- package/dist/run/providerRetry.js +0 -226
- package/dist/run/providerRetryConfig.js +0 -27
- package/dist/runtime/providerErrorCodes.js +0 -278
- package/dist/runtime/providerRecoveryDecision.js +0 -55
|
@@ -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, settleProviderTurn, settleProviderTurnSubmission, 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 = {
|
|
@@ -11,10 +11,10 @@ export function createRoleSessionSet(owner, activeAgentId, now) {
|
|
|
11
11
|
updatedAt: now.toISOString()
|
|
12
12
|
};
|
|
13
13
|
return owner.scope === "global"
|
|
14
|
-
? { ...base, schemaVersion:
|
|
14
|
+
? { ...base, schemaVersion: 4 }
|
|
15
15
|
: {
|
|
16
16
|
...base,
|
|
17
|
-
schemaVersion:
|
|
17
|
+
schemaVersion: 8,
|
|
18
18
|
inFlight: null,
|
|
19
19
|
providerBinding: null
|
|
20
20
|
};
|
|
@@ -28,7 +28,7 @@ export function activeRoleAgentSession(set) {
|
|
|
28
28
|
/** The immutable snapshot that remains actual until its native process terminates. */
|
|
29
29
|
export function activeLiveRoleAgentSession(set) {
|
|
30
30
|
const session = activeRoleAgentSession(set);
|
|
31
|
-
return session === null || session.status === "
|
|
31
|
+
return session === null || session.status === "ended"
|
|
32
32
|
? null
|
|
33
33
|
: session;
|
|
34
34
|
}
|
|
@@ -73,13 +73,13 @@ export function recordRoleAgentSession(set, input, now) {
|
|
|
73
73
|
throw new Error(`Role Agent session effective launch cannot change: ${agentId}.`);
|
|
74
74
|
}
|
|
75
75
|
if (existing !== undefined && existing.nativeSessionId !== nativeSessionId
|
|
76
|
-
&& existing.status
|
|
76
|
+
&& existing.status === "active") {
|
|
77
77
|
throw new Error(`Live Role Agent native session cannot be replaced: ${agentId}.`);
|
|
78
78
|
}
|
|
79
79
|
const timestamp = now.toISOString();
|
|
80
80
|
const continuing = existing?.nativeSessionId === nativeSessionId ? existing : undefined;
|
|
81
81
|
const session = {
|
|
82
|
-
schemaVersion:
|
|
82
|
+
schemaVersion: 4,
|
|
83
83
|
agentId,
|
|
84
84
|
adapterId,
|
|
85
85
|
nativeSessionId,
|
|
@@ -91,6 +91,9 @@ export function recordRoleAgentSession(set, input, now) {
|
|
|
91
91
|
policy: input.policy,
|
|
92
92
|
effective: continuing?.effective ?? effective,
|
|
93
93
|
status: input.status,
|
|
94
|
+
...(input.status === "ended"
|
|
95
|
+
? { endReason: input.endReason ?? continuing?.endReason ?? "stopped" }
|
|
96
|
+
: {}),
|
|
94
97
|
recentCompletedTurnIds: continuing?.recentCompletedTurnIds ?? [],
|
|
95
98
|
createdAt: continuing?.createdAt ?? timestamp,
|
|
96
99
|
updatedAt: timestamp
|
|
@@ -127,7 +130,7 @@ export function replaceTaskRoleAgentSession(set, input, now) {
|
|
|
127
130
|
if (existing === undefined || existing.nativeSessionId === input.nativeSessionId) {
|
|
128
131
|
return recordRoleAgentSession(set, input, now);
|
|
129
132
|
}
|
|
130
|
-
const terminalized = updateRoleAgentSessionStatus(set, input.agentId, "
|
|
133
|
+
const terminalized = updateRoleAgentSessionStatus(set, input.agentId, "ended", now, "stopped");
|
|
131
134
|
return recordRoleAgentSession(terminalized, input, now);
|
|
132
135
|
}
|
|
133
136
|
/**
|
|
@@ -147,7 +150,7 @@ export function createRoleAgentSession(input, now) {
|
|
|
147
150
|
const set = createRoleSessionSet({ scope: "global", roleName: "session-factory" }, agentId, now);
|
|
148
151
|
return recordRoleAgentSession(set, input, now).sessions[agentId];
|
|
149
152
|
}
|
|
150
|
-
export function updateRoleAgentSessionStatus(set, agentId, status, now) {
|
|
153
|
+
export function updateRoleAgentSessionStatus(set, agentId, status, now, endReason) {
|
|
151
154
|
validateRoleSessionSet(set);
|
|
152
155
|
const normalizedAgentId = requireSafeIdentity(agentId, "Agent id");
|
|
153
156
|
if (!isAgentSessionStatus(status)) {
|
|
@@ -162,7 +165,12 @@ export function updateRoleAgentSessionStatus(set, agentId, status, now) {
|
|
|
162
165
|
...set,
|
|
163
166
|
sessions: {
|
|
164
167
|
...set.sessions,
|
|
165
|
-
[normalizedAgentId]: {
|
|
168
|
+
[normalizedAgentId]: {
|
|
169
|
+
...existing,
|
|
170
|
+
status,
|
|
171
|
+
...(status === "ended" ? { endReason: endReason ?? "stopped" } : { endReason: undefined }),
|
|
172
|
+
updatedAt: timestamp
|
|
173
|
+
}
|
|
166
174
|
},
|
|
167
175
|
updatedAt: timestamp
|
|
168
176
|
};
|
|
@@ -174,7 +182,7 @@ export function retireTaskRoleSessionsForWorkspace(set, now) {
|
|
|
174
182
|
if (set.inFlight !== null) {
|
|
175
183
|
throw new Error("Cannot retire a Task Role session with unsettled Run state.");
|
|
176
184
|
}
|
|
177
|
-
const live = Object.values(set.sessions).find(({ status }) => status
|
|
185
|
+
const live = Object.values(set.sessions).find(({ status }) => status === "active");
|
|
178
186
|
if (live !== undefined) {
|
|
179
187
|
throw new Error(`Task Role session must be stopped before workspace migration: ${live.agentId}.`);
|
|
180
188
|
}
|
|
@@ -205,13 +213,18 @@ export function retireConfirmedAbsentInactiveTaskRolePlaceholders(set, now) {
|
|
|
205
213
|
const sessions = Object.fromEntries(Object.entries(set.sessions).map(([agentId, session]) => {
|
|
206
214
|
const isNeverStartedInactiveClaude = agentId !== set.activeAgentId
|
|
207
215
|
&& session.adapterId === "claude"
|
|
208
|
-
&&
|
|
216
|
+
&& session.status === "active"
|
|
209
217
|
&& session.launchId === undefined
|
|
210
218
|
&& session.recentCompletedTurnIds.length === 0;
|
|
211
219
|
if (!isNeverStartedInactiveClaude)
|
|
212
220
|
return [agentId, session];
|
|
213
221
|
changed = true;
|
|
214
|
-
return [agentId, {
|
|
222
|
+
return [agentId, {
|
|
223
|
+
...session,
|
|
224
|
+
status: "ended",
|
|
225
|
+
endReason: "failed",
|
|
226
|
+
updatedAt: timestamp
|
|
227
|
+
}];
|
|
215
228
|
}));
|
|
216
229
|
if (!changed)
|
|
217
230
|
return set;
|
|
@@ -231,7 +244,7 @@ export function rememberRoleAgentCompletedTurn(set, agentId, nativeSessionId, tu
|
|
|
231
244
|
if (session.nativeSessionId !== requireText(nativeSessionId, "Native session id")) {
|
|
232
245
|
throw new Error("Completed Turn native session does not match the Role Agent session.");
|
|
233
246
|
}
|
|
234
|
-
const normalizedTurnId =
|
|
247
|
+
const normalizedTurnId = requireText(turnId, "Turn id");
|
|
235
248
|
if (hasRecentTurnId(session.recentCompletedTurnIds, normalizedTurnId))
|
|
236
249
|
return set;
|
|
237
250
|
const recentCompletedTurnIds = rememberRecentTurnId(session.recentCompletedTurnIds, normalizedTurnId);
|
|
@@ -244,9 +257,6 @@ export function rememberRoleAgentCompletedTurn(set, agentId, nativeSessionId, tu
|
|
|
244
257
|
...set.sessions,
|
|
245
258
|
[normalizedAgentId]: {
|
|
246
259
|
...session,
|
|
247
|
-
status: session.status === "stopped" || session.status === "broken"
|
|
248
|
-
? session.status
|
|
249
|
-
: "ready",
|
|
250
260
|
recentCompletedTurnIds,
|
|
251
261
|
updatedAt: timestamp
|
|
252
262
|
}
|
|
@@ -268,13 +278,13 @@ export function roleAgentSessionResumeMode(set, agentId, desired) {
|
|
|
268
278
|
// permits the normal fresh-generation path.
|
|
269
279
|
if (typeof session.nativeSessionId !== "string"
|
|
270
280
|
|| session.nativeSessionId.trim().length === 0) {
|
|
271
|
-
if (session.status
|
|
281
|
+
if (session.status === "active") {
|
|
272
282
|
throw new Error(`Role Agent session has no native Session identity: ${agentId}. `
|
|
273
283
|
+ "Restore the exact native Session or explicitly stop it before starting a fresh Session.");
|
|
274
284
|
}
|
|
275
285
|
return "new";
|
|
276
286
|
}
|
|
277
|
-
if (session.status === "
|
|
287
|
+
if (session.status === "ended") {
|
|
278
288
|
return "new";
|
|
279
289
|
}
|
|
280
290
|
const compatible = set.owner.scope === "task"
|
|
@@ -298,15 +308,14 @@ export function bindTaskRoleRun(set, fence, preparedAt, mode) {
|
|
|
298
308
|
throw new Error("Task Role session set already has an in-flight Run.");
|
|
299
309
|
}
|
|
300
310
|
const timestamp = requireDate(preparedAt, "Task Role Run preparedAt");
|
|
301
|
-
// A fresh Session follows physical cleanup and does not inherit
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
311
|
+
// A fresh Session follows physical cleanup and does not inherit runtime
|
|
312
|
+
// evidence from the disposable old Conversation. A resumed Session keeps
|
|
313
|
+
// that evidence unchanged here: claiming a durable Run is a workflow
|
|
314
|
+
// mutation, not a Provider operation. AgentHost binds the Conversation to
|
|
315
|
+
// this Run only when it can actually submit the next serialized Turn.
|
|
305
316
|
const providerBinding = mode === "new"
|
|
306
317
|
? null
|
|
307
|
-
: set.providerBinding
|
|
308
|
-
? rebindProviderRuntimeRun(set.providerBinding, normalized.runId)
|
|
309
|
-
: null;
|
|
318
|
+
: set.providerBinding;
|
|
310
319
|
const updated = {
|
|
311
320
|
...set,
|
|
312
321
|
inFlight: { ...normalized, preparedAt: timestamp },
|
|
@@ -315,34 +324,9 @@ export function bindTaskRoleRun(set, fence, preparedAt, mode) {
|
|
|
315
324
|
};
|
|
316
325
|
return validateRoleSessionSet(updated);
|
|
317
326
|
}
|
|
318
|
-
/** Re-fences the same active Run for an exact Provider retry without losing its Conversation. */
|
|
319
|
-
export function prepareTaskRoleRunRedispatch(set, fence, preparedAt) {
|
|
320
|
-
validateRoleSessionSet(set);
|
|
321
|
-
const normalized = normalizeTaskRoleRunFence(fence);
|
|
322
|
-
if (set.inFlight === null
|
|
323
|
-
|| set.inFlight.agentId !== normalized.agentId
|
|
324
|
-
|| set.inFlight.runId !== normalized.runId) {
|
|
325
|
-
throw new Error("Provider retry does not match the in-flight Task Run.");
|
|
326
|
-
}
|
|
327
|
-
if (set.providerBinding !== null
|
|
328
|
-
&& set.providerBinding.turn !== null
|
|
329
|
-
&& ["submitting", "accepted", "running", "delivery-unknown"]
|
|
330
|
-
.includes(set.providerBinding.turn.status)) {
|
|
331
|
-
throw new Error("Provider retry cannot redispatch an unsettled Turn.");
|
|
332
|
-
}
|
|
333
|
-
const timestamp = requireDate(preparedAt, "Task Role retry preparedAt");
|
|
334
|
-
return validateRoleSessionSet({
|
|
335
|
-
...set,
|
|
336
|
-
inFlight: { ...normalized, preparedAt: timestamp },
|
|
337
|
-
updatedAt: timestamp
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
327
|
export function bindTaskRoleProviderRuntime(set, binding, updatedAt) {
|
|
341
328
|
validateRoleSessionSet(set);
|
|
342
329
|
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
330
|
if (set.providerBinding !== null) {
|
|
347
331
|
if (JSON.stringify(set.providerBinding) === JSON.stringify(normalized))
|
|
348
332
|
return set;
|
|
@@ -358,7 +342,6 @@ export function updateTaskRoleProviderRuntime(set, binding, updatedAt) {
|
|
|
358
342
|
validateRoleSessionSet(set);
|
|
359
343
|
const normalized = validateProviderRuntimeBinding(binding);
|
|
360
344
|
if (set.providerBinding === null
|
|
361
|
-
|| normalized.runId !== set.providerBinding.runId
|
|
362
345
|
|| normalized.providerNamespace !== set.providerBinding.providerNamespace
|
|
363
346
|
|| normalized.accountScope !== set.providerBinding.accountScope) {
|
|
364
347
|
throw new Error("Provider Runtime Binding identity cannot change in place.");
|
|
@@ -370,33 +353,65 @@ export function updateTaskRoleProviderRuntime(set, binding, updatedAt) {
|
|
|
370
353
|
});
|
|
371
354
|
}
|
|
372
355
|
/**
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
356
|
+
* Detaches a confirmed-dead local Host without ending its resumable native
|
|
357
|
+
* Session. The Host launch and Provider Activation are disposable execution
|
|
358
|
+
* facts; the native Session remains the durable continuation identity.
|
|
376
359
|
*/
|
|
377
|
-
export function
|
|
360
|
+
export function detachRoleAgentSessionHost(set, now) {
|
|
378
361
|
validateRoleSessionSet(set);
|
|
379
362
|
const active = set.sessions[set.activeAgentId];
|
|
380
|
-
if (active === undefined)
|
|
381
|
-
return set;
|
|
382
|
-
const activation = set.providerBinding === null
|
|
383
|
-
? null
|
|
384
|
-
: currentProviderActivation(set.providerBinding);
|
|
385
|
-
if (active.status === "stopped") {
|
|
386
|
-
if (activation !== null) {
|
|
387
|
-
throw new Error("Stopped Task Role Session cannot retain a live Provider Activation.");
|
|
388
|
-
}
|
|
363
|
+
if (active === undefined || active.status === "ended")
|
|
389
364
|
return set;
|
|
365
|
+
const timestamp = requireDate(now, "Role Host detach timestamp");
|
|
366
|
+
const { launchId: _launchId, endReason: _endReason, ...session } = active;
|
|
367
|
+
let updated = validateRoleSessionSet({
|
|
368
|
+
...set,
|
|
369
|
+
sessions: {
|
|
370
|
+
...set.sessions,
|
|
371
|
+
[set.activeAgentId]: {
|
|
372
|
+
...session,
|
|
373
|
+
status: "active",
|
|
374
|
+
updatedAt: timestamp
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
updatedAt: timestamp
|
|
378
|
+
});
|
|
379
|
+
if (updated.owner.scope !== "task")
|
|
380
|
+
return updated;
|
|
381
|
+
let taskSet = updated;
|
|
382
|
+
let binding = taskSet.providerBinding;
|
|
383
|
+
const turn = binding?.turn;
|
|
384
|
+
if (binding !== null && binding !== undefined && turn !== null && turn !== undefined
|
|
385
|
+
&& ["accepted", "running"].includes(turn.status)) {
|
|
386
|
+
binding = settleProviderTurn(binding, {
|
|
387
|
+
turnId: turn.turnId,
|
|
388
|
+
status: "cancelled",
|
|
389
|
+
settledAt: timestamp,
|
|
390
|
+
reason: "runtime-physical-exit"
|
|
391
|
+
});
|
|
392
|
+
taskSet = updateTaskRoleProviderRuntime(taskSet, binding, now);
|
|
393
|
+
}
|
|
394
|
+
else if (binding !== null && binding !== undefined && turn !== null && turn !== undefined
|
|
395
|
+
&& ["submitting", "delivery-unknown"].includes(turn.status)) {
|
|
396
|
+
binding = settleProviderTurnSubmission(binding, {
|
|
397
|
+
attemptId: turn.attemptId,
|
|
398
|
+
status: "rejected",
|
|
399
|
+
resolvedAt: timestamp,
|
|
400
|
+
reason: "runtime-physical-exit"
|
|
401
|
+
});
|
|
402
|
+
taskSet = updateTaskRoleProviderRuntime(taskSet, binding, now);
|
|
390
403
|
}
|
|
391
|
-
|
|
404
|
+
const activation = binding === null
|
|
405
|
+
? null
|
|
406
|
+
: currentProviderActivation(binding);
|
|
392
407
|
if (activation !== null) {
|
|
393
|
-
|
|
408
|
+
taskSet = updateTaskRoleProviderRuntime(taskSet, endProviderActivation(binding, activation.activationId, {
|
|
394
409
|
status: "ended",
|
|
395
410
|
endedAt: now.toISOString(),
|
|
396
411
|
reason: "runtime-physical-exit"
|
|
397
412
|
}), now);
|
|
398
413
|
}
|
|
399
|
-
return
|
|
414
|
+
return taskSet;
|
|
400
415
|
}
|
|
401
416
|
export function markTaskRoleRunPushed(set, fence, pushedAt) {
|
|
402
417
|
validateRoleSessionSet(set);
|
|
@@ -450,7 +465,7 @@ export function recordTaskRoleTurnBoundary(set, input, completedAt) {
|
|
|
450
465
|
assertTaskRoleSessionSet(set);
|
|
451
466
|
const agentId = requireSafeIdentity(input.agentId, "Agent id");
|
|
452
467
|
const nativeSessionId = requireText(input.nativeSessionId, "Native session id");
|
|
453
|
-
const turnId =
|
|
468
|
+
const turnId = requireText(input.turnId, "Turn id");
|
|
454
469
|
const session = set.sessions[agentId];
|
|
455
470
|
if (session === undefined || session.nativeSessionId !== nativeSessionId) {
|
|
456
471
|
throw new Error("Completed Turn has no matching Role Agent native session.");
|
|
@@ -461,16 +476,12 @@ export function recordTaskRoleTurnBoundary(set, input, completedAt) {
|
|
|
461
476
|
throw new Error("Completed Turn Agent does not match the in-flight Run.");
|
|
462
477
|
}
|
|
463
478
|
const timestamp = requireDate(completedAt, "Turn completedAt");
|
|
464
|
-
const status = session.status === "stopped" || session.status === "broken"
|
|
465
|
-
? session.status
|
|
466
|
-
: "ready";
|
|
467
479
|
return validateRoleSessionSet({
|
|
468
480
|
...set,
|
|
469
481
|
sessions: {
|
|
470
482
|
...set.sessions,
|
|
471
483
|
[agentId]: {
|
|
472
484
|
...session,
|
|
473
|
-
status,
|
|
474
485
|
recentCompletedTurnIds: rememberRecentTurnId(session.recentCompletedTurnIds, turnId),
|
|
475
486
|
updatedAt: timestamp
|
|
476
487
|
}
|
|
@@ -525,28 +536,22 @@ export function clearTaskRoleRun(set, fence, clearedAt) {
|
|
|
525
536
|
return validateRoleSessionSet(updated);
|
|
526
537
|
}
|
|
527
538
|
/**
|
|
528
|
-
* Applies an authoritative application-level Run terminal fact to
|
|
529
|
-
*
|
|
530
|
-
*
|
|
539
|
+
* Applies an authoritative application-level Run terminal fact to its exact
|
|
540
|
+
* delivery receipt. Provider lifecycle remains independent: only the matching
|
|
541
|
+
* native Turn boundary may move the Session from running to ready. AgentHost
|
|
542
|
+
* serializes a retained next delivery while that Turn is still active.
|
|
531
543
|
*/
|
|
532
544
|
export function terminalizeTaskRoleRunSession(set, fence, terminalAt) {
|
|
533
545
|
validateRoleSessionSet(set);
|
|
534
546
|
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;
|
|
547
|
+
return inFlight === null ? set : clearTaskRoleRun(set, fence, terminalAt);
|
|
543
548
|
}
|
|
544
549
|
export function settleTaskRoleCompletion(set, expected, settledAt) {
|
|
545
550
|
validateRoleSessionSet(set);
|
|
546
551
|
assertTaskRoleSessionSet(set);
|
|
547
552
|
const agentId = requireSafeIdentity(expected.agentId, "Agent id");
|
|
548
553
|
const runId = requireSafeIdentity(expected.runId, "Run id");
|
|
549
|
-
const turnId =
|
|
554
|
+
const turnId = requireText(expected.turnId, "Turn id");
|
|
550
555
|
const inFlight = set.inFlight;
|
|
551
556
|
if (inFlight === null || inFlight.agentId !== agentId || inFlight.runId !== runId) {
|
|
552
557
|
throw new Error("Pending Turn completion does not match the in-flight Run.");
|
|
@@ -578,7 +583,7 @@ export function validateRoleSessionSet(set) {
|
|
|
578
583
|
validateRoleAgentSession(session, agentId);
|
|
579
584
|
}
|
|
580
585
|
if (set.owner.scope === "global") {
|
|
581
|
-
if (set.schemaVersion !==
|
|
586
|
+
if (set.schemaVersion !== 4) {
|
|
582
587
|
throw new Error("Global Role session set schema version is invalid.");
|
|
583
588
|
}
|
|
584
589
|
if (Object.hasOwn(set, "inFlight")
|
|
@@ -590,14 +595,14 @@ export function validateRoleSessionSet(set) {
|
|
|
590
595
|
for (const [ref, session] of Object.entries(history)) {
|
|
591
596
|
requireSafeIdentity(ref, "Operator session ref");
|
|
592
597
|
validateRoleAgentSession(session);
|
|
593
|
-
if (session.status !== "
|
|
598
|
+
if (session.status !== "ended") {
|
|
594
599
|
throw new Error(`Operator history session must be stopped: ${ref}.`);
|
|
595
600
|
}
|
|
596
601
|
}
|
|
597
602
|
}
|
|
598
603
|
}
|
|
599
604
|
else {
|
|
600
|
-
if (set.schemaVersion !==
|
|
605
|
+
if (set.schemaVersion !== 8) {
|
|
601
606
|
throw new Error("Task Role session set schema version is invalid.");
|
|
602
607
|
}
|
|
603
608
|
if (!Object.hasOwn(set, "inFlight")
|
|
@@ -612,7 +617,7 @@ export function validateRoleSessionSet(set) {
|
|
|
612
617
|
const identities = new Set();
|
|
613
618
|
for (const session of taskSet.history) {
|
|
614
619
|
validateRoleAgentSession(session);
|
|
615
|
-
if (session.status !== "
|
|
620
|
+
if (session.status !== "ended") {
|
|
616
621
|
throw new Error("Task Role session history must be terminal.");
|
|
617
622
|
}
|
|
618
623
|
const key = taskRoleSessionIdentity(session);
|
|
@@ -637,9 +642,6 @@ export function validateRoleSessionSet(set) {
|
|
|
637
642
|
throw new Error("Task Role in-flight Run Agent must be active.");
|
|
638
643
|
}
|
|
639
644
|
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
645
|
const session = taskSet.sessions[inFlight?.agentId ?? set.activeAgentId];
|
|
644
646
|
if (session === undefined) {
|
|
645
647
|
throw new Error("Provider Runtime Binding has no active Role Agent session.");
|
|
@@ -653,7 +655,7 @@ export function validateRoleSessionSet(set) {
|
|
|
653
655
|
return set;
|
|
654
656
|
}
|
|
655
657
|
export function validateRoleAgentSession(session, expectedAgentId = session.agentId) {
|
|
656
|
-
if (session.schemaVersion !==
|
|
658
|
+
if (session.schemaVersion !== 4) {
|
|
657
659
|
throw new Error(`Role Agent session schema version is invalid: ${expectedAgentId}.`);
|
|
658
660
|
}
|
|
659
661
|
const agentId = requireSafeIdentity(session.agentId, "Agent id");
|
|
@@ -691,6 +693,14 @@ export function validateRoleAgentSession(session, expectedAgentId = session.agen
|
|
|
691
693
|
if (!isAgentSessionStatus(session.status)) {
|
|
692
694
|
throw new Error(`Role Agent session status is invalid: ${agentId}.`);
|
|
693
695
|
}
|
|
696
|
+
if (session.status === "active" && session.endReason !== undefined) {
|
|
697
|
+
throw new Error(`Active Role Agent session cannot have an end reason: ${agentId}.`);
|
|
698
|
+
}
|
|
699
|
+
if (session.status === "ended"
|
|
700
|
+
&& session.endReason !== "stopped"
|
|
701
|
+
&& session.endReason !== "failed") {
|
|
702
|
+
throw new Error(`Ended Role Agent session requires an end reason: ${agentId}.`);
|
|
703
|
+
}
|
|
694
704
|
validateRecentTurnIds(session.recentCompletedTurnIds);
|
|
695
705
|
requireText(session.createdAt, "Role Agent session creation timestamp");
|
|
696
706
|
requireText(session.updatedAt, "Role Agent session update timestamp");
|
|
@@ -820,7 +830,7 @@ function normalizeOwner(owner) {
|
|
|
820
830
|
};
|
|
821
831
|
}
|
|
822
832
|
function isAgentSessionStatus(value) {
|
|
823
|
-
return
|
|
833
|
+
return value === "active" || value === "ended";
|
|
824
834
|
}
|
|
825
835
|
function requireSafeIdentity(value, label) {
|
|
826
836
|
const normalized = requireText(value, label);
|
|
@@ -82,7 +82,10 @@ export class ExecutorRegistry {
|
|
|
82
82
|
: {
|
|
83
83
|
...common,
|
|
84
84
|
mode: "resume",
|
|
85
|
-
nativeSessionId: input.nativeSessionId
|
|
85
|
+
nativeSessionId: input.nativeSessionId,
|
|
86
|
+
...(input.hostActivationId === undefined
|
|
87
|
+
? {}
|
|
88
|
+
: { hostActivationId: input.hostActivationId })
|
|
86
89
|
}, "deferred", undefined, input.beforeHostStart);
|
|
87
90
|
}
|
|
88
91
|
else {
|
|
@@ -100,7 +103,7 @@ export class ExecutorRegistry {
|
|
|
100
103
|
});
|
|
101
104
|
binding = request.mode === "new"
|
|
102
105
|
? await this.runtimePorts.sessionHost.start(request, input.beforeHostStart)
|
|
103
|
-
: await this.runtimePorts.sessionHost.
|
|
106
|
+
: await this.runtimePorts.sessionHost.restore(request, input.beforeHostStart);
|
|
104
107
|
}
|
|
105
108
|
sessionStarted = binding.hostCreated === true;
|
|
106
109
|
session = binding.nativeSessionId === undefined
|
|
@@ -109,7 +112,7 @@ export class ExecutorRegistry {
|
|
|
109
112
|
agentId: binding.agentId,
|
|
110
113
|
adapterId: binding.adapterId,
|
|
111
114
|
nativeSessionId: binding.nativeSessionId,
|
|
112
|
-
status: "
|
|
115
|
+
status: "active",
|
|
113
116
|
effective: input.effective
|
|
114
117
|
};
|
|
115
118
|
}
|
|
@@ -118,22 +121,6 @@ export class ExecutorRegistry {
|
|
|
118
121
|
...(binding === undefined ? {} : { launchId: binding.launchId }),
|
|
119
122
|
sessionStarted,
|
|
120
123
|
session,
|
|
121
|
-
...(input.runId !== undefined
|
|
122
|
-
&& ((binding?.initialTurnRunId === input.runId))
|
|
123
|
-
? { turnAcceptedDuringLaunch: true }
|
|
124
|
-
: {}),
|
|
125
|
-
...(input.runId !== undefined
|
|
126
|
-
&& binding?.initialTurnDeliveryUnknownRunId === input.runId
|
|
127
|
-
? { turnDeliveryUnknownDuringLaunch: true }
|
|
128
|
-
: {}),
|
|
129
|
-
...(input.runId !== undefined
|
|
130
|
-
&& binding?.initialTurnBusyRunId === input.runId
|
|
131
|
-
? { turnBusyDuringLaunch: true }
|
|
132
|
-
: {}),
|
|
133
|
-
...(input.runId !== undefined
|
|
134
|
-
&& binding?.initialTurnRejectedRunId === input.runId
|
|
135
|
-
? { turnRejectedDuringLaunch: true }
|
|
136
|
-
: {})
|
|
137
124
|
};
|
|
138
125
|
this.#prepared.set(delivery.deliveryId, {
|
|
139
126
|
delivery,
|
|
@@ -362,7 +349,8 @@ function preparedDeliveryId(input) {
|
|
|
362
349
|
input.effective,
|
|
363
350
|
input.mode,
|
|
364
351
|
input.runId ?? null,
|
|
365
|
-
input.nativeSessionId ?? null
|
|
352
|
+
input.nativeSessionId ?? null,
|
|
353
|
+
input.hostActivationId ?? null
|
|
366
354
|
])).digest("hex");
|
|
367
355
|
}
|
|
368
356
|
function hasText(value) {
|