@zq-silk/yui 0.13.9 → 0.14.0
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 +69 -428
- package/README.md +108 -298
- package/dist/agent/managedRuntimeEnvironment.js +2 -2
- package/dist/cli/commandCatalog.js +39 -77
- package/dist/cli/interactionCandidates.js +4 -4
- package/dist/cli/interactionPolicy.js +7 -10
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/updateCommand.js +6 -27
- package/dist/cli/updateOrchestrator.js +63 -484
- package/dist/cli/updatePorts.js +15 -333
- package/dist/cli/upgradeCommand.js +8 -65
- package/dist/cli.js +57 -242
- package/dist/commands/configCommands.js +11 -62
- package/dist/commands/executionAuditCommands.js +23 -23
- package/dist/commands/globalRoleCommands.js +2 -7
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/projectCommands.js +5 -5
- package/dist/commands/releaseCommands.js +3 -10
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/sessionCommands.js +2 -2
- package/dist/commands/taskActor.js +31 -39
- package/dist/commands/taskChangeSetCommands.js +5 -12
- package/dist/commands/taskCommands.js +464 -1249
- package/dist/commands/taskContextCommand.js +35 -36
- package/dist/commands/taskExecutionCommands.js +33 -46
- package/dist/commands/taskInputCommands.js +20 -42
- package/dist/commands/taskNextActionCommand.js +9 -9
- package/dist/commands/taskOverviewCommand.js +56 -95
- package/dist/commands/taskRoleRuntimeStatus.js +77 -107
- package/dist/commands/taskUpstreamCommands.js +3 -3
- package/dist/commands/taskWorkspaceCommands.js +7 -79
- package/dist/commands/telemetryCommands.js +32 -124
- package/dist/commands/workflowCommands.js +3 -3
- package/dist/completion/fileCompletionManager.js +1 -1
- package/dist/config/configCatalog.js +4 -6
- package/dist/config/yuiConfig.js +6 -43
- package/dist/context/dispatchContext.js +14 -30
- package/dist/context/roleSessionContext.js +2 -2
- package/dist/context/sessionBootstrapManifest.js +2 -2
- package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
- package/dist/context/turnInputContract.js +204 -0
- package/dist/context/wakeNotification.js +2 -2
- package/dist/controller/agentRuntimeObserver.js +18 -18
- package/dist/controller/clientRuntime.js +13 -20
- package/dist/controller/controller.js +89 -116
- package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
- package/dist/controller/jobControl.js +17 -17
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +38 -59
- package/dist/controller/resourceInventoryRpc.js +3 -3
- package/dist/controller/resourceInventoryWorker.js +1 -1
- package/dist/controller/runtime.js +46 -53
- package/dist/controller/runtimeEventInbox.js +23 -31
- package/dist/controller/runtimeEventProcessor.js +15 -15
- package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
- package/dist/controller/runtimeLaunchCoordinator.js +3 -3
- package/dist/controller/runtimeObservationHook.js +11 -8
- package/dist/controller/sessionNotify.js +11 -11
- package/dist/controller/structuredProviderObservation.js +89 -16
- package/dist/coordination/workMailbox.js +309 -579
- package/dist/coordination/workMailboxQueue.js +5 -6
- package/dist/core/controllerClient.js +1 -91
- package/dist/core/controllerServer.js +3 -6
- package/dist/doctor/doctor.js +51 -314
- package/dist/execution/candidateConvergence.js +3 -3
- package/dist/execution/executionGroup.js +32 -33
- package/dist/execution/executionHealth.js +36 -38
- package/dist/execution/resourceBroker.js +12 -12
- package/dist/executor/agentExecutor.js +30 -265
- package/dist/executor/effectiveLaunch.js +9 -19
- package/dist/executor/executorRegistry.js +43 -17
- package/dist/executor/fileRoleLaunchPlanner.js +56 -56
- package/dist/executor/launchPlan.js +3 -3
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +20 -25
- package/dist/input/inputRequest.js +12 -12
- package/dist/integration/changeSet.js +9 -13
- package/dist/integration/changeSetManifest.js +1 -1
- package/dist/integration/gitIntegrationService.js +2 -2
- package/dist/integration/integrationQueueService.js +5 -21
- package/dist/integration/overlapDiagnostics.js +2 -2
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/job/jobRunner.js +1 -1
- package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
- package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
- package/dist/message/message.js +6 -6
- package/dist/observability/executionAudit.js +74 -86
- package/dist/observability/faultClassification.js +7 -24
- package/dist/observability/orchestrationMetrics.js +5 -5
- package/dist/observability/runtimeIdentity.js +34 -101
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +3 -3
- package/dist/release/releaseWorkflowEngine.js +6 -10
- package/dist/release/releaseWorkflowPorts.js +2 -2
- package/dist/release/workflowFileLock.js +1 -1
- package/dist/repository/checkoutSwap.js +1 -1
- package/dist/repository/gitWorkspace.js +0 -13
- package/dist/repository/project.js +1 -1
- package/dist/repository/projectMaintenanceLock.js +1 -2
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +21 -25
- package/dist/repository/taskWorkspaceIdentity.js +8 -21
- package/dist/repository/taskWorkspacePreparer.js +63 -620
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +1 -1
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewFindingLedger.js +1 -1
- package/dist/review/reviewOutcomeClassifier.js +35 -50
- package/dist/review/reviewRound.js +14 -25
- package/dist/review/reviewerAvailability.js +4 -4
- package/dist/review/taskFinalReviewContractResolution.js +41 -0
- package/dist/role/role.js +6 -3
- package/dist/runtime/agentDriver.js +4 -0
- package/dist/runtime/agentHost.js +151 -36
- package/dist/runtime/builtinAgentDrivers.js +79 -2
- package/dist/runtime/codexAppServerRuntime.js +122 -0
- package/dist/runtime/continuationManager.js +3 -5
- package/dist/runtime/exactControlPlane.js +17 -42
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +4 -4
- package/dist/runtime/processExitObservation.js +1 -1
- package/dist/runtime/processExitOutbox.js +0 -32
- package/dist/runtime/promptEnvelope.js +9 -9
- package/dist/runtime/providerContinuation.js +2 -2
- package/dist/runtime/providerContinuationReconciliationService.js +2 -2
- package/dist/runtime/providerRuntimeIdentity.js +76 -24
- package/dist/runtime/recentTurnIds.js +38 -0
- package/dist/runtime/runtimeContinuationProjection.js +2 -2
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +58 -21
- package/dist/runtime/runtimeProjection.js +19 -27
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/structuredProviderHost.js +177 -12
- package/dist/runtime/tmuxAdapters.js +46 -11
- package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
- package/dist/scheduler/leaderWakeupProcessor.js +147 -445
- package/dist/scheduler/operatorEvent.js +4 -6
- package/dist/scheduler/resourceQueueProjection.js +71 -0
- package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
- package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
- package/dist/scheduler/taskExecutionProjection.js +99 -99
- package/dist/scheduler/taskObservabilityProjection.js +10 -10
- package/dist/scheduler/taskWake.js +4 -4
- package/dist/scheduler/wakeReason.js +6 -10
- package/dist/scheduler/wakeupQueue.js +0 -9
- package/dist/setup/setupCommand.js +2 -2
- package/dist/storage/currentTaskStore.js +46 -0
- package/dist/storage/persistenceWorker.js +1 -1
- package/dist/storage/sqliteSchema.js +81 -485
- package/dist/storage/sqliteStore.js +206 -365
- package/dist/storage/storageSchema.js +1 -15
- package/dist/storage/storageVersions.js +8 -9
- package/dist/storage/storeRpc.js +10 -34
- package/dist/storage/taskStore.js +63 -3415
- package/dist/storage/upgrade/recordVersions.js +55 -149
- package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
- package/dist/task/completionReadiness.js +5 -5
- package/dist/task/deliveryGuard.js +10 -10
- package/dist/task/nextAction.js +74 -77
- package/dist/task/task.js +2 -4
- package/dist/task/taskRecordReference.js +3 -3
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/telemetry/sqliteTelemetryStore.js +53 -53
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +8 -14
- package/dist/turn/turn.js +356 -0
- package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
- package/dist/verification/gateArtifactStore.js +2 -2
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/components.js +29 -54
- package/dist/web/assets/client/i18n.js +17 -37
- package/dist/web/assets/client/view.js +37 -37
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +5 -5
- package/dist/web/assets/styles/widgets.js +1 -2
- package/dist/web/webSnapshot.js +65 -60
- package/dist/workItem/workItem.js +11 -11
- package/dist/workspace/workItemChangeSetManager.js +1 -1
- package/docs/task-local-identity.md +5 -5
- package/i18n/README.zh-CN.md +41 -54
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +85 -83
- package/skills/yui-operator/SKILL.md +16 -16
- package/skills/yui-reviewer/SKILL.md +10 -10
- package/skills/yui-runtime/SKILL.md +17 -18
- package/skills/yui-worker/SKILL.md +16 -18
- package/dist/context/runContextContract.js +0 -162
- package/dist/executor/turnCompletion.js +0 -126
- package/dist/review/taskFinalReviewContractEvent.js +0 -1
- package/dist/review/taskFinalReviewContractRebind.js +0 -367
- package/dist/run/agentRun.js +0 -356
- package/dist/run/rejectedYieldAttempt.js +0 -221
- package/dist/run/runControlRequest.js +0 -50
- package/dist/run/yieldReceipt.js +0 -65
- package/dist/runtime/turnCompletion.js +0 -3
- package/dist/scheduler/actionability.js +0 -323
- package/dist/scheduler/activeRoleRunDelivery.js +0 -868
- package/dist/scheduler/activeTaskProgress.js +0 -126
- package/dist/storage/compatibleTaskStore.js +0 -248
- package/dist/storage/migration/baseline.js +0 -78
- package/dist/storage/migration/classifier.js +0 -51
- package/dist/storage/migration/compatibleCodec.js +0 -53
- package/dist/storage/migration/engine.js +0 -147
- package/dist/storage/migration/index.js +0 -33
- package/dist/storage/migration/planner.js +0 -154
- package/dist/storage/migration/productionRegistry.js +0 -3931
- package/dist/storage/migration/registry.js +0 -169
- package/dist/storage/migration/report.js +0 -54
- package/dist/storage/migration/types.js +0 -31
- package/dist/storage/upgrade/homeClassification.js +0 -353
- package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
- package/dist/storage/upgrade/migrationReceipt.js +0 -67
- package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
- package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
- package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
- package/dist/storage/upgrade/recordVersionScan.js +0 -176
- package/dist/storage/upgrade/sqliteFileSet.js +0 -50
- package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
- package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
- package/dist/storage/upgrade/switchProgress.js +0 -80
- package/dist/storage/upgrade/upgradeReceipt.js +0 -161
- package/dist/storage/upgradeCoordination.js +0 -187
- package/dist/storage/upgradeFence.js +0 -366
- package/dist/telemetry/telemetryCompaction.js +0 -252
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { validateEffectiveLaunchSnapshot } from "../executor/effectiveLaunch.js";
|
|
2
|
+
import { validateTaskRecordReference } from "../task/taskRecordReference.js";
|
|
3
|
+
import { validateManagedWorkspace } from "../worktree/managedWorkspace.js";
|
|
4
|
+
import { createTurnInput, createTurnInputEnvelope, validateTurnInput } from "../context/turnInputContract.js";
|
|
5
|
+
export function createTurn(id, taskId, roleName, mode, input, now, context) {
|
|
6
|
+
if (mode !== "new" && mode !== "resume") {
|
|
7
|
+
throw new Error(`Turn dispatch mode is invalid: ${mode}.`);
|
|
8
|
+
}
|
|
9
|
+
const timestamp = now.toISOString();
|
|
10
|
+
const normalizedInput = validateTurnInput(input);
|
|
11
|
+
return {
|
|
12
|
+
schemaVersion: 1,
|
|
13
|
+
id: requireSafeIdentity(id, "Turn id"),
|
|
14
|
+
taskId: requireSafeIdentity(taskId, "Task id"),
|
|
15
|
+
roleName: requireSafeIdentity(roleName, "Role name"),
|
|
16
|
+
mode,
|
|
17
|
+
inputs: [turnInputRecord(normalizedInput, 1, timestamp)],
|
|
18
|
+
purpose: context.purpose ?? "execution",
|
|
19
|
+
...(context.workItemId === undefined
|
|
20
|
+
? {}
|
|
21
|
+
: { workItemId: requireSafeIdentity(context.workItemId, "Work item id") }),
|
|
22
|
+
...(context.reviewRoundId === undefined
|
|
23
|
+
? {}
|
|
24
|
+
: { reviewRoundId: requireSafeIdentity(context.reviewRoundId, "ReviewRound id") }),
|
|
25
|
+
...(context.executionGroupId === undefined
|
|
26
|
+
? {}
|
|
27
|
+
: { executionGroupId: requireSafeIdentity(context.executionGroupId, "ExecutionGroup id") }),
|
|
28
|
+
...(context.executionLaneId === undefined
|
|
29
|
+
? {}
|
|
30
|
+
: { executionLaneId: requireSafeIdentity(context.executionLaneId, "ExecutionLane id") }),
|
|
31
|
+
...(context.workspace === undefined
|
|
32
|
+
? {}
|
|
33
|
+
: { workspace: validateManagedWorkspace(context.workspace) }),
|
|
34
|
+
effective: validateEffectiveLaunchSnapshot(context.effective),
|
|
35
|
+
status: "active",
|
|
36
|
+
createdAt: timestamp,
|
|
37
|
+
updatedAt: timestamp
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function isActiveTurn(run) {
|
|
41
|
+
return run.status === "active";
|
|
42
|
+
}
|
|
43
|
+
/** Binds a freshly created, not-yet-persisted Turn to its frozen Context. */
|
|
44
|
+
export function withTurnContextSnapshot(run, snapshot, deltaRefIds = []) {
|
|
45
|
+
const initial = run.inputs[0];
|
|
46
|
+
if (run.status !== "active" || initial.input.contextSnapshotRef !== undefined) {
|
|
47
|
+
throw new Error(`Cannot replace the Turn Context Snapshot: ${run.id}.`);
|
|
48
|
+
}
|
|
49
|
+
const input = createTurnInput({
|
|
50
|
+
...initial.input,
|
|
51
|
+
contextSnapshotRef: snapshot,
|
|
52
|
+
deltaRefIds
|
|
53
|
+
});
|
|
54
|
+
return validateTurn(Object.freeze({
|
|
55
|
+
...run,
|
|
56
|
+
inputs: [turnInputRecord(input, 1, initial.submittedAt), ...run.inputs.slice(1)]
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
export function appendTurnInput(run, input, now) {
|
|
60
|
+
validateTurn(run);
|
|
61
|
+
if (run.status !== "active")
|
|
62
|
+
throw new Error(`Cannot append input to terminal Turn: ${run.id}.`);
|
|
63
|
+
const timestamp = now.toISOString();
|
|
64
|
+
if (Date.parse(timestamp) < Date.parse(run.updatedAt)) {
|
|
65
|
+
throw new Error("Turn input timestamp moved backwards.");
|
|
66
|
+
}
|
|
67
|
+
return validateTurn({
|
|
68
|
+
...run,
|
|
69
|
+
inputs: [...run.inputs, turnInputRecord(input, run.inputs.length + 1, timestamp)],
|
|
70
|
+
updatedAt: timestamp
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/** Derives Provider-visible identity from the Turn, the sole semantic owner. */
|
|
74
|
+
export function turnInputEnvelope(run, sequence = 1) {
|
|
75
|
+
validateTurn(run);
|
|
76
|
+
const record = run.inputs[sequence - 1];
|
|
77
|
+
if (record === undefined)
|
|
78
|
+
throw new Error(`Turn input does not exist: ${run.id}/${sequence}.`);
|
|
79
|
+
return createTurnInputEnvelope(turnEnvelopeContext(run), record.input);
|
|
80
|
+
}
|
|
81
|
+
export function validateTurn(run) {
|
|
82
|
+
rejectUnknownFields(run, [
|
|
83
|
+
"schemaVersion",
|
|
84
|
+
"id",
|
|
85
|
+
"taskId",
|
|
86
|
+
"roleName",
|
|
87
|
+
"mode",
|
|
88
|
+
"inputs",
|
|
89
|
+
"purpose",
|
|
90
|
+
"workItemId",
|
|
91
|
+
"reviewRoundId",
|
|
92
|
+
"executionGroupId",
|
|
93
|
+
"executionLaneId",
|
|
94
|
+
"workspace",
|
|
95
|
+
"effective",
|
|
96
|
+
"status",
|
|
97
|
+
"result",
|
|
98
|
+
"createdAt",
|
|
99
|
+
"updatedAt"
|
|
100
|
+
], "Turn");
|
|
101
|
+
if (run.schemaVersion !== 1)
|
|
102
|
+
throw new Error("Turn must use schemaVersion 1.");
|
|
103
|
+
validateTaskRecordReference({ taskId: run.taskId, localId: run.id }, "turn");
|
|
104
|
+
requireSafeIdentity(run.roleName, "Role name");
|
|
105
|
+
if (run.mode !== "new" && run.mode !== "resume") {
|
|
106
|
+
throw new Error(`Turn dispatch mode is invalid: ${String(run.mode)}.`);
|
|
107
|
+
}
|
|
108
|
+
if (!Array.isArray(run.inputs) || run.inputs.length === 0) {
|
|
109
|
+
throw new Error("Turn requires at least one input.");
|
|
110
|
+
}
|
|
111
|
+
for (const [index, record] of run.inputs.entries()) {
|
|
112
|
+
if (record.sequence !== index + 1)
|
|
113
|
+
throw new Error("Turn input sequence is invalid.");
|
|
114
|
+
requireTimestamp(record.submittedAt, "Turn input submittedAt");
|
|
115
|
+
validateTurnInput(record.input);
|
|
116
|
+
if (index > 0 && Date.parse(record.submittedAt) < Date.parse(run.inputs[index - 1].submittedAt)) {
|
|
117
|
+
throw new Error("Turn input timestamps moved backwards.");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!["execution", "review"].includes(run.purpose)) {
|
|
121
|
+
throw new Error(`Turn purpose is invalid: ${String(run.purpose)}.`);
|
|
122
|
+
}
|
|
123
|
+
if (run.workItemId !== undefined) {
|
|
124
|
+
validateTaskRecordReference({ taskId: run.taskId, localId: run.workItemId }, "workItem");
|
|
125
|
+
}
|
|
126
|
+
if (run.reviewRoundId !== undefined) {
|
|
127
|
+
validateTaskRecordReference({ taskId: run.taskId, localId: run.reviewRoundId }, "reviewRound");
|
|
128
|
+
}
|
|
129
|
+
if ((run.executionGroupId === undefined) !== (run.executionLaneId === undefined)) {
|
|
130
|
+
throw new Error("Turn execution lineage is incomplete.");
|
|
131
|
+
}
|
|
132
|
+
if (run.executionGroupId !== undefined) {
|
|
133
|
+
requireSafeIdentity(run.executionGroupId, "ExecutionGroup id");
|
|
134
|
+
requireSafeIdentity(run.executionLaneId, "ExecutionLane id");
|
|
135
|
+
}
|
|
136
|
+
if (run.workspace !== undefined) {
|
|
137
|
+
validateManagedWorkspace(run.workspace);
|
|
138
|
+
if (run.workspace.owner.taskId !== run.taskId) {
|
|
139
|
+
throw new Error("Turn workspace belongs to another Task.");
|
|
140
|
+
}
|
|
141
|
+
if (run.workspace.owner.type === "work-item"
|
|
142
|
+
&& run.workspace.owner.workItemId !== run.workItemId) {
|
|
143
|
+
throw new Error("Turn workspace belongs to another Work Item.");
|
|
144
|
+
}
|
|
145
|
+
if (run.workspace.owner.type === "work-item" && run.workItemId === undefined) {
|
|
146
|
+
throw new Error("A WorkItem workspace requires a WorkItem Turn reference.");
|
|
147
|
+
}
|
|
148
|
+
if (run.workspace.owner.type === "review-round" && run.purpose !== "review") {
|
|
149
|
+
throw new Error("A ReviewRound workspace requires a review Turn.");
|
|
150
|
+
}
|
|
151
|
+
if (run.workspace.owner.type === "integration-attempt") {
|
|
152
|
+
throw new Error("An IntegrationAttempt workspace cannot be used by a Turn.");
|
|
153
|
+
}
|
|
154
|
+
if (run.workspace.owner.type === "execution-lane") {
|
|
155
|
+
if (run.workspace.owner.executionGroupId !== run.executionGroupId
|
|
156
|
+
|| run.workspace.owner.executionLaneId !== run.executionLaneId) {
|
|
157
|
+
throw new Error("Turn Execution Lane workspace lineage does not match the Turn.");
|
|
158
|
+
}
|
|
159
|
+
if (run.workspace.owner.purpose === "execution"
|
|
160
|
+
&& run.workspace.owner.workItemId !== run.workItemId) {
|
|
161
|
+
throw new Error("Turn Execution Lane workspace WorkItem does not match the Turn.");
|
|
162
|
+
}
|
|
163
|
+
if (run.workspace.owner.purpose === "review"
|
|
164
|
+
&& run.workspace.owner.reviewRoundId !== run.reviewRoundId) {
|
|
165
|
+
throw new Error("Turn review Lane workspace ReviewRound does not match the Turn.");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (run.workspace.owner.type === "review-round"
|
|
169
|
+
&& run.workspace.owner.reviewRoundId !== run.reviewRoundId) {
|
|
170
|
+
throw new Error(`Turn ReviewRound workspace owner does not match ${run.reviewRoundId ?? "none"}.`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (run.purpose === "review") {
|
|
174
|
+
if (run.reviewRoundId === undefined) {
|
|
175
|
+
throw new Error("A review Turn requires a ReviewRound reference.");
|
|
176
|
+
}
|
|
177
|
+
if (run.workspace === undefined
|
|
178
|
+
|| !((run.workspace.owner.type === "review-round"
|
|
179
|
+
&& run.workspace.owner.reviewRoundId === run.reviewRoundId)
|
|
180
|
+
|| (run.workspace.owner.type === "execution-lane"
|
|
181
|
+
&& run.workspace.owner.purpose === "review"
|
|
182
|
+
&& run.workspace.owner.reviewRoundId === run.reviewRoundId))) {
|
|
183
|
+
throw new Error(`A review Turn requires its exact ReviewRound workspace owner: ${run.reviewRoundId}.`);
|
|
184
|
+
}
|
|
185
|
+
if (run.workspace.entries.length === 0
|
|
186
|
+
|| run.workspace.entries.some(({ access }) => access !== "write")) {
|
|
187
|
+
throw new Error("A review Turn requires only isolated writable workspace entries.");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
if (run.reviewRoundId !== undefined) {
|
|
192
|
+
throw new Error("An execution Turn cannot reference a ReviewRound.");
|
|
193
|
+
}
|
|
194
|
+
if (run.workspace?.owner.type === "review-round") {
|
|
195
|
+
throw new Error("An execution Turn cannot use a ReviewRound-owned workspace.");
|
|
196
|
+
}
|
|
197
|
+
if (run.workspace?.owner.type === "execution-lane" && run.workspace.owner.purpose !== "execution") {
|
|
198
|
+
throw new Error("An execution Turn cannot use a review Lane workspace.");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
validateEffectiveLaunchSnapshot(run.effective);
|
|
202
|
+
if (run.workspace !== undefined
|
|
203
|
+
&& (run.effective.workspace.root !== run.workspace.root
|
|
204
|
+
|| JSON.stringify(run.effective.workspace.entries) !== JSON.stringify(run.workspace.entries))) {
|
|
205
|
+
throw new Error("Turn effective workspace does not match its managed workspace.");
|
|
206
|
+
}
|
|
207
|
+
if (run.purpose === "review") {
|
|
208
|
+
if (run.effective.reviewRoundId !== run.reviewRoundId) {
|
|
209
|
+
throw new Error("Review Turn effective provenance does not match its ReviewRound.");
|
|
210
|
+
}
|
|
211
|
+
if (!run.workspace.entries.some(({ baseCommit }) => baseCommit === run.effective.reviewBaseCommit)) {
|
|
212
|
+
throw new Error("Review Turn effective base does not match its workspace.");
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (run.effective.reviewRoundId !== undefined) {
|
|
216
|
+
throw new Error("Execution Turn cannot carry Review effective provenance.");
|
|
217
|
+
}
|
|
218
|
+
for (const record of run.inputs) {
|
|
219
|
+
createTurnInputEnvelope(turnEnvelopeContext(run), record.input);
|
|
220
|
+
}
|
|
221
|
+
if (!["active", "completed", "failed"].includes(run.status)) {
|
|
222
|
+
throw new Error(`Turn status is invalid: ${String(run.status)}.`);
|
|
223
|
+
}
|
|
224
|
+
requireTimestamp(run.createdAt, "Turn createdAt");
|
|
225
|
+
requireTimestamp(run.updatedAt, "Turn updatedAt");
|
|
226
|
+
if (run.status === "active") {
|
|
227
|
+
if (run.result !== undefined)
|
|
228
|
+
throw new Error("An active Turn cannot have a result.");
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
const result = validateTurnResult(run.result);
|
|
232
|
+
if (run.status === "failed") {
|
|
233
|
+
if (!isTurnFailureReason(result.failureReason)) {
|
|
234
|
+
throw new Error(`Failed Turn reason is invalid: ${String(result.failureReason)}.`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else if (result.failureReason !== undefined) {
|
|
238
|
+
throw new Error("A completed Turn cannot carry a failure reason.");
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return run;
|
|
242
|
+
}
|
|
243
|
+
export function completeTurn(run, output, now, provider) {
|
|
244
|
+
return finishTurn(run, "completed", output, now, undefined, provider);
|
|
245
|
+
}
|
|
246
|
+
export function failTurn(run, reason, output, now, provider) {
|
|
247
|
+
return finishTurn(run, "failed", output, now, reason, provider);
|
|
248
|
+
}
|
|
249
|
+
function finishTurn(run, status, output, now, failureReason, provider) {
|
|
250
|
+
if (run.status !== "active") {
|
|
251
|
+
throw new Error(`Turn is already terminal: ${run.id}.`);
|
|
252
|
+
}
|
|
253
|
+
const timestamp = now.toISOString();
|
|
254
|
+
const terminal = {
|
|
255
|
+
...run,
|
|
256
|
+
status,
|
|
257
|
+
result: {
|
|
258
|
+
schemaVersion: 1,
|
|
259
|
+
output: requireResultText(output, "Turn result output"),
|
|
260
|
+
completedAt: timestamp,
|
|
261
|
+
...(provider === undefined ? {} : { provider: validateTurnProviderResult(provider) }),
|
|
262
|
+
...(failureReason === undefined ? {} : { failureReason })
|
|
263
|
+
},
|
|
264
|
+
updatedAt: timestamp,
|
|
265
|
+
};
|
|
266
|
+
return validateTurn(terminal);
|
|
267
|
+
}
|
|
268
|
+
function validateTurnResult(result) {
|
|
269
|
+
if (result === undefined || result.schemaVersion !== 1) {
|
|
270
|
+
throw new Error("A terminal Turn requires TurnResult schemaVersion 1.");
|
|
271
|
+
}
|
|
272
|
+
requireResultText(result.output, "Turn result output");
|
|
273
|
+
requireTimestamp(result.completedAt, "Turn result completedAt");
|
|
274
|
+
if (result.provider !== undefined)
|
|
275
|
+
validateTurnProviderResult(result.provider);
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
function validateTurnProviderResult(provider) {
|
|
279
|
+
requireText(provider.providerNamespace, "Provider namespace");
|
|
280
|
+
requireText(provider.accountScope, "Provider account scope");
|
|
281
|
+
requireText(provider.conversationId, "Provider Conversation id");
|
|
282
|
+
requireText(provider.activationId, "Provider Activation id");
|
|
283
|
+
requireText(provider.nativeTurnId, "Provider native Turn id");
|
|
284
|
+
if (!["completed", "failed", "cancelled"].includes(provider.status)) {
|
|
285
|
+
throw new Error(`Provider Turn result status is invalid: ${String(provider.status)}.`);
|
|
286
|
+
}
|
|
287
|
+
return provider;
|
|
288
|
+
}
|
|
289
|
+
function turnInputRecord(input, sequence, submittedAt) {
|
|
290
|
+
const normalized = validateTurnInput(input);
|
|
291
|
+
return Object.freeze({
|
|
292
|
+
sequence,
|
|
293
|
+
submittedAt,
|
|
294
|
+
input: normalized
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
function turnEnvelopeContext(run) {
|
|
298
|
+
return {
|
|
299
|
+
turnId: run.id,
|
|
300
|
+
roleName: run.roleName,
|
|
301
|
+
purpose: run.purpose,
|
|
302
|
+
subject: {
|
|
303
|
+
taskId: run.taskId,
|
|
304
|
+
...(run.workItemId === undefined ? {} : { workItemId: run.workItemId }),
|
|
305
|
+
...(run.reviewRoundId === undefined ? {} : { reviewRoundId: run.reviewRoundId }),
|
|
306
|
+
...(run.executionGroupId === undefined ? {} : {
|
|
307
|
+
executionGroupId: run.executionGroupId,
|
|
308
|
+
executionLaneId: run.executionLaneId
|
|
309
|
+
})
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function requireSafeIdentity(value, label) {
|
|
314
|
+
const normalized = requireText(value, label);
|
|
315
|
+
if (["__proto__", "prototype", "constructor", ".", ".."].includes(normalized)
|
|
316
|
+
|| /[\/\\\0]/.test(normalized)) {
|
|
317
|
+
throw new Error(`${label} is invalid.`);
|
|
318
|
+
}
|
|
319
|
+
return normalized;
|
|
320
|
+
}
|
|
321
|
+
function requireText(value, label) {
|
|
322
|
+
if (typeof value !== "string" || value.includes("\0"))
|
|
323
|
+
throw new Error(`${label} is invalid.`);
|
|
324
|
+
const normalized = value.trim();
|
|
325
|
+
if (normalized.length === 0)
|
|
326
|
+
throw new Error(`${label} is required.`);
|
|
327
|
+
return normalized;
|
|
328
|
+
}
|
|
329
|
+
/** Result transport preserves the provider's complete text, including outer whitespace. */
|
|
330
|
+
function requireResultText(value, label) {
|
|
331
|
+
if (typeof value !== "string" || value.includes("\0"))
|
|
332
|
+
throw new Error(`${label} is invalid.`);
|
|
333
|
+
if (value.trim().length === 0)
|
|
334
|
+
throw new Error(`${label} is required.`);
|
|
335
|
+
return value;
|
|
336
|
+
}
|
|
337
|
+
function requireTimestamp(value, label) {
|
|
338
|
+
if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) {
|
|
339
|
+
throw new Error(`${label} must be an ISO timestamp.`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function isTurnFailureReason(value) {
|
|
343
|
+
return [
|
|
344
|
+
"startup-failed",
|
|
345
|
+
"runtime-failed",
|
|
346
|
+
"delivery-unknown",
|
|
347
|
+
"missing-result",
|
|
348
|
+
"cancelled"
|
|
349
|
+
].includes(String(value));
|
|
350
|
+
}
|
|
351
|
+
function rejectUnknownFields(value, fields, label) {
|
|
352
|
+
const allowed = new Set(fields);
|
|
353
|
+
const unknown = Object.keys(value).find((field) => !allowed.has(field));
|
|
354
|
+
if (unknown !== undefined)
|
|
355
|
+
throw new Error(`${label} has unknown field: ${unknown}.`);
|
|
356
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { MAX_SESSION_TITLE_LENGTH } from "../runtime/sessionTitle.js";
|
|
2
2
|
/**
|
|
3
3
|
* Prefixes managed launch input with its session title. The bootstrap body
|
|
4
|
-
* exposes the exact current
|
|
4
|
+
* exposes the exact current Turn identity to the Agent, while structured
|
|
5
5
|
* delivery metadata and control hooks retain the delivery fence.
|
|
6
6
|
*/
|
|
7
7
|
export function prefixYuiTitleInput(input, title) {
|
|
8
8
|
const text = input.replace(/\r/g, "").trim();
|
|
9
9
|
if (text.length === 0)
|
|
10
10
|
throw new Error("Managed input body is required.");
|
|
11
|
-
return `${
|
|
11
|
+
return `${requireTurnTitle(title)}\n\n${text}`;
|
|
12
12
|
}
|
|
13
|
-
function
|
|
13
|
+
function requireTurnTitle(value) {
|
|
14
14
|
const title = value.trim();
|
|
15
15
|
if (title.length === 0
|
|
16
16
|
|| title.length > MAX_SESSION_TITLE_LENGTH
|
|
17
17
|
|| /[\r\n\0]/u.test(title)
|
|
18
18
|
|| !title.startsWith("Yui ")) {
|
|
19
|
-
throw new Error("
|
|
19
|
+
throw new Error("Turn title is invalid.");
|
|
20
20
|
}
|
|
21
21
|
return title;
|
|
22
22
|
}
|
|
@@ -4,8 +4,8 @@ import { gateArtifactKey, isReusableGateArtifact, validateGateArtifact, verifyGa
|
|
|
4
4
|
/**
|
|
5
5
|
* Issue 08: the GateArtifact store adapter.
|
|
6
6
|
*
|
|
7
|
-
* Every function delegates to a {@link GateArtifactStorePort}
|
|
8
|
-
*
|
|
7
|
+
* Every function delegates to a {@link GateArtifactStorePort}. The artifact
|
|
8
|
+
* record and its step
|
|
9
9
|
* logs are persisted atomically by the store implementation; this module
|
|
10
10
|
* owns the domain logic (key computation, log hashing, reuse lookup,
|
|
11
11
|
* retention) but no filesystem paths.
|
|
@@ -87,7 +87,7 @@ function urlTaskId() {
|
|
|
87
87
|
return readQuery().get("task") || null;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
// The visible detail section is part of the URL (?task=X§ion=
|
|
90
|
+
// The visible detail section is part of the URL (?task=X§ion=exec) so a
|
|
91
91
|
// refresh or a shared link lands on the same section. Writes always replace —
|
|
92
92
|
// section moves are not history entries.
|
|
93
93
|
function setSectionParam(targetId) {
|
|
@@ -114,9 +114,9 @@ export function executionBand(projection, t, locale) {
|
|
|
114
114
|
head.append(pill(t, "exec.status", projection.status));
|
|
115
115
|
head.append(node("span", "exec-band-owner",
|
|
116
116
|
t("exec.owner." + projection.owner) + " · " + t("exec.action." + projection.action)));
|
|
117
|
-
if (projection.
|
|
117
|
+
if (projection.activeTurns && projection.activeTurns.length > 0) {
|
|
118
118
|
head.append(node("span", "exec-band-executors",
|
|
119
|
-
projection.
|
|
119
|
+
projection.activeTurns.length + " " + t("exec.executors")));
|
|
120
120
|
}
|
|
121
121
|
if (projection.monitoring === "stopped") {
|
|
122
122
|
head.append(node("span", "exec-band-stopped", t("exec.monitoring.stopped")));
|
|
@@ -296,7 +296,7 @@ export function candidateList(candidates, t, locale) {
|
|
|
296
296
|
row.append(node("span", "candidate-summary", candidate.summary));
|
|
297
297
|
const source = candidate.source.type === "direct"
|
|
298
298
|
? t("candidate.source.direct")
|
|
299
|
-
: t("candidate.source.
|
|
299
|
+
: t("candidate.source.turn") + " " + candidate.source.turnId;
|
|
300
300
|
row.append(node("span", "candidate-source", source));
|
|
301
301
|
row.append(node("time", "", formatDateTime(candidate.createdAt, locale)));
|
|
302
302
|
list.append(row);
|
|
@@ -478,7 +478,7 @@ export function inputCard(input, _options, t, locale, actions) {
|
|
|
478
478
|
}
|
|
479
479
|
if (input.requester) {
|
|
480
480
|
top.append(node("span", "input-requester",
|
|
481
|
-
t("detail.requester") + " · " + input.requester.roleName + " / " + input.requester.
|
|
481
|
+
t("detail.requester") + " · " + input.requester.roleName + " / " + input.requester.turnId));
|
|
482
482
|
}
|
|
483
483
|
card.append(top);
|
|
484
484
|
card.append(answerActions(input, actions, t));
|
|
@@ -611,71 +611,46 @@ export function workItemCard(item, titles, t, locale, actions, taskId) {
|
|
|
611
611
|
return card;
|
|
612
612
|
}
|
|
613
613
|
|
|
614
|
-
export function
|
|
614
|
+
export function turnCard(turn, t, locale) {
|
|
615
615
|
const card = node("article", "execute-card");
|
|
616
|
-
card.dataset.status =
|
|
616
|
+
card.dataset.status = turn.status;
|
|
617
617
|
|
|
618
618
|
const idRow = node("div", "execute-id");
|
|
619
|
-
idRow.append(statusDot(
|
|
620
|
-
idRow.append(node("span", "role",
|
|
621
|
-
idRow.append(node("span", "",
|
|
622
|
-
if (
|
|
623
|
-
if (
|
|
624
|
-
idRow.append(node("time", "", formatDateTime(
|
|
619
|
+
idRow.append(statusDot(turn.status));
|
|
620
|
+
idRow.append(node("span", "role", turn.roleName));
|
|
621
|
+
idRow.append(node("span", "", turn.id));
|
|
622
|
+
if (turn.workItemId) idRow.append(node("span", "", t("detail.workItem") + " · " + turn.workItemId));
|
|
623
|
+
if (turn.purpose) idRow.append(chip(t("turn.purpose." + turn.purpose)));
|
|
624
|
+
idRow.append(node("time", "", formatDateTime(turn.result?.completedAt || turn.updatedAt, locale)));
|
|
625
625
|
card.append(idRow);
|
|
626
626
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
627
|
+
const visibleInput = turn.inputs && turn.inputs.length ? turn.inputs[0].input : null;
|
|
628
|
+
card.append(richText(t("detail.instruction"), visibleInput?.directive || visibleInput?.action || "-", t, { className: "execute-io", threshold: 320 }));
|
|
629
|
+
if (turn.result?.output) {
|
|
630
|
+
card.append(richText(t("detail.outcome"), turn.result.output, t, { className: "execute-io outcome", threshold: 320 }));
|
|
630
631
|
}
|
|
631
632
|
|
|
632
633
|
const foot = node("div", "execute-foot");
|
|
633
634
|
const tags = node("div", "execute-tags");
|
|
634
|
-
tags.append(chip(t("mode." +
|
|
635
|
-
if (
|
|
636
|
-
tags.append(chip(t("detail.lineage") + " · " +
|
|
637
|
-
+ (
|
|
638
|
-
}
|
|
639
|
-
const
|
|
640
|
-
? "delivery.delivered"
|
|
641
|
-
: run.pushedAt
|
|
642
|
-
? "delivery.pushed"
|
|
643
|
-
: "delivery.pending";
|
|
644
|
-
tags.append(chip(t(deliveryKey)));
|
|
645
|
-
const badge = run.effective ? agentBadge(run.effective) : (run.agentId ? agentBadge(run) : null);
|
|
635
|
+
tags.append(chip(t("mode." + turn.mode)));
|
|
636
|
+
if (turn.executionGroupId) {
|
|
637
|
+
tags.append(chip(t("detail.lineage") + " · " + turn.executionGroupId
|
|
638
|
+
+ (turn.executionLaneId ? "/" + turn.executionLaneId : "")));
|
|
639
|
+
}
|
|
640
|
+
const badge = turn.effective ? agentBadge(turn.effective) : (turn.agentId ? agentBadge(turn) : null);
|
|
646
641
|
if (badge) tags.append(badge);
|
|
647
642
|
foot.append(tags);
|
|
648
|
-
foot.append(pill(t, "
|
|
643
|
+
foot.append(pill(t, "turn", turn.status));
|
|
649
644
|
card.append(foot);
|
|
650
645
|
|
|
651
|
-
if (
|
|
646
|
+
if (turn.effective) {
|
|
652
647
|
const eff = node("div", "record-meta");
|
|
653
|
-
eff.append(node("span", "", t("detail.effective") + " · r" +
|
|
654
|
-
eff.append(node("span", "", t("detail.profileIntent") + " · " +
|
|
655
|
-
eff.append(node("span", "", t("detail.permission") + " · " +
|
|
648
|
+
eff.append(node("span", "", t("detail.effective") + " · r" + turn.effective.sourceDesiredRevision));
|
|
649
|
+
eff.append(node("span", "", t("detail.profileIntent") + " · " + turn.effective.profileAccess));
|
|
650
|
+
eff.append(node("span", "", t("detail.permission") + " · " + turn.effective.permission.strategy));
|
|
656
651
|
card.append(eff);
|
|
657
652
|
}
|
|
658
653
|
|
|
659
|
-
// Leader Run disposition: the machine-derived outcome of a terminal Leader
|
|
660
|
-
// Run, plus any structured wait reference.
|
|
661
|
-
if (run.disposition) {
|
|
662
|
-
const disposition = node("div", "record-meta");
|
|
663
|
-
disposition.append(node("span", "", t("run.disposition") + " · " + t("run.disposition." + run.disposition)));
|
|
664
|
-
if (run.waitReason) {
|
|
665
|
-
disposition.append(node("span", "", t("run.waitReason") + " · " + run.waitReason.kind
|
|
666
|
-
+ (run.waitReason.ref ? " (" + run.waitReason.ref + ")" : "")));
|
|
667
|
-
}
|
|
668
|
-
card.append(disposition);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
// Idempotent yield receipt on a yielded Run.
|
|
672
|
-
if (run.yieldReceipt) {
|
|
673
|
-
const receipt = node("div", "record-meta");
|
|
674
|
-
receipt.append(node("span", "mono", t("run.yieldReceipt") + " · " + run.yieldReceipt.receiptId));
|
|
675
|
-
receipt.append(node("time", "", formatDateTime(run.yieldReceipt.committedAt, locale)));
|
|
676
|
-
card.append(receipt);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
654
|
return card;
|
|
680
655
|
}
|
|
681
656
|
|
|
@@ -728,7 +703,7 @@ export function roleCard(role, task, t, locale, actions) {
|
|
|
728
703
|
headRight.append(pill(t, "role", role.status));
|
|
729
704
|
const open = node("button", "record-open", "");
|
|
730
705
|
open.type = "button";
|
|
731
|
-
open.append(node("span", "", t("actions.
|
|
706
|
+
open.append(node("span", "", t("actions.openSession")), node("span", "arrow", "→"));
|
|
732
707
|
open.addEventListener("click", function () {
|
|
733
708
|
if (actions.openTerminal) actions.openTerminal({ scope: "task", taskId: task.id, roleName: role.name });
|
|
734
709
|
});
|
|
@@ -861,7 +836,7 @@ export function messageCard(message, t, locale) {
|
|
|
861
836
|
const meta = node("div", "record-meta");
|
|
862
837
|
meta.append(node("span", "mono", message.id));
|
|
863
838
|
meta.append(node("time", "", formatDateTime(message.createdAt, locale)));
|
|
864
|
-
if (message.
|
|
839
|
+
if (message.turnId) meta.append(node("span", "mono", message.turnId + (message.workItemId ? " · " + message.workItemId : "")));
|
|
865
840
|
card.append(meta);
|
|
866
841
|
|
|
867
842
|
card.append(richText(null, message.body, t));
|