@zq-silk/yui 0.13.7 → 0.13.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +40 -4
- package/README.md +19 -1
- package/dist/cli/updatePorts.js +4 -4
- package/dist/cli.js +1 -1
- package/dist/commands/taskCommands.js +1 -12
- package/dist/commands/taskContextCommand.js +1 -1
- package/dist/commands/taskExecutionCommands.js +0 -5
- package/dist/commands/taskOverviewCommand.js +5 -1
- package/dist/commands/taskRoleRuntimeStatus.js +0 -13
- package/dist/context/sessionBootstrapManifest.js +26 -23
- package/dist/controller/controller.js +0 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +84 -78
- package/dist/executor/agentExecutor.js +14 -28
- package/dist/executor/fileRoleLaunchPlanner.js +2 -3
- package/dist/lifecycle/exactRunTerminalization.js +1 -3
- package/dist/output/rolePresentation.js +0 -1
- package/dist/repository/taskWorkspacePreparer.js +0 -1
- package/dist/role/role.js +12 -20
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/providerRuntimeIdentity.js +8 -17
- package/dist/scheduler/activeRoleRunDelivery.js +2 -10
- package/dist/scheduler/activeTaskProgress.js +1 -4
- package/dist/scheduler/leaderWakeupProcessor.js +0 -5
- package/dist/scheduler/roleRunStall.js +3 -3
- package/dist/storage/migration/productionRegistry.js +143 -0
- package/dist/storage/taskStore.js +2 -2
- package/dist/web/webSnapshot.js +3 -0
- package/i18n/README.zh-CN.md +11 -3
- package/package.json +1 -1
- package/skills/yui-runtime/SKILL.md +5 -4
package/dist/role/role.js
CHANGED
|
@@ -19,19 +19,22 @@ export function createRole(taskId, name, bindings, activeAgentId, workspace, now
|
|
|
19
19
|
const owner = createRoleOwner(name, bindings, activeAgentId, workspace, now, profile, defaultAccess);
|
|
20
20
|
return validateTaskRole({
|
|
21
21
|
...owner,
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
schemaVersion: 4,
|
|
23
|
+
taskId: requireSafeIdentity(taskId, "Task id")
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
export function createGlobalRole(name, bindings, activeAgentId, workspace, now, profile = {}, defaultAccess = "write") {
|
|
27
|
-
return
|
|
27
|
+
return validateGlobalRole({
|
|
28
|
+
...createRoleOwner(name, bindings, activeAgentId, workspace, now, profile, defaultAccess),
|
|
29
|
+
schemaVersion: 3
|
|
30
|
+
});
|
|
28
31
|
}
|
|
29
32
|
export function copyGlobalRoleToTaskRole(globalRole, taskId, now, name = globalRole.name) {
|
|
30
33
|
validateGlobalRole(globalRole);
|
|
31
34
|
const timestamp = now.toISOString();
|
|
32
35
|
return validateTaskRole({
|
|
33
36
|
...cloneProfile(globalRole),
|
|
34
|
-
schemaVersion:
|
|
37
|
+
schemaVersion: 4,
|
|
35
38
|
launchRevision: 1,
|
|
36
39
|
defaultAccess: globalRole.defaultAccess,
|
|
37
40
|
taskId: requireSafeIdentity(taskId, "Task id"),
|
|
@@ -39,7 +42,6 @@ export function copyGlobalRoleToTaskRole(globalRole, taskId, now, name = globalR
|
|
|
39
42
|
activeAgentId: globalRole.activeAgentId,
|
|
40
43
|
agentBindings: cloneBindings(globalRole.agentBindings),
|
|
41
44
|
workspace: globalRole.workspace,
|
|
42
|
-
status: "idle",
|
|
43
45
|
createdAt: timestamp,
|
|
44
46
|
updatedAt: timestamp
|
|
45
47
|
});
|
|
@@ -76,12 +78,6 @@ export function updateRole(role, patch, now) {
|
|
|
76
78
|
: role.launchRevision + 1;
|
|
77
79
|
return validateTaskRole(updated);
|
|
78
80
|
}
|
|
79
|
-
export function updateRoleStatus(role, status, now) {
|
|
80
|
-
validateTaskRole(role);
|
|
81
|
-
if (!isRoleStatus(status))
|
|
82
|
-
throw new Error(`Role status is invalid: ${status}.`);
|
|
83
|
-
return { ...role, status, updatedAt: now.toISOString() };
|
|
84
|
-
}
|
|
85
81
|
export function switchActiveRoleAgent(role, sessions, targetAgentId, runtime, now) {
|
|
86
82
|
validateRoleOwner(role);
|
|
87
83
|
const normalizedTarget = requireSafeIdentity(targetAgentId, "Target Agent id");
|
|
@@ -175,8 +171,6 @@ export function validateGlobalRole(role) {
|
|
|
175
171
|
}
|
|
176
172
|
export function validateTaskRole(role) {
|
|
177
173
|
requireSafeIdentity(role.taskId, "Task id");
|
|
178
|
-
if (!isRoleStatus(role.status))
|
|
179
|
-
throw new Error(`Role status is invalid: ${role.status}.`);
|
|
180
174
|
return validateRoleOwner(role);
|
|
181
175
|
}
|
|
182
176
|
function createRoleOwner(name, bindings, activeAgentId, workspace, now, profile, defaultAccess) {
|
|
@@ -189,9 +183,8 @@ function createRoleOwner(name, bindings, activeAgentId, workspace, now, profile,
|
|
|
189
183
|
mappedBindings[binding.agentId] = binding;
|
|
190
184
|
}
|
|
191
185
|
const timestamp = now.toISOString();
|
|
192
|
-
return
|
|
186
|
+
return {
|
|
193
187
|
...cloneProfile(profile),
|
|
194
|
-
schemaVersion: 3,
|
|
195
188
|
launchRevision: 1,
|
|
196
189
|
defaultAccess,
|
|
197
190
|
name: requireSafeIdentity(name, "Role name"),
|
|
@@ -200,11 +193,13 @@ function createRoleOwner(name, bindings, activeAgentId, workspace, now, profile,
|
|
|
200
193
|
workspace: requireText(workspace, "Role workspace"),
|
|
201
194
|
createdAt: timestamp,
|
|
202
195
|
updatedAt: timestamp
|
|
203
|
-
}
|
|
196
|
+
};
|
|
204
197
|
}
|
|
205
198
|
function validateRoleOwner(role) {
|
|
206
|
-
|
|
199
|
+
const expectedSchemaVersion = "taskId" in role ? 4 : 3;
|
|
200
|
+
if (role.schemaVersion !== expectedSchemaVersion) {
|
|
207
201
|
throw new Error("Role schema version is invalid.");
|
|
202
|
+
}
|
|
208
203
|
if (!Number.isSafeInteger(role.launchRevision) || role.launchRevision < 1) {
|
|
209
204
|
throw new Error("Role launch revision must be a positive integer.");
|
|
210
205
|
}
|
|
@@ -333,6 +328,3 @@ function requireText(value, label) {
|
|
|
333
328
|
throw new Error(`${label} is required.`);
|
|
334
329
|
return normalized;
|
|
335
330
|
}
|
|
336
|
-
function isRoleStatus(value) {
|
|
337
|
-
return ["idle", "running", "detached", "exited", "failed"].includes(value);
|
|
338
|
-
}
|
package/dist/runtime/index.js
CHANGED
|
@@ -14,7 +14,7 @@ export { formatRuntimeLaunchDiagnostic, redactLaunchArgument, redactLaunchText,
|
|
|
14
14
|
export { DEFAULT_FORCED_GRACE_MS, DEFAULT_GRACEFUL_GRACE_MS, terminateSessionOwners } from "./sessionTerminationGuard.js";
|
|
15
15
|
export { ProviderContinuationReconciliationService } from "./providerContinuationReconciliationService.js";
|
|
16
16
|
export { codexNotificationBoundary, codexAppServerErrorIsMissing, CodexAppServerRequestError, CodexAppServerRuntime } from "./codexAppServerRuntime.js";
|
|
17
|
-
export { createProviderRuntimeBinding, acceptProviderTurn, beginProviderTurn, currentProviderActivation, currentProviderAuthority, currentProviderConversation, endProviderActivation, markProviderTurnDeliveryUnknown,
|
|
17
|
+
export { createProviderRuntimeBinding, acceptProviderTurn, beginProviderTurn, currentProviderActivation, currentProviderAuthority, currentProviderConversation, endProviderActivation, markProviderTurnDeliveryUnknown, rejectProviderTurn, settleProviderTurnSubmission, settleProviderTurn, startProviderActivation, supersedeProviderConversation, transferProviderAuthority, updateProviderConversationRecoverability, validateProviderRuntimeBinding } from "./providerRuntimeIdentity.js";
|
|
18
18
|
export { FencedProviderControl } from "./providerControl.js";
|
|
19
19
|
export { sameProviderAuthorityFence, validateProviderAuthorityFence } from "./providerAuthorityFence.js";
|
|
20
20
|
export { decideProviderRecovery } from "./providerRecoveryDecision.js";
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
export function createProviderRuntimeBinding(input) {
|
|
2
2
|
const startedAt = timestamp(input.startedAt, "Provider Activation startedAt");
|
|
3
3
|
return validateProviderRuntimeBinding({
|
|
4
|
-
schemaVersion:
|
|
4
|
+
schemaVersion: 3,
|
|
5
5
|
providerNamespace: identity(input.providerNamespace, "Provider namespace"),
|
|
6
6
|
accountScope: identity(input.accountScope, "Provider account scope"),
|
|
7
|
-
runId: identity(input.runId, "Run id"),
|
|
8
7
|
currentConversationEpoch: 1,
|
|
9
8
|
conversations: [{
|
|
10
9
|
conversationId: identity(input.conversationId, "Provider Conversation id"),
|
|
@@ -40,17 +39,6 @@ export function currentProviderActivation(binding) {
|
|
|
40
39
|
const conversation = currentProviderConversation(binding);
|
|
41
40
|
return [...binding.activations].reverse().find((entry) => (entry.conversationId === conversation.conversationId && entry.status === "active")) ?? null;
|
|
42
41
|
}
|
|
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
|
-
}
|
|
54
42
|
export function startProviderActivation(raw, input) {
|
|
55
43
|
const binding = validateProviderRuntimeBinding(raw);
|
|
56
44
|
if (currentProviderActivation(binding) !== null) {
|
|
@@ -158,8 +146,10 @@ export function transferProviderAuthority(raw, input) {
|
|
|
158
146
|
}
|
|
159
147
|
export function beginProviderTurn(raw, input) {
|
|
160
148
|
const binding = validateProviderRuntimeBinding(raw);
|
|
149
|
+
const runId = identity(input.runId, "Run id");
|
|
161
150
|
const attemptId = identity(input.attemptId, "Provider input attempt id");
|
|
162
|
-
if (binding.turn?.
|
|
151
|
+
if (binding.turn?.runId === runId
|
|
152
|
+
&& binding.turn.attemptId === attemptId
|
|
163
153
|
&& binding.turn.authorityEpoch === input.authorityEpoch
|
|
164
154
|
&& binding.turn.status === "submitting") {
|
|
165
155
|
return binding;
|
|
@@ -176,6 +166,7 @@ export function beginProviderTurn(raw, input) {
|
|
|
176
166
|
return validateProviderRuntimeBinding({
|
|
177
167
|
...binding,
|
|
178
168
|
turn: {
|
|
169
|
+
runId,
|
|
179
170
|
attemptId,
|
|
180
171
|
authorityEpoch: input.authorityEpoch,
|
|
181
172
|
status: "submitting",
|
|
@@ -350,11 +341,10 @@ export function supersedeProviderConversation(raw, input) {
|
|
|
350
341
|
});
|
|
351
342
|
}
|
|
352
343
|
export function validateProviderRuntimeBinding(value) {
|
|
353
|
-
if (value.schemaVersion !==
|
|
354
|
-
throw new Error("Provider Runtime Binding schemaVersion must be
|
|
344
|
+
if (value.schemaVersion !== 3)
|
|
345
|
+
throw new Error("Provider Runtime Binding schemaVersion must be 3.");
|
|
355
346
|
identity(value.providerNamespace, "Provider namespace");
|
|
356
347
|
identity(value.accountScope, "Provider account scope");
|
|
357
|
-
identity(value.runId, "Run id");
|
|
358
348
|
integer(value.currentConversationEpoch, 1, "Current Provider Conversation epoch");
|
|
359
349
|
if (!Array.isArray(value.conversations) || value.conversations.length === 0) {
|
|
360
350
|
throw new Error("Provider Runtime Binding requires a Conversation.");
|
|
@@ -459,6 +449,7 @@ export function validateProviderRuntimeBinding(value) {
|
|
|
459
449
|
return value;
|
|
460
450
|
}
|
|
461
451
|
function validateProviderTurn(turn, currentAuthorityEpoch) {
|
|
452
|
+
identity(turn.runId, "Run id");
|
|
462
453
|
identity(turn.attemptId, "Provider input attempt id");
|
|
463
454
|
integer(turn.authorityEpoch, 1, "Provider Turn authority epoch");
|
|
464
455
|
if (turn.authorityEpoch > currentAuthorityEpoch) {
|
|
@@ -497,17 +497,9 @@ async function processActiveRunContinuation(store, delivery, task, role, run, no
|
|
|
497
497
|
};
|
|
498
498
|
}
|
|
499
499
|
// Managed input has one write path: a new structured Turn at a settled
|
|
500
|
-
// boundary.
|
|
500
|
+
// boundary. AgentHost owns Provider serialization; a busy send releases the
|
|
501
|
+
// claim and leaves the durable correction pending for the next pass.
|
|
501
502
|
const mode = "followup";
|
|
502
|
-
if (session.status === "running") {
|
|
503
|
-
return {
|
|
504
|
-
taskId: task.id,
|
|
505
|
-
roleName: role.name,
|
|
506
|
-
runId: run.id,
|
|
507
|
-
status: "skipped",
|
|
508
|
-
reason: "not-ready"
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
503
|
if (writer === null) {
|
|
512
504
|
return {
|
|
513
505
|
taskId: task.id,
|
|
@@ -25,8 +25,6 @@ export function repairOrphanedActiveTasks(store, now, selection) {
|
|
|
25
25
|
const run = store.getActiveAgentRun(task.id, role.name);
|
|
26
26
|
return run === null ? [] : [run];
|
|
27
27
|
});
|
|
28
|
-
const hasInFlightTurn = roles.some((role) => store.hasInFlightTurn(task.id, role.name));
|
|
29
|
-
const hasLeaderInFlightTurn = store.hasInFlightTurn(task.id, "leader");
|
|
30
28
|
const leaderTarget = { kind: "role", taskId: task.id, roleName: "leader" };
|
|
31
29
|
const leaderMailbox = store.getWorkMailbox(leaderTarget);
|
|
32
30
|
const projection = projectTaskExecution({
|
|
@@ -41,8 +39,7 @@ export function repairOrphanedActiveTasks(store, now, selection) {
|
|
|
41
39
|
&& !activeRuns.some(({ roleName }) => roleName === "leader")
|
|
42
40
|
&& activeRuns.some(({ roleName }) => roleName !== "leader")
|
|
43
41
|
&& projection.status === "waiting-on-agents";
|
|
44
|
-
if ((
|
|
45
|
-
|| store.hasOpenInputRequest(task.id)
|
|
42
|
+
if (store.hasOpenInputRequest(task.id)
|
|
46
43
|
|| store.getLeaderFailure(task.id) !== null
|
|
47
44
|
|| (projection.status !== "needs-leader-action" && !queuedAlongsideActiveSibling)
|
|
48
45
|
|| hasUnclaimedLeaderWork(store, task.id, leaderMailbox)) {
|
|
@@ -42,11 +42,6 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
|
|
|
42
42
|
results.push({ taskId: task.id, status: "skipped", reason: "busy" });
|
|
43
43
|
continue;
|
|
44
44
|
}
|
|
45
|
-
if (typeof store.hasInFlightTurn === "function"
|
|
46
|
-
&& store.hasInFlightTurn(task.id, role.name)) {
|
|
47
|
-
results.push({ taskId: task.id, status: "skipped", reason: "busy" });
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
45
|
const reopening = wakeup.reasons.includes("task-reopened");
|
|
51
46
|
let existingSession = store.getRoleSession(task.id, role.name, reopening ? undefined : role.effective.agentId);
|
|
52
47
|
let effectiveSession = existingSession;
|
|
@@ -786,7 +786,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
|
|
|
786
786
|
continue;
|
|
787
787
|
const evidenceKey = [
|
|
788
788
|
kind,
|
|
789
|
-
stallEvidenceKey(candidate.
|
|
789
|
+
stallEvidenceKey(candidate.session?.status),
|
|
790
790
|
classification,
|
|
791
791
|
...(candidate.role.name === "leader"
|
|
792
792
|
? [leaderStallEvidence(store, candidate.task.id, observed, now, windowMs)]
|
|
@@ -1001,8 +1001,8 @@ function leaderStallEvidence(store, taskId, observed, now, windowMs) {
|
|
|
1001
1001
|
: "recent";
|
|
1002
1002
|
return `downstream=active:${active},healthy:${healthy},stalled:${stalled}:leader-mailbox=${pendingAge},leader-processing=${processingAge}`;
|
|
1003
1003
|
}
|
|
1004
|
-
function stallEvidenceKey(
|
|
1005
|
-
return `live-pane-no-progress:
|
|
1004
|
+
function stallEvidenceKey(sessionStatus) {
|
|
1005
|
+
return `live-pane-no-progress:session=${sessionStatus ?? "unknown"}`;
|
|
1006
1006
|
}
|
|
1007
1007
|
function exactLiveStatuses(statuses, candidates) {
|
|
1008
1008
|
const expected = new Set(candidates.map(({ task, role }) => `${task.id}\0${role.name}`));
|
|
@@ -99,10 +99,14 @@ const RELEASE_WORKFLOW_FROM_VERSION = 0;
|
|
|
99
99
|
const RELEASE_WORKFLOW_TO_VERSION = 1;
|
|
100
100
|
const WORK_MAILBOX_FROM_VERSION = 1;
|
|
101
101
|
const WORK_MAILBOX_TO_VERSION = 2;
|
|
102
|
+
const TASK_ROLE_FROM_VERSION = 3;
|
|
103
|
+
const TASK_ROLE_TO_VERSION = 4;
|
|
102
104
|
const TASK_ROLE_SESSION_SET_FROM_VERSION = 4;
|
|
103
105
|
const TASK_ROLE_SESSION_SET_TO_VERSION = 5;
|
|
104
106
|
const STRUCTURED_PROVIDER_SESSION_SET_FROM_VERSION = 5;
|
|
105
107
|
const STRUCTURED_PROVIDER_SESSION_SET_TO_VERSION = 6;
|
|
108
|
+
const AGENT_FIRST_SESSION_SET_FROM_VERSION = 6;
|
|
109
|
+
const AGENT_FIRST_SESSION_SET_TO_VERSION = 7;
|
|
106
110
|
const PUBLICATION_REFERENCE_FROM_VERSION = 0;
|
|
107
111
|
const PUBLICATION_REFERENCE_TO_VERSION = 1;
|
|
108
112
|
const CONFIG_FROM_VERSION = 1;
|
|
@@ -203,12 +207,68 @@ export function createProductionStorageRegistry() {
|
|
|
203
207
|
.registerOfflineMigration(capabilityGrantIntroductionStep())
|
|
204
208
|
.registerOfflineMigration(releaseWorkflowIntroductionStep())
|
|
205
209
|
.registerOfflineMigration(workMailboxV2Step())
|
|
210
|
+
.registerOfflineMigration(agentRunOwnedTaskRoleStep())
|
|
206
211
|
.registerOfflineMigration(taskRoleSessionSetV5Step())
|
|
207
212
|
.registerOfflineMigration(structuredProviderSessionSetV6Step())
|
|
213
|
+
.registerOfflineMigration(agentFirstSessionSetV7Step())
|
|
208
214
|
.registerOfflineMigration(publicationReferenceIntroductionStep());
|
|
209
215
|
assertRegistryCoversBaselineToCurrent(registry);
|
|
210
216
|
return registry;
|
|
211
217
|
}
|
|
218
|
+
/** Removes the independently writable Role runtime projection; AgentRun owns workflow activity. */
|
|
219
|
+
function agentRunOwnedTaskRoleStep() {
|
|
220
|
+
return {
|
|
221
|
+
axis: "record",
|
|
222
|
+
recordKind: "taskRole",
|
|
223
|
+
fromVersion: TASK_ROLE_FROM_VERSION,
|
|
224
|
+
toVersion: TASK_ROLE_TO_VERSION,
|
|
225
|
+
preconditions: requireTaskRoleV3Family,
|
|
226
|
+
transform: migrateTaskRoleV3ToV4,
|
|
227
|
+
declaredEffects: []
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function requireTaskRoleV3Family(snapshot) {
|
|
231
|
+
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
232
|
+
if (versions.taskRole !== TASK_ROLE_FROM_VERSION) {
|
|
233
|
+
throw new Error(`Record taskRole migration requires manifest version ${TASK_ROLE_FROM_VERSION}.`);
|
|
234
|
+
}
|
|
235
|
+
if (snapshot.state === null)
|
|
236
|
+
return;
|
|
237
|
+
const tasks = asObject(snapshot.state.tasks, "state tasks");
|
|
238
|
+
for (const [taskId, rawAggregate] of Object.entries(tasks)) {
|
|
239
|
+
const aggregate = asObject(rawAggregate, `Task aggregate ${taskId}`);
|
|
240
|
+
const roles = asObject(aggregate.roles, `Task Roles ${taskId}`);
|
|
241
|
+
for (const [roleName, rawRole] of Object.entries(roles)) {
|
|
242
|
+
const role = asObject(rawRole, `Task Role ${taskId}/${roleName}`);
|
|
243
|
+
if (role.schemaVersion !== TASK_ROLE_FROM_VERSION) {
|
|
244
|
+
throw new Error(`Task Role ${taskId}/${roleName} must use schemaVersion ${TASK_ROLE_FROM_VERSION}.`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function migrateTaskRoleV3ToV4(snapshot) {
|
|
250
|
+
requireTaskRoleV3Family(snapshot);
|
|
251
|
+
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
252
|
+
const schemaManifest = {
|
|
253
|
+
...snapshot.schemaManifest,
|
|
254
|
+
recordVersions: { ...versions, taskRole: TASK_ROLE_TO_VERSION }
|
|
255
|
+
};
|
|
256
|
+
if (snapshot.state === null)
|
|
257
|
+
return { schemaManifest, state: null };
|
|
258
|
+
const tasks = asObject(snapshot.state.tasks, "state tasks");
|
|
259
|
+
const nextTasks = Object.fromEntries(Object.entries(tasks).map(([taskId, rawAggregate]) => {
|
|
260
|
+
const aggregate = asObject(rawAggregate, `Task aggregate ${taskId}`);
|
|
261
|
+
const roles = asObject(aggregate.roles, `Task Roles ${taskId}`);
|
|
262
|
+
const nextRoles = Object.fromEntries(Object.entries(roles).map(([roleName, rawRole]) => {
|
|
263
|
+
const role = asObject(rawRole, `Task Role ${taskId}/${roleName}`);
|
|
264
|
+
const { status: _status, ...retained } = role;
|
|
265
|
+
void _status;
|
|
266
|
+
return [roleName, { ...retained, schemaVersion: TASK_ROLE_TO_VERSION }];
|
|
267
|
+
}));
|
|
268
|
+
return [taskId, { ...aggregate, roles: nextRoles }];
|
|
269
|
+
}));
|
|
270
|
+
return { schemaManifest, state: { ...snapshot.state, tasks: nextTasks } };
|
|
271
|
+
}
|
|
212
272
|
function configV2Step() {
|
|
213
273
|
return {
|
|
214
274
|
axis: "record",
|
|
@@ -2718,6 +2778,89 @@ function structuredProviderSessionSetV6Step() {
|
|
|
2718
2778
|
declaredEffects: []
|
|
2719
2779
|
};
|
|
2720
2780
|
}
|
|
2781
|
+
/**
|
|
2782
|
+
* A Provider binding describes one reusable Conversation. Run correlation now
|
|
2783
|
+
* belongs to the concrete Provider Turn instead of making the Conversation a
|
|
2784
|
+
* second writable projection of the current AgentRun.
|
|
2785
|
+
*/
|
|
2786
|
+
function agentFirstSessionSetV7Step() {
|
|
2787
|
+
return {
|
|
2788
|
+
axis: "record",
|
|
2789
|
+
recordKind: "taskRoleSessionSet",
|
|
2790
|
+
fromVersion: AGENT_FIRST_SESSION_SET_FROM_VERSION,
|
|
2791
|
+
toVersion: AGENT_FIRST_SESSION_SET_TO_VERSION,
|
|
2792
|
+
preconditions: requireTaskRoleSessionSetV6Family,
|
|
2793
|
+
transform: migrateTaskRoleSessionSetV6ToV7,
|
|
2794
|
+
declaredEffects: []
|
|
2795
|
+
};
|
|
2796
|
+
}
|
|
2797
|
+
function requireTaskRoleSessionSetV6Family(snapshot) {
|
|
2798
|
+
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
2799
|
+
if (versions.taskRoleSessionSet !== AGENT_FIRST_SESSION_SET_FROM_VERSION) {
|
|
2800
|
+
throw new Error(`Record taskRoleSessionSet migration requires manifest version ${AGENT_FIRST_SESSION_SET_FROM_VERSION}.`);
|
|
2801
|
+
}
|
|
2802
|
+
if (snapshot.state === null)
|
|
2803
|
+
return;
|
|
2804
|
+
const tasks = asObject(snapshot.state.tasks, "state tasks");
|
|
2805
|
+
for (const [taskId, rawAggregate] of Object.entries(tasks)) {
|
|
2806
|
+
const aggregate = asObject(rawAggregate, `Task aggregate ${taskId}`);
|
|
2807
|
+
const sets = asObject(aggregate.roleSessionSets, `Task Role session sets ${taskId}`);
|
|
2808
|
+
for (const [roleName, rawSet] of Object.entries(sets)) {
|
|
2809
|
+
const set = asObject(rawSet, `Task Role session set ${taskId}/${roleName}`);
|
|
2810
|
+
if (set.schemaVersion !== AGENT_FIRST_SESSION_SET_FROM_VERSION) {
|
|
2811
|
+
throw new Error(`Task Role session set ${taskId}/${roleName} must use schemaVersion ${AGENT_FIRST_SESSION_SET_FROM_VERSION}.`);
|
|
2812
|
+
}
|
|
2813
|
+
if (set.providerBinding !== null) {
|
|
2814
|
+
const binding = asObject(set.providerBinding, `Provider Binding ${taskId}/${roleName}`);
|
|
2815
|
+
if (binding.schemaVersion !== 2 || typeof binding.runId !== "string") {
|
|
2816
|
+
throw new Error(`Provider Binding ${taskId}/${roleName} must use schemaVersion 2.`);
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
function migrateTaskRoleSessionSetV6ToV7(snapshot) {
|
|
2823
|
+
requireTaskRoleSessionSetV6Family(snapshot);
|
|
2824
|
+
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
2825
|
+
const schemaManifest = {
|
|
2826
|
+
...snapshot.schemaManifest,
|
|
2827
|
+
recordVersions: {
|
|
2828
|
+
...versions,
|
|
2829
|
+
taskRoleSessionSet: AGENT_FIRST_SESSION_SET_TO_VERSION
|
|
2830
|
+
}
|
|
2831
|
+
};
|
|
2832
|
+
if (snapshot.state === null)
|
|
2833
|
+
return { schemaManifest, state: null };
|
|
2834
|
+
const tasks = asObject(snapshot.state.tasks, "state tasks");
|
|
2835
|
+
const nextTasks = {};
|
|
2836
|
+
for (const [taskId, rawAggregate] of Object.entries(tasks)) {
|
|
2837
|
+
const aggregate = asObject(rawAggregate, `Task aggregate ${taskId}`);
|
|
2838
|
+
const rawSets = asObject(aggregate.roleSessionSets, `Task Role session sets ${taskId}`);
|
|
2839
|
+
const nextSets = {};
|
|
2840
|
+
for (const [roleName, rawSet] of Object.entries(rawSets)) {
|
|
2841
|
+
const set = asObject(rawSet, `Task Role session set ${taskId}/${roleName}`);
|
|
2842
|
+
let providerBinding = null;
|
|
2843
|
+
if (set.providerBinding !== null) {
|
|
2844
|
+
const binding = asObject(set.providerBinding, `Provider Binding ${taskId}/${roleName}`);
|
|
2845
|
+
const { runId, ...conversation } = binding;
|
|
2846
|
+
providerBinding = {
|
|
2847
|
+
...conversation,
|
|
2848
|
+
schemaVersion: 3,
|
|
2849
|
+
turn: binding.turn === null
|
|
2850
|
+
? null
|
|
2851
|
+
: { ...asObject(binding.turn, `Provider Turn ${taskId}/${roleName}`), runId }
|
|
2852
|
+
};
|
|
2853
|
+
}
|
|
2854
|
+
nextSets[roleName] = {
|
|
2855
|
+
...set,
|
|
2856
|
+
schemaVersion: AGENT_FIRST_SESSION_SET_TO_VERSION,
|
|
2857
|
+
providerBinding
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2860
|
+
nextTasks[taskId] = { ...aggregate, roleSessionSets: nextSets };
|
|
2861
|
+
}
|
|
2862
|
+
return { schemaManifest, state: { ...snapshot.state, tasks: nextTasks } };
|
|
2863
|
+
}
|
|
2721
2864
|
function requireTaskRoleSessionSetV5Family(snapshot) {
|
|
2722
2865
|
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
2723
2866
|
if (versions.taskRoleSessionSet !== STRUCTURED_PROVIDER_SESSION_SET_FROM_VERSION) {
|
|
@@ -64,7 +64,7 @@ export const CURRENT_GLOBAL_ROLE_SESSION_SET_SCHEMA_VERSION = 3;
|
|
|
64
64
|
export const CURRENT_TASK_SCHEMA_VERSION = 6;
|
|
65
65
|
export const CURRENT_TASK_BRIEF_SCHEMA_VERSION = 2;
|
|
66
66
|
export const CURRENT_CONTEXT_SNAPSHOT_SCHEMA_VERSION = 1;
|
|
67
|
-
export const CURRENT_TASK_ROLE_SCHEMA_VERSION =
|
|
67
|
+
export const CURRENT_TASK_ROLE_SCHEMA_VERSION = 4;
|
|
68
68
|
export const CURRENT_MANAGED_WORKSPACE_SCHEMA_VERSION = 2;
|
|
69
69
|
export const CURRENT_WORK_ITEM_SCHEMA_VERSION = 12;
|
|
70
70
|
export const CURRENT_REVIEW_ROUND_SCHEMA_VERSION = 6;
|
|
@@ -118,7 +118,7 @@ export const CURRENT_STORED_TASK_SCHEMA_VERSION = 18;
|
|
|
118
118
|
* Keep these named at the storage boundary so the upgrade record-axis map can
|
|
119
119
|
* assert it is classifying the same bytes the store reads and writes.
|
|
120
120
|
*/
|
|
121
|
-
export const CURRENT_TASK_ROLE_SESSION_SET_SCHEMA_VERSION =
|
|
121
|
+
export const CURRENT_TASK_ROLE_SESSION_SET_SCHEMA_VERSION = 7;
|
|
122
122
|
/**
|
|
123
123
|
* v7 combines optional Issue 04 retry/receipt fields and Issue 05 Leader
|
|
124
124
|
* actionability fields. All are optional, so the v6→v7 migration is a
|
package/dist/web/webSnapshot.js
CHANGED
|
@@ -92,6 +92,9 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
|
|
|
92
92
|
const effectiveLaunch = activeRun?.effective ?? activeSession?.effective ?? null;
|
|
93
93
|
return {
|
|
94
94
|
...role,
|
|
95
|
+
// Presentation only: workflow activity is derived from AgentRun; the
|
|
96
|
+
// native Session contributes lifecycle detail when no Run is active.
|
|
97
|
+
status: activeRun === undefined ? activeSession?.status ?? "idle" : "running",
|
|
95
98
|
sessionTokens: projectSessionTokenMetrics(events, resolveSessionTokenIdentity(activeSession === undefined
|
|
96
99
|
? null
|
|
97
100
|
: { taskId, roleName: role.name, ...activeSession })),
|
package/i18n/README.zh-CN.md
CHANGED
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
# Yui
|
|
4
4
|
|
|
5
|
-
Yui
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Yui 是面向智能 Codex/Claude Agent 的本地控制平面。它持久保存用户意图、
|
|
6
|
+
Project Knowledge、Task、交接和结果,并提供上下文、消息、委派、工作区、
|
|
7
|
+
Session、审查和集成等小而原子的能力。Agent 组合这些能力,自主决定规划、
|
|
8
|
+
顺序、委派、重试和恢复。
|
|
9
|
+
|
|
10
|
+
Yui 不把 Agent 的判断固化成确定性的工作流引擎。核心只负责持久身份、用户
|
|
11
|
+
授权、工作区隔离和原子状态变更;Provider Session 与运行时观测用于执行和
|
|
12
|
+
连续性,但不是 Task 事实的另一套来源。用户只需和 Operator 对话,Operator
|
|
13
|
+
负责路由,Leader 负责目标拆解、执行选择、验收和集成。
|
|
8
14
|
|
|
9
15
|
当前实现保留实用的 Role/Agent/session 与 CLI 框架,不恢复后期膨胀的数据维护、租约、定时调度和恢复账本体系。
|
|
10
16
|
|
|
@@ -514,6 +520,8 @@ Task 生命周期的交互选择只展示有效来源状态:activate 只展示
|
|
|
514
520
|
|
|
515
521
|
Run、Conversation、Activation 与 Turn 是四个独立身份。Conversation 可以跨多个 Run 和客户端连接;Activation 只代表 Yui 当前的连接,而不是对 Provider thread 的独占所有权;Turn 在写入前先持久化。写入超时或结果不明确会进入 `delivery-unknown`,不会自动重发。Codex 已存在的原生 active Turn 只会让 Yui 暂时等待,不会导致 Yui Run 失败;Claude 等独立进程 Provider 继续通过 Yui 的 view/takeover 边界进行人工控制。
|
|
516
522
|
|
|
523
|
+
AgentRun 是 Role 是否有工作正在执行的唯一持久调度状态。Conversation 不再维护另一份 current Run;Provider Turn 中的 Run id 只用于关联接收回执和终态事件。TaskRole 本身只保存身份和期望启动配置,不再保存可写的运行状态;CLI/Web 展示的 Role 状态由活动 AgentRun 派生,并叠加 Session/Driver 生命周期事实用于诊断。Agent 可以在 Provider 终态到达前声明旧 Run 的语义结果,下一批 mailbox 工作也可以先形成新的 AgentRun;Agent Host 仍会串行等待旧 Turn 结束,再投递被保留的新输入,不回滚事件,也不把旧 Conversation 强行换绑到新 Run。
|
|
524
|
+
|
|
517
525
|
Task Role 使用以下显式入口:
|
|
518
526
|
|
|
519
527
|
```sh
|
package/package.json
CHANGED
|
@@ -12,10 +12,11 @@ workspace layout, native transcript, or an earlier Run.
|
|
|
12
12
|
For every managed Task Run:
|
|
13
13
|
|
|
14
14
|
1. Read the exact Run identity from the newest Bootstrap Envelope.
|
|
15
|
-
2. Before acting, load its authorized pack with the
|
|
15
|
+
2. Before acting, load its authorized pack with the Session CLI named by the
|
|
16
|
+
current Session Manifest:
|
|
16
17
|
|
|
17
18
|
```sh
|
|
18
|
-
|
|
19
|
+
"$YUI_SESSION_CLI" task run context "$YUI_TASK_ID/<run-id>" --json
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
3. Verify that the returned Task, Run, Role, purpose, Snapshot digest, workspace,
|
|
@@ -27,7 +28,7 @@ For every managed Task Run:
|
|
|
27
28
|
`refId`:
|
|
28
29
|
|
|
29
30
|
```sh
|
|
30
|
-
|
|
31
|
+
"$YUI_SESSION_CLI" task run context expand "$YUI_TASK_ID/<run-id>" <ref-id> --store <store> --mode full --json
|
|
31
32
|
```
|
|
32
33
|
|
|
33
34
|
A bare `<ref-id>` remains supported only when it identifies exactly one
|
|
@@ -46,7 +47,7 @@ For a global Operator or custom GlobalRole Session, load the stable authorized v
|
|
|
46
47
|
before routing or acting:
|
|
47
48
|
|
|
48
49
|
```sh
|
|
49
|
-
|
|
50
|
+
"$YUI_SESSION_CLI" session context "$YUI_ROLE" --json
|
|
50
51
|
```
|
|
51
52
|
|
|
52
53
|
Global context grants no Task implementation workspace. Read a Task only after
|