@zq-silk/yui 0.8.1 → 0.8.3
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 +27 -28
- package/README.md +57 -46
- package/dist/cli/commandCatalog.js +57 -17
- package/dist/cli/interactionPolicy.js +4 -10
- package/dist/cli/invocationRouter.js +2 -1
- package/dist/cli.js +106 -27
- package/dist/commands/taskCommands.js +458 -77
- package/dist/commands/taskCompletionGate.js +152 -0
- package/dist/context/runContextPack.js +19 -4
- package/dist/context/sessionBootstrapManifest.js +1 -1
- package/dist/controller/fileSchedulerStoreAdapter.js +389 -70
- package/dist/controller/resourceInventory.js +9 -5
- package/dist/controller/runtime.js +80 -7
- package/dist/controller/runtimeLaunchCoordinator.js +18 -78
- package/dist/controller/structuredProviderObservation.js +273 -0
- package/dist/executor/agentAdapter.js +40 -0
- package/dist/executor/agentExecutor.js +31 -7
- package/dist/executor/executorRegistry.js +11 -49
- package/dist/executor/fileRoleLaunchPlanner.js +115 -37
- package/dist/lifecycle/canonicalLifecycleEvent.js +5 -2
- package/dist/repository/gitWorkspace.js +7 -4
- package/dist/repository/taskBaseFreshness.js +4 -2
- package/dist/run/agentRun.js +4 -4
- package/dist/runtime/agentHost.js +767 -158
- package/dist/runtime/builtinAgentDrivers.js +1 -5
- package/dist/runtime/codexAppServerRuntime.js +67 -60
- package/dist/runtime/exactControlPlane.js +7 -2
- package/dist/runtime/index.js +6 -2
- package/dist/runtime/launchBroker.js +30 -8
- package/dist/runtime/providerAuthorityFence.js +24 -0
- package/dist/runtime/providerControl.js +63 -0
- package/dist/runtime/providerRecoveryDecision.js +55 -0
- package/dist/runtime/providerRuntimeIdentity.js +269 -19
- package/dist/runtime/runtimeBinding.js +20 -11
- package/dist/runtime/structuredProviderHost.js +476 -0
- package/dist/runtime/tmuxAdapters.js +143 -42
- package/dist/scheduler/activeRoleRunDelivery.js +206 -120
- package/dist/scheduler/leaderWakeupProcessor.js +141 -16
- package/dist/scheduler/wakeReason.js +1 -0
- package/dist/storage/migration/productionRegistry.js +111 -0
- package/dist/storage/sqliteStore.js +2 -0
- package/dist/storage/taskStore.js +3 -1
- package/dist/task/completionReadiness.js +43 -0
- package/dist/task/nextAction.js +6 -4
- package/dist/task/publicationReference.js +1 -0
- package/dist/tmux/tmuxManager.js +1 -1
- package/dist/workItem/workItem.js +12 -0
- package/dist/workspace/workItemChangeSetManager.js +2 -1
- package/i18n/README.zh-CN.md +11 -8
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +8 -3
- package/skills/yui-runtime/SKILL.md +7 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export function createProviderRuntimeBinding(input) {
|
|
2
2
|
const startedAt = timestamp(input.startedAt, "Provider Activation startedAt");
|
|
3
3
|
return validateProviderRuntimeBinding({
|
|
4
|
-
schemaVersion:
|
|
4
|
+
schemaVersion: 2,
|
|
5
5
|
providerNamespace: identity(input.providerNamespace, "Provider namespace"),
|
|
6
6
|
accountScope: identity(input.accountScope, "Provider account scope"),
|
|
7
7
|
runId: identity(input.runId, "Run id"),
|
|
@@ -18,11 +18,20 @@ export function createProviderRuntimeBinding(input) {
|
|
|
18
18
|
conversationId: input.conversationId,
|
|
19
19
|
generation: 1,
|
|
20
20
|
status: "active",
|
|
21
|
-
writerLease: true,
|
|
22
21
|
startedAt
|
|
23
|
-
}]
|
|
22
|
+
}],
|
|
23
|
+
authority: {
|
|
24
|
+
epoch: 1,
|
|
25
|
+
owner: "controller",
|
|
26
|
+
holderId: input.activationId,
|
|
27
|
+
changedAt: startedAt
|
|
28
|
+
},
|
|
29
|
+
turn: null
|
|
24
30
|
});
|
|
25
31
|
}
|
|
32
|
+
export function currentProviderAuthority(binding) {
|
|
33
|
+
return validateProviderRuntimeBinding(binding).authority;
|
|
34
|
+
}
|
|
26
35
|
export function currentProviderConversation(binding) {
|
|
27
36
|
validateProviderRuntimeBinding(binding);
|
|
28
37
|
return binding.conversations.find((entry) => (entry.epoch === binding.currentConversationEpoch && entry.status === "current"));
|
|
@@ -31,25 +40,46 @@ export function currentProviderActivation(binding) {
|
|
|
31
40
|
const conversation = currentProviderConversation(binding);
|
|
32
41
|
return [...binding.activations].reverse().find((entry) => (entry.conversationId === conversation.conversationId && entry.status === "active")) ?? null;
|
|
33
42
|
}
|
|
43
|
+
/** Rebinds the live Conversation state to the next Yui Run without resetting authority. */
|
|
44
|
+
export function rebindProviderRuntimeRun(raw, runId) {
|
|
45
|
+
const binding = validateProviderRuntimeBinding(raw);
|
|
46
|
+
if (providerTurnIsActive(binding.turn)) {
|
|
47
|
+
throw new Error("Provider Runtime cannot bind another Run while a Turn is unsettled.");
|
|
48
|
+
}
|
|
49
|
+
return validateProviderRuntimeBinding({
|
|
50
|
+
...binding,
|
|
51
|
+
runId: identity(runId, "Run id")
|
|
52
|
+
});
|
|
53
|
+
}
|
|
34
54
|
export function startProviderActivation(raw, input) {
|
|
35
55
|
const binding = validateProviderRuntimeBinding(raw);
|
|
36
56
|
if (currentProviderActivation(binding) !== null) {
|
|
37
57
|
throw new Error("Provider Conversation already has a live writer Activation.");
|
|
38
58
|
}
|
|
59
|
+
if (binding.authority.owner !== "none") {
|
|
60
|
+
throw new Error("Provider Conversation authority must be unowned before a new Activation starts.");
|
|
61
|
+
}
|
|
39
62
|
const conversation = currentProviderConversation(binding);
|
|
40
63
|
const generation = binding.activations
|
|
41
64
|
.filter((entry) => entry.conversationId === conversation.conversationId)
|
|
42
65
|
.reduce((maximum, entry) => Math.max(maximum, entry.generation), 0) + 1;
|
|
66
|
+
const startedAt = timestamp(input.startedAt, "Provider Activation startedAt");
|
|
67
|
+
const activationId = identity(input.activationId, "Provider Activation id");
|
|
43
68
|
return validateProviderRuntimeBinding({
|
|
44
69
|
...binding,
|
|
45
70
|
activations: [...binding.activations, {
|
|
46
|
-
activationId
|
|
71
|
+
activationId,
|
|
47
72
|
conversationId: conversation.conversationId,
|
|
48
73
|
generation,
|
|
49
74
|
status: "active",
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
startedAt
|
|
76
|
+
}],
|
|
77
|
+
authority: {
|
|
78
|
+
epoch: binding.authority.epoch + 1,
|
|
79
|
+
owner: "controller",
|
|
80
|
+
holderId: activationId,
|
|
81
|
+
changedAt: startedAt
|
|
82
|
+
}
|
|
53
83
|
});
|
|
54
84
|
}
|
|
55
85
|
export function endProviderActivation(raw, activationId, input) {
|
|
@@ -70,13 +100,161 @@ export function endProviderActivation(raw, activationId, input) {
|
|
|
70
100
|
? {
|
|
71
101
|
...entry,
|
|
72
102
|
status: input.status,
|
|
73
|
-
writerLease: false,
|
|
74
103
|
endedAt,
|
|
75
104
|
...(input.reason === undefined
|
|
76
105
|
? {}
|
|
77
106
|
: { terminalReason: identity(input.reason, "Provider Activation terminal reason") })
|
|
78
107
|
}
|
|
79
|
-
: entry)
|
|
108
|
+
: entry),
|
|
109
|
+
authority: {
|
|
110
|
+
epoch: binding.authority.epoch + 1,
|
|
111
|
+
owner: "none",
|
|
112
|
+
changedAt: endedAt
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Compare-and-swap the only Provider writer. A stale Controller or detached
|
|
118
|
+
* terminal cannot regain authority with an older epoch.
|
|
119
|
+
*/
|
|
120
|
+
export function transferProviderAuthority(raw, input) {
|
|
121
|
+
const binding = validateProviderRuntimeBinding(raw);
|
|
122
|
+
if (binding.authority.epoch !== input.expectedEpoch
|
|
123
|
+
|| binding.authority.owner !== input.expectedOwner) {
|
|
124
|
+
throw new Error("Provider authority fence is stale.");
|
|
125
|
+
}
|
|
126
|
+
if (providerTurnIsActive(binding.turn)) {
|
|
127
|
+
throw new Error("Provider authority cannot transfer while a Turn is unsettled.");
|
|
128
|
+
}
|
|
129
|
+
const active = currentProviderActivation(binding);
|
|
130
|
+
const changedAt = timestamp(input.changedAt, "Provider authority changedAt");
|
|
131
|
+
if (Date.parse(changedAt) < Date.parse(binding.authority.changedAt)) {
|
|
132
|
+
throw new Error("Provider authority changedAt moved backwards.");
|
|
133
|
+
}
|
|
134
|
+
let holderId;
|
|
135
|
+
if (input.owner === "none") {
|
|
136
|
+
if (input.holderId !== undefined) {
|
|
137
|
+
throw new Error("Unowned Provider authority cannot name a holder.");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
if (active === null) {
|
|
142
|
+
throw new Error("Provider authority requires a live Activation.");
|
|
143
|
+
}
|
|
144
|
+
holderId = identity(input.holderId, "Provider authority holder id");
|
|
145
|
+
if (input.owner === "controller" && holderId !== active.activationId) {
|
|
146
|
+
throw new Error("Controller authority must be held by the live Activation.");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return validateProviderRuntimeBinding({
|
|
150
|
+
...binding,
|
|
151
|
+
authority: {
|
|
152
|
+
epoch: binding.authority.epoch + 1,
|
|
153
|
+
owner: input.owner,
|
|
154
|
+
...(holderId === undefined ? {} : { holderId }),
|
|
155
|
+
changedAt
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
export function beginProviderTurn(raw, input) {
|
|
160
|
+
const binding = validateProviderRuntimeBinding(raw);
|
|
161
|
+
const attemptId = identity(input.attemptId, "Provider input attempt id");
|
|
162
|
+
if (binding.turn?.attemptId === attemptId
|
|
163
|
+
&& binding.turn.authorityEpoch === input.authorityEpoch
|
|
164
|
+
&& binding.turn.status === "submitting") {
|
|
165
|
+
return binding;
|
|
166
|
+
}
|
|
167
|
+
if (binding.authority.epoch !== input.authorityEpoch
|
|
168
|
+
|| binding.authority.owner === "none"
|
|
169
|
+
|| binding.authority.owner === "unknown") {
|
|
170
|
+
throw new Error("Provider Turn authority fence is stale.");
|
|
171
|
+
}
|
|
172
|
+
if (providerTurnIsActive(binding.turn)) {
|
|
173
|
+
throw new Error("Provider Conversation already has an unsettled Turn.");
|
|
174
|
+
}
|
|
175
|
+
const submittedAt = timestamp(input.submittedAt, "Provider Turn submittedAt");
|
|
176
|
+
return validateProviderRuntimeBinding({
|
|
177
|
+
...binding,
|
|
178
|
+
turn: {
|
|
179
|
+
attemptId,
|
|
180
|
+
authorityEpoch: input.authorityEpoch,
|
|
181
|
+
status: "submitting",
|
|
182
|
+
submittedAt,
|
|
183
|
+
updatedAt: submittedAt
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
export function acceptProviderTurn(raw, input) {
|
|
188
|
+
const binding = validateProviderRuntimeBinding(raw);
|
|
189
|
+
const attemptId = identity(input.attemptId, "Provider input attempt id");
|
|
190
|
+
const turn = binding.turn;
|
|
191
|
+
if (turn === null || turn.attemptId !== attemptId
|
|
192
|
+
|| (turn.status !== "submitting" && turn.status !== "delivery-unknown")) {
|
|
193
|
+
throw new Error("Provider Turn does not match an acceptable delivery state.");
|
|
194
|
+
}
|
|
195
|
+
const acceptedAt = orderedTurnTimestamp(turn, input.acceptedAt, "Provider Turn acceptedAt");
|
|
196
|
+
return validateProviderRuntimeBinding({
|
|
197
|
+
...binding,
|
|
198
|
+
turn: {
|
|
199
|
+
...turn,
|
|
200
|
+
status: "accepted",
|
|
201
|
+
turnId: identity(input.turnId, "Provider Turn id"),
|
|
202
|
+
updatedAt: acceptedAt
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
export function markProviderTurnDeliveryUnknown(raw, input) {
|
|
207
|
+
const binding = validateProviderRuntimeBinding(raw);
|
|
208
|
+
const turn = requireProviderTurn(binding, input.attemptId, "submitting");
|
|
209
|
+
const observedAt = orderedTurnTimestamp(turn, input.observedAt, "Provider Turn unknownAt");
|
|
210
|
+
return validateProviderRuntimeBinding({
|
|
211
|
+
...binding,
|
|
212
|
+
turn: {
|
|
213
|
+
...turn,
|
|
214
|
+
status: "delivery-unknown",
|
|
215
|
+
terminalReason: identity(input.reason, "Provider Turn unknown reason"),
|
|
216
|
+
updatedAt: observedAt
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
/** Exact negative acknowledgement before a Provider Turn identity existed. */
|
|
221
|
+
export function rejectProviderTurn(raw, input) {
|
|
222
|
+
const binding = validateProviderRuntimeBinding(raw);
|
|
223
|
+
const turn = binding.turn;
|
|
224
|
+
if (turn === null || turn.attemptId !== identity(input.attemptId, "Provider input attempt id")
|
|
225
|
+
|| (turn.status !== "submitting" && turn.status !== "delivery-unknown")) {
|
|
226
|
+
throw new Error("Provider Turn does not match a rejectable delivery state.");
|
|
227
|
+
}
|
|
228
|
+
const rejectedAt = orderedTurnTimestamp(turn, input.rejectedAt, "Provider Turn rejectedAt");
|
|
229
|
+
return validateProviderRuntimeBinding({
|
|
230
|
+
...binding,
|
|
231
|
+
turn: {
|
|
232
|
+
...turn,
|
|
233
|
+
status: "rejected",
|
|
234
|
+
terminalReason: identity(input.reason, "Provider Turn rejection reason"),
|
|
235
|
+
updatedAt: rejectedAt
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
export function settleProviderTurn(raw, input) {
|
|
240
|
+
const binding = validateProviderRuntimeBinding(raw);
|
|
241
|
+
const turn = binding.turn;
|
|
242
|
+
const turnId = identity(input.turnId, "Provider Turn id");
|
|
243
|
+
if (turn === null || turn.turnId !== turnId
|
|
244
|
+
|| (turn.status !== "accepted" && turn.status !== "running")) {
|
|
245
|
+
throw new Error("Provider Turn settlement does not match the current Turn.");
|
|
246
|
+
}
|
|
247
|
+
const settledAt = orderedTurnTimestamp(turn, input.settledAt, "Provider Turn settledAt");
|
|
248
|
+
return validateProviderRuntimeBinding({
|
|
249
|
+
...binding,
|
|
250
|
+
turn: {
|
|
251
|
+
...turn,
|
|
252
|
+
status: input.status,
|
|
253
|
+
updatedAt: settledAt,
|
|
254
|
+
...(input.reason === undefined
|
|
255
|
+
? {}
|
|
256
|
+
: { terminalReason: identity(input.reason, "Provider Turn terminal reason") })
|
|
257
|
+
}
|
|
80
258
|
});
|
|
81
259
|
}
|
|
82
260
|
export function updateProviderConversationRecoverability(raw, recoverability) {
|
|
@@ -98,7 +276,7 @@ export function supersedeProviderConversation(raw, input) {
|
|
|
98
276
|
if (!input.noUnsettledInputDelivery) {
|
|
99
277
|
throw new Error("Cannot replace a Provider Conversation with unsettled input delivery.");
|
|
100
278
|
}
|
|
101
|
-
if (
|
|
279
|
+
if (binding.authority.owner !== "none" || currentProviderActivation(binding) !== null) {
|
|
102
280
|
throw new Error("Cannot replace a Provider Conversation while its writer umbrella is owned.");
|
|
103
281
|
}
|
|
104
282
|
const switchedAt = timestamp(input.switchedAt, "Provider Conversation switch timestamp");
|
|
@@ -123,14 +301,19 @@ export function supersedeProviderConversation(raw, input) {
|
|
|
123
301
|
conversationId: input.conversationId,
|
|
124
302
|
generation: 1,
|
|
125
303
|
status: "active",
|
|
126
|
-
writerLease: true,
|
|
127
304
|
startedAt: switchedAt
|
|
128
|
-
}]
|
|
305
|
+
}],
|
|
306
|
+
authority: {
|
|
307
|
+
epoch: binding.authority.epoch + 1,
|
|
308
|
+
owner: "controller",
|
|
309
|
+
holderId: input.activationId,
|
|
310
|
+
changedAt: switchedAt
|
|
311
|
+
}
|
|
129
312
|
});
|
|
130
313
|
}
|
|
131
314
|
export function validateProviderRuntimeBinding(value) {
|
|
132
|
-
if (value.schemaVersion !==
|
|
133
|
-
throw new Error("Provider Runtime Binding schemaVersion must be
|
|
315
|
+
if (value.schemaVersion !== 2)
|
|
316
|
+
throw new Error("Provider Runtime Binding schemaVersion must be 2.");
|
|
134
317
|
identity(value.providerNamespace, "Provider namespace");
|
|
135
318
|
identity(value.accountScope, "Provider account scope");
|
|
136
319
|
identity(value.runId, "Run id");
|
|
@@ -197,8 +380,8 @@ export function validateProviderRuntimeBinding(value) {
|
|
|
197
380
|
}
|
|
198
381
|
timestamp(activation.startedAt, "Provider Activation startedAt");
|
|
199
382
|
if (activation.status === "active") {
|
|
200
|
-
if (
|
|
201
|
-
throw new Error("Active Provider Activation
|
|
383
|
+
if (activation.endedAt !== undefined) {
|
|
384
|
+
throw new Error("Active Provider Activation cannot have endedAt.");
|
|
202
385
|
}
|
|
203
386
|
if (activeByConversation.has(activation.conversationId)) {
|
|
204
387
|
throw new Error("Provider Conversation has multiple live writer Activations.");
|
|
@@ -206,14 +389,81 @@ export function validateProviderRuntimeBinding(value) {
|
|
|
206
389
|
activeByConversation.add(activation.conversationId);
|
|
207
390
|
}
|
|
208
391
|
else {
|
|
209
|
-
if (activation.
|
|
210
|
-
throw new Error("Terminal Provider Activation
|
|
211
|
-
}
|
|
392
|
+
if (activation.endedAt === undefined)
|
|
393
|
+
throw new Error("Terminal Provider Activation requires endedAt.");
|
|
212
394
|
timestamp(activation.endedAt, "Provider Activation endedAt");
|
|
213
395
|
}
|
|
214
396
|
}
|
|
397
|
+
integer(value.authority.epoch, 1, "Provider authority epoch");
|
|
398
|
+
timestamp(value.authority.changedAt, "Provider authority changedAt");
|
|
399
|
+
if (!["controller", "human", "none", "unknown"].includes(value.authority.owner)) {
|
|
400
|
+
throw new Error("Provider authority owner is invalid.");
|
|
401
|
+
}
|
|
402
|
+
const active = currentActivationUnchecked(value);
|
|
403
|
+
if (value.authority.owner === "controller" || value.authority.owner === "human") {
|
|
404
|
+
const holderId = identity(value.authority.holderId, "Provider authority holder id");
|
|
405
|
+
if (active === null)
|
|
406
|
+
throw new Error("Owned Provider authority requires a live Activation.");
|
|
407
|
+
if (value.authority.owner === "controller" && holderId !== active.activationId) {
|
|
408
|
+
throw new Error("Controller authority must be held by the live Activation.");
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else if (value.authority.holderId !== undefined) {
|
|
412
|
+
throw new Error("Unowned or unknown Provider authority cannot name a holder.");
|
|
413
|
+
}
|
|
414
|
+
if (active !== null && (value.authority.owner === "none" || value.authority.owner === "unknown")) {
|
|
415
|
+
throw new Error("A live Provider Activation requires an exact writer authority.");
|
|
416
|
+
}
|
|
417
|
+
if (!Object.hasOwn(value, "turn"))
|
|
418
|
+
throw new Error("Provider Runtime Binding requires Turn state.");
|
|
419
|
+
if (value.turn !== null)
|
|
420
|
+
validateProviderTurn(value.turn, value.authority.epoch);
|
|
215
421
|
return value;
|
|
216
422
|
}
|
|
423
|
+
function validateProviderTurn(turn, currentAuthorityEpoch) {
|
|
424
|
+
identity(turn.attemptId, "Provider input attempt id");
|
|
425
|
+
integer(turn.authorityEpoch, 1, "Provider Turn authority epoch");
|
|
426
|
+
if (turn.authorityEpoch > currentAuthorityEpoch) {
|
|
427
|
+
throw new Error("Provider Turn authority epoch is ahead of current authority.");
|
|
428
|
+
}
|
|
429
|
+
if (!["submitting", "accepted", "running", "completed", "failed", "cancelled", "rejected", "delivery-unknown"]
|
|
430
|
+
.includes(turn.status)) {
|
|
431
|
+
throw new Error("Provider Turn status is invalid.");
|
|
432
|
+
}
|
|
433
|
+
timestamp(turn.submittedAt, "Provider Turn submittedAt");
|
|
434
|
+
timestamp(turn.updatedAt, "Provider Turn updatedAt");
|
|
435
|
+
if (Date.parse(turn.updatedAt) < Date.parse(turn.submittedAt)) {
|
|
436
|
+
throw new Error("Provider Turn updatedAt is earlier than submittedAt.");
|
|
437
|
+
}
|
|
438
|
+
const hasAcceptedIdentity = turn.status === "accepted" || turn.status === "running"
|
|
439
|
+
|| turn.status === "completed" || turn.status === "failed" || turn.status === "cancelled";
|
|
440
|
+
if (hasAcceptedIdentity)
|
|
441
|
+
identity(turn.turnId, "Provider Turn id");
|
|
442
|
+
else if (turn.turnId !== undefined)
|
|
443
|
+
throw new Error("Unaccepted Provider Turn cannot have a Turn id.");
|
|
444
|
+
}
|
|
445
|
+
function requireProviderTurn(binding, attemptId, status) {
|
|
446
|
+
const id = identity(attemptId, "Provider input attempt id");
|
|
447
|
+
if (binding.turn === null || binding.turn.attemptId !== id || binding.turn.status !== status) {
|
|
448
|
+
throw new Error("Provider Turn does not match the expected delivery state.");
|
|
449
|
+
}
|
|
450
|
+
return binding.turn;
|
|
451
|
+
}
|
|
452
|
+
function orderedTurnTimestamp(turn, value, label) {
|
|
453
|
+
const normalized = timestamp(value, label);
|
|
454
|
+
if (Date.parse(normalized) < Date.parse(turn.updatedAt)) {
|
|
455
|
+
throw new Error(`${label} moved backwards.`);
|
|
456
|
+
}
|
|
457
|
+
return normalized;
|
|
458
|
+
}
|
|
459
|
+
function providerTurnIsActive(turn) {
|
|
460
|
+
return turn !== null && ["submitting", "accepted", "running", "delivery-unknown"]
|
|
461
|
+
.includes(turn.status);
|
|
462
|
+
}
|
|
463
|
+
function currentActivationUnchecked(binding) {
|
|
464
|
+
const current = binding.conversations.find((entry) => (entry.epoch === binding.currentConversationEpoch && entry.status === "current"));
|
|
465
|
+
return current === undefined ? null : [...binding.activations].reverse().find((entry) => (entry.conversationId === current.conversationId && entry.status === "active")) ?? null;
|
|
466
|
+
}
|
|
217
467
|
function identity(value, label) {
|
|
218
468
|
if (typeof value !== "string" || value.trim().length === 0)
|
|
219
469
|
throw new Error(`${label} is invalid.`);
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { normalizeRuntimeOwner } from "./runtimeOwner.js";
|
|
2
2
|
import { requireSafeIdentity, requireText } from "./validation.js";
|
|
3
|
+
import { validateProviderAuthorityFence } from "./providerAuthorityFence.js";
|
|
3
4
|
export function createRuntimeBinding(input) {
|
|
4
5
|
const hostCreated = input.hostCreated === undefined
|
|
5
6
|
? undefined
|
|
6
7
|
: requireBoolean(input.hostCreated, "Runtime host-created flag");
|
|
7
|
-
const
|
|
8
|
+
const initialTurnRunId = input.initialTurnRunId === undefined
|
|
8
9
|
? undefined
|
|
9
|
-
: requireSafeIdentity(input.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
10
|
+
: requireSafeIdentity(input.initialTurnRunId, "Initial Turn Run id");
|
|
11
|
+
const initialTurnDeliveryUnknownRunId = input.initialTurnDeliveryUnknownRunId === undefined
|
|
12
|
+
? undefined
|
|
13
|
+
: requireSafeIdentity(input.initialTurnDeliveryUnknownRunId, "Delivery-unknown initial Turn Run id");
|
|
14
|
+
const initialTurnRejectedRunId = input.initialTurnRejectedRunId === undefined
|
|
14
15
|
? undefined
|
|
15
|
-
: requireSafeIdentity(input.
|
|
16
|
+
: requireSafeIdentity(input.initialTurnRejectedRunId, "Rejected initial Turn Run id");
|
|
17
|
+
if ([initialTurnRunId, initialTurnDeliveryUnknownRunId, initialTurnRejectedRunId]
|
|
18
|
+
.filter((value) => value !== undefined).length > 1) {
|
|
19
|
+
throw new TypeError("Runtime binding must report at most one initial Turn outcome.");
|
|
20
|
+
}
|
|
16
21
|
return {
|
|
17
22
|
id: requireSafeIdentity(input.id, "Runtime binding id"),
|
|
18
23
|
launchId: requireSafeIdentity(input.launchId, "Launch id"),
|
|
@@ -21,13 +26,17 @@ export function createRuntimeBinding(input) {
|
|
|
21
26
|
adapterId: requireSafeIdentity(input.adapterId, "Agent adapter id"),
|
|
22
27
|
hostRef: requireText(input.hostRef, "Session host reference"),
|
|
23
28
|
...(hostCreated === undefined ? {} : { hostCreated }),
|
|
24
|
-
...(
|
|
25
|
-
...(
|
|
29
|
+
...(initialTurnRunId === undefined ? {} : { initialTurnRunId }),
|
|
30
|
+
...(initialTurnDeliveryUnknownRunId === undefined
|
|
26
31
|
? {}
|
|
27
|
-
: {
|
|
32
|
+
: { initialTurnDeliveryUnknownRunId }),
|
|
33
|
+
...(initialTurnRejectedRunId === undefined ? {} : { initialTurnRejectedRunId }),
|
|
28
34
|
...(input.nativeSessionId === undefined
|
|
29
35
|
? {}
|
|
30
|
-
: { nativeSessionId: requireText(input.nativeSessionId, "Native session id") })
|
|
36
|
+
: { nativeSessionId: requireText(input.nativeSessionId, "Native session id") }),
|
|
37
|
+
...(input.providerAuthority === undefined
|
|
38
|
+
? {}
|
|
39
|
+
: { providerAuthority: validateProviderAuthorityFence(input.providerAuthority) })
|
|
31
40
|
};
|
|
32
41
|
}
|
|
33
42
|
function requireBoolean(value, label) {
|