@zq-silk/yui 0.5.3 → 0.6.1
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/README.md +5 -5
- package/dist/agent/managedRuntimeEnvironment.js +2 -1
- package/dist/cli/agentConfigurationPicker.js +1 -1
- package/dist/cli/commandCatalog.js +251 -13
- package/dist/cli/updateOrchestrator.js +8 -0
- package/dist/cli/updatePorts.js +76 -22
- package/dist/cli.js +264 -20
- package/dist/commands/configCommands.js +83 -9
- package/dist/commands/controllerCommands.js +103 -0
- package/dist/commands/deliveryGuardPreflight.js +35 -0
- package/dist/commands/durableJobCommands.js +231 -0
- package/dist/commands/executionAuditCommands.js +193 -0
- package/dist/commands/grantCommands.js +374 -0
- package/dist/commands/projectCommands.js +119 -81
- package/dist/commands/releaseCommands.js +444 -0
- package/dist/commands/resourcesCommands.js +274 -0
- package/dist/commands/sessionCommands.js +104 -0
- package/dist/commands/taskActor.js +117 -0
- package/dist/commands/taskChangeSetCommands.js +60 -0
- package/dist/commands/taskCommands.js +610 -201
- package/dist/commands/taskCompletionGate.js +78 -1
- package/dist/commands/taskContextCommand.js +24 -6
- package/dist/commands/taskInputCommands.js +1 -1
- package/dist/commands/taskIntegrationCommands.js +136 -33
- package/dist/commands/taskIntegrationQueueCommands.js +228 -0
- package/dist/commands/taskNextActionCommand.js +85 -0
- package/dist/commands/taskOverlapCommands.js +120 -0
- package/dist/commands/taskOverviewCommand.js +36 -8
- package/dist/commands/telemetryCommands.js +330 -0
- package/dist/commands/workflowCommands.js +415 -0
- package/dist/config/yuiConfig.js +60 -0
- package/dist/controller/clientRuntime.js +42 -1
- package/dist/controller/controller.js +413 -61
- package/dist/controller/controllerMain.js +25 -2
- package/dist/controller/domainIdentity.js +16 -8
- package/dist/controller/ephemeralResourceReaper.js +2 -1
- package/dist/controller/fileSchedulerStoreAdapter.js +423 -31
- package/dist/controller/handoverCandidate.js +168 -0
- package/dist/controller/jobClient.js +102 -0
- package/dist/controller/jobControl.js +613 -0
- package/dist/controller/jobSupervisor.js +498 -0
- package/dist/controller/providerHookRunFence.js +34 -5
- package/dist/controller/resourceCleanupLinux.js +18 -9
- package/dist/controller/resourceInventoryLinux.js +90 -39
- package/dist/controller/resourceInventoryRpc.js +85 -0
- package/dist/controller/resourceInventoryWorker.js +50 -0
- package/dist/controller/runtime.js +238 -22
- package/dist/controller/runtimeEventInbox.js +234 -57
- package/dist/controller/runtimeEventProcessor.js +549 -42
- package/dist/controller/sessionOwnerReconciliation.js +321 -0
- package/dist/core/boundedRpc.js +475 -0
- package/dist/core/controllerServer.js +416 -27
- package/dist/core/controllerTelemetry.js +167 -0
- package/dist/doctor/doctor.js +113 -16
- package/dist/domain/validation.js +9 -0
- package/dist/execution/executionGroup.js +40 -3
- package/dist/executor/agentExecutor.js +6 -3
- package/dist/executor/effectiveLaunch.js +52 -0
- package/dist/executor/executorRegistry.js +50 -0
- package/dist/executor/fileRoleLaunchPlanner.js +61 -6
- package/dist/grant/capabilityGrant.js +282 -0
- package/dist/integration/changeSet.js +16 -3
- package/dist/integration/changeSetManifest.js +46 -0
- package/dist/integration/gitIntegrationService.js +528 -147
- package/dist/integration/integrationAttempt.js +54 -5
- package/dist/integration/integrationQueueEntry.js +221 -0
- package/dist/integration/integrationQueueService.js +955 -0
- package/dist/integration/manifestTags.js +99 -0
- package/dist/integration/overlapDiagnostics.js +211 -0
- package/dist/job/durableJob.js +449 -0
- package/dist/job/jobRunner.js +350 -0
- package/dist/lifecycle/exactRunTerminalization.js +24 -2
- package/dist/lifecycle/providerErrorClass.js +126 -0
- package/dist/message/message.js +16 -3
- package/dist/observability/executionAudit.js +545 -0
- package/dist/observability/faultClassification.js +160 -0
- package/dist/observability/runtimeIdentity.js +367 -0
- package/dist/release/fakeReleasePorts.js +55 -0
- package/dist/release/releaseHandover.js +475 -0
- package/dist/release/releaseIdempotencyStore.js +165 -0
- package/dist/release/releaseWorkflow.js +459 -0
- package/dist/release/releaseWorkflowEngine.js +688 -0
- package/dist/release/releaseWorkflowPorts.js +1720 -0
- package/dist/release/runtimeRelease.js +495 -0
- package/dist/release/workflowFileLock.js +218 -0
- package/dist/repository/gitWorkspace.js +177 -1
- package/dist/repository/projectMaintenanceLock.js +315 -0
- package/dist/repository/taskWorkspaceCoordinator.js +87 -17
- package/dist/repository/taskWorkspacePreparer.js +1091 -517
- package/dist/resources/autoResourceGc.js +116 -0
- package/dist/resources/liveReferences.js +574 -0
- package/dist/resources/resourceDiscovery.js +477 -0
- package/dist/resources/resourceGc.js +645 -0
- package/dist/resources/resourceRegistrar.js +256 -0
- package/dist/resources/resourceRegistry.js +150 -0
- package/dist/resources/resourceRegistryStore.js +41 -0
- package/dist/resources/resourceTypes.js +42 -0
- package/dist/resources/sqliteResourceRegistry.js +111 -0
- package/dist/review/reviewConfig.js +10 -0
- package/dist/review/reviewFinding.js +240 -0
- package/dist/review/reviewFindingLedger.js +545 -0
- package/dist/review/reviewOutcomeClassifier.js +61 -0
- package/dist/review/reviewRound.js +56 -4
- package/dist/run/agentRun.js +80 -4
- package/dist/run/providerRetry.js +84 -0
- package/dist/run/providerRetryConfig.js +63 -0
- package/dist/run/yieldReceipt.js +65 -0
- package/dist/runtime/exactControlPlane.js +79 -2
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/sessionOwnerIdentity.js +269 -0
- package/dist/runtime/sessionOwnerRegistry.js +132 -0
- package/dist/runtime/sessionReconciliation.js +93 -0
- package/dist/runtime/sessionTerminationGuard.js +211 -0
- package/dist/runtime/taskRuntimeIsolation.js +13 -0
- package/dist/runtime/tmuxAdapters.js +34 -1
- package/dist/scheduler/actionability.js +155 -0
- package/dist/scheduler/activeRoleRunDelivery.js +14 -5
- package/dist/scheduler/activeTaskProgress.js +60 -0
- package/dist/scheduler/leaderWakeupProcessor.js +22 -11
- package/dist/scheduler/roleRunStall.js +135 -29
- package/dist/scheduler/taskExecutionProjection.js +11 -0
- package/dist/setup/setupCommand.js +27 -4
- package/dist/storage/compatibleTaskStore.js +112 -5
- package/dist/storage/migration/productionRegistry.js +769 -1
- package/dist/storage/persistenceWorker.js +194 -0
- package/dist/storage/sqliteSchema.js +705 -0
- package/dist/storage/sqliteStore.js +1695 -0
- package/dist/storage/storageVersions.js +9 -2
- package/dist/storage/storeRpc.js +298 -0
- package/dist/storage/taskStore.js +982 -21
- package/dist/storage/upgrade/homeClassification.js +157 -12
- package/dist/storage/upgrade/migrationReceipt.js +67 -0
- package/dist/storage/upgrade/pseudoLayoutRepair.js +241 -0
- package/dist/storage/upgrade/recordVersions.js +10 -1
- package/dist/storage/upgrade/sqliteMigrationTarget.js +351 -0
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +290 -0
- package/dist/storage/upgrade/sqliteStateMigration.js +713 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +510 -18
- package/dist/task/deliveryGuard.js +226 -0
- package/dist/task/nextAction.js +343 -0
- package/dist/task/repairWave.js +137 -0
- package/dist/task/taskRecordReference.js +6 -1
- package/dist/telemetry/sqliteTelemetryStore.js +387 -0
- package/dist/telemetry/telemetryCompaction.js +251 -0
- package/dist/telemetry/telemetryConfig.js +64 -0
- package/dist/telemetry/telemetryRouter.js +32 -0
- package/dist/telemetry/telemetryStore.js +19 -0
- package/dist/telemetry/telemetryWiring.js +33 -0
- package/dist/tmux/tmuxManager.js +20 -1
- package/dist/tmux/tmuxSocketEndpoint.js +20 -0
- package/dist/verification/gateArtifact.js +216 -0
- package/dist/verification/gateArtifactStore.js +87 -0
- package/dist/verification/verificationGateService.js +414 -0
- package/dist/verification/verificationPlan.js +308 -0
- package/dist/workspace/gitChangeSetCapture.js +12 -2
- package/dist/workspace/workItemChangeSetManager.js +60 -3
- package/package.json +2 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
const DEFAULT_MAX_RUNTIME_EVENTS_PER_DRAIN = 64;
|
|
2
|
+
/** Folds immutable Hook facts in one bounded transaction before acknowledging them. */
|
|
2
3
|
export class FileRuntimeEventProcessor {
|
|
3
4
|
inbox;
|
|
4
5
|
observer;
|
|
@@ -17,36 +18,513 @@ export class FileRuntimeEventProcessor {
|
|
|
17
18
|
events = this.inbox.list();
|
|
18
19
|
}
|
|
19
20
|
catch (error) {
|
|
20
|
-
return
|
|
21
|
+
return emptyDrainFailure(error);
|
|
22
|
+
}
|
|
23
|
+
const coalesced = coalesceRuntimeProgress(events);
|
|
24
|
+
const maximum = positiveInteger(this.options.maxEventsPerDrain, DEFAULT_MAX_RUNTIME_EVENTS_PER_DRAIN);
|
|
25
|
+
const selected = selectDrainBatch(coalesced, maximum);
|
|
26
|
+
let stateTransactions = 0;
|
|
27
|
+
if (selected.length > 0 && this.observer.withRuntimeEventTransaction !== undefined) {
|
|
28
|
+
try {
|
|
29
|
+
stateTransactions += 1;
|
|
30
|
+
const folded = this.observer.withRuntimeEventTransaction(() => (selected.map((candidate) => this.foldOne(candidate, now))));
|
|
31
|
+
for (const result of folded) {
|
|
32
|
+
this.finalizeOne(result, acknowledgedEventIds, deferred, failed);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// A failed aggregate transaction commits nothing. Retry each candidate
|
|
37
|
+
// independently so one bad event cannot strand unrelated facts.
|
|
38
|
+
for (const candidate of selected) {
|
|
39
|
+
try {
|
|
40
|
+
stateTransactions += 1;
|
|
41
|
+
const folded = this.observer.withRuntimeEventTransaction(() => (this.foldOne(candidate, now)));
|
|
42
|
+
this.finalizeOne(folded, acknowledgedEventIds, deferred, failed);
|
|
43
|
+
}
|
|
44
|
+
catch (candidateError) {
|
|
45
|
+
failed.push({ eventId: candidate.event.id, error: candidateError });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
for (const candidate of selected) {
|
|
52
|
+
try {
|
|
53
|
+
const folded = this.foldOne(candidate, now);
|
|
54
|
+
this.finalizeOne(folded, acknowledgedEventIds, deferred, failed);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
failed.push({ eventId: candidate.event.id, error });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const progressEventsSelected = selected.filter(({ event }) => (event.type === "native-turn-progress")).length;
|
|
62
|
+
const representedEventCount = selected.reduce((count, candidate) => (count + candidate.representedEventIds.length), 0);
|
|
63
|
+
const acknowledged = new Set(acknowledgedEventIds);
|
|
64
|
+
const remaining = events.filter(({ id }) => !acknowledged.has(id));
|
|
65
|
+
const remainingProgressEventCount = remaining.filter(({ type }) => (type === "native-turn-progress")).length;
|
|
66
|
+
return {
|
|
67
|
+
acknowledgedEventIds,
|
|
68
|
+
deferred,
|
|
69
|
+
failed,
|
|
70
|
+
remainingEventCount: Math.max(0, events.length - acknowledgedEventIds.length),
|
|
71
|
+
metrics: {
|
|
72
|
+
listedEventCount: events.length,
|
|
73
|
+
selectedEventCount: selected.length,
|
|
74
|
+
semanticEventsSelected: selected.length - progressEventsSelected,
|
|
75
|
+
progressEventsSelected,
|
|
76
|
+
progressEventsCoalesced: representedEventCount - selected.length,
|
|
77
|
+
stateTransactions,
|
|
78
|
+
remainingSemanticEventCount: remaining.length - remainingProgressEventCount,
|
|
79
|
+
remainingProgressEventCount
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
foldOne(candidate, now) {
|
|
84
|
+
const event = candidate.event;
|
|
85
|
+
let outcome = "applied";
|
|
86
|
+
if (event.type === "native-turn-completed") {
|
|
87
|
+
outcome = this.applyCodex(event, now);
|
|
88
|
+
}
|
|
89
|
+
else if (event.type === "claude-stop-failure") {
|
|
90
|
+
this.applyClaudeStopFailure(event, now);
|
|
91
|
+
}
|
|
92
|
+
else if (event.type === "native-session-lifecycle") {
|
|
93
|
+
outcome = this.applySessionLifecycle(event, now);
|
|
94
|
+
}
|
|
95
|
+
else if (event.type === "native-turn-progress") {
|
|
96
|
+
outcome = this.applyProviderTurnProgress(event, now);
|
|
97
|
+
}
|
|
98
|
+
else if (event.type === "durable-job-terminal") {
|
|
99
|
+
// f7/rr5: The supervisor already transitioned the job and
|
|
100
|
+
// enqueued the Leader wakeup. This event is the durable terminal
|
|
101
|
+
// channel: acknowledge it so the Controller's event pipeline
|
|
102
|
+
// converges without re-processing. No state change to apply.
|
|
103
|
+
this.applyDurableJobTerminal(event);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
outcome = this.applyPromptAccepted(event, now);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
candidate,
|
|
110
|
+
outcome,
|
|
111
|
+
notifyTaskRuntime: outcome === "applied" && (event.type === "native-session-lifecycle"
|
|
112
|
+
|| event.type === "native-prompt-accepted"
|
|
113
|
+
|| (event.type === "native-turn-completed" && event.scope === "task"))
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
finalizeOne(folded, acknowledged, deferred, failed) {
|
|
117
|
+
const { candidate, outcome } = folded;
|
|
118
|
+
try {
|
|
119
|
+
if (outcome === "deferred") {
|
|
120
|
+
deferred.push(candidate.event);
|
|
121
|
+
this.acknowledge(candidate.representedEventIds.filter((id) => id !== candidate.event.id), acknowledged);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (folded.notifyTaskRuntime) {
|
|
125
|
+
const event = candidate.event;
|
|
126
|
+
if (event.scope === "task" && event.taskId !== undefined && event.type !== "durable-job-terminal") {
|
|
127
|
+
this.options.onTaskRuntimeApplied?.({
|
|
128
|
+
taskId: event.taskId,
|
|
129
|
+
roleName: event.roleName,
|
|
130
|
+
agentId: event.agentId,
|
|
131
|
+
adapterId: event.adapterId,
|
|
132
|
+
...(event.launchId === undefined ? {} : { launchId: event.launchId }),
|
|
133
|
+
nativeSessionId: event.nativeSessionId,
|
|
134
|
+
...(event.runId === undefined ? {} : { runId: event.runId })
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
this.acknowledge(candidate.representedEventIds, acknowledged);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
failed.push({ eventId: candidate.event.id, error });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
applySessionLifecycle(event, now) {
|
|
145
|
+
const task = this.observer.getTask(event.taskId);
|
|
146
|
+
if (task === null || task.status !== "active")
|
|
147
|
+
return "obsolete";
|
|
148
|
+
if (this.observer.observeProviderSessionLifecycle === undefined)
|
|
149
|
+
return "obsolete";
|
|
150
|
+
const outcome = this.observer.observeProviderSessionLifecycle({
|
|
151
|
+
eventId: event.id,
|
|
152
|
+
taskId: event.taskId,
|
|
153
|
+
roleName: event.roleName,
|
|
154
|
+
agentId: event.agentId,
|
|
155
|
+
adapterId: event.adapterId,
|
|
156
|
+
launchId: event.launchId,
|
|
157
|
+
nativeSessionId: event.nativeSessionId,
|
|
158
|
+
...(event.runId === undefined ? {} : { runId: event.runId }),
|
|
159
|
+
...(event.sessionSource === undefined ? {} : { sessionSource: event.sessionSource })
|
|
160
|
+
}, now);
|
|
161
|
+
return outcome === "deferred" ? "deferred" : outcome;
|
|
162
|
+
}
|
|
163
|
+
applyPromptAccepted(event, now) {
|
|
164
|
+
const task = this.observer.getTask(event.taskId);
|
|
165
|
+
if (task === null || task.status !== "active")
|
|
166
|
+
return "obsolete";
|
|
167
|
+
if (this.observer.observeProviderPromptAccepted === undefined)
|
|
168
|
+
return "obsolete";
|
|
169
|
+
const outcome = this.observer.observeProviderPromptAccepted({
|
|
170
|
+
eventId: event.id,
|
|
171
|
+
taskId: event.taskId,
|
|
172
|
+
roleName: event.roleName,
|
|
173
|
+
agentId: event.agentId,
|
|
174
|
+
adapterId: event.adapterId,
|
|
175
|
+
launchId: event.launchId,
|
|
176
|
+
nativeSessionId: event.nativeSessionId,
|
|
177
|
+
runId: event.runId,
|
|
178
|
+
receiptId: event.receiptId
|
|
179
|
+
}, now);
|
|
180
|
+
return outcome === "deferred" ? "deferred" : outcome;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* f7/rr5: Acknowledge a durable-job-terminal event. The supervisor
|
|
184
|
+
* already committed the terminal transition and the Leader wakeup;
|
|
185
|
+
* this event only needs to be acknowledged so it leaves the inbox.
|
|
186
|
+
*/
|
|
187
|
+
applyDurableJobTerminal(event) {
|
|
188
|
+
// The event is a notification of a committed state change. No observer
|
|
189
|
+
// call is needed — the Leader wakeup was enqueued in the same
|
|
190
|
+
// transaction as the terminal transition.
|
|
191
|
+
void event;
|
|
192
|
+
}
|
|
193
|
+
applyProviderTurnProgress(event, now) {
|
|
194
|
+
const task = this.observer.getTask(event.taskId);
|
|
195
|
+
if (task === null || task.status !== "active")
|
|
196
|
+
return "obsolete";
|
|
197
|
+
if (this.observer.observeProviderTurnProgress === undefined)
|
|
198
|
+
return "obsolete";
|
|
199
|
+
const outcome = this.observer.observeProviderTurnProgress({
|
|
200
|
+
eventId: event.id,
|
|
201
|
+
receivedAt: event.receivedAt,
|
|
202
|
+
taskId: event.taskId,
|
|
203
|
+
roleName: event.roleName,
|
|
204
|
+
agentId: event.agentId,
|
|
205
|
+
adapterId: event.adapterId,
|
|
206
|
+
launchId: event.launchId,
|
|
207
|
+
nativeSessionId: event.nativeSessionId,
|
|
208
|
+
runId: event.runId,
|
|
209
|
+
progressId: event.progressId,
|
|
210
|
+
...(event.sequence === undefined ? {} : { sequence: event.sequence })
|
|
211
|
+
}, now);
|
|
212
|
+
return outcome === "deferred" ? "deferred" : "applied";
|
|
213
|
+
}
|
|
214
|
+
applyCodex(event, now) {
|
|
215
|
+
if (event.scope === "task") {
|
|
216
|
+
const task = this.observer.getTask(event.taskId);
|
|
217
|
+
const input = {
|
|
218
|
+
eventId: event.id,
|
|
219
|
+
taskId: event.taskId,
|
|
220
|
+
roleName: event.roleName,
|
|
221
|
+
agentId: event.agentId,
|
|
222
|
+
adapterId: event.adapterId,
|
|
223
|
+
...(event.launchId === undefined ? {} : { launchId: event.launchId }),
|
|
224
|
+
nativeSessionId: event.nativeSessionId,
|
|
225
|
+
turnId: event.turnId,
|
|
226
|
+
...(event.runId === undefined ? {} : { runId: event.runId }),
|
|
227
|
+
summary: event.summary
|
|
228
|
+
};
|
|
229
|
+
if (task === null)
|
|
230
|
+
return "obsolete";
|
|
231
|
+
if (task.status !== "active") {
|
|
232
|
+
this.recordObsolete(event, task.status === "archived" ? "task-archived" : "task-retired", now);
|
|
233
|
+
return "obsolete";
|
|
234
|
+
}
|
|
235
|
+
const disposition = this.observer.classifyRuntimeTurnCompleted?.(input) ?? "apply";
|
|
236
|
+
if (disposition === "deferred")
|
|
237
|
+
return disposition;
|
|
238
|
+
if (disposition === "obsolete") {
|
|
239
|
+
this.recordObsolete(event, "identity-mismatch-or-terminal", now);
|
|
240
|
+
return disposition;
|
|
241
|
+
}
|
|
242
|
+
const observed = this.observer.observeRuntimeTurnCompleted(input, now);
|
|
243
|
+
if (isObsoleteRuntimeTurnObservation(observed)) {
|
|
244
|
+
this.recordObsolete(event, "runtime-cleanup-or-stopped-session", now);
|
|
245
|
+
return "obsolete";
|
|
246
|
+
}
|
|
247
|
+
return "applied";
|
|
248
|
+
}
|
|
249
|
+
const input = {
|
|
250
|
+
eventId: event.id,
|
|
251
|
+
roleName: event.roleName,
|
|
252
|
+
agentId: event.agentId,
|
|
253
|
+
adapterId: event.adapterId,
|
|
254
|
+
...(event.launchId === undefined ? {} : { launchId: event.launchId }),
|
|
255
|
+
nativeSessionId: event.nativeSessionId,
|
|
256
|
+
turnId: event.turnId,
|
|
257
|
+
...(event.title === undefined ? {} : { title: event.title }),
|
|
258
|
+
summary: event.summary
|
|
259
|
+
};
|
|
260
|
+
if (this.observer.classifyGlobalRuntimeTurnCompleted?.(input) !== "obsolete") {
|
|
261
|
+
this.observer.observeGlobalRuntimeTurnCompleted(input, now);
|
|
262
|
+
return "applied";
|
|
263
|
+
}
|
|
264
|
+
return "obsolete";
|
|
265
|
+
}
|
|
266
|
+
applyClaudeStopFailure(event, now) {
|
|
267
|
+
const task = this.observer.getTask(event.taskId);
|
|
268
|
+
if (task === null)
|
|
269
|
+
return;
|
|
270
|
+
const input = {
|
|
271
|
+
eventId: event.id,
|
|
272
|
+
type: event.type,
|
|
273
|
+
taskId: event.taskId,
|
|
274
|
+
roleName: event.roleName,
|
|
275
|
+
agentId: event.agentId,
|
|
276
|
+
adapterId: event.adapterId,
|
|
277
|
+
launchId: event.launchId,
|
|
278
|
+
nativeSessionId: event.nativeSessionId,
|
|
279
|
+
runId: event.runId,
|
|
280
|
+
error: event.error,
|
|
281
|
+
...(event.errorDetails === undefined ? {} : { errorDetails: event.errorDetails }),
|
|
282
|
+
...(event.lastAssistantMessage === undefined
|
|
283
|
+
? {}
|
|
284
|
+
: { lastAssistantMessage: event.lastAssistantMessage })
|
|
285
|
+
};
|
|
286
|
+
if (task.status !== "active"
|
|
287
|
+
|| this.observer.classifyClaudeStopFailureEvent?.(input) === "obsolete") {
|
|
288
|
+
this.recordObsolete(event, task.status === "archived"
|
|
289
|
+
? "task-archived"
|
|
290
|
+
: task.status !== "active"
|
|
291
|
+
? "task-retired"
|
|
292
|
+
: "identity-mismatch-or-terminal", now);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (this.observer.observeClaudeStopFailureEvent === undefined) {
|
|
296
|
+
throw new Error("Claude StopFailure observer is unavailable.");
|
|
297
|
+
}
|
|
298
|
+
this.observer.observeClaudeStopFailureEvent(input, now);
|
|
299
|
+
}
|
|
300
|
+
recordObsolete(event, reason, now) {
|
|
301
|
+
if (event.scope !== "task" || event.taskId === undefined)
|
|
302
|
+
return;
|
|
303
|
+
// durable-job-terminal events have no provider identity; they are never
|
|
304
|
+
// recorded as obsolete.
|
|
305
|
+
if (!("roleName" in event) || !("nativeSessionId" in event))
|
|
306
|
+
return;
|
|
307
|
+
this.observer.observeObsoleteRuntimeEvent?.({
|
|
308
|
+
eventId: event.id,
|
|
309
|
+
eventType: event.type,
|
|
310
|
+
taskId: event.taskId,
|
|
311
|
+
roleName: event.roleName,
|
|
312
|
+
agentId: event.agentId,
|
|
313
|
+
...(event.runId === undefined ? {} : { runId: event.runId }),
|
|
314
|
+
...(event.launchId === undefined ? {} : { launchId: event.launchId }),
|
|
315
|
+
nativeSessionId: event.nativeSessionId,
|
|
316
|
+
reason
|
|
317
|
+
}, now);
|
|
318
|
+
}
|
|
319
|
+
acknowledge(ids, acknowledged) {
|
|
320
|
+
if (this.inbox.acknowledgeMany !== undefined) {
|
|
321
|
+
acknowledged.push(...this.inbox.acknowledgeMany(ids));
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
for (const id of ids) {
|
|
325
|
+
if (this.inbox.acknowledge(id))
|
|
326
|
+
acknowledged.push(id);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
export function coalesceRuntimeProgress(events, instrumentation = {}) {
|
|
331
|
+
const result = [];
|
|
332
|
+
let segment = [];
|
|
333
|
+
const flush = () => {
|
|
334
|
+
if (segment.length === 0)
|
|
335
|
+
return;
|
|
336
|
+
const frontiers = new Map();
|
|
337
|
+
for (let index = 0; index < segment.length; index += 1) {
|
|
338
|
+
instrumentation.onProgressVisit?.();
|
|
339
|
+
const event = segment[index];
|
|
340
|
+
const key = progressStreamKey(event);
|
|
341
|
+
const frontier = frontiers.get(key);
|
|
342
|
+
if (frontier === undefined || event.receivedAt > frontier.latestReceivedAt) {
|
|
343
|
+
frontiers.set(key, {
|
|
344
|
+
latestReceivedAt: event.receivedAt,
|
|
345
|
+
...(event.sequence === undefined
|
|
346
|
+
? {}
|
|
347
|
+
: {
|
|
348
|
+
greatestSequence: event.sequence,
|
|
349
|
+
firstGreatestSequenceIndex: index
|
|
350
|
+
}),
|
|
351
|
+
firstLatestIndex: index
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
else if (event.receivedAt === frontier.latestReceivedAt
|
|
355
|
+
&& event.sequence !== undefined
|
|
356
|
+
&& (frontier.greatestSequence === undefined
|
|
357
|
+
|| event.sequence > frontier.greatestSequence)) {
|
|
358
|
+
frontiers.set(key, {
|
|
359
|
+
...frontier,
|
|
360
|
+
greatestSequence: event.sequence,
|
|
361
|
+
firstGreatestSequenceIndex: index
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const representedByIndex = new Map();
|
|
366
|
+
for (let index = 0; index < segment.length; index += 1) {
|
|
367
|
+
instrumentation.onProgressVisit?.();
|
|
368
|
+
const event = segment[index];
|
|
369
|
+
const frontier = frontiers.get(progressStreamKey(event));
|
|
370
|
+
const strictlyDominated = event.receivedAt < frontier.latestReceivedAt
|
|
371
|
+
|| (event.receivedAt === frontier.latestReceivedAt
|
|
372
|
+
&& event.sequence !== undefined
|
|
373
|
+
&& frontier.greatestSequence !== undefined
|
|
374
|
+
&& event.sequence < frontier.greatestSequence);
|
|
375
|
+
const representativeIndex = strictlyDominated
|
|
376
|
+
? frontier.firstGreatestSequenceIndex ?? frontier.firstLatestIndex
|
|
377
|
+
: index;
|
|
378
|
+
const represented = representedByIndex.get(representativeIndex) ?? [];
|
|
379
|
+
represented.push(event.id);
|
|
380
|
+
representedByIndex.set(representativeIndex, represented);
|
|
381
|
+
}
|
|
382
|
+
for (let index = 0; index < segment.length; index += 1) {
|
|
383
|
+
const representedEventIds = representedByIndex.get(index);
|
|
384
|
+
if (representedEventIds === undefined)
|
|
385
|
+
continue;
|
|
386
|
+
result.push({ event: segment[index], representedEventIds });
|
|
387
|
+
}
|
|
388
|
+
segment = [];
|
|
389
|
+
};
|
|
390
|
+
for (const event of events) {
|
|
391
|
+
if (event.type !== "native-turn-progress") {
|
|
392
|
+
flush();
|
|
393
|
+
result.push({ event, representedEventIds: [event.id] });
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
segment.push(event);
|
|
397
|
+
}
|
|
398
|
+
flush();
|
|
399
|
+
return result;
|
|
400
|
+
}
|
|
401
|
+
function selectDrainBatch(events, maximum) {
|
|
402
|
+
// A batch is always an arrival-order prefix. Admission and drain coalescing
|
|
403
|
+
// keep a progress flood bounded without allowing a later semantic fact to
|
|
404
|
+
// overtake an earlier progress fence from another Run.
|
|
405
|
+
return events.slice(0, maximum);
|
|
406
|
+
}
|
|
407
|
+
function progressStreamKey(event) {
|
|
408
|
+
return JSON.stringify([
|
|
409
|
+
event.taskId,
|
|
410
|
+
event.roleName,
|
|
411
|
+
event.agentId,
|
|
412
|
+
event.adapterId,
|
|
413
|
+
event.launchId,
|
|
414
|
+
event.nativeSessionId,
|
|
415
|
+
event.runId
|
|
416
|
+
]);
|
|
417
|
+
}
|
|
418
|
+
function emptyDrainFailure(error) {
|
|
419
|
+
return {
|
|
420
|
+
acknowledgedEventIds: [],
|
|
421
|
+
deferred: [],
|
|
422
|
+
failed: [{ error }],
|
|
423
|
+
remainingEventCount: 0,
|
|
424
|
+
metrics: {
|
|
425
|
+
listedEventCount: 0,
|
|
426
|
+
selectedEventCount: 0,
|
|
427
|
+
semanticEventsSelected: 0,
|
|
428
|
+
progressEventsSelected: 0,
|
|
429
|
+
progressEventsCoalesced: 0,
|
|
430
|
+
stateTransactions: 0,
|
|
431
|
+
remainingSemanticEventCount: 0,
|
|
432
|
+
remainingProgressEventCount: 0
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function positiveInteger(value, fallback) {
|
|
437
|
+
const resolved = value ?? fallback;
|
|
438
|
+
if (!Number.isSafeInteger(resolved) || resolved <= 0) {
|
|
439
|
+
throw new TypeError("Runtime event drain limit must be a positive integer.");
|
|
440
|
+
}
|
|
441
|
+
return resolved;
|
|
442
|
+
}
|
|
443
|
+
function isObsoleteRuntimeTurnObservation(value) {
|
|
444
|
+
return typeof value === "object"
|
|
445
|
+
&& value !== null
|
|
446
|
+
&& value.disposition === "obsolete";
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Build an {@link AsyncRuntimeTurnEventObserver} that forwards every fold to the
|
|
450
|
+
* worker-hosted adapter via {@link AsyncObserverInvoker}. The adapter's fold
|
|
451
|
+
* logic (read-then-write transactions) runs in the worker, off the main event
|
|
452
|
+
* loop; the main thread only awaits the result.
|
|
453
|
+
*/
|
|
454
|
+
export function createAsyncRuntimeObserver(invoke) {
|
|
455
|
+
const call = (method, args) => invoke(method, args);
|
|
456
|
+
const withNow = (method, input, now) => now === undefined ? call(method, [input]) : call(method, [input, now]);
|
|
457
|
+
return {
|
|
458
|
+
getTask: (taskId) => call("getTask", [taskId]),
|
|
459
|
+
observeRuntimeTurnCompleted: (input, now) => withNow("observeRuntimeTurnCompleted", input, now),
|
|
460
|
+
observeGlobalRuntimeTurnCompleted: (input, now) => withNow("observeGlobalRuntimeTurnCompleted", input, now),
|
|
461
|
+
observeClaudeStopFailureEvent: (input, now) => withNow("observeClaudeStopFailureEvent", input, now),
|
|
462
|
+
observeObsoleteRuntimeEvent: (input, now) => withNow("observeObsoleteRuntimeEvent", input, now),
|
|
463
|
+
observeProviderSessionLifecycle: (input, now) => withNow("observeProviderSessionLifecycle", input, now),
|
|
464
|
+
observeProviderPromptAccepted: (input, now) => withNow("observeProviderPromptAccepted", input, now),
|
|
465
|
+
observeProviderTurnProgress: (input, now) => withNow("observeProviderTurnProgress", input, now)
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* The async counterpart to {@link FileRuntimeEventProcessor}: folds immutable
|
|
470
|
+
* Hook facts one at a time (awaiting the worker-hosted observer) before
|
|
471
|
+
* acknowledging them. The inbox itself is file-based and stays on the main
|
|
472
|
+
* thread; only the db-touching folds are proxied to the worker.
|
|
473
|
+
*/
|
|
474
|
+
export class AsyncRuntimeEventProcessor {
|
|
475
|
+
inbox;
|
|
476
|
+
observer;
|
|
477
|
+
options;
|
|
478
|
+
constructor(inbox, observer, options = {}) {
|
|
479
|
+
this.inbox = inbox;
|
|
480
|
+
this.observer = observer;
|
|
481
|
+
this.options = options;
|
|
482
|
+
}
|
|
483
|
+
async drainAsync(now) {
|
|
484
|
+
const acknowledgedEventIds = [];
|
|
485
|
+
const deferred = [];
|
|
486
|
+
const failed = [];
|
|
487
|
+
let events;
|
|
488
|
+
try {
|
|
489
|
+
events = this.inbox.list();
|
|
490
|
+
}
|
|
491
|
+
catch (error) {
|
|
492
|
+
return emptyDrainFailure(error);
|
|
21
493
|
}
|
|
22
494
|
for (const event of events) {
|
|
23
495
|
try {
|
|
24
496
|
if (event.type === "native-turn-completed") {
|
|
25
|
-
const outcome = this.applyCodex(event, now);
|
|
497
|
+
const outcome = await this.applyCodex(event, now);
|
|
26
498
|
if (outcome === "deferred") {
|
|
27
499
|
deferred.push(event);
|
|
28
500
|
continue;
|
|
29
501
|
}
|
|
30
502
|
}
|
|
31
503
|
else if (event.type === "claude-stop-failure") {
|
|
32
|
-
this.applyClaudeStopFailure(event, now);
|
|
504
|
+
await this.applyClaudeStopFailure(event, now);
|
|
33
505
|
}
|
|
34
506
|
else if (event.type === "native-session-lifecycle") {
|
|
35
|
-
const outcome = this.applySessionLifecycle(event, now);
|
|
507
|
+
const outcome = await this.applySessionLifecycle(event, now);
|
|
36
508
|
if (outcome === "deferred") {
|
|
37
509
|
deferred.push(event);
|
|
38
510
|
continue;
|
|
39
511
|
}
|
|
40
512
|
}
|
|
41
513
|
else if (event.type === "native-turn-progress") {
|
|
42
|
-
const outcome = this.applyProviderTurnProgress(event, now);
|
|
514
|
+
const outcome = await this.applyProviderTurnProgress(event, now);
|
|
43
515
|
if (outcome === "deferred") {
|
|
44
516
|
deferred.push(event);
|
|
45
517
|
continue;
|
|
46
518
|
}
|
|
47
519
|
}
|
|
520
|
+
else if (event.type === "durable-job-terminal") {
|
|
521
|
+
// f7/rr5: The supervisor already transitioned the job and
|
|
522
|
+
// enqueued the Leader wakeup. This event is the durable terminal
|
|
523
|
+
// channel: acknowledge it so the pipeline converges without
|
|
524
|
+
// re-processing. No state change to apply.
|
|
525
|
+
}
|
|
48
526
|
else {
|
|
49
|
-
const outcome = this.applyPromptAccepted(event, now);
|
|
527
|
+
const outcome = await this.applyPromptAccepted(event, now);
|
|
50
528
|
if (outcome === "deferred") {
|
|
51
529
|
deferred.push(event);
|
|
52
530
|
continue;
|
|
@@ -58,15 +536,34 @@ export class FileRuntimeEventProcessor {
|
|
|
58
536
|
failed.push({ eventId: event.id, error });
|
|
59
537
|
}
|
|
60
538
|
}
|
|
61
|
-
|
|
539
|
+
const acknowledged = new Set(acknowledgedEventIds);
|
|
540
|
+
const remaining = events.filter(({ id }) => !acknowledged.has(id));
|
|
541
|
+
const progressEventsSelected = events.filter(({ type }) => (type === "native-turn-progress")).length;
|
|
542
|
+
const remainingProgressEventCount = remaining.filter(({ type }) => (type === "native-turn-progress")).length;
|
|
543
|
+
return {
|
|
544
|
+
acknowledgedEventIds,
|
|
545
|
+
deferred,
|
|
546
|
+
failed,
|
|
547
|
+
remainingEventCount: remaining.length,
|
|
548
|
+
metrics: {
|
|
549
|
+
listedEventCount: events.length,
|
|
550
|
+
selectedEventCount: events.length,
|
|
551
|
+
semanticEventsSelected: events.length - progressEventsSelected,
|
|
552
|
+
progressEventsSelected,
|
|
553
|
+
progressEventsCoalesced: 0,
|
|
554
|
+
stateTransactions: 0,
|
|
555
|
+
remainingSemanticEventCount: remaining.length - remainingProgressEventCount,
|
|
556
|
+
remainingProgressEventCount
|
|
557
|
+
}
|
|
558
|
+
};
|
|
62
559
|
}
|
|
63
|
-
applySessionLifecycle(event, now) {
|
|
64
|
-
const task = this.observer.getTask(event.taskId);
|
|
560
|
+
async applySessionLifecycle(event, now) {
|
|
561
|
+
const task = await this.observer.getTask(event.taskId);
|
|
65
562
|
if (task === null || task.status !== "active")
|
|
66
563
|
return "obsolete";
|
|
67
564
|
if (this.observer.observeProviderSessionLifecycle === undefined)
|
|
68
565
|
return "obsolete";
|
|
69
|
-
const outcome = this.observer.observeProviderSessionLifecycle({
|
|
566
|
+
const outcome = await this.observer.observeProviderSessionLifecycle({
|
|
70
567
|
eventId: event.id,
|
|
71
568
|
taskId: event.taskId,
|
|
72
569
|
roleName: event.roleName,
|
|
@@ -80,18 +577,23 @@ export class FileRuntimeEventProcessor {
|
|
|
80
577
|
if (outcome === "applied") {
|
|
81
578
|
this.options.onTaskRuntimeApplied?.({
|
|
82
579
|
taskId: event.taskId,
|
|
83
|
-
roleName: event.roleName
|
|
580
|
+
roleName: event.roleName,
|
|
581
|
+
agentId: event.agentId,
|
|
582
|
+
adapterId: event.adapterId,
|
|
583
|
+
launchId: event.launchId,
|
|
584
|
+
nativeSessionId: event.nativeSessionId,
|
|
585
|
+
...(event.runId === undefined ? {} : { runId: event.runId })
|
|
84
586
|
});
|
|
85
587
|
}
|
|
86
588
|
return outcome === "deferred" ? "deferred" : outcome;
|
|
87
589
|
}
|
|
88
|
-
applyPromptAccepted(event, now) {
|
|
89
|
-
const task = this.observer.getTask(event.taskId);
|
|
590
|
+
async applyPromptAccepted(event, now) {
|
|
591
|
+
const task = await this.observer.getTask(event.taskId);
|
|
90
592
|
if (task === null || task.status !== "active")
|
|
91
593
|
return "obsolete";
|
|
92
594
|
if (this.observer.observeProviderPromptAccepted === undefined)
|
|
93
595
|
return "obsolete";
|
|
94
|
-
const outcome = this.observer.observeProviderPromptAccepted({
|
|
596
|
+
const outcome = await this.observer.observeProviderPromptAccepted({
|
|
95
597
|
eventId: event.id,
|
|
96
598
|
taskId: event.taskId,
|
|
97
599
|
roleName: event.roleName,
|
|
@@ -105,18 +607,23 @@ export class FileRuntimeEventProcessor {
|
|
|
105
607
|
if (outcome === "applied") {
|
|
106
608
|
this.options.onTaskRuntimeApplied?.({
|
|
107
609
|
taskId: event.taskId,
|
|
108
|
-
roleName: event.roleName
|
|
610
|
+
roleName: event.roleName,
|
|
611
|
+
agentId: event.agentId,
|
|
612
|
+
adapterId: event.adapterId,
|
|
613
|
+
launchId: event.launchId,
|
|
614
|
+
nativeSessionId: event.nativeSessionId,
|
|
615
|
+
runId: event.runId
|
|
109
616
|
});
|
|
110
617
|
}
|
|
111
618
|
return outcome === "deferred" ? "deferred" : outcome;
|
|
112
619
|
}
|
|
113
|
-
applyProviderTurnProgress(event, now) {
|
|
114
|
-
const task = this.observer.getTask(event.taskId);
|
|
620
|
+
async applyProviderTurnProgress(event, now) {
|
|
621
|
+
const task = await this.observer.getTask(event.taskId);
|
|
115
622
|
if (task === null || task.status !== "active")
|
|
116
623
|
return "obsolete";
|
|
117
624
|
if (this.observer.observeProviderTurnProgress === undefined)
|
|
118
625
|
return "obsolete";
|
|
119
|
-
const outcome = this.observer.observeProviderTurnProgress({
|
|
626
|
+
const outcome = await this.observer.observeProviderTurnProgress({
|
|
120
627
|
eventId: event.id,
|
|
121
628
|
receivedAt: event.receivedAt,
|
|
122
629
|
taskId: event.taskId,
|
|
@@ -131,9 +638,9 @@ export class FileRuntimeEventProcessor {
|
|
|
131
638
|
}, now);
|
|
132
639
|
return outcome === "deferred" ? "deferred" : "applied";
|
|
133
640
|
}
|
|
134
|
-
applyCodex(event, now) {
|
|
641
|
+
async applyCodex(event, now) {
|
|
135
642
|
if (event.scope === "task") {
|
|
136
|
-
const task = this.observer.getTask(event.taskId);
|
|
643
|
+
const task = await this.observer.getTask(event.taskId);
|
|
137
644
|
const input = {
|
|
138
645
|
eventId: event.id,
|
|
139
646
|
taskId: event.taskId,
|
|
@@ -149,24 +656,29 @@ export class FileRuntimeEventProcessor {
|
|
|
149
656
|
if (task === null)
|
|
150
657
|
return "obsolete";
|
|
151
658
|
if (task.status !== "active") {
|
|
152
|
-
this.recordObsolete(event, task.status === "archived" ? "task-archived" : "task-retired", now);
|
|
659
|
+
await this.recordObsolete(event, task.status === "archived" ? "task-archived" : "task-retired", now);
|
|
153
660
|
return "obsolete";
|
|
154
661
|
}
|
|
155
|
-
const disposition = this.observer.classifyRuntimeTurnCompleted?.(input) ?? "apply";
|
|
662
|
+
const disposition = (await this.observer.classifyRuntimeTurnCompleted?.(input)) ?? "apply";
|
|
156
663
|
if (disposition === "deferred")
|
|
157
664
|
return disposition;
|
|
158
665
|
if (disposition === "obsolete") {
|
|
159
|
-
this.recordObsolete(event, "identity-mismatch-or-terminal", now);
|
|
666
|
+
await this.recordObsolete(event, "identity-mismatch-or-terminal", now);
|
|
160
667
|
return disposition;
|
|
161
668
|
}
|
|
162
|
-
const observed = this.observer.observeRuntimeTurnCompleted(input, now);
|
|
669
|
+
const observed = await this.observer.observeRuntimeTurnCompleted(input, now);
|
|
163
670
|
if (isObsoleteRuntimeTurnObservation(observed)) {
|
|
164
|
-
this.recordObsolete(event, "runtime-cleanup-or-stopped-session", now);
|
|
671
|
+
await this.recordObsolete(event, "runtime-cleanup-or-stopped-session", now);
|
|
165
672
|
return "obsolete";
|
|
166
673
|
}
|
|
167
674
|
this.options.onTaskRuntimeApplied?.({
|
|
168
675
|
taskId: event.taskId,
|
|
169
|
-
roleName: event.roleName
|
|
676
|
+
roleName: event.roleName,
|
|
677
|
+
agentId: event.agentId,
|
|
678
|
+
adapterId: event.adapterId,
|
|
679
|
+
...(event.launchId === undefined ? {} : { launchId: event.launchId }),
|
|
680
|
+
nativeSessionId: event.nativeSessionId,
|
|
681
|
+
...(event.runId === undefined ? {} : { runId: event.runId })
|
|
170
682
|
});
|
|
171
683
|
return "applied";
|
|
172
684
|
}
|
|
@@ -181,14 +693,14 @@ export class FileRuntimeEventProcessor {
|
|
|
181
693
|
...(event.title === undefined ? {} : { title: event.title }),
|
|
182
694
|
summary: event.summary
|
|
183
695
|
};
|
|
184
|
-
if (this.observer.classifyGlobalRuntimeTurnCompleted?.(input) !== "obsolete") {
|
|
185
|
-
this.observer.observeGlobalRuntimeTurnCompleted(input, now);
|
|
696
|
+
if ((await this.observer.classifyGlobalRuntimeTurnCompleted?.(input)) !== "obsolete") {
|
|
697
|
+
await this.observer.observeGlobalRuntimeTurnCompleted(input, now);
|
|
186
698
|
return "applied";
|
|
187
699
|
}
|
|
188
700
|
return "obsolete";
|
|
189
701
|
}
|
|
190
|
-
applyClaudeStopFailure(event, now) {
|
|
191
|
-
const task = this.observer.getTask(event.taskId);
|
|
702
|
+
async applyClaudeStopFailure(event, now) {
|
|
703
|
+
const task = await this.observer.getTask(event.taskId);
|
|
192
704
|
if (task === null)
|
|
193
705
|
return;
|
|
194
706
|
const input = {
|
|
@@ -208,8 +720,8 @@ export class FileRuntimeEventProcessor {
|
|
|
208
720
|
: { lastAssistantMessage: event.lastAssistantMessage })
|
|
209
721
|
};
|
|
210
722
|
if (task.status !== "active"
|
|
211
|
-
|| this.observer.classifyClaudeStopFailureEvent?.(input) === "obsolete") {
|
|
212
|
-
this.recordObsolete(event, task.status === "archived"
|
|
723
|
+
|| (await this.observer.classifyClaudeStopFailureEvent?.(input)) === "obsolete") {
|
|
724
|
+
await this.recordObsolete(event, task.status === "archived"
|
|
213
725
|
? "task-archived"
|
|
214
726
|
: task.status !== "active"
|
|
215
727
|
? "task-retired"
|
|
@@ -219,12 +731,12 @@ export class FileRuntimeEventProcessor {
|
|
|
219
731
|
if (this.observer.observeClaudeStopFailureEvent === undefined) {
|
|
220
732
|
throw new Error("Claude StopFailure observer is unavailable.");
|
|
221
733
|
}
|
|
222
|
-
this.observer.observeClaudeStopFailureEvent(input, now);
|
|
734
|
+
await this.observer.observeClaudeStopFailureEvent(input, now);
|
|
223
735
|
}
|
|
224
|
-
recordObsolete(event, reason, now) {
|
|
225
|
-
if (event.scope !== "task" || event.taskId === undefined)
|
|
736
|
+
async recordObsolete(event, reason, now) {
|
|
737
|
+
if (event.scope !== "task" || event.taskId === undefined || event.type === "durable-job-terminal")
|
|
226
738
|
return;
|
|
227
|
-
this.observer.observeObsoleteRuntimeEvent?.({
|
|
739
|
+
await this.observer.observeObsoleteRuntimeEvent?.({
|
|
228
740
|
eventId: event.id,
|
|
229
741
|
eventType: event.type,
|
|
230
742
|
taskId: event.taskId,
|
|
@@ -241,8 +753,3 @@ export class FileRuntimeEventProcessor {
|
|
|
241
753
|
acknowledged.push(id);
|
|
242
754
|
}
|
|
243
755
|
}
|
|
244
|
-
function isObsoleteRuntimeTurnObservation(value) {
|
|
245
|
-
return typeof value === "object"
|
|
246
|
-
&& value !== null
|
|
247
|
-
&& value.disposition === "obsolete";
|
|
248
|
-
}
|