@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
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { rejectedYieldAttemptFromTaskEvent, RUN_YIELD_REJECTED_EVENT } from "../run/rejectedYieldAttempt.js";
|
|
2
1
|
import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
3
2
|
import { TASK_COMPLETION_PUBLISHED_TREE_AUTHORIZED_EVENT } from "../task/publicationReference.js";
|
|
4
|
-
import {
|
|
3
|
+
import { TURN_INPUT_MAX_DELTAS } from "./turnInputContract.js";
|
|
5
4
|
import { contextContentDigest, contextSnapshotRef, createContextSnapshot, validateContextSnapshot } from "./contextSnapshot.js";
|
|
6
|
-
export const
|
|
7
|
-
export const
|
|
8
|
-
export const
|
|
9
|
-
export const
|
|
10
|
-
export function
|
|
5
|
+
export const TURN_CONTEXT_PACK_SCHEMA_VERSION = 1;
|
|
6
|
+
export const TURN_CONTEXT_PACK_MAX_REFS = 256;
|
|
7
|
+
export const TURN_CONTEXT_PACK_MAX_BYTES = 8 * 1024 * 1024;
|
|
8
|
+
export const TURN_CONTEXT_EXPAND_MAX_BYTES = 4 * 1024 * 1024;
|
|
9
|
+
export function freezeTurnContextSnapshot(store, run, now, frozenBy = "controller", baselineRef) {
|
|
11
10
|
if (baselineRef !== undefined) {
|
|
12
11
|
const baseline = store.getContextSnapshot(run.taskId, baselineRef.id);
|
|
13
12
|
if (baseline === null
|
|
@@ -18,10 +17,10 @@ export function freezeRunContextSnapshot(store, run, now, frozenBy = "controller
|
|
|
18
17
|
|| baseline.scope !== "stage"
|
|
19
18
|
|| baselineRef.scope !== baseline.scope
|
|
20
19
|
|| baseline.scopeRef !== baselineRef.scopeRef) {
|
|
21
|
-
throw new Error(`
|
|
20
|
+
throw new Error(`Turn Context baseline is missing or drifted: ${baselineRef.id}.`);
|
|
22
21
|
}
|
|
23
22
|
validateContextSnapshot(baseline);
|
|
24
|
-
const overlays =
|
|
23
|
+
const overlays = collectTurnContextOverlays(store, run);
|
|
25
24
|
const resources = [...new Map([...baseline.resources, ...overlays].map((entry) => [
|
|
26
25
|
contextRefIdentity(entry.ref),
|
|
27
26
|
entry
|
|
@@ -77,7 +76,7 @@ export function freezeRunContextSnapshot(store, run, now, frozenBy = "controller
|
|
|
77
76
|
}
|
|
78
77
|
/**
|
|
79
78
|
* Freeze the shared, role-neutral ContextSnapshot anchored by one WorkItem
|
|
80
|
-
* exploration stage Group.
|
|
79
|
+
* exploration stage Group. Turn snapshots remain role-specific; this
|
|
81
80
|
* record is the durable stage baseline and derives from a fresh WorkItem
|
|
82
81
|
* snapshot so the Group never depends on ambient latest state.
|
|
83
82
|
*/
|
|
@@ -102,7 +101,7 @@ export function freezeExecutionStageContextSnapshot(store, input, now) {
|
|
|
102
101
|
for (const binding of task.projectBindings) {
|
|
103
102
|
const project = store.getProject(binding.projectId);
|
|
104
103
|
if (project === null)
|
|
105
|
-
throw new Error(`
|
|
104
|
+
throw new Error(`Turn Project not found: ${binding.projectId}.`);
|
|
106
105
|
const { knowledge, ...projectPolicy } = project;
|
|
107
106
|
materialized.push(materialize("L1", "project-policy", project.id, projectPolicy));
|
|
108
107
|
for (const entry of knowledge.filter(({ status }) => status === "active")) {
|
|
@@ -217,27 +216,27 @@ export function contextSnapshotDeltaRefIds(store, snapshot) {
|
|
|
217
216
|
|| before.store !== ref.store
|
|
218
217
|
|| before.layer !== ref.layer;
|
|
219
218
|
}).map(({ refId }) => refId);
|
|
220
|
-
return Object.freeze([...new Set(changed)].sort().slice(0,
|
|
219
|
+
return Object.freeze([...new Set(changed)].sort().slice(0, TURN_INPUT_MAX_DELTAS));
|
|
221
220
|
}
|
|
222
|
-
export function
|
|
223
|
-
const run =
|
|
221
|
+
export function buildTurnContextPack(store, taskId, turnId) {
|
|
222
|
+
const run = requireExactTurn(store, taskId, turnId);
|
|
224
223
|
const current = collectAuthorizedContext(store, run);
|
|
225
224
|
let pointers = current.map(({ ref }) => ref);
|
|
226
225
|
let snapshotRef;
|
|
227
|
-
if (run.
|
|
228
|
-
const expected = run.
|
|
226
|
+
if (run.inputs[0].input.contextSnapshotRef !== undefined) {
|
|
227
|
+
const expected = run.inputs[0].input.contextSnapshotRef;
|
|
229
228
|
const snapshot = store.getContextSnapshot(taskId, expected.id);
|
|
230
229
|
if (snapshot === null)
|
|
231
|
-
throw new Error(`
|
|
230
|
+
throw new Error(`Turn Context Snapshot is missing: ${expected.id}.`);
|
|
232
231
|
validateContextSnapshot(snapshot);
|
|
233
232
|
if (snapshot.digest !== expected.digest || snapshot.taskId !== taskId) {
|
|
234
|
-
throw new Error(`
|
|
233
|
+
throw new Error(`Turn Context Snapshot identity drifted: ${expected.id}.`);
|
|
235
234
|
}
|
|
236
235
|
pointers = snapshot.refs;
|
|
237
236
|
snapshotRef = contextSnapshotRef(snapshot);
|
|
238
237
|
}
|
|
239
|
-
if (pointers.length >
|
|
240
|
-
throw new Error(`
|
|
238
|
+
if (pointers.length > TURN_CONTEXT_PACK_MAX_REFS) {
|
|
239
|
+
throw new Error(`Turn Context exceeds ${TURN_CONTEXT_PACK_MAX_REFS} authorized refs.`);
|
|
241
240
|
}
|
|
242
241
|
const view = contextView(run);
|
|
243
242
|
const writableProjectIds = writableProjects(store, run, view);
|
|
@@ -248,10 +247,10 @@ export function buildRunContextPack(store, taskId, runId) {
|
|
|
248
247
|
digest: ref.digest
|
|
249
248
|
}));
|
|
250
249
|
const body = {
|
|
251
|
-
schemaVersion:
|
|
250
|
+
schemaVersion: TURN_CONTEXT_PACK_SCHEMA_VERSION,
|
|
252
251
|
identity: Object.freeze({
|
|
253
252
|
taskId,
|
|
254
|
-
|
|
253
|
+
turnId: turnId,
|
|
255
254
|
roleName: run.roleName,
|
|
256
255
|
purpose: run.purpose,
|
|
257
256
|
agentId: run.effective.agentId,
|
|
@@ -259,27 +258,27 @@ export function buildRunContextPack(store, taskId, runId) {
|
|
|
259
258
|
workspace: run.effective.workspace.root
|
|
260
259
|
}),
|
|
261
260
|
...(snapshotRef === undefined ? {} : { snapshot: snapshotRef }),
|
|
262
|
-
|
|
261
|
+
input: run.inputs[0].input,
|
|
263
262
|
authority: Object.freeze({ view, readableRefs: pointers, writableProjectIds }),
|
|
264
263
|
pointers,
|
|
265
264
|
summaries,
|
|
266
|
-
deltas: pointers.filter((ref) => run.
|
|
265
|
+
deltas: pointers.filter((ref) => run.inputs[0].input.deltaRefIds.includes(ref.refId)),
|
|
267
266
|
completion: Object.freeze({
|
|
268
267
|
allowedActions: completionActions(view),
|
|
269
|
-
|
|
268
|
+
exactTurnRef: `${taskId}/${turnId}`
|
|
270
269
|
})
|
|
271
270
|
};
|
|
272
271
|
const digest = contextContentDigest(body);
|
|
273
272
|
const preliminaryBytes = Buffer.byteLength(JSON.stringify({ ...body, digest }), "utf8");
|
|
274
|
-
if (preliminaryBytes >
|
|
275
|
-
throw new Error(`
|
|
273
|
+
if (preliminaryBytes > TURN_CONTEXT_PACK_MAX_BYTES) {
|
|
274
|
+
throw new Error(`Turn Context Pack exceeds ${TURN_CONTEXT_PACK_MAX_BYTES} bytes.`);
|
|
276
275
|
}
|
|
277
276
|
const pack = Object.freeze({
|
|
278
277
|
...body,
|
|
279
278
|
budget: Object.freeze({
|
|
280
|
-
maxRefs:
|
|
279
|
+
maxRefs: TURN_CONTEXT_PACK_MAX_REFS,
|
|
281
280
|
returnedRefs: pointers.length,
|
|
282
|
-
maxBytes:
|
|
281
|
+
maxBytes: TURN_CONTEXT_PACK_MAX_BYTES,
|
|
283
282
|
returnedBytes: preliminaryBytes,
|
|
284
283
|
truncated: false
|
|
285
284
|
}),
|
|
@@ -287,24 +286,24 @@ export function buildRunContextPack(store, taskId, runId) {
|
|
|
287
286
|
});
|
|
288
287
|
return pack;
|
|
289
288
|
}
|
|
290
|
-
export function
|
|
291
|
-
const pack =
|
|
289
|
+
export function expandTurnContextRef(store, taskId, turnId, refId, refStore) {
|
|
290
|
+
const pack = buildTurnContextPack(store, taskId, turnId);
|
|
292
291
|
const authorized = pack.pointers.filter((ref) => (ref.refId === refId && (refStore === undefined || ref.store === refStore)));
|
|
293
292
|
const selector = refStore === undefined ? refId : `${refStore}/${refId}`;
|
|
294
293
|
if (authorized.length !== 1) {
|
|
295
|
-
throw new Error(`
|
|
294
|
+
throw new Error(`Turn Context ref is not uniquely authorized: ${selector}.`);
|
|
296
295
|
}
|
|
297
|
-
const run =
|
|
298
|
-
const snapshotRef = run.
|
|
296
|
+
const run = requireExactTurn(store, taskId, turnId);
|
|
297
|
+
const snapshotRef = run.inputs[0].input.contextSnapshotRef;
|
|
299
298
|
const materialized = snapshotRef === undefined
|
|
300
299
|
? collectAuthorizedContext(store, run).find(({ ref }) => (contextRefIdentity(ref) === contextRefIdentity(authorized[0])))
|
|
301
300
|
: store.getContextSnapshot(taskId, snapshotRef.id)?.resources.find(({ ref }) => (contextRefIdentity(ref) === contextRefIdentity(authorized[0])));
|
|
302
301
|
if (materialized === undefined || materialized.ref.digest !== authorized[0].digest) {
|
|
303
|
-
throw new Error(`
|
|
302
|
+
throw new Error(`Turn Context ref is unavailable or drifted: ${selector}.`);
|
|
304
303
|
}
|
|
305
304
|
const bytes = Buffer.byteLength(JSON.stringify(materialized.value), "utf8");
|
|
306
|
-
if (bytes >
|
|
307
|
-
throw new Error(`
|
|
305
|
+
if (bytes > TURN_CONTEXT_EXPAND_MAX_BYTES) {
|
|
306
|
+
throw new Error(`Turn Context expansion exceeds ${TURN_CONTEXT_EXPAND_MAX_BYTES} bytes.`);
|
|
308
307
|
}
|
|
309
308
|
return Object.freeze({
|
|
310
309
|
ref: materialized.ref,
|
|
@@ -312,21 +311,21 @@ export function expandRunContextRef(store, taskId, runId, refId, refStore) {
|
|
|
312
311
|
digest: contextContentDigest({ ref: materialized.ref, value: materialized.value })
|
|
313
312
|
});
|
|
314
313
|
}
|
|
315
|
-
/** Fail-closed delta cursor resolution for one immutable
|
|
316
|
-
export function
|
|
317
|
-
const pack =
|
|
314
|
+
/** Fail-closed delta cursor resolution for one immutable Turn lineage. */
|
|
315
|
+
export function buildTurnContextDelta(store, taskId, turnId, after) {
|
|
316
|
+
const pack = buildTurnContextPack(store, taskId, turnId);
|
|
318
317
|
if (after === pack.digest || after === pack.snapshot?.digest) {
|
|
319
318
|
return Object.freeze({ schemaVersion: 1, after, cursor: pack.digest, refs: [] });
|
|
320
319
|
}
|
|
321
|
-
const run =
|
|
322
|
-
const snapshotRef = run.
|
|
320
|
+
const run = requireExactTurn(store, taskId, turnId);
|
|
321
|
+
const snapshotRef = run.inputs[0].input.contextSnapshotRef;
|
|
323
322
|
const snapshot = snapshotRef === undefined
|
|
324
323
|
? null
|
|
325
324
|
: store.getContextSnapshot(taskId, snapshotRef.id);
|
|
326
325
|
const parentDigest = snapshot?.parentRef?.digest;
|
|
327
326
|
if (!((parentDigest !== undefined && after === parentDigest)
|
|
328
327
|
|| (parentDigest === undefined && after === "none"))) {
|
|
329
|
-
throw new Error("
|
|
328
|
+
throw new Error("Turn Context delta cursor is outside the frozen Snapshot lineage.");
|
|
330
329
|
}
|
|
331
330
|
return Object.freeze({
|
|
332
331
|
schemaVersion: 1,
|
|
@@ -335,10 +334,10 @@ export function buildRunContextDelta(store, taskId, runId, after) {
|
|
|
335
334
|
refs: pack.deltas
|
|
336
335
|
});
|
|
337
336
|
}
|
|
338
|
-
function
|
|
339
|
-
const run = store.
|
|
340
|
-
if (run === null || run.taskId !== taskId || run.id !==
|
|
341
|
-
throw new Error(`
|
|
337
|
+
function requireExactTurn(store, taskId, turnId) {
|
|
338
|
+
const run = store.getTurn(taskId, turnId);
|
|
339
|
+
if (run === null || run.taskId !== taskId || run.id !== turnId) {
|
|
340
|
+
throw new Error(`Turn not found: ${taskId}/${turnId}.`);
|
|
342
341
|
}
|
|
343
342
|
return run;
|
|
344
343
|
}
|
|
@@ -352,18 +351,18 @@ function collectAuthorizedContext(store, run) {
|
|
|
352
351
|
if (brief !== null && view === "leader") {
|
|
353
352
|
result.push(materialize("L2", "task-brief", task.id, brief));
|
|
354
353
|
}
|
|
355
|
-
result.push(...
|
|
354
|
+
result.push(...collectTurnContextOverlays(store, run));
|
|
356
355
|
if (run.workItemId !== undefined) {
|
|
357
356
|
const item = store.getWorkItem(task.id, run.workItemId);
|
|
358
357
|
if (item === null)
|
|
359
|
-
throw new Error(`
|
|
358
|
+
throw new Error(`Turn WorkItem not found: ${run.workItemId}.`);
|
|
360
359
|
result.push(materialize("L3", "work-item", item.id, item));
|
|
361
360
|
if (view === "worker") {
|
|
362
361
|
for (const dependencyId of item.dependsOn) {
|
|
363
362
|
const dependency = store.getWorkItem(task.id, dependencyId);
|
|
364
363
|
if (dependency === null
|
|
365
364
|
|| (dependency.status !== "completed" && dependency.status !== "retired")) {
|
|
366
|
-
throw new Error(`
|
|
365
|
+
throw new Error(`Turn WorkItem dependency is not accepted: ${dependencyId}.`);
|
|
367
366
|
}
|
|
368
367
|
if (dependency.status === "retired")
|
|
369
368
|
continue;
|
|
@@ -374,7 +373,7 @@ function collectAuthorizedContext(store, run) {
|
|
|
374
373
|
if (run.reviewRoundId !== undefined) {
|
|
375
374
|
const round = store.getReviewRound(task.id, run.reviewRoundId);
|
|
376
375
|
if (round === null)
|
|
377
|
-
throw new Error(`
|
|
376
|
+
throw new Error(`Turn ReviewRound not found: ${run.reviewRoundId}.`);
|
|
378
377
|
result.push(materialize("L3", "review-round", round.id, round));
|
|
379
378
|
for (const finding of store.listReviewFindings(task.id).filter((candidate) => (candidate.firstReviewRoundId === round.id
|
|
380
379
|
|| candidate.lastReviewRoundId === round.id
|
|
@@ -385,7 +384,7 @@ function collectAuthorizedContext(store, run) {
|
|
|
385
384
|
for (const binding of task.projectBindings) {
|
|
386
385
|
const project = store.getProject(binding.projectId);
|
|
387
386
|
if (project === null)
|
|
388
|
-
throw new Error(`
|
|
387
|
+
throw new Error(`Turn Project not found: ${binding.projectId}.`);
|
|
389
388
|
const { knowledge, ...projectPolicy } = project;
|
|
390
389
|
result.push(materialize("L1", "project-policy", project.id, projectPolicy));
|
|
391
390
|
for (const entry of knowledge.filter(({ status }) => status === "active")) {
|
|
@@ -409,8 +408,8 @@ function collectAuthorizedContext(store, run) {
|
|
|
409
408
|
for (const finding of store.listReviewFindings(task.id)) {
|
|
410
409
|
result.push(materialize("L3", "review-finding", finding.id, finding));
|
|
411
410
|
}
|
|
412
|
-
for (const
|
|
413
|
-
result.push(materialize("L4", "
|
|
411
|
+
for (const turn of operationalTaskRecords(store.listTurns(task.id), events, "turn").slice(-24)) {
|
|
412
|
+
result.push(materialize("L4", "turn", turn.id, turn));
|
|
414
413
|
}
|
|
415
414
|
for (const message of operationalTaskRecords(store.listMessages(task.id), events, "message").slice(-16)) {
|
|
416
415
|
result.push(materialize("L4", "task-message", message.id, message));
|
|
@@ -427,20 +426,6 @@ function collectAuthorizedContext(store, run) {
|
|
|
427
426
|
for (const event of publishedTreeAuthorizations.reverse().slice(-16)) {
|
|
428
427
|
result.push(materialize("L4", "task-event", event.id, event));
|
|
429
428
|
}
|
|
430
|
-
for (const event of events.filter(({ type }) => (type === RUN_YIELD_REJECTED_EVENT)).slice(-16)) {
|
|
431
|
-
result.push(materialize("L4", "task-event", event.id, event));
|
|
432
|
-
const attempt = rejectedYieldAttemptFromTaskEvent(event);
|
|
433
|
-
if (attempt === null)
|
|
434
|
-
continue;
|
|
435
|
-
const rejectedRun = store.getAgentRun(task.id, attempt.runId);
|
|
436
|
-
if (rejectedRun !== null) {
|
|
437
|
-
result.push(materialize("L4", "agent-run", rejectedRun.id, rejectedRun));
|
|
438
|
-
}
|
|
439
|
-
const rejectedRound = store.getReviewRound(task.id, attempt.reviewRoundId);
|
|
440
|
-
if (rejectedRound !== null) {
|
|
441
|
-
result.push(materialize("L3", "review-round", rejectedRound.id, rejectedRound));
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
429
|
for (const request of store.listOpenInputRequests([task.id])) {
|
|
445
430
|
result.push(materialize("L4", "input-request", request.id, request));
|
|
446
431
|
}
|
|
@@ -449,10 +434,10 @@ function collectAuthorizedContext(store, run) {
|
|
|
449
434
|
return [...unique.values()].sort((left, right) => (contextRefIdentity(left.ref).localeCompare(contextRefIdentity(right.ref))));
|
|
450
435
|
}
|
|
451
436
|
/** Lane-specific context that may be layered over an immutable stage base. */
|
|
452
|
-
function
|
|
437
|
+
function collectTurnContextOverlays(store, run) {
|
|
453
438
|
const role = store.getRole(run.taskId, run.roleName);
|
|
454
439
|
if (role === null)
|
|
455
|
-
throw new Error(`
|
|
440
|
+
throw new Error(`Turn Role not found: ${run.taskId}/${run.roleName}.`);
|
|
456
441
|
return [
|
|
457
442
|
materialize("L1", "role-profile", role.name, {
|
|
458
443
|
name: role.name,
|
|
@@ -514,10 +499,10 @@ function writableProjects(store, run, view) {
|
|
|
514
499
|
}
|
|
515
500
|
function completionActions(view) {
|
|
516
501
|
if (view === "leader")
|
|
517
|
-
return Object.freeze(["
|
|
502
|
+
return Object.freeze(["finish-turn", "complete-task", "request-input"]);
|
|
518
503
|
if (view === "reviewer")
|
|
519
|
-
return Object.freeze(["checkpoint", "
|
|
504
|
+
return Object.freeze(["checkpoint", "finish-turn"]);
|
|
520
505
|
if (view === "operator")
|
|
521
506
|
return Object.freeze(["answer-input", "recover"]);
|
|
522
|
-
return Object.freeze(["checkpoint", "
|
|
507
|
+
return Object.freeze(["checkpoint", "finish-turn"]);
|
|
523
508
|
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { normalizedUniqueIdentities, requireIdentity, requireText } from "../domain/validation.js";
|
|
2
|
+
import { validateContextSnapshotRef } from "./contextSnapshot.js";
|
|
3
|
+
export const TURN_INPUT_PROTOCOL_VERSION = 1;
|
|
4
|
+
export const TURN_INPUT_PROTOCOL = "yui-turn/v1";
|
|
5
|
+
export const TURN_INPUT_MAX_BYTES = 4 * 1024;
|
|
6
|
+
export const TURN_INPUT_MAX_DELTAS = 16;
|
|
7
|
+
export const YUI_TURN_INPUT_CHANNELS = [
|
|
8
|
+
"user-message",
|
|
9
|
+
"input-response",
|
|
10
|
+
"task-dispatch",
|
|
11
|
+
"workitem-dispatch",
|
|
12
|
+
"leader-wakeup",
|
|
13
|
+
"leader-forced-wakeup"
|
|
14
|
+
];
|
|
15
|
+
export function createTurnInput(input) {
|
|
16
|
+
const common = normalizeInput(input);
|
|
17
|
+
return Object.freeze({
|
|
18
|
+
schemaVersion: TURN_INPUT_PROTOCOL_VERSION,
|
|
19
|
+
...common,
|
|
20
|
+
...(input.directive === undefined
|
|
21
|
+
? {}
|
|
22
|
+
: { directive: requireText(input.directive, "Turn input directive") })
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export function validateTurnInput(value) {
|
|
26
|
+
if (value.schemaVersion !== TURN_INPUT_PROTOCOL_VERSION) {
|
|
27
|
+
throw new Error("Turn input must use schemaVersion 1.");
|
|
28
|
+
}
|
|
29
|
+
const normalized = createTurnInput(value);
|
|
30
|
+
if (JSON.stringify(normalized) !== JSON.stringify(value)) {
|
|
31
|
+
throw new Error("Turn input is not canonical.");
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
export function createTurnInputEnvelope(context, input) {
|
|
36
|
+
validateTurnInput(input);
|
|
37
|
+
const normalized = normalizeEnvelopeContext(context, input);
|
|
38
|
+
return Object.freeze({
|
|
39
|
+
protocol: TURN_INPUT_PROTOCOL,
|
|
40
|
+
turnId: normalized.turnId,
|
|
41
|
+
source: input.source,
|
|
42
|
+
roleName: normalized.roleName,
|
|
43
|
+
purpose: normalized.purpose,
|
|
44
|
+
subject: normalized.subject,
|
|
45
|
+
...(input.contextSnapshotRef === undefined
|
|
46
|
+
? {}
|
|
47
|
+
: { contextSnapshotRef: input.contextSnapshotRef }),
|
|
48
|
+
deltaRefIds: input.deltaRefIds
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export function validateTurnInputEnvelope(value) {
|
|
52
|
+
if (value.protocol !== TURN_INPUT_PROTOCOL) {
|
|
53
|
+
throw new Error("Turn input protocol is unsupported.");
|
|
54
|
+
}
|
|
55
|
+
normalizeEnvelope(value);
|
|
56
|
+
const serialized = serializeTurnInputEnvelope(value);
|
|
57
|
+
if (Buffer.byteLength(serialized, "utf8") > TURN_INPUT_MAX_BYTES) {
|
|
58
|
+
throw new Error("Turn input envelope exceeds its protocol byte limit.");
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
export function serializeTurnInputEnvelope(value) {
|
|
63
|
+
if (value.protocol !== TURN_INPUT_PROTOCOL) {
|
|
64
|
+
throw new Error("Turn input protocol is unsupported.");
|
|
65
|
+
}
|
|
66
|
+
const normalized = normalizeEnvelope(value);
|
|
67
|
+
const subject = Object.entries(normalized.subject)
|
|
68
|
+
.map(([key, id]) => `${key}:${id}`)
|
|
69
|
+
.join(",");
|
|
70
|
+
const snapshot = normalized.contextSnapshotRef;
|
|
71
|
+
const lines = [
|
|
72
|
+
"Yui Turn input. Follow the Session Manifest and injected Skills.",
|
|
73
|
+
`${normalized.subject.taskId === undefined ? "" : `task=${normalized.subject.taskId} `}turn=${normalized.turnId} role=${normalized.roleName}`,
|
|
74
|
+
`source=${normalized.source.type}/${normalized.source.channel} purpose=${normalized.purpose} subject=${subject || "global"} snapshot=${snapshot === undefined ? "none" : `${snapshot.id}@${snapshot.digest}`}`,
|
|
75
|
+
normalized.deltaRefIds.length === 0
|
|
76
|
+
? "delta=none"
|
|
77
|
+
: `delta=${normalized.deltaRefIds.join(",")}`,
|
|
78
|
+
"Load the exact Turn context before acting; fail closed if it is unavailable or mismatched."
|
|
79
|
+
];
|
|
80
|
+
const serialized = lines.join("\n");
|
|
81
|
+
if (Buffer.byteLength(serialized, "utf8") > TURN_INPUT_MAX_BYTES) {
|
|
82
|
+
throw new Error("Turn input envelope exceeds its protocol byte limit.");
|
|
83
|
+
}
|
|
84
|
+
return serialized;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Bounded infrastructure-recovery input for an already-submitted Turn. It resumes
|
|
88
|
+
* the provider-native transcript after Host/child replacement and never
|
|
89
|
+
* replays the Assignment or its directive.
|
|
90
|
+
*/
|
|
91
|
+
export function serializeTurnHostRecoveryEnvelope(value) {
|
|
92
|
+
const normalized = normalizeEnvelope(value);
|
|
93
|
+
const serialized = [
|
|
94
|
+
"Yui Host recovery for an existing Turn.",
|
|
95
|
+
`${normalized.subject.taskId === undefined ? "" : `task=${normalized.subject.taskId} `}turn=${normalized.turnId} role=${normalized.roleName}`,
|
|
96
|
+
"Resume the same native conversation from its latest durable state. Do not repeat completed work or replay the original input.",
|
|
97
|
+
"Load the exact Turn context or delta only if needed, then continue the same Turn."
|
|
98
|
+
].join("\n");
|
|
99
|
+
if (Buffer.byteLength(serialized, "utf8") > TURN_INPUT_MAX_BYTES) {
|
|
100
|
+
throw new Error("Turn Host recovery envelope exceeds its protocol byte limit.");
|
|
101
|
+
}
|
|
102
|
+
return serialized;
|
|
103
|
+
}
|
|
104
|
+
function normalizeInput(input) {
|
|
105
|
+
const source = normalizeSource(input.source);
|
|
106
|
+
const snapshot = input.contextSnapshotRef === undefined
|
|
107
|
+
? undefined
|
|
108
|
+
: validateContextSnapshotRef(input.contextSnapshotRef);
|
|
109
|
+
const deltaRefIds = normalizedUniqueIdentities(input.deltaRefIds, "Turn input delta ref");
|
|
110
|
+
if (deltaRefIds.length > TURN_INPUT_MAX_DELTAS) {
|
|
111
|
+
throw new Error(`Turn input supports at most ${TURN_INPUT_MAX_DELTAS} delta refs.`);
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
source,
|
|
115
|
+
...(snapshot === undefined ? {} : { contextSnapshotRef: snapshot }),
|
|
116
|
+
deltaRefIds
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function normalizeEnvelopeContext(context, input) {
|
|
120
|
+
const normalized = normalizeEnvelope({
|
|
121
|
+
protocol: TURN_INPUT_PROTOCOL,
|
|
122
|
+
turnId: context.turnId,
|
|
123
|
+
source: input.source,
|
|
124
|
+
roleName: context.roleName,
|
|
125
|
+
purpose: context.purpose,
|
|
126
|
+
subject: context.subject,
|
|
127
|
+
...(input.contextSnapshotRef === undefined
|
|
128
|
+
? {}
|
|
129
|
+
: { contextSnapshotRef: input.contextSnapshotRef }),
|
|
130
|
+
deltaRefIds: input.deltaRefIds
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
turnId: normalized.turnId,
|
|
134
|
+
roleName: normalized.roleName,
|
|
135
|
+
purpose: normalized.purpose,
|
|
136
|
+
subject: normalized.subject
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function normalizeEnvelope(input) {
|
|
140
|
+
if (!["execution", "review", "global"].includes(input.purpose)) {
|
|
141
|
+
throw new Error("Turn input purpose is invalid.");
|
|
142
|
+
}
|
|
143
|
+
const normalizedInput = normalizeInput(input);
|
|
144
|
+
const subject = normalizeSubject(input.subject);
|
|
145
|
+
if (normalizedInput.contextSnapshotRef !== undefined
|
|
146
|
+
&& normalizedInput.contextSnapshotRef.taskId !== subject.taskId) {
|
|
147
|
+
throw new Error("Turn input Context Snapshot belongs to another Task.");
|
|
148
|
+
}
|
|
149
|
+
if (input.purpose !== "global" && subject.taskId === undefined) {
|
|
150
|
+
throw new Error("A Task Turn input requires a Task subject.");
|
|
151
|
+
}
|
|
152
|
+
if (input.purpose === "review" && subject.reviewRoundId === undefined) {
|
|
153
|
+
throw new Error("A review Turn input requires a ReviewRound subject.");
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
protocol: TURN_INPUT_PROTOCOL,
|
|
157
|
+
turnId: requireIdentity(input.turnId, "Turn input Turn id"),
|
|
158
|
+
source: normalizedInput.source,
|
|
159
|
+
roleName: requireIdentity(input.roleName, "Turn input Role name"),
|
|
160
|
+
purpose: input.purpose,
|
|
161
|
+
subject,
|
|
162
|
+
...(normalizedInput.contextSnapshotRef === undefined
|
|
163
|
+
? {}
|
|
164
|
+
: { contextSnapshotRef: normalizedInput.contextSnapshotRef }),
|
|
165
|
+
deltaRefIds: normalizedInput.deltaRefIds
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function normalizeSubject(subject) {
|
|
169
|
+
const normalized = Object.fromEntries(Object.entries(subject).map(([key, value]) => [
|
|
170
|
+
key,
|
|
171
|
+
requireIdentity(value, `Turn input ${key}`)
|
|
172
|
+
]));
|
|
173
|
+
const allowed = [
|
|
174
|
+
"taskId",
|
|
175
|
+
"workItemId",
|
|
176
|
+
"reviewRoundId",
|
|
177
|
+
"executionGroupId",
|
|
178
|
+
"executionLaneId"
|
|
179
|
+
];
|
|
180
|
+
if (Object.keys(normalized).some((key) => !allowed.includes(key))) {
|
|
181
|
+
throw new Error("Turn input subject contains an unknown field.");
|
|
182
|
+
}
|
|
183
|
+
if ((normalized.executionGroupId === undefined) !== (normalized.executionLaneId === undefined)) {
|
|
184
|
+
throw new Error("Turn input execution lineage is incomplete.");
|
|
185
|
+
}
|
|
186
|
+
if (normalized.taskId === undefined
|
|
187
|
+
&& (normalized.workItemId !== undefined || normalized.reviewRoundId !== undefined
|
|
188
|
+
|| normalized.executionGroupId !== undefined)) {
|
|
189
|
+
throw new Error("Turn input child subject requires a Task id.");
|
|
190
|
+
}
|
|
191
|
+
return Object.freeze(normalized);
|
|
192
|
+
}
|
|
193
|
+
function normalizeSource(source) {
|
|
194
|
+
if (source.type === "yui" && YUI_TURN_INPUT_CHANNELS.includes(source.channel)) {
|
|
195
|
+
return Object.freeze({ type: "yui", channel: source.channel });
|
|
196
|
+
}
|
|
197
|
+
if (source.type === "user" && source.channel === "direct") {
|
|
198
|
+
return Object.freeze({ type: "user", channel: "direct" });
|
|
199
|
+
}
|
|
200
|
+
if (source.type === "provider" && source.channel === "goal-continuation") {
|
|
201
|
+
return Object.freeze({ type: "provider", channel: "goal-continuation" });
|
|
202
|
+
}
|
|
203
|
+
throw new Error("Turn input source is invalid.");
|
|
204
|
+
}
|
|
@@ -33,7 +33,7 @@ export function buildTaskWakeEnvelope(reader, request) {
|
|
|
33
33
|
.filter((record) => Date.parse(record.createdAt) > fromTime).length,
|
|
34
34
|
messages: operationalTaskRecords(reader.listMessages(request.taskId), events, "message")
|
|
35
35
|
.filter((record) => Date.parse(record.createdAt) > fromTime).length,
|
|
36
|
-
|
|
36
|
+
turns: operationalTaskRecords(reader.listTurns(request.taskId), events, "turn")
|
|
37
37
|
.filter((record) => Date.parse(record.createdAt) > fromTime).length
|
|
38
38
|
};
|
|
39
39
|
const activeReviews = reader.listReviewRounds(request.taskId).filter((round) => ((round.scope ?? "work-item") === "task"
|
|
@@ -50,7 +50,7 @@ export function buildTaskWakeEnvelope(reader, request) {
|
|
|
50
50
|
const lines = [
|
|
51
51
|
`Wake: ${request.wakeId} — delta since ${request.fromCursor}`,
|
|
52
52
|
` Reasons: ${renderReasons(request.reasons)}`,
|
|
53
|
-
` Changed: ${counts.events} events, ${counts.messages} messages, ${counts.
|
|
53
|
+
` Changed: ${counts.events} events, ${counts.messages} messages, ${counts.turns} Turns`
|
|
54
54
|
+ ` → yui task wake show ${request.taskId} ${request.wakeId}`,
|
|
55
55
|
` Active Task Reviews: ${activeReviews.length === 0
|
|
56
56
|
? "none"
|
|
@@ -5,7 +5,7 @@ const DEFAULT_MAX_CONCURRENT_SAMPLES = 8;
|
|
|
5
5
|
const MAX_CONCURRENT_SAMPLES = 64;
|
|
6
6
|
/**
|
|
7
7
|
* Controller-owned, provider-independent sampler. Drivers own source parsing;
|
|
8
|
-
* this component owns active-
|
|
8
|
+
* this component owns active-Turn discovery, cursor lifetime, canonical event
|
|
9
9
|
* creation, and low-latency mailbox wakes.
|
|
10
10
|
*/
|
|
11
11
|
export class AgentRuntimeObserver {
|
|
@@ -73,7 +73,7 @@ export class AgentRuntimeObserver {
|
|
|
73
73
|
if (state.usage === undefined) {
|
|
74
74
|
// Cumulative counters need a lower bound. An exact initial sample
|
|
75
75
|
// proves the complete Session-generation history even when the
|
|
76
|
-
// current
|
|
76
|
+
// current Turn resumed that Session, so preserve every occurrence and
|
|
77
77
|
// anchor it at zero. A partial sample cannot prove the omitted request
|
|
78
78
|
// boundaries; retain its latest total as a partial baseline, plus the
|
|
79
79
|
// smallest ordered witness if the visible counter already rolled back.
|
|
@@ -239,8 +239,8 @@ export class AgentRuntimeObserver {
|
|
|
239
239
|
activeSources() {
|
|
240
240
|
const result = [];
|
|
241
241
|
const indexedTaskIds = this.store.listActiveTaskIds?.();
|
|
242
|
-
//
|
|
243
|
-
//
|
|
242
|
+
// Stores without the indexed projection remain useful for deterministic
|
|
243
|
+
// tests. The production SQLite store discovers only its indexed hot set.
|
|
244
244
|
const activeTasks = indexedTaskIds === undefined
|
|
245
245
|
? this.store.listTasks().filter((task) => (task.status === "active" && task.executionGate.state === "enabled"))
|
|
246
246
|
: [...new Set(indexedTaskIds)]
|
|
@@ -251,43 +251,43 @@ export class AgentRuntimeObserver {
|
|
|
251
251
|
&& task.executionGate.state === "enabled"));
|
|
252
252
|
for (const task of activeTasks) {
|
|
253
253
|
// A Task still incurs one O(E) event projection. Group those observations
|
|
254
|
-
// by
|
|
254
|
+
// by Turn and sort each group once so every active Turn can reuse the same
|
|
255
255
|
// ordered slice instead of repeatedly filtering/sorting all E events.
|
|
256
|
-
const
|
|
256
|
+
const observationsByTurnId = new Map();
|
|
257
257
|
const taskObservations = [];
|
|
258
258
|
for (const event of this.store.listEvents(task.id)) {
|
|
259
259
|
const observation = runtimeObservationFromTaskEvent(event);
|
|
260
|
-
const
|
|
261
|
-
if (observation === null ||
|
|
260
|
+
const turnId = observation?.fence.turnId;
|
|
261
|
+
if (observation === null || turnId === undefined)
|
|
262
262
|
continue;
|
|
263
263
|
taskObservations.push(observation);
|
|
264
|
-
const grouped =
|
|
264
|
+
const grouped = observationsByTurnId.get(turnId);
|
|
265
265
|
if (grouped === undefined) {
|
|
266
|
-
|
|
266
|
+
observationsByTurnId.set(turnId, [observation]);
|
|
267
267
|
}
|
|
268
268
|
else {
|
|
269
269
|
grouped.push(observation);
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
|
-
for (const observations of
|
|
272
|
+
for (const observations of observationsByTurnId.values()) {
|
|
273
273
|
observations.sort(compareObservations);
|
|
274
274
|
}
|
|
275
275
|
taskObservations.sort(compareObservations);
|
|
276
|
-
for (const run of this.store.
|
|
276
|
+
for (const run of this.store.listTurns(task.id)) {
|
|
277
277
|
if (run.status !== "active"
|
|
278
|
-
|| this.store.
|
|
278
|
+
|| this.store.getActiveTurn(task.id, run.roleName)?.id !== run.id)
|
|
279
279
|
continue;
|
|
280
|
-
const observations =
|
|
280
|
+
const observations = observationsByTurnId.get(run.id) ?? [];
|
|
281
281
|
const accepted = observations
|
|
282
282
|
.filter((observation) => observation.kind === "turn.accepted"
|
|
283
|
-
&& observation.fence.
|
|
283
|
+
&& observation.fence.turnId === run.id
|
|
284
284
|
&& observation.fence.roleName === run.roleName
|
|
285
285
|
&& observation.fence.agentId === run.effective.agentId
|
|
286
286
|
&& observation.payload.observerSource !== undefined)
|
|
287
287
|
.at(-1);
|
|
288
288
|
const source = accepted?.payload.observerSource;
|
|
289
289
|
if (accepted === undefined || source === undefined
|
|
290
|
-
|| accepted.fence.taskId === undefined || accepted.fence.
|
|
290
|
+
|| accepted.fence.taskId === undefined || accepted.fence.turnId === undefined)
|
|
291
291
|
continue;
|
|
292
292
|
try {
|
|
293
293
|
if (this.drivers.require(accepted.fence.driverId).runtime.observer === undefined)
|
|
@@ -313,7 +313,7 @@ export class AgentRuntimeObserver {
|
|
|
313
313
|
key: JSON.stringify([
|
|
314
314
|
fence.taskId,
|
|
315
315
|
fence.roleName,
|
|
316
|
-
fence.
|
|
316
|
+
fence.turnId,
|
|
317
317
|
fence.agentId,
|
|
318
318
|
fence.driverId,
|
|
319
319
|
fence.launchId,
|
|
@@ -374,7 +374,7 @@ function sampleConcurrency(value) {
|
|
|
374
374
|
if (!Number.isSafeInteger(resolved)
|
|
375
375
|
|| resolved < 1
|
|
376
376
|
|| resolved > MAX_CONCURRENT_SAMPLES) {
|
|
377
|
-
throw new Error(`
|
|
377
|
+
throw new Error(`AgentRuntime observer maxConcurrentSamples must be between 1 and ${MAX_CONCURRENT_SAMPLES}.`);
|
|
378
378
|
}
|
|
379
379
|
return resolved;
|
|
380
380
|
}
|