@zq-silk/yui 0.8.1 → 0.8.2

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.
Files changed (38) hide show
  1. package/ARCHITECTURE.md +27 -28
  2. package/README.md +35 -46
  3. package/dist/cli/commandCatalog.js +49 -14
  4. package/dist/cli/interactionPolicy.js +4 -10
  5. package/dist/cli/invocationRouter.js +2 -1
  6. package/dist/cli.js +73 -21
  7. package/dist/commands/taskCommands.js +108 -53
  8. package/dist/controller/fileSchedulerStoreAdapter.js +389 -70
  9. package/dist/controller/resourceInventory.js +9 -5
  10. package/dist/controller/runtime.js +80 -7
  11. package/dist/controller/runtimeLaunchCoordinator.js +18 -78
  12. package/dist/controller/structuredProviderObservation.js +273 -0
  13. package/dist/executor/agentAdapter.js +40 -0
  14. package/dist/executor/agentExecutor.js +31 -7
  15. package/dist/executor/executorRegistry.js +11 -49
  16. package/dist/executor/fileRoleLaunchPlanner.js +115 -37
  17. package/dist/lifecycle/canonicalLifecycleEvent.js +5 -2
  18. package/dist/run/agentRun.js +2 -2
  19. package/dist/runtime/agentHost.js +767 -158
  20. package/dist/runtime/builtinAgentDrivers.js +1 -5
  21. package/dist/runtime/codexAppServerRuntime.js +67 -60
  22. package/dist/runtime/exactControlPlane.js +7 -2
  23. package/dist/runtime/index.js +6 -2
  24. package/dist/runtime/launchBroker.js +30 -8
  25. package/dist/runtime/providerAuthorityFence.js +24 -0
  26. package/dist/runtime/providerControl.js +63 -0
  27. package/dist/runtime/providerRecoveryDecision.js +55 -0
  28. package/dist/runtime/providerRuntimeIdentity.js +269 -19
  29. package/dist/runtime/runtimeBinding.js +20 -11
  30. package/dist/runtime/structuredProviderHost.js +476 -0
  31. package/dist/runtime/tmuxAdapters.js +143 -42
  32. package/dist/scheduler/activeRoleRunDelivery.js +206 -120
  33. package/dist/scheduler/leaderWakeupProcessor.js +141 -16
  34. package/dist/storage/migration/productionRegistry.js +111 -0
  35. package/dist/storage/taskStore.js +1 -1
  36. package/dist/tmux/tmuxManager.js +1 -1
  37. package/i18n/README.zh-CN.md +11 -8
  38. package/package.json +1 -1
@@ -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: 1,
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: identity(input.activationId, "Provider Activation id"),
71
+ activationId,
47
72
  conversationId: conversation.conversationId,
48
73
  generation,
49
74
  status: "active",
50
- writerLease: true,
51
- startedAt: timestamp(input.startedAt, "Provider Activation startedAt")
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 (!input.writerUmbrellaClear || currentProviderActivation(binding) !== null) {
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 !== 1)
133
- throw new Error("Provider Runtime Binding schemaVersion must be 1.");
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 (!activation.writerLease || activation.endedAt !== undefined) {
201
- throw new Error("Active Provider Activation must hold its writer lease.");
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.writerLease || activation.endedAt === undefined) {
210
- throw new Error("Terminal Provider Activation must release its writer lease.");
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 initialPromptRunId = input.initialPromptRunId === undefined
8
+ const initialTurnRunId = input.initialTurnRunId === undefined
8
9
  ? undefined
9
- : requireSafeIdentity(input.initialPromptRunId, "Initial prompt Run id");
10
- if (initialPromptRunId !== undefined && hostCreated !== true) {
11
- throw new TypeError("An initial prompt Run id requires a newly-created runtime host.");
12
- }
13
- const launchPromptUncertainRunId = input.launchPromptUncertainRunId === undefined
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.launchPromptUncertainRunId, "Uncertain launch prompt Run id");
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
- ...(initialPromptRunId === undefined ? {} : { initialPromptRunId }),
25
- ...(launchPromptUncertainRunId === undefined
29
+ ...(initialTurnRunId === undefined ? {} : { initialTurnRunId }),
30
+ ...(initialTurnDeliveryUnknownRunId === undefined
26
31
  ? {}
27
- : { launchPromptUncertainRunId }),
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) {