@zq-silk/yui 0.13.3 → 0.13.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/cli/commandCatalog.js +22 -21
- package/dist/cli/interactionPolicy.js +0 -14
- package/dist/cli.js +42 -0
- package/dist/commands/executionAuditCommands.js +1 -1
- package/dist/commands/taskCommands.js +60 -326
- package/dist/commands/taskContextCommand.js +3 -14
- package/dist/commands/taskExecutionCommands.js +254 -0
- package/dist/commands/taskNextActionCommand.js +1 -3
- package/dist/commands/taskOverviewCommand.js +9 -2
- package/dist/commands/taskRoleRuntimeStatus.js +2 -25
- package/dist/controller/agentRuntimeObserver.js +4 -2
- package/dist/controller/clientRuntime.js +45 -2
- package/dist/controller/controller.js +6 -3
- package/dist/controller/fileSchedulerStoreAdapter.js +70 -188
- package/dist/controller/jobControl.js +3 -2
- package/dist/controller/runtime.js +6 -33
- package/dist/controller/runtimeEventProcessor.js +8 -4
- package/dist/controller/runtimeHookRunFence.js +4 -10
- package/dist/execution/executionHealth.js +8 -16
- package/dist/executor/agentExecutor.js +13 -14
- package/dist/executor/fileRoleLaunchPlanner.js +9 -16
- package/dist/lifecycle/exactRunTerminalization.js +24 -322
- package/dist/repository/taskWorkspaceCoordinator.js +0 -9
- package/dist/runtime/agentHost.js +20 -80
- package/dist/runtime/exactControlPlane.js +15 -9
- package/dist/runtime/providerContinuationReconciliationService.js +1 -1
- package/dist/runtime/providerRecoveryDecision.js +1 -1
- package/dist/runtime/providerRuntimeIdentity.js +25 -15
- package/dist/scheduler/activeRoleRunDelivery.js +1 -19
- package/dist/scheduler/leaderWakeupProcessor.js +12 -63
- package/dist/scheduler/ports.js +3 -2
- package/dist/scheduler/roleRunLiveness.js +4 -1
- package/dist/scheduler/roleRunStall.js +0 -2
- package/dist/scheduler/taskExecutionProjection.js +18 -1
- package/dist/scheduler/wakeupQueue.js +2 -1
- package/dist/storage/migration/productionRegistry.js +65 -0
- package/dist/storage/sqliteStore.js +10 -2
- package/dist/storage/taskStore.js +11 -3
- package/dist/task/completionReadiness.js +0 -67
- package/dist/task/nextAction.js +16 -32
- package/dist/task/task.js +38 -3
- package/dist/web/assets/client/i18n.js +0 -4
- package/dist/web/assets/client/view.js +0 -18
- package/dist/web/webSnapshot.js +7 -13
- package/package.json +1 -1
- package/dist/run/recoveryProjection.js +0 -252
- package/dist/runtime/conversationSwitch.js +0 -277
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
import { roleAgentSessionResumeMode } from "../executor/agentExecutor.js";
|
|
2
|
-
import { effectiveLaunchSnapshotsCompatibleForTaskSession } from "../executor/effectiveLaunch.js";
|
|
3
|
-
import { currentProviderActivation, currentProviderConversation } from "./providerRuntimeIdentity.js";
|
|
4
|
-
import { blockingProviderContinuations } from "./runtimeContinuationProjection.js";
|
|
5
|
-
import { runtimeObservationFromTaskEvent } from "./runtimeObservation.js";
|
|
6
|
-
export const CONVERSATION_SWITCH_REQUESTED_EVENT = "runtime.conversation-switch-requested";
|
|
7
|
-
export const CONVERSATION_SWITCH_RESOLVED_EVENT = "runtime.conversation-switch-resolved";
|
|
8
|
-
export const CONVERSATION_SWITCH_DETACHED_EVENT = "runtime.conversation-switch-detached";
|
|
9
|
-
export function providerConversationGeneration(sessions) {
|
|
10
|
-
const binding = sessions?.providerBinding;
|
|
11
|
-
if (binding === null || binding === undefined)
|
|
12
|
-
return null;
|
|
13
|
-
const current = currentProviderConversation(binding);
|
|
14
|
-
return `${binding.providerNamespace}:${binding.accountScope}:${current.epoch}:${current.conversationId}`;
|
|
15
|
-
}
|
|
16
|
-
export function projectConversationSwitch(events, roleName, sessions) {
|
|
17
|
-
const requests = events.filter((event) => (event.type === CONVERSATION_SWITCH_REQUESTED_EVENT
|
|
18
|
-
&& event.payload.roleName === roleName));
|
|
19
|
-
const request = requests.at(-1);
|
|
20
|
-
if (request === undefined)
|
|
21
|
-
return null;
|
|
22
|
-
const requestId = request.payload.requestId;
|
|
23
|
-
const generation = request.payload.generation;
|
|
24
|
-
const requestedBy = request.payload.requestedBy;
|
|
25
|
-
const reason = request.payload.reason;
|
|
26
|
-
if (requestId === undefined || generation === undefined || reason === undefined
|
|
27
|
-
|| (requestedBy !== "user" && requestedBy !== "operator" && requestedBy !== "leader")) {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
const resolution = [...events].reverse().find((event) => (event.type === CONVERSATION_SWITCH_RESOLVED_EVENT
|
|
31
|
-
&& event.payload.requestId === requestId));
|
|
32
|
-
const explicitStatus = resolution?.payload.status;
|
|
33
|
-
const currentGeneration = providerConversationGeneration(sessions);
|
|
34
|
-
const status = explicitStatus === "applied" || explicitStatus === "obsolete"
|
|
35
|
-
? explicitStatus
|
|
36
|
-
: currentGeneration !== null && currentGeneration !== generation
|
|
37
|
-
? "obsolete"
|
|
38
|
-
: "pending";
|
|
39
|
-
return {
|
|
40
|
-
requestId,
|
|
41
|
-
roleName,
|
|
42
|
-
generation,
|
|
43
|
-
requestedBy,
|
|
44
|
-
reason,
|
|
45
|
-
requestedAt: request.createdAt,
|
|
46
|
-
status,
|
|
47
|
-
...(resolution === undefined ? {} : { resolvedAt: resolution.createdAt })
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
export function pendingConversationSwitch(events, roleName, sessions) {
|
|
51
|
-
const projected = projectConversationSwitch(events, roleName, sessions);
|
|
52
|
-
return projected?.status === "pending" ? projected : null;
|
|
53
|
-
}
|
|
54
|
-
export function roleSessionDispatchModeWithConversationSwitch(sessions, events, mailbox, roleName, agentId, effective) {
|
|
55
|
-
const ordinary = roleAgentSessionResumeMode(sessions, agentId, effective);
|
|
56
|
-
if (ordinary !== "resume") {
|
|
57
|
-
if (sessions?.providerBinding !== null && sessions?.providerBinding !== undefined) {
|
|
58
|
-
if (freshConversationLaunchAllowed({ sessions, events, mailbox, roleName })) {
|
|
59
|
-
return "new";
|
|
60
|
-
}
|
|
61
|
-
const existing = sessions.sessions[agentId];
|
|
62
|
-
if (existing?.nativeSessionId !== undefined
|
|
63
|
-
&& (existing.status === "stopped" || existing.status === "broken")
|
|
64
|
-
&& effectiveLaunchSnapshotsCompatibleForTaskSession(existing.effective, effective)) {
|
|
65
|
-
// A terminal local Activation does not prove the Provider Conversation
|
|
66
|
-
// is gone. Reattach to the same native identity by default.
|
|
67
|
-
return "resume";
|
|
68
|
-
}
|
|
69
|
-
throw new Error(`Fresh Provider Conversation is not yet safe: ${sessions.owner.taskId}/${roleName}.`);
|
|
70
|
-
}
|
|
71
|
-
return ordinary;
|
|
72
|
-
}
|
|
73
|
-
if (sessions?.providerBinding !== null && sessions?.providerBinding !== undefined
|
|
74
|
-
&& freshConversationLaunchAllowed({ sessions, events, mailbox, roleName })) {
|
|
75
|
-
return "new";
|
|
76
|
-
}
|
|
77
|
-
return "resume";
|
|
78
|
-
}
|
|
79
|
-
export function conversationDetachmentBasis(input) {
|
|
80
|
-
const { sessions, mailbox } = input;
|
|
81
|
-
const binding = sessions.providerBinding;
|
|
82
|
-
const activation = binding === null ? null : currentProviderActivation(binding);
|
|
83
|
-
if (input.runMode !== "new" || binding === null || activation === null
|
|
84
|
-
|| sessions.inFlight?.runId !== input.runId
|
|
85
|
-
|| mailbox?.processing?.batchId !== sessions.inFlight.receiptId
|
|
86
|
-
|| mailbox.processing.owner !== "controller"
|
|
87
|
-
|| mailbox.processing.executionRef?.type !== "run"
|
|
88
|
-
|| mailbox.processing.executionRef.taskId !== sessions.owner.taskId
|
|
89
|
-
|| mailbox.processing.executionRef.id !== input.runId
|
|
90
|
-
|| !actorRequestedSwitchBoundaryReady(sessions, mailbox)
|
|
91
|
-
|| currentConversationExecutionBlockers(sessions, input.events, input.roleName).length > 0
|
|
92
|
-
|| pendingConversationSwitch(input.events, input.roleName, sessions) === null) {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
return "actor-request";
|
|
96
|
-
}
|
|
97
|
-
export function conversationReplacementBasis(input) {
|
|
98
|
-
const { sessions, mailbox } = input;
|
|
99
|
-
const binding = sessions.providerBinding;
|
|
100
|
-
if (input.runMode !== "new" || binding === null
|
|
101
|
-
|| sessions.inFlight?.runId !== input.runId
|
|
102
|
-
|| mailbox?.processing?.batchId !== sessions.inFlight.receiptId
|
|
103
|
-
|| mailbox.processing.owner !== "controller"
|
|
104
|
-
|| mailbox?.processing?.executionRef?.type !== "run"
|
|
105
|
-
|| mailbox.processing.executionRef.taskId !== sessions.owner.taskId
|
|
106
|
-
|| mailbox.processing.executionRef.id !== input.runId
|
|
107
|
-
|| mailbox.inputDelivery !== null
|
|
108
|
-
|| !conversationIsQuiescent(sessions, mailbox)
|
|
109
|
-
|| currentConversationExecutionBlockers(sessions, input.events, input.roleName).length > 0) {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
if (currentConversationIsExactlyUnrecoverable(sessions, input.events, input.roleName))
|
|
113
|
-
return "exact-unrecoverable";
|
|
114
|
-
return pendingConversationSwitch(input.events, input.roleName, sessions) === null
|
|
115
|
-
? null
|
|
116
|
-
: "actor-request";
|
|
117
|
-
}
|
|
118
|
-
export function freshConversationLaunchAllowed(input) {
|
|
119
|
-
return freshConversationLaunchBlockers(input).length === 0;
|
|
120
|
-
}
|
|
121
|
-
/** Exact reasons a fresh Conversation cannot currently be admitted. */
|
|
122
|
-
export function freshConversationLaunchBlockers(input) {
|
|
123
|
-
const { sessions } = input;
|
|
124
|
-
if (sessions === null)
|
|
125
|
-
return [];
|
|
126
|
-
const session = sessions.sessions[sessions.activeAgentId];
|
|
127
|
-
if (sessions.providerBinding === null) {
|
|
128
|
-
return session === undefined
|
|
129
|
-
|| session.status === "stopped"
|
|
130
|
-
|| session.status === "broken"
|
|
131
|
-
? []
|
|
132
|
-
: ["native-session-not-terminal"];
|
|
133
|
-
}
|
|
134
|
-
const request = pendingConversationSwitch(input.events, input.roleName, sessions);
|
|
135
|
-
const binding = sessions.providerBinding;
|
|
136
|
-
const blockers = [];
|
|
137
|
-
if (input.mailbox?.inputDelivery != null)
|
|
138
|
-
blockers.push("provider-input-delivery-unsettled");
|
|
139
|
-
if (binding.turn !== null
|
|
140
|
-
&& ["submitting", "accepted", "running", "delivery-unknown"].includes(binding.turn.status))
|
|
141
|
-
blockers.push("provider-turn-unsettled");
|
|
142
|
-
blockers.push(...currentConversationExecutionBlockers(sessions, input.events, input.roleName));
|
|
143
|
-
if (blockers.length > 0)
|
|
144
|
-
return blockers;
|
|
145
|
-
if (request !== null && actorRequestedSwitchBoundaryReady(sessions, input.mailbox, input.candidateRunId))
|
|
146
|
-
return [];
|
|
147
|
-
if (currentProviderActivation(binding) !== null)
|
|
148
|
-
blockers.push("provider-activation-active");
|
|
149
|
-
if (binding.authority.owner !== "none")
|
|
150
|
-
blockers.push("provider-writer-authority-owned");
|
|
151
|
-
if (blockers.length > 0)
|
|
152
|
-
return blockers;
|
|
153
|
-
if (currentProviderConversation(binding).recoverability === "unrecoverable") {
|
|
154
|
-
return currentConversationIsExactlyUnrecoverable(sessions, input.events, input.roleName) ? [] : ["exact-unrecoverable-evidence-missing"];
|
|
155
|
-
}
|
|
156
|
-
if (request !== null)
|
|
157
|
-
return ["actor-switch-boundary-not-ready"];
|
|
158
|
-
return ["current-conversation-recoverable"];
|
|
159
|
-
}
|
|
160
|
-
function actorRequestedSwitchBoundaryReady(sessions, mailbox, candidateRunId) {
|
|
161
|
-
const binding = sessions?.providerBinding;
|
|
162
|
-
if (sessions === null || sessions === undefined
|
|
163
|
-
|| binding === null || binding === undefined
|
|
164
|
-
|| mailbox?.inputDelivery != null)
|
|
165
|
-
return false;
|
|
166
|
-
const turnSettled = binding.turn === null
|
|
167
|
-
|| ["completed", "failed", "cancelled", "rejected"].includes(binding.turn.status);
|
|
168
|
-
if (!turnSettled)
|
|
169
|
-
return false;
|
|
170
|
-
const session = sessions.sessions[sessions.activeAgentId];
|
|
171
|
-
if (session?.status === "running")
|
|
172
|
-
return false;
|
|
173
|
-
const activation = currentProviderActivation(binding);
|
|
174
|
-
if (activation === null)
|
|
175
|
-
return binding.authority.owner === "none";
|
|
176
|
-
if (binding.authority.owner !== "controller"
|
|
177
|
-
|| binding.authority.holderId !== activation.activationId)
|
|
178
|
-
return false;
|
|
179
|
-
if (session === undefined)
|
|
180
|
-
return false;
|
|
181
|
-
if (sessions.inFlight === null) {
|
|
182
|
-
if (mailbox?.processing === null)
|
|
183
|
-
return true;
|
|
184
|
-
return candidateRunId !== undefined
|
|
185
|
-
&& mailbox?.processing?.owner === "controller"
|
|
186
|
-
&& mailbox.processing.executionRef?.type === "run"
|
|
187
|
-
&& mailbox.processing.executionRef.taskId === sessions.owner.taskId
|
|
188
|
-
&& mailbox.processing.executionRef.id === candidateRunId;
|
|
189
|
-
}
|
|
190
|
-
return mailbox?.processing?.batchId === sessions.inFlight.receiptId
|
|
191
|
-
&& mailbox.processing.owner === "controller"
|
|
192
|
-
&& mailbox.processing.executionRef?.type === "run"
|
|
193
|
-
&& mailbox.processing.executionRef.taskId === sessions.owner.taskId
|
|
194
|
-
&& mailbox.processing.executionRef.id === sessions.inFlight.runId;
|
|
195
|
-
}
|
|
196
|
-
function conversationIsQuiescent(sessions, mailbox) {
|
|
197
|
-
const binding = sessions?.providerBinding;
|
|
198
|
-
if (binding === null || binding === undefined)
|
|
199
|
-
return false;
|
|
200
|
-
const turnSettled = binding.turn === null
|
|
201
|
-
|| ["completed", "failed", "cancelled", "rejected"].includes(binding.turn.status);
|
|
202
|
-
return turnSettled
|
|
203
|
-
&& currentProviderActivation(binding) === null
|
|
204
|
-
&& binding.authority.owner === "none"
|
|
205
|
-
&& mailbox?.inputDelivery == null;
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* A projected recoverability flag is necessary but not sufficient to replace
|
|
209
|
-
* a Conversation. Require the exact structured missing-Conversation fact for
|
|
210
|
-
* the current Provider identity and latest Activation generation, so an old
|
|
211
|
-
* observation cannot authorize a later fresh Conversation. The binding's Run
|
|
212
|
-
* id is deliberately excluded: replacement work rebinds that fence before
|
|
213
|
-
* launching, while the missing fact necessarily came from the preceding Run's
|
|
214
|
-
* exact attempt to resume this same Activation.
|
|
215
|
-
*/
|
|
216
|
-
function currentConversationIsExactlyUnrecoverable(sessions, events, roleName) {
|
|
217
|
-
const binding = sessions.providerBinding;
|
|
218
|
-
if (binding === null)
|
|
219
|
-
return false;
|
|
220
|
-
const conversation = currentProviderConversation(binding);
|
|
221
|
-
if (conversation.recoverability !== "unrecoverable")
|
|
222
|
-
return false;
|
|
223
|
-
const activation = [...binding.activations].reverse().find((entry) => (entry.conversationId === conversation.conversationId));
|
|
224
|
-
const generationStartedAt = activation?.startedAt ?? conversation.createdAt;
|
|
225
|
-
return events.some((event) => {
|
|
226
|
-
const observation = runtimeObservationFromTaskEvent(event);
|
|
227
|
-
if (observation === null
|
|
228
|
-
|| observation.kind !== "conversation.observed"
|
|
229
|
-
|| observation.payload.recoverability !== "unrecoverable"
|
|
230
|
-
|| (observation.authority !== "provider-structured"
|
|
231
|
-
&& observation.authority !== "controller"))
|
|
232
|
-
return false;
|
|
233
|
-
const fence = observation.fence;
|
|
234
|
-
if (fence.taskId !== sessions.owner.taskId
|
|
235
|
-
|| fence.roleName !== roleName
|
|
236
|
-
|| fence.agentId !== binding.accountScope
|
|
237
|
-
|| fence.driverId !== binding.providerNamespace
|
|
238
|
-
|| fence.conversationId !== conversation.conversationId
|
|
239
|
-
|| (activation !== undefined && fence.activationId !== activation.activationId)) {
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
return Date.parse(observation.observedAt ?? observation.receivedAt)
|
|
243
|
-
>= Date.parse(generationStartedAt);
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
/** Current Provider-owned work that may outlive a foreground Turn. */
|
|
247
|
-
function currentConversationExecutionBlockers(sessions, events, roleName) {
|
|
248
|
-
const binding = sessions.providerBinding;
|
|
249
|
-
if (binding === null)
|
|
250
|
-
return [];
|
|
251
|
-
const conversation = currentProviderConversation(binding);
|
|
252
|
-
const activation = [...binding.activations].reverse().find((entry) => (entry.conversationId === conversation.conversationId));
|
|
253
|
-
if (activation === undefined)
|
|
254
|
-
return ["provider-activation-identity-missing"];
|
|
255
|
-
const activeOperation = events.some((event) => {
|
|
256
|
-
const observation = runtimeObservationFromTaskEvent(event);
|
|
257
|
-
if (observation?.kind !== "operation.started")
|
|
258
|
-
return false;
|
|
259
|
-
const fence = observation.fence;
|
|
260
|
-
return fence.taskId === sessions.owner.taskId
|
|
261
|
-
&& fence.roleName === roleName
|
|
262
|
-
&& fence.agentId === binding.accountScope
|
|
263
|
-
&& fence.driverId === binding.providerNamespace
|
|
264
|
-
&& (fence.conversationId ?? fence.nativeSessionId) === conversation.conversationId
|
|
265
|
-
&& (fence.activationId ?? fence.launchId) === activation.activationId;
|
|
266
|
-
});
|
|
267
|
-
const blockingContinuation = blockingProviderContinuations(events).some((entry) => (entry.taskId === sessions.owner.taskId
|
|
268
|
-
&& entry.roleName === roleName
|
|
269
|
-
&& entry.identity.providerNamespace === binding.providerNamespace
|
|
270
|
-
&& entry.identity.accountScope === binding.accountScope
|
|
271
|
-
&& entry.identity.conversationId === conversation.conversationId
|
|
272
|
-
&& entry.identity.activationId === activation.activationId));
|
|
273
|
-
return [
|
|
274
|
-
...(activeOperation ? ["provider-operation-active"] : []),
|
|
275
|
-
...(blockingContinuation ? ["provider-continuation-writer-owned"] : [])
|
|
276
|
-
];
|
|
277
|
-
}
|