@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
|
@@ -8,7 +8,7 @@ import { callController, controllerCallMayHaveApplied, ControllerClientError } f
|
|
|
8
8
|
import { readHomeFilesystemId } from "../core/homeFilesystemIdentity.js";
|
|
9
9
|
import { callFileTaskController } from "../controller/clientRuntime.js";
|
|
10
10
|
import { isForeignHandoverLockHeld } from "../release/runtimeRelease.js";
|
|
11
|
-
import { publishStructuredProviderAccepted, publishStructuredProviderActivationTerminal, publishStructuredConversationRecoverability, publishStructuredProviderOpened, publishStructuredProviderTerminal } from "../controller/structuredProviderObservation.js";
|
|
11
|
+
import { publishStructuredProviderAccepted, publishStructuredProviderActivationTerminal, publishStructuredProviderGoal, publishStructuredConversationRecoverability, publishStructuredProviderOpened, publishStructuredProviderStarted, publishStructuredProviderTerminal } from "../controller/structuredProviderObservation.js";
|
|
12
12
|
import { validateAgentHostLaunchPayload } from "./launchBroker.js";
|
|
13
13
|
import { ProviderDeliveryUnknownError, ProviderConversationMissingError, ProviderTurnBusyError, ProviderTurnRejectedError, startStructuredProviderSession } from "./structuredProviderHost.js";
|
|
14
14
|
import { sameProviderAuthorityFence, validateProviderAuthorityFence } from "./providerAuthorityFence.js";
|
|
@@ -42,6 +42,7 @@ export async function runAgentHost(input) {
|
|
|
42
42
|
let snapshot = hostSnapshot("idle");
|
|
43
43
|
let dispatchTail = Promise.resolve();
|
|
44
44
|
let promptHuman = () => { };
|
|
45
|
+
const recentSteerAttempts = new Set();
|
|
45
46
|
await replayExitOutbox(input.home);
|
|
46
47
|
const updateSnapshot = (next) => {
|
|
47
48
|
snapshot = validateSnapshot(next);
|
|
@@ -51,6 +52,29 @@ export async function runAgentHost(input) {
|
|
|
51
52
|
authorityOwner: authority.owner,
|
|
52
53
|
authorityHolderId: authority.holderId
|
|
53
54
|
};
|
|
55
|
+
const handleStarted = (started, observedPayload = sessionPayload) => {
|
|
56
|
+
if (started.clientOwned || observedPayload === undefined)
|
|
57
|
+
return;
|
|
58
|
+
updateSnapshot(hostSnapshot("busy", {
|
|
59
|
+
launchId: observedPayload.launchId,
|
|
60
|
+
adapterId: observedPayload.providerControl?.adapterId,
|
|
61
|
+
processInstanceId: session?.processInstanceId,
|
|
62
|
+
nativeSessionId: started.nativeSessionId,
|
|
63
|
+
conversationId: started.conversationId,
|
|
64
|
+
nativeTurnId: started.nativeTurnId,
|
|
65
|
+
...authorityFields()
|
|
66
|
+
}));
|
|
67
|
+
const directActivationId = activationId ?? observedPayload.launchId;
|
|
68
|
+
void enqueueSerialized(async () => {
|
|
69
|
+
await publishStructuredProviderStarted({
|
|
70
|
+
home: input.home,
|
|
71
|
+
environment: observedPayload.environment,
|
|
72
|
+
activationId: directActivationId,
|
|
73
|
+
started
|
|
74
|
+
});
|
|
75
|
+
signalRoleMailbox(input.home, observedPayload);
|
|
76
|
+
}).catch(() => { });
|
|
77
|
+
};
|
|
54
78
|
const handleTerminal = (terminal) => {
|
|
55
79
|
if (!terminal.clientOwned) {
|
|
56
80
|
const busyPayload = sessionPayload;
|
|
@@ -58,19 +82,29 @@ export async function runAgentHost(input) {
|
|
|
58
82
|
|| session === undefined
|
|
59
83
|
|| terminal.conversationId !== session.conversationId)
|
|
60
84
|
return;
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
|
|
85
|
+
// A direct Provider client shares the Session but still produces a Yui
|
|
86
|
+
// Turn audit record. It has no WorkItem ownership and no managed input
|
|
87
|
+
// fence; the runtime observer records only visible input/output.
|
|
88
|
+
const directActivationId = activationId ?? busyPayload.launchId;
|
|
89
|
+
void enqueueSerialized(async () => {
|
|
90
|
+
await publishStructuredProviderTerminal({
|
|
91
|
+
home: input.home,
|
|
92
|
+
environment: busyPayload.environment,
|
|
93
|
+
activationId: directActivationId,
|
|
94
|
+
terminal
|
|
95
|
+
});
|
|
96
|
+
if (session !== undefined && terminal.conversationId === session.conversationId) {
|
|
97
|
+
updateSnapshot(hostSnapshot(session.activeTurnId === undefined ? "idle" : "busy", {
|
|
98
|
+
launchId: busyPayload.launchId,
|
|
99
|
+
adapterId: session.adapterId,
|
|
100
|
+
processInstanceId: session.processInstanceId,
|
|
101
|
+
nativeSessionId: session.nativeSessionId,
|
|
102
|
+
conversationId: session.conversationId,
|
|
103
|
+
...authorityFields()
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
signalRoleMailbox(input.home, busyPayload);
|
|
107
|
+
}).catch(() => { });
|
|
74
108
|
return;
|
|
75
109
|
}
|
|
76
110
|
const terminalPayload = activeTurnPayload;
|
|
@@ -130,6 +164,23 @@ export async function runAgentHost(input) {
|
|
|
130
164
|
}
|
|
131
165
|
}).catch(() => { });
|
|
132
166
|
};
|
|
167
|
+
const handleGoal = (goal) => {
|
|
168
|
+
const currentPayload = activeTurnPayload ?? sessionPayload;
|
|
169
|
+
const currentSession = session;
|
|
170
|
+
if (currentPayload === undefined || currentSession === undefined)
|
|
171
|
+
return;
|
|
172
|
+
const currentActivationId = activationId ?? currentPayload.launchId;
|
|
173
|
+
void enqueueSerialized(async () => {
|
|
174
|
+
await publishStructuredProviderGoal({
|
|
175
|
+
home: input.home,
|
|
176
|
+
environment: currentPayload.environment,
|
|
177
|
+
activationId: currentActivationId,
|
|
178
|
+
conversationId: currentSession.conversationId,
|
|
179
|
+
goal
|
|
180
|
+
});
|
|
181
|
+
signalRoleMailbox(input.home, currentPayload);
|
|
182
|
+
}).catch(() => { });
|
|
183
|
+
};
|
|
133
184
|
const reconnectCodexClient = async (disconnectedSession, currentPayload) => {
|
|
134
185
|
const previousControl = currentPayload.providerControl;
|
|
135
186
|
if (previousControl?.adapterId !== "codex" || authority === undefined) {
|
|
@@ -169,7 +220,9 @@ export async function runAgentHost(input) {
|
|
|
169
220
|
return;
|
|
170
221
|
try {
|
|
171
222
|
const started = await startStructuredProviderSession(reconnectPayload, {
|
|
172
|
-
|
|
223
|
+
onStarted: (turn) => handleStarted(turn, currentPayload),
|
|
224
|
+
onTerminal: handleTerminal,
|
|
225
|
+
onGoal: handleGoal
|
|
173
226
|
});
|
|
174
227
|
session = started.session;
|
|
175
228
|
sessionPayload = currentPayload;
|
|
@@ -197,6 +250,17 @@ export async function runAgentHost(input) {
|
|
|
197
250
|
if (started.recoveredTerminal !== undefined) {
|
|
198
251
|
handleTerminal(started.recoveredTerminal);
|
|
199
252
|
}
|
|
253
|
+
if (ownedTurn === undefined && started.session.activeTurnId !== undefined) {
|
|
254
|
+
handleStarted({
|
|
255
|
+
conversationId: started.session.conversationId,
|
|
256
|
+
nativeSessionId: started.session.nativeSessionId,
|
|
257
|
+
nativeTurnId: started.session.activeTurnId,
|
|
258
|
+
clientOwned: false,
|
|
259
|
+
observedAt: new Date().toISOString()
|
|
260
|
+
}, currentPayload);
|
|
261
|
+
}
|
|
262
|
+
if (started.goal !== undefined)
|
|
263
|
+
handleGoal(started.goal);
|
|
200
264
|
return;
|
|
201
265
|
}
|
|
202
266
|
catch (error) {
|
|
@@ -298,9 +362,9 @@ export async function runAgentHost(input) {
|
|
|
298
362
|
? {}
|
|
299
363
|
: { taskId: currentPayload.environment.YUI_TASK_ID }),
|
|
300
364
|
roleName: currentPayload.environment.YUI_ROLE ?? "unknown-role",
|
|
301
|
-
...(currentPayload.environment.
|
|
365
|
+
...(currentPayload.environment.YUI_TURN_ID === undefined
|
|
302
366
|
? {}
|
|
303
|
-
: {
|
|
367
|
+
: { turnId: currentPayload.environment.YUI_TURN_ID }),
|
|
304
368
|
launchId: currentPayload.launchId,
|
|
305
369
|
...(providerSession.nativeSessionId.length === 0
|
|
306
370
|
? {}
|
|
@@ -372,6 +436,7 @@ export async function runAgentHost(input) {
|
|
|
372
436
|
}),
|
|
373
437
|
...authorityFields()
|
|
374
438
|
}));
|
|
439
|
+
let recoveredGoal;
|
|
375
440
|
try {
|
|
376
441
|
if (session !== undefined) {
|
|
377
442
|
if (session.adapterId !== providerControl.adapterId
|
|
@@ -391,9 +456,14 @@ export async function runAgentHost(input) {
|
|
|
391
456
|
activeTurnAttemptId = providerControl.ownedTurn.attemptId;
|
|
392
457
|
activeNativeTurnId = providerControl.ownedTurn.turnId;
|
|
393
458
|
}
|
|
394
|
-
const started = await startStructuredProviderSession(next, {
|
|
459
|
+
const started = await startStructuredProviderSession(next, {
|
|
460
|
+
onStarted: (turn) => handleStarted(turn, next),
|
|
461
|
+
onTerminal: handleTerminal,
|
|
462
|
+
onGoal: handleGoal
|
|
463
|
+
});
|
|
395
464
|
session = started.session;
|
|
396
465
|
sessionPayload = next;
|
|
466
|
+
recoveredGoal = started.goal;
|
|
397
467
|
if (started.session.adapterId === "codex") {
|
|
398
468
|
codexClientAttachedAt = Date.now();
|
|
399
469
|
consecutiveCodexDisconnects = 0;
|
|
@@ -402,6 +472,16 @@ export async function runAgentHost(input) {
|
|
|
402
472
|
if (started.recoveredTerminal !== undefined) {
|
|
403
473
|
handleTerminal(started.recoveredTerminal);
|
|
404
474
|
}
|
|
475
|
+
if ((providerControl.kind !== "restore" || providerControl.ownedTurn === undefined)
|
|
476
|
+
&& started.session.activeTurnId !== undefined) {
|
|
477
|
+
handleStarted({
|
|
478
|
+
conversationId: started.session.conversationId,
|
|
479
|
+
nativeSessionId: started.session.nativeSessionId,
|
|
480
|
+
nativeTurnId: started.session.activeTurnId,
|
|
481
|
+
clientOwned: false,
|
|
482
|
+
observedAt: new Date().toISOString()
|
|
483
|
+
}, next);
|
|
484
|
+
}
|
|
405
485
|
}
|
|
406
486
|
await publishStructuredProviderOpened({
|
|
407
487
|
home: input.home,
|
|
@@ -412,6 +492,15 @@ export async function runAgentHost(input) {
|
|
|
412
492
|
recoverability: conversationRecoverability,
|
|
413
493
|
observedAt: new Date().toISOString()
|
|
414
494
|
});
|
|
495
|
+
if (recoveredGoal !== undefined) {
|
|
496
|
+
await publishStructuredProviderGoal({
|
|
497
|
+
home: input.home,
|
|
498
|
+
environment: next.environment,
|
|
499
|
+
activationId: activationId ?? next.launchId,
|
|
500
|
+
conversationId: session.conversationId,
|
|
501
|
+
goal: recoveredGoal
|
|
502
|
+
});
|
|
503
|
+
}
|
|
415
504
|
const restoredOwnedTurn = providerControl.kind === "restore"
|
|
416
505
|
? providerControl.ownedTurn
|
|
417
506
|
: undefined;
|
|
@@ -488,11 +577,12 @@ export async function runAgentHost(input) {
|
|
|
488
577
|
if (activeTurnPayload !== undefined || snapshot.state === "settling") {
|
|
489
578
|
throw new ProviderTurnBusyError("Agent Host still owns an unsettled Provider Turn.", request.turn.attemptId, activeNativeTurnId);
|
|
490
579
|
}
|
|
580
|
+
const { YUI_TURN_ID: _launchTurnId, ...baseEnvironment } = sessionPayload.environment;
|
|
491
581
|
activeTurnPayload = {
|
|
492
582
|
...sessionPayload,
|
|
493
583
|
environment: {
|
|
494
|
-
...
|
|
495
|
-
|
|
584
|
+
...baseEnvironment,
|
|
585
|
+
...(request.turnId === undefined ? {} : { YUI_TURN_ID: request.turnId })
|
|
496
586
|
}
|
|
497
587
|
};
|
|
498
588
|
activeTurnAttemptId = request.turn.attemptId;
|
|
@@ -506,7 +596,7 @@ export async function runAgentHost(input) {
|
|
|
506
596
|
attemptId: request.turn.attemptId,
|
|
507
597
|
...authorityFields()
|
|
508
598
|
}));
|
|
509
|
-
const durableTurn = hostTurnControlParams(sessionPayload, session.nativeSessionId, request.authority, request.turn.attemptId, request.
|
|
599
|
+
const durableTurn = hostTurnControlParams(sessionPayload, session.nativeSessionId, request.authority, request.turn.attemptId, request.turnId);
|
|
510
600
|
await beginDurableProviderTurn(input.home, durableTurn);
|
|
511
601
|
let providerAccepted = false;
|
|
512
602
|
try {
|
|
@@ -516,7 +606,7 @@ export async function runAgentHost(input) {
|
|
|
516
606
|
try {
|
|
517
607
|
await publishStructuredProviderAccepted({
|
|
518
608
|
home: input.home,
|
|
519
|
-
environment:
|
|
609
|
+
environment: activeTurnPayload.environment,
|
|
520
610
|
activationId: activationId ?? sessionPayload.launchId,
|
|
521
611
|
receipt
|
|
522
612
|
});
|
|
@@ -569,6 +659,29 @@ export async function runAgentHost(input) {
|
|
|
569
659
|
throw error;
|
|
570
660
|
}
|
|
571
661
|
};
|
|
662
|
+
const steerTurn = async (request) => {
|
|
663
|
+
if (session === undefined || sessionPayload === undefined || activeTurnPayload === undefined) {
|
|
664
|
+
throw new ProviderTurnRejectedError("Agent Host has no active Provider Turn to steer.", request.turn.attemptId);
|
|
665
|
+
}
|
|
666
|
+
if (request.nativeSessionId !== session.nativeSessionId
|
|
667
|
+
|| request.nativeTurnId !== activeNativeTurnId) {
|
|
668
|
+
throw new ProviderTurnRejectedError("Agent Host steer targets a different Provider Turn.", request.turn.attemptId);
|
|
669
|
+
}
|
|
670
|
+
if (authority === undefined || !sameProviderAuthorityFence(authority, request.authority)) {
|
|
671
|
+
throw new Error("Agent Host rejected a stale Provider writer fence.");
|
|
672
|
+
}
|
|
673
|
+
if (recentSteerAttempts.has(request.turn.attemptId))
|
|
674
|
+
return snapshot;
|
|
675
|
+
const receipt = await session.steerTurn(request.turn);
|
|
676
|
+
if (receipt.nativeTurnId !== request.nativeTurnId) {
|
|
677
|
+
throw new ProviderDeliveryUnknownError("Provider accepted steer against an unexpected native Turn.", request.turn.attemptId);
|
|
678
|
+
}
|
|
679
|
+
recentSteerAttempts.add(request.turn.attemptId);
|
|
680
|
+
if (recentSteerAttempts.size > 256) {
|
|
681
|
+
recentSteerAttempts.delete(recentSteerAttempts.values().next().value);
|
|
682
|
+
}
|
|
683
|
+
return snapshot;
|
|
684
|
+
};
|
|
572
685
|
const setAuthority = (request) => {
|
|
573
686
|
if (session === undefined || request.nativeSessionId !== session.nativeSessionId) {
|
|
574
687
|
throw new Error("Agent Host authority targets a different Provider Conversation.");
|
|
@@ -605,6 +718,10 @@ export async function runAgentHost(input) {
|
|
|
605
718
|
const accepted = await enqueueSerialized(() => submitTurn(request));
|
|
606
719
|
return controlResult("accepted", accepted);
|
|
607
720
|
}
|
|
721
|
+
if (request.type === "steer-turn") {
|
|
722
|
+
const accepted = await enqueueSerialized(() => steerTurn(request));
|
|
723
|
+
return controlResult("accepted", accepted);
|
|
724
|
+
}
|
|
608
725
|
if (request.type === "set-authority") {
|
|
609
726
|
const accepted = await enqueueSerialized(async () => setAuthority(request));
|
|
610
727
|
return controlResult("accepted", accepted);
|
|
@@ -652,22 +769,13 @@ export async function runAgentHost(input) {
|
|
|
652
769
|
type: "submit-turn",
|
|
653
770
|
launchId: currentPayload.launchId,
|
|
654
771
|
nativeSessionId: currentSession.nativeSessionId,
|
|
655
|
-
runId: requiredEnvironment(currentPayload.environment.YUI_RUN_ID, "Run id"),
|
|
656
772
|
authority: currentAuthority,
|
|
657
773
|
turn: {
|
|
658
774
|
attemptId,
|
|
659
775
|
boundedText
|
|
660
776
|
}
|
|
661
777
|
};
|
|
662
|
-
|
|
663
|
-
await beginDurableProviderTurn(input.home, durableTurn);
|
|
664
|
-
try {
|
|
665
|
-
await submitTurn(turnControl);
|
|
666
|
-
}
|
|
667
|
-
catch (error) {
|
|
668
|
-
await resolveProviderTurnSubmission(input.home, durableTurn, error);
|
|
669
|
-
throw error;
|
|
670
|
-
}
|
|
778
|
+
await submitTurn(turnControl);
|
|
671
779
|
process.stdout.write("Provider accepted the human Turn; waiting for its terminal boundary.\n");
|
|
672
780
|
}).catch((error) => {
|
|
673
781
|
process.stderr.write(`Provider input failed: ${errorText(error)}\n`);
|
|
@@ -736,6 +844,9 @@ export async function sendAgentHostLaunchControl(input) {
|
|
|
736
844
|
export async function sendAgentHostTurnControl(input) {
|
|
737
845
|
return await sendAgentHostControl(input);
|
|
738
846
|
}
|
|
847
|
+
export async function sendAgentHostSteerControl(input) {
|
|
848
|
+
return await sendAgentHostControl(input);
|
|
849
|
+
}
|
|
739
850
|
export async function sendAgentHostAuthorityControl(input) {
|
|
740
851
|
return await sendAgentHostControl(input);
|
|
741
852
|
}
|
|
@@ -919,10 +1030,14 @@ function validateControl(control) {
|
|
|
919
1030
|
}
|
|
920
1031
|
if (control.type === "status")
|
|
921
1032
|
return Object.freeze({ ...control });
|
|
922
|
-
if (control.type === "submit-turn") {
|
|
1033
|
+
if (control.type === "submit-turn" || control.type === "steer-turn") {
|
|
923
1034
|
validateLaunchId(control.launchId);
|
|
924
1035
|
validateIdentity(control.nativeSessionId, "native Session id");
|
|
925
|
-
|
|
1036
|
+
if (control.type === "submit-turn" && control.turnId !== undefined) {
|
|
1037
|
+
validateIdentity(control.turnId, "Turn id");
|
|
1038
|
+
}
|
|
1039
|
+
if (control.type === "steer-turn")
|
|
1040
|
+
validateIdentity(control.nativeTurnId, "native Turn id");
|
|
926
1041
|
validateProviderAuthorityFence(control.authority);
|
|
927
1042
|
validateIdentity(control.turn.attemptId, "Provider input attempt id");
|
|
928
1043
|
if (typeof control.turn.boundedText !== "string"
|
|
@@ -1028,12 +1143,12 @@ async function delay(milliseconds) {
|
|
|
1028
1143
|
timer.unref();
|
|
1029
1144
|
});
|
|
1030
1145
|
}
|
|
1031
|
-
function hostTurnControlParams(payload, nativeSessionId, authority, attemptId,
|
|
1146
|
+
function hostTurnControlParams(payload, nativeSessionId, authority, attemptId, turnId) {
|
|
1032
1147
|
const environment = payload.environment;
|
|
1033
1148
|
return Object.freeze({
|
|
1034
1149
|
taskId: requiredEnvironment(environment.YUI_TASK_ID, "Task id"),
|
|
1035
1150
|
roleName: requiredEnvironment(environment.YUI_ROLE, "Role name"),
|
|
1036
|
-
|
|
1151
|
+
...(turnId === undefined ? {} : { turnId: requiredEnvironment(turnId, "Turn id") }),
|
|
1037
1152
|
agentId: requiredEnvironment(environment.YUI_AGENT_ID, "Agent id"),
|
|
1038
1153
|
launchId: payload.launchId,
|
|
1039
1154
|
nativeSessionId: requiredEnvironment(nativeSessionId, "native Session id"),
|
|
@@ -35,7 +35,7 @@ const STRUCTURED_CLI_CAPABILITIES = Object.freeze({
|
|
|
35
35
|
}),
|
|
36
36
|
input: Object.freeze({
|
|
37
37
|
startTurn: true,
|
|
38
|
-
steer: "
|
|
38
|
+
steer: "fenced",
|
|
39
39
|
inject: "unavailable",
|
|
40
40
|
acceptance: "exact",
|
|
41
41
|
idempotency: "unavailable"
|
|
@@ -52,6 +52,7 @@ const STRUCTURED_CLI_CAPABILITIES = Object.freeze({
|
|
|
52
52
|
preInputReadiness: "unavailable",
|
|
53
53
|
promptAcceptance: "exact",
|
|
54
54
|
turnLifecycle: "exact",
|
|
55
|
+
goalLifecycle: "partial",
|
|
55
56
|
// Transcript sources may emit an explicit activity identity separately
|
|
56
57
|
// from usage snapshots. Numeric token values never decide runtime health.
|
|
57
58
|
operations: Object.freeze(["tool", "subagent"]),
|
|
@@ -129,6 +130,7 @@ export const BUILTIN_AGENT_DRIVERS = Object.freeze([
|
|
|
129
130
|
// to manufacture tool/wait/usage observations.
|
|
130
131
|
operations: Object.freeze([]),
|
|
131
132
|
waiting: Object.freeze([]),
|
|
133
|
+
goalLifecycle: "exact",
|
|
132
134
|
usage: "unavailable"
|
|
133
135
|
})
|
|
134
136
|
}),
|
|
@@ -161,6 +163,16 @@ export function builtinAgentDriverRegistry() {
|
|
|
161
163
|
return registry;
|
|
162
164
|
}
|
|
163
165
|
function mapClaudeHook(name, payload, occurrenceId) {
|
|
166
|
+
const lifecycle = mapClaudeLifecycleHook(name, payload, occurrenceId);
|
|
167
|
+
const goal = claudeGoalObservation(payload, occurrenceId);
|
|
168
|
+
if (goal === undefined)
|
|
169
|
+
return lifecycle;
|
|
170
|
+
return Object.freeze([
|
|
171
|
+
...(Array.isArray(lifecycle) ? lifecycle : [lifecycle]),
|
|
172
|
+
goal
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
function mapClaudeLifecycleHook(name, payload, occurrenceId) {
|
|
164
176
|
switch (name) {
|
|
165
177
|
case "SessionStart":
|
|
166
178
|
return [
|
|
@@ -248,6 +260,71 @@ function mapClaudeHook(name, payload, occurrenceId) {
|
|
|
248
260
|
throw new Error(`Claude Code Driver does not support Hook event: ${name}.`);
|
|
249
261
|
}
|
|
250
262
|
}
|
|
263
|
+
function claudeGoalObservation(payload, occurrenceId) {
|
|
264
|
+
if (!Object.hasOwn(payload, "active_goal"))
|
|
265
|
+
return undefined;
|
|
266
|
+
if (payload.active_goal === null)
|
|
267
|
+
return mapped("goal.cleared");
|
|
268
|
+
const goal = objectValue(payload.active_goal);
|
|
269
|
+
if (goal === undefined)
|
|
270
|
+
return undefined;
|
|
271
|
+
const objective = textValue(goal.objective);
|
|
272
|
+
const status = normalizedGoalStatus(goal.status);
|
|
273
|
+
const updatedAt = timestampValue(goal.updated_at ?? goal.updatedAt)
|
|
274
|
+
?? timestampValue(payload.timestamp)
|
|
275
|
+
?? occurrenceTimestamp(occurrenceId);
|
|
276
|
+
if (objective === undefined || status === undefined || updatedAt === undefined)
|
|
277
|
+
return undefined;
|
|
278
|
+
const tokenBudget = positiveIntegerValue(goal.token_budget ?? goal.tokenBudget);
|
|
279
|
+
return mapped("goal.updated", {
|
|
280
|
+
goalStatus: status,
|
|
281
|
+
goalObjective: objective,
|
|
282
|
+
goalUpdatedAt: updatedAt,
|
|
283
|
+
...(optionalIdentityFrom(goal, ["turn_id", "turnId"]) === undefined
|
|
284
|
+
? {}
|
|
285
|
+
: { goalNativeTurnId: optionalIdentityFrom(goal, ["turn_id", "turnId"]) }),
|
|
286
|
+
...(tokenBudget === undefined ? {} : { goalTokenBudget: tokenBudget })
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function normalizedGoalStatus(value) {
|
|
290
|
+
if (value === "active" || value === "paused" || value === "blocked" || value === "complete") {
|
|
291
|
+
return value;
|
|
292
|
+
}
|
|
293
|
+
if (value === "usageLimited" || value === "usage_limited" || value === "usage-limited") {
|
|
294
|
+
return "usage-limited";
|
|
295
|
+
}
|
|
296
|
+
if (value === "budgetLimited" || value === "budget_limited" || value === "budget-limited") {
|
|
297
|
+
return "budget-limited";
|
|
298
|
+
}
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
function objectValue(value) {
|
|
302
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
303
|
+
? value
|
|
304
|
+
: undefined;
|
|
305
|
+
}
|
|
306
|
+
function textValue(value) {
|
|
307
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
308
|
+
}
|
|
309
|
+
function positiveIntegerValue(value) {
|
|
310
|
+
return Number.isSafeInteger(value) && value > 0 ? value : undefined;
|
|
311
|
+
}
|
|
312
|
+
function timestampValue(value) {
|
|
313
|
+
if (typeof value === "string" && Number.isFinite(Date.parse(value))) {
|
|
314
|
+
return new Date(value).toISOString();
|
|
315
|
+
}
|
|
316
|
+
if (Number.isSafeInteger(value) && value >= 0) {
|
|
317
|
+
const epoch = value;
|
|
318
|
+
return new Date(epoch < 1_000_000_000_000 ? epoch * 1_000 : epoch).toISOString();
|
|
319
|
+
}
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
function occurrenceTimestamp(value) {
|
|
323
|
+
const prefix = value?.slice(0, 24);
|
|
324
|
+
return prefix !== undefined && Number.isFinite(Date.parse(prefix))
|
|
325
|
+
? new Date(prefix).toISOString()
|
|
326
|
+
: undefined;
|
|
327
|
+
}
|
|
251
328
|
function mapCodexHook(name, payload, occurrenceId) {
|
|
252
329
|
switch (name) {
|
|
253
330
|
case "SessionStart":
|
|
@@ -402,7 +479,7 @@ function claudeFailure(payload) {
|
|
|
402
479
|
}),
|
|
403
480
|
...(lastOutput === undefined ? {} : { lastOutput }),
|
|
404
481
|
...(payload.run_terminal === true || payload.unrecoverable === true
|
|
405
|
-
? {
|
|
482
|
+
? { turnTerminal: true }
|
|
406
483
|
: {})
|
|
407
484
|
},
|
|
408
485
|
summary: [
|
|
@@ -49,6 +49,12 @@ export class CodexAppServerRuntime {
|
|
|
49
49
|
});
|
|
50
50
|
return parseThreadSnapshot(result, id, "unknown");
|
|
51
51
|
}
|
|
52
|
+
async readGoal(conversationId) {
|
|
53
|
+
const id = text(conversationId, "Codex thread id");
|
|
54
|
+
const result = await this.transport.request("thread/goal/get", { threadId: id });
|
|
55
|
+
const goal = objectMember(result, "goal");
|
|
56
|
+
return goal === null ? null : codexThreadGoal(goal, id);
|
|
57
|
+
}
|
|
52
58
|
async inspectConversation(conversationId) {
|
|
53
59
|
const id = text(conversationId, "Codex thread id");
|
|
54
60
|
try {
|
|
@@ -235,12 +241,29 @@ export function codexNotificationBoundary(input) {
|
|
|
235
241
|
?? optionalId(objectMember(input.params, "turn")?.id);
|
|
236
242
|
if (input.method === "thread/closed")
|
|
237
243
|
return { kind: "activation-ended", conversationId };
|
|
244
|
+
if (input.method === "thread/goal/updated")
|
|
245
|
+
return { kind: "goal-updated", conversationId, turnId };
|
|
246
|
+
if (input.method === "thread/goal/cleared")
|
|
247
|
+
return { kind: "goal-cleared", conversationId };
|
|
238
248
|
if (input.method === "turn/started")
|
|
239
249
|
return { kind: "turn-started", conversationId, turnId };
|
|
240
250
|
if (input.method === "turn/completed")
|
|
241
251
|
return { kind: "turn-completed", conversationId, turnId };
|
|
242
252
|
return { kind: "other", conversationId, turnId };
|
|
243
253
|
}
|
|
254
|
+
export function codexGoalNotification(input) {
|
|
255
|
+
const conversationId = optionalId(input.params.threadId);
|
|
256
|
+
if (conversationId === undefined)
|
|
257
|
+
return undefined;
|
|
258
|
+
if (input.method === "thread/goal/cleared")
|
|
259
|
+
return null;
|
|
260
|
+
if (input.method !== "thread/goal/updated")
|
|
261
|
+
return undefined;
|
|
262
|
+
const goal = objectMember(input.params, "goal");
|
|
263
|
+
if (goal === null)
|
|
264
|
+
return undefined;
|
|
265
|
+
return codexThreadGoal(goal, conversationId);
|
|
266
|
+
}
|
|
244
267
|
function parseThreadSnapshot(result, expectedThreadId, loaded) {
|
|
245
268
|
const thread = objectMember(result, "thread") ?? result;
|
|
246
269
|
const id = optionalId(thread.id) ?? expectedThreadId;
|
|
@@ -254,10 +277,12 @@ function parseThreadSnapshot(result, expectedThreadId, loaded) {
|
|
|
254
277
|
if (turnId === undefined)
|
|
255
278
|
return [];
|
|
256
279
|
const status = optionalTurnStatus(turn.status);
|
|
280
|
+
const summary = codexTurnSummary(turn);
|
|
257
281
|
const error = providerError(turn.error);
|
|
258
282
|
return [{
|
|
259
283
|
turnId,
|
|
260
284
|
...(status === undefined ? {} : { status }),
|
|
285
|
+
...(summary === undefined ? {} : { summary }),
|
|
261
286
|
...(error === undefined ? {} : { error })
|
|
262
287
|
}];
|
|
263
288
|
});
|
|
@@ -275,6 +300,62 @@ function parseThreadSnapshot(result, expectedThreadId, loaded) {
|
|
|
275
300
|
raw: result
|
|
276
301
|
};
|
|
277
302
|
}
|
|
303
|
+
/** Extract the last assistant message from one native Codex Turn. */
|
|
304
|
+
export function codexTurnSummary(turn) {
|
|
305
|
+
for (const value of [...arrayMember(turn, "items")].reverse()) {
|
|
306
|
+
const item = object(value);
|
|
307
|
+
if (item === null)
|
|
308
|
+
continue;
|
|
309
|
+
const type = item.type;
|
|
310
|
+
const message = object(item.message);
|
|
311
|
+
const agentMessage = type === "agentMessage"
|
|
312
|
+
|| type === "agent_message"
|
|
313
|
+
|| (type === "message" && (item.role === "assistant" || message?.role === "assistant"));
|
|
314
|
+
if (!agentMessage)
|
|
315
|
+
continue;
|
|
316
|
+
const text = messageText(item) ?? (message === null ? undefined : messageText(message));
|
|
317
|
+
if (text !== undefined)
|
|
318
|
+
return text;
|
|
319
|
+
}
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
/** Extract the provider-visible human input without copying tool or reasoning items. */
|
|
323
|
+
export function codexTurnInput(turn) {
|
|
324
|
+
for (const value of arrayMember(turn, "items")) {
|
|
325
|
+
const item = object(value);
|
|
326
|
+
if (item === null)
|
|
327
|
+
continue;
|
|
328
|
+
const type = item.type;
|
|
329
|
+
const message = object(item.message);
|
|
330
|
+
const userMessage = type === "userMessage"
|
|
331
|
+
|| type === "user_message"
|
|
332
|
+
|| (type === "message" && (item.role === "user" || message?.role === "user"));
|
|
333
|
+
if (!userMessage)
|
|
334
|
+
continue;
|
|
335
|
+
const text = messageText(item) ?? (message === null ? undefined : messageText(message));
|
|
336
|
+
if (text !== undefined)
|
|
337
|
+
return text;
|
|
338
|
+
}
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
function messageText(message) {
|
|
342
|
+
for (const value of [message.text, message.content]) {
|
|
343
|
+
if (typeof value === "string" && value.trim().length > 0)
|
|
344
|
+
return value.trim();
|
|
345
|
+
if (!Array.isArray(value))
|
|
346
|
+
continue;
|
|
347
|
+
const parts = value.flatMap((part) => {
|
|
348
|
+
if (typeof part === "string" && part.trim().length > 0)
|
|
349
|
+
return [part];
|
|
350
|
+
const record = object(part);
|
|
351
|
+
const text = record?.text ?? record?.content;
|
|
352
|
+
return typeof text === "string" && text.trim().length > 0 ? [text] : [];
|
|
353
|
+
});
|
|
354
|
+
if (parts.length > 0)
|
|
355
|
+
return parts.join("\n").trim();
|
|
356
|
+
}
|
|
357
|
+
return undefined;
|
|
358
|
+
}
|
|
278
359
|
function providerError(value) {
|
|
279
360
|
if (typeof value === "string" && value.trim().length > 0)
|
|
280
361
|
return value.trim();
|
|
@@ -286,6 +367,47 @@ function providerError(value) {
|
|
|
286
367
|
}
|
|
287
368
|
return JSON.stringify(record);
|
|
288
369
|
}
|
|
370
|
+
function codexThreadGoal(goal, expectedThreadId) {
|
|
371
|
+
const conversationId = text(goal.threadId, "Codex Goal thread id");
|
|
372
|
+
if (conversationId !== expectedThreadId) {
|
|
373
|
+
throw new Error("Codex Goal belongs to a different thread.");
|
|
374
|
+
}
|
|
375
|
+
const status = codexGoalStatus(goal.status);
|
|
376
|
+
const tokenBudget = goal.tokenBudget;
|
|
377
|
+
return Object.freeze({
|
|
378
|
+
conversationId,
|
|
379
|
+
status,
|
|
380
|
+
objective: text(goal.objective, "Codex Goal objective"),
|
|
381
|
+
updatedAt: codexEpochTimestamp(goal.updatedAt),
|
|
382
|
+
...(tokenBudget === null || tokenBudget === undefined
|
|
383
|
+
? {}
|
|
384
|
+
: { tokenBudget: positiveInteger(tokenBudget, "Codex Goal token budget") })
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
function codexGoalStatus(value) {
|
|
388
|
+
switch (value) {
|
|
389
|
+
case "active":
|
|
390
|
+
case "paused":
|
|
391
|
+
case "blocked":
|
|
392
|
+
case "complete":
|
|
393
|
+
return value;
|
|
394
|
+
case "usageLimited": return "usage-limited";
|
|
395
|
+
case "budgetLimited": return "budget-limited";
|
|
396
|
+
default: throw new Error("Codex Goal status is invalid.");
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
function codexEpochTimestamp(value) {
|
|
400
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
401
|
+
throw new Error("Codex Goal updatedAt is invalid.");
|
|
402
|
+
}
|
|
403
|
+
const epoch = value;
|
|
404
|
+
return new Date(epoch < 1_000_000_000_000 ? epoch * 1_000 : epoch).toISOString();
|
|
405
|
+
}
|
|
406
|
+
function positiveInteger(value, label) {
|
|
407
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
408
|
+
throw new Error(`${label} is invalid.`);
|
|
409
|
+
return value;
|
|
410
|
+
}
|
|
289
411
|
function codexContinuationState(snapshot) {
|
|
290
412
|
if (snapshot.status === "active" || snapshot.latestTurnStatus === "inProgress") {
|
|
291
413
|
return {
|
|
@@ -11,7 +11,7 @@ export function foldContinuationObservation(existing, raw) {
|
|
|
11
11
|
const base = existing ?? createProviderContinuation({
|
|
12
12
|
taskId: observation.fence.taskId,
|
|
13
13
|
roleName: observation.fence.roleName,
|
|
14
|
-
|
|
14
|
+
turnId: observation.fence.turnId,
|
|
15
15
|
identity,
|
|
16
16
|
...(observation.fence.parentContinuationId === undefined
|
|
17
17
|
? {}
|
|
@@ -107,9 +107,7 @@ export function applyContinuationObservationAtomically(store, raw, factRef) {
|
|
|
107
107
|
refs: [factRef],
|
|
108
108
|
occurredAt: observation.observedAt ?? observation.receivedAt,
|
|
109
109
|
source: "continuation-manager",
|
|
110
|
-
dedupeKey: observation.semanticKey
|
|
111
|
-
deliveryMode: "followup",
|
|
112
|
-
lane: "normal"
|
|
110
|
+
dedupeKey: observation.semanticKey
|
|
113
111
|
}
|
|
114
112
|
});
|
|
115
113
|
}
|
|
@@ -118,7 +116,7 @@ export function applyContinuationObservationAtomically(store, raw, factRef) {
|
|
|
118
116
|
}
|
|
119
117
|
function continuationIdentity(observation) {
|
|
120
118
|
const fence = observation.fence;
|
|
121
|
-
if (fence.taskId === undefined || fence.
|
|
119
|
+
if (fence.taskId === undefined || fence.turnId === undefined
|
|
122
120
|
|| fence.conversationId === undefined || fence.activationId === undefined
|
|
123
121
|
|| fence.continuationId === undefined) {
|
|
124
122
|
throw new Error("Continuation observation fence is incomplete.");
|