@zq-silk/yui 0.6.2 → 0.6.4
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 +28 -4
- package/README.md +60 -97
- package/dist/agent/argumentPolicy.js +1 -1
- package/dist/agent/managedRuntimeEnvironment.js +1 -0
- package/dist/cli/commandCatalog.js +19 -9
- package/dist/cli/interactionPolicy.js +4 -2
- package/dist/cli.js +77 -32
- package/dist/commands/taskCommands.js +46 -11
- package/dist/commands/taskContextCommand.js +1 -1
- package/dist/commands/taskRoleRuntimeStatus.js +170 -10
- package/dist/controller/agentRuntimeObserver.js +210 -0
- package/dist/controller/clientRuntime.js +3 -21
- package/dist/controller/controller.js +47 -7
- package/dist/controller/fileSchedulerStoreAdapter.js +522 -388
- package/dist/controller/runtime.js +9 -3
- package/dist/controller/runtimeEventInbox.js +49 -295
- package/dist/controller/runtimeEventProcessor.js +184 -321
- package/dist/controller/runtimeHookRunFence.js +226 -0
- package/dist/controller/runtimeLaunchCoordinator.js +91 -26
- package/dist/controller/runtimeObservationHook.js +112 -0
- package/dist/core/controllerServer.js +5 -0
- package/dist/executor/agentAdapter.js +18 -3
- package/dist/executor/fileRoleLaunchPlanner.js +64 -15
- package/dist/executor/managedClaudeRunner.js +121 -0
- package/dist/observability/executionAudit.js +6 -3
- package/dist/repository/taskWorkspacePreparer.js +1 -4
- package/dist/run/providerRetryConfig.js +8 -3
- package/dist/runtime/agentDriver.js +229 -0
- package/dist/runtime/agentDriverObservation.js +57 -0
- package/dist/runtime/builtinAgentDrivers.js +235 -0
- package/dist/runtime/builtinTranscriptObserver.js +290 -0
- package/dist/runtime/builtinTranscriptUsage.js +97 -0
- package/dist/runtime/exactControlPlane.js +2 -2
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/ports.js +12 -1
- package/dist/runtime/runtimeObservation.js +297 -0
- package/dist/runtime/runtimeProjection.js +277 -0
- package/dist/runtime/sessionTerminationGuard.js +78 -22
- package/dist/runtime/tmuxAdapters.js +35 -0
- package/dist/scheduler/activeRoleRunDelivery.js +28 -13
- package/dist/scheduler/leaderWakeupProcessor.js +21 -2
- package/dist/scheduler/roleRunLiveness.js +2 -2
- package/dist/scheduler/roleRunStall.js +62 -114
- package/dist/storage/migration/productionRegistry.js +85 -0
- package/dist/storage/sqliteStore.js +3 -3
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +2 -1
- package/dist/storage/upgrade/sqliteStateMigration.js +123 -0
- package/dist/telemetry/sqliteTelemetryStore.js +0 -28
- package/dist/telemetry/telemetryCompaction.js +1 -0
- package/dist/telemetry/telemetryConfig.js +4 -5
- package/dist/tmux/tmuxManager.js +136 -22
- package/dist/web/assets/client/view.js +1 -1
- package/dist/web/tmuxWebTerminal.js +17 -12
- package/dist/web/webSnapshot.js +1 -1
- package/dist/worktree/managedWorkspace.js +14 -0
- package/i18n/README.zh-CN.md +12 -7
- package/package.json +1 -1
- package/dist/controller/claudeLifecycleHook.js +0 -203
- package/dist/controller/codexLifecycleHook.js +0 -108
- package/dist/controller/providerHookRunFence.js +0 -156
- package/dist/lifecycle/providerLifecycleMapping.js +0 -190
- package/dist/telemetry/telemetryRouter.js +0 -32
|
@@ -8,7 +8,7 @@ import { hasRuntimeCleanupObligation, runtimeLifecycleSignalKey, runtimeLifecycl
|
|
|
8
8
|
import { agentProcessReadinessProbe, ExecutorRegistry } from "../executor/executorRegistry.js";
|
|
9
9
|
import { activeLiveRoleAgentSession, roleAgentSessionResumeMode } from "../executor/agentExecutor.js";
|
|
10
10
|
import { effectiveLaunchSnapshotsCompatible, effectiveLaunchSnapshotsCompatibleForTaskMain, resolveEffectiveLaunch } from "../executor/effectiveLaunch.js";
|
|
11
|
-
import { isTaskOwnedWorkspace } from "../worktree/managedWorkspace.js";
|
|
11
|
+
import { isTaskOwnedWorkspace, managedWorkspaceIdentity, sameManagedWorkspaceIdentity } from "../worktree/managedWorkspace.js";
|
|
12
12
|
import { FileRoleLaunchPlanner } from "../executor/fileRoleLaunchPlanner.js";
|
|
13
13
|
import { openCompatibleFileTaskStore } from "../storage/compatibleTaskStore.js";
|
|
14
14
|
import { SqliteTaskStore } from "../storage/sqliteStore.js";
|
|
@@ -23,6 +23,7 @@ import { openSchedulerTelemetry } from "../telemetry/telemetryWiring.js";
|
|
|
23
23
|
import { createFileArtifactPort, createLinuxProcessPort, DurableJobSupervisor } from "./jobSupervisor.js";
|
|
24
24
|
import { createDurableJobControl } from "./jobControl.js";
|
|
25
25
|
import { FileRuntimeEventInbox } from "./runtimeEventInbox.js";
|
|
26
|
+
import { AgentRuntimeObserver } from "./agentRuntimeObserver.js";
|
|
26
27
|
import { AsyncRuntimeEventProcessor, FileRuntimeEventProcessor, createAsyncRuntimeObserver } from "./runtimeEventProcessor.js";
|
|
27
28
|
import { RuntimeLaunchCoordinator } from "./runtimeLaunchCoordinator.js";
|
|
28
29
|
import { ephemeralDomainFromEnvironment, recordEphemeralTmuxTarget } from "./domainIdentity.js";
|
|
@@ -399,6 +400,8 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
399
400
|
refreshAppliedTaskRuntimeDescriptor(store, planner, input);
|
|
400
401
|
}
|
|
401
402
|
})),
|
|
403
|
+
runtimeObserver: options.runtimeObserver
|
|
404
|
+
?? new AgentRuntimeObserver(store, runtimeEventInbox),
|
|
402
405
|
domainIdentity,
|
|
403
406
|
...(options.configuration !== undefined
|
|
404
407
|
? { configuration: options.configuration }
|
|
@@ -638,7 +641,8 @@ function assertRuntimeLaunchRequestCurrent(store, request) {
|
|
|
638
641
|
if (request.owner.scope === "task" && request.managedWorkspace !== undefined) {
|
|
639
642
|
const expectedWorkspace = activeRun?.workspace
|
|
640
643
|
?? currentDesiredManagedWorkspace(store, request.owner.taskId, request.owner.roleName);
|
|
641
|
-
if (
|
|
644
|
+
if (expectedWorkspace === undefined
|
|
645
|
+
|| !sameManagedWorkspaceIdentity(expectedWorkspace, request.managedWorkspace)) {
|
|
642
646
|
throw new Error(`Managed workspace launch state changed: ${request.owner.roleName}.`);
|
|
643
647
|
}
|
|
644
648
|
}
|
|
@@ -694,7 +698,9 @@ function runtimeLaunchFingerprint(store, request) {
|
|
|
694
698
|
return createHash("sha256").update(JSON.stringify([
|
|
695
699
|
request.owner,
|
|
696
700
|
request.effective,
|
|
697
|
-
request.managedWorkspace
|
|
701
|
+
request.managedWorkspace === undefined
|
|
702
|
+
? undefined
|
|
703
|
+
: managedWorkspaceIdentity(request.managedWorkspace),
|
|
698
704
|
request.runtimePolicy,
|
|
699
705
|
agent
|
|
700
706
|
])).digest("hex");
|
|
@@ -2,8 +2,8 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
import { closeSync, constants, existsSync, fchmodSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { withUpgradeCoordinationLock } from "../storage/upgradeCoordination.js";
|
|
5
|
+
import { createRuntimeObservation } from "../runtime/runtimeObservation.js";
|
|
5
6
|
export const MAX_RUNTIME_TURN_SUMMARY_BYTES = 32 * 1024;
|
|
6
|
-
export const MAX_CLAUDE_HOOK_TEXT_BYTES = 4 * 1024 * 1024;
|
|
7
7
|
export const MAX_RUNTIME_EVENT_FILE_BYTES = 16 * 1024 * 1024;
|
|
8
8
|
const RUNTIME_EVENT_DIRECTORY = join("runtime", "inbox");
|
|
9
9
|
const INVALID_RUNTIME_EVENT_DIRECTORY = join("runtime", "inbox-invalid");
|
|
@@ -24,6 +24,22 @@ export class FileRuntimeEventInbox {
|
|
|
24
24
|
this.hooks = hooks;
|
|
25
25
|
this.directory = join(home, RUNTIME_EVENT_DIRECTORY);
|
|
26
26
|
}
|
|
27
|
+
enqueueObservation(input) {
|
|
28
|
+
const observation = createRuntimeObservation(input);
|
|
29
|
+
const scope = observation.fence.taskId === undefined ? "global" : "task";
|
|
30
|
+
const event = Object.freeze({
|
|
31
|
+
schemaVersion: 1,
|
|
32
|
+
id: runtimeEventId("runtime-observation", { observation }),
|
|
33
|
+
type: "runtime-observation",
|
|
34
|
+
receivedAt: observation.receivedAt,
|
|
35
|
+
scope,
|
|
36
|
+
...(observation.fence.taskId === undefined
|
|
37
|
+
? {}
|
|
38
|
+
: { taskId: observation.fence.taskId }),
|
|
39
|
+
observation
|
|
40
|
+
});
|
|
41
|
+
return this.publish(event);
|
|
42
|
+
}
|
|
27
43
|
enqueueTurnCompleted(input) {
|
|
28
44
|
const normalized = normalizeCodexInput(input);
|
|
29
45
|
return this.publish(Object.freeze({
|
|
@@ -34,46 +50,6 @@ export class FileRuntimeEventInbox {
|
|
|
34
50
|
...normalized
|
|
35
51
|
}));
|
|
36
52
|
}
|
|
37
|
-
enqueueClaudeStopFailure(input) {
|
|
38
|
-
const normalized = normalizeClaudeStopFailureInput(input);
|
|
39
|
-
return this.publish(Object.freeze({
|
|
40
|
-
schemaVersion: 1,
|
|
41
|
-
id: runtimeEventId("claude-stop-failure", normalized),
|
|
42
|
-
type: "claude-stop-failure",
|
|
43
|
-
receivedAt: this.now().toISOString(),
|
|
44
|
-
...normalized
|
|
45
|
-
}));
|
|
46
|
-
}
|
|
47
|
-
enqueueSessionLifecycle(input) {
|
|
48
|
-
const normalized = normalizeSessionLifecycleInput(input);
|
|
49
|
-
return this.publish(Object.freeze({
|
|
50
|
-
schemaVersion: 1,
|
|
51
|
-
id: runtimeEventId("native-session-lifecycle", normalized),
|
|
52
|
-
type: "native-session-lifecycle",
|
|
53
|
-
receivedAt: this.now().toISOString(),
|
|
54
|
-
...normalized
|
|
55
|
-
}));
|
|
56
|
-
}
|
|
57
|
-
enqueuePromptAccepted(input) {
|
|
58
|
-
const normalized = normalizePromptAcceptedInput(input);
|
|
59
|
-
return this.publish(Object.freeze({
|
|
60
|
-
schemaVersion: 1,
|
|
61
|
-
id: runtimeEventId("native-prompt-accepted", normalized),
|
|
62
|
-
type: "native-prompt-accepted",
|
|
63
|
-
receivedAt: this.now().toISOString(),
|
|
64
|
-
...normalized
|
|
65
|
-
}));
|
|
66
|
-
}
|
|
67
|
-
enqueueProviderProgress(input) {
|
|
68
|
-
const normalized = normalizeProviderProgressInput(input);
|
|
69
|
-
return this.publishProgress(Object.freeze({
|
|
70
|
-
schemaVersion: 1,
|
|
71
|
-
id: runtimeEventId("native-turn-progress", normalized),
|
|
72
|
-
type: "native-turn-progress",
|
|
73
|
-
receivedAt: this.now().toISOString(),
|
|
74
|
-
...normalized
|
|
75
|
-
}));
|
|
76
|
-
}
|
|
77
53
|
enqueueDurableJobTerminal(input) {
|
|
78
54
|
const normalized = normalizeDurableJobTerminalInput(input);
|
|
79
55
|
return this.publish(Object.freeze({
|
|
@@ -179,19 +155,6 @@ export class FileRuntimeEventInbox {
|
|
|
179
155
|
}
|
|
180
156
|
return acknowledged;
|
|
181
157
|
}
|
|
182
|
-
publishProgress(event) {
|
|
183
|
-
return withUpgradeCoordinationLock(this.home, () => {
|
|
184
|
-
this.hooks.afterAdmission?.();
|
|
185
|
-
const result = this.publishUnlocked(event);
|
|
186
|
-
if (!result.created)
|
|
187
|
-
return result;
|
|
188
|
-
const superseded = this.supersededProgressIds(event);
|
|
189
|
-
if (superseded.length === 0)
|
|
190
|
-
return result;
|
|
191
|
-
this.acknowledgeMany(superseded);
|
|
192
|
-
return { ...result, coalescedEventCount: superseded.length };
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
158
|
publish(event) {
|
|
196
159
|
// The fence check and the complete durable write share one sibling
|
|
197
160
|
// coordination lock with upgrade's final inbox scan/copy/two-step switch.
|
|
@@ -243,35 +206,6 @@ export class FileRuntimeEventInbox {
|
|
|
243
206
|
rmSync(temporary, { force: true });
|
|
244
207
|
}
|
|
245
208
|
}
|
|
246
|
-
supersededProgressIds(event) {
|
|
247
|
-
const events = this.list();
|
|
248
|
-
const segment = semanticSegment(events, event);
|
|
249
|
-
const candidates = events.filter((candidate) => (candidate.type === "native-turn-progress"
|
|
250
|
-
&& sameProgressStream(candidate, event)
|
|
251
|
-
&& compareRuntimeEvents(candidate, segment.before) > 0
|
|
252
|
-
&& (segment.after === undefined || compareRuntimeEvents(candidate, segment.after) < 0)));
|
|
253
|
-
let latestReceivedAt = "";
|
|
254
|
-
let greatestSequence;
|
|
255
|
-
for (const candidate of candidates) {
|
|
256
|
-
const receivedAtOrder = candidate.receivedAt.localeCompare(latestReceivedAt);
|
|
257
|
-
if (receivedAtOrder > 0) {
|
|
258
|
-
latestReceivedAt = candidate.receivedAt;
|
|
259
|
-
greatestSequence = candidate.sequence;
|
|
260
|
-
}
|
|
261
|
-
else if (receivedAtOrder === 0
|
|
262
|
-
&& candidate.sequence !== undefined
|
|
263
|
-
&& (greatestSequence === undefined || candidate.sequence > greatestSequence)) {
|
|
264
|
-
greatestSequence = candidate.sequence;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return candidates.flatMap((candidate) => (candidate.receivedAt.localeCompare(latestReceivedAt) < 0
|
|
268
|
-
|| (candidate.receivedAt === latestReceivedAt
|
|
269
|
-
&& candidate.sequence !== undefined
|
|
270
|
-
&& greatestSequence !== undefined
|
|
271
|
-
&& candidate.sequence < greatestSequence)
|
|
272
|
-
? [candidate.id]
|
|
273
|
-
: []));
|
|
274
|
-
}
|
|
275
209
|
eventPath(id) {
|
|
276
210
|
return join(this.directory, `${id}.json`);
|
|
277
211
|
}
|
|
@@ -300,6 +234,14 @@ export class RuntimeEventInboxError extends Error {
|
|
|
300
234
|
}
|
|
301
235
|
}
|
|
302
236
|
function runtimeEventId(type, input) {
|
|
237
|
+
if (type === "runtime-observation") {
|
|
238
|
+
const observation = input.observation;
|
|
239
|
+
return `turn-${createHash("sha256").update(JSON.stringify([
|
|
240
|
+
1,
|
|
241
|
+
type,
|
|
242
|
+
observation
|
|
243
|
+
])).digest("hex")}`;
|
|
244
|
+
}
|
|
303
245
|
if (type === "durable-job-terminal") {
|
|
304
246
|
const job = input;
|
|
305
247
|
return `turn-${createHash("sha256").update(JSON.stringify([
|
|
@@ -312,8 +254,6 @@ function runtimeEventId(type, input) {
|
|
|
312
254
|
job.outcome
|
|
313
255
|
])).digest("hex")}`;
|
|
314
256
|
}
|
|
315
|
-
// The durable-job-terminal branch returned above; narrow the union so
|
|
316
|
-
// provider-only identity fields are accessible without `in` guards.
|
|
317
257
|
const provider = input;
|
|
318
258
|
const common = [
|
|
319
259
|
1,
|
|
@@ -326,11 +266,7 @@ function runtimeEventId(type, input) {
|
|
|
326
266
|
provider.launchId ?? null,
|
|
327
267
|
provider.nativeSessionId,
|
|
328
268
|
"turnId" in provider ? provider.turnId : null,
|
|
329
|
-
provider.runId ?? null
|
|
330
|
-
"progressId" in provider ? provider.progressId : null,
|
|
331
|
-
"sequence" in provider ? provider.sequence ?? null : null,
|
|
332
|
-
"sessionSource" in provider ? provider.sessionSource ?? null : null,
|
|
333
|
-
"receiptId" in provider ? provider.receiptId ?? null : null
|
|
269
|
+
provider.runId ?? null
|
|
334
270
|
];
|
|
335
271
|
return `turn-${createHash("sha256").update(JSON.stringify(common)).digest("hex")}`;
|
|
336
272
|
}
|
|
@@ -364,93 +300,6 @@ function normalizeCodexInput(input) {
|
|
|
364
300
|
? { ...common, taskId: requireIdentityText(input.taskId, "Task id") }
|
|
365
301
|
: common;
|
|
366
302
|
}
|
|
367
|
-
function normalizeClaudeEnvelope(input) {
|
|
368
|
-
if (input.scope !== "task" || input.adapterId !== "claude")
|
|
369
|
-
throw invalidEvent();
|
|
370
|
-
return {
|
|
371
|
-
scope: "task",
|
|
372
|
-
taskId: requireIdentityText(input.taskId, "Task id"),
|
|
373
|
-
roleName: requireIdentityText(input.roleName, "Role name"),
|
|
374
|
-
agentId: requireIdentityText(input.agentId, "Agent id"),
|
|
375
|
-
adapterId: "claude",
|
|
376
|
-
launchId: requireIdentityText(input.launchId, "Launch id"),
|
|
377
|
-
nativeSessionId: requireIdentityText(input.nativeSessionId, "Native session id"),
|
|
378
|
-
runId: requireIdentityText(input.runId, "Run id")
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
function normalizeClaudeStopFailureInput(input) {
|
|
382
|
-
return {
|
|
383
|
-
...normalizeClaudeEnvelope(input),
|
|
384
|
-
error: requireLongText(input.error, "Claude failure error"),
|
|
385
|
-
...(input.errorDetails === undefined
|
|
386
|
-
? {}
|
|
387
|
-
: { errorDetails: requireLongText(input.errorDetails, "Claude failure details") }),
|
|
388
|
-
...(input.lastAssistantMessage === undefined
|
|
389
|
-
? {}
|
|
390
|
-
: {
|
|
391
|
-
lastAssistantMessage: requireLongText(input.lastAssistantMessage, "Claude failure last assistant message")
|
|
392
|
-
})
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
function normalizeSessionLifecycleInput(input) {
|
|
396
|
-
if (input.scope !== "task")
|
|
397
|
-
throw invalidEvent();
|
|
398
|
-
if (input.adapterId !== "codex" && input.adapterId !== "claude")
|
|
399
|
-
throw invalidEvent();
|
|
400
|
-
return {
|
|
401
|
-
scope: "task",
|
|
402
|
-
taskId: requireIdentityText(input.taskId, "Task id"),
|
|
403
|
-
roleName: requireIdentityText(input.roleName, "Role name"),
|
|
404
|
-
agentId: requireIdentityText(input.agentId, "Agent id"),
|
|
405
|
-
adapterId: input.adapterId,
|
|
406
|
-
launchId: requireIdentityText(input.launchId, "Launch id"),
|
|
407
|
-
nativeSessionId: requireIdentityText(input.nativeSessionId, "Native session id"),
|
|
408
|
-
...(input.runId === undefined
|
|
409
|
-
? {}
|
|
410
|
-
: { runId: requireIdentityText(input.runId, "Run id") }),
|
|
411
|
-
...(input.sessionSource === undefined
|
|
412
|
-
? {}
|
|
413
|
-
: { sessionSource: requireIdentityText(input.sessionSource, "Session source") })
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
function normalizePromptAcceptedInput(input) {
|
|
417
|
-
if (input.scope !== "task")
|
|
418
|
-
throw invalidEvent();
|
|
419
|
-
if (input.adapterId !== "codex" && input.adapterId !== "claude")
|
|
420
|
-
throw invalidEvent();
|
|
421
|
-
return {
|
|
422
|
-
scope: "task",
|
|
423
|
-
taskId: requireIdentityText(input.taskId, "Task id"),
|
|
424
|
-
roleName: requireIdentityText(input.roleName, "Role name"),
|
|
425
|
-
agentId: requireIdentityText(input.agentId, "Agent id"),
|
|
426
|
-
adapterId: input.adapterId,
|
|
427
|
-
launchId: requireIdentityText(input.launchId, "Launch id"),
|
|
428
|
-
nativeSessionId: requireIdentityText(input.nativeSessionId, "Native session id"),
|
|
429
|
-
runId: requireIdentityText(input.runId, "Run id"),
|
|
430
|
-
receiptId: requireIdentityText(input.receiptId, "Receipt id")
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
function normalizeProviderProgressInput(input) {
|
|
434
|
-
if (input.scope !== "task")
|
|
435
|
-
throw invalidEvent();
|
|
436
|
-
if (input.adapterId !== "codex" && input.adapterId !== "claude")
|
|
437
|
-
throw invalidEvent();
|
|
438
|
-
if (input.sequence !== undefined && !Number.isSafeInteger(input.sequence)) {
|
|
439
|
-
throw invalidEvent();
|
|
440
|
-
}
|
|
441
|
-
return {
|
|
442
|
-
scope: "task",
|
|
443
|
-
taskId: requireIdentityText(input.taskId, "Task id"),
|
|
444
|
-
roleName: requireIdentityText(input.roleName, "Role name"),
|
|
445
|
-
agentId: requireIdentityText(input.agentId, "Agent id"),
|
|
446
|
-
adapterId: input.adapterId,
|
|
447
|
-
launchId: requireIdentityText(input.launchId, "Launch id"),
|
|
448
|
-
nativeSessionId: requireIdentityText(input.nativeSessionId, "Native session id"),
|
|
449
|
-
runId: requireIdentityText(input.runId, "Run id"),
|
|
450
|
-
progressId: requireIdentityText(input.progressId, "Provider progress id"),
|
|
451
|
-
...(input.sequence === undefined ? {} : { sequence: input.sequence })
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
303
|
function normalizeDurableJobTerminalInput(input) {
|
|
455
304
|
if (input.scope !== "task")
|
|
456
305
|
throw invalidEvent();
|
|
@@ -472,64 +321,34 @@ function parseRuntimeEvent(value) {
|
|
|
472
321
|
if (!isObject(value))
|
|
473
322
|
throw invalidEvent();
|
|
474
323
|
switch (value.type) {
|
|
324
|
+
case "runtime-observation": return parseRuntimeObservationEvent(value);
|
|
475
325
|
case "native-turn-completed": return parseCodexEvent(value);
|
|
476
|
-
case "claude-stop-failure": return parseClaudeStopFailureEvent(value);
|
|
477
|
-
case "native-session-lifecycle": return parseSessionLifecycleEvent(value);
|
|
478
|
-
case "native-prompt-accepted": return parsePromptAcceptedEvent(value);
|
|
479
|
-
case "native-turn-progress": return parseProviderProgressEvent(value);
|
|
480
326
|
case "durable-job-terminal": return parseDurableJobTerminalEvent(value);
|
|
481
327
|
default: throw invalidEvent();
|
|
482
328
|
}
|
|
483
329
|
}
|
|
484
|
-
function
|
|
485
|
-
const expected = [
|
|
486
|
-
"schemaVersion", "id", "type", "receivedAt", "scope", "taskId", "roleName",
|
|
487
|
-
"agentId", "adapterId", "launchId", "nativeSessionId",
|
|
488
|
-
...(value.runId === undefined ? [] : ["runId"]),
|
|
489
|
-
...(value.sessionSource === undefined ? [] : ["sessionSource"])
|
|
490
|
-
];
|
|
491
|
-
if (value.schemaVersion !== 1 || !hasExactKeys(value, expected))
|
|
492
|
-
throw invalidEvent();
|
|
493
|
-
const normalized = normalizeSessionLifecycleInput(value);
|
|
494
|
-
return Object.freeze({
|
|
495
|
-
schemaVersion: 1,
|
|
496
|
-
id: requireIdentityText(value.id, "Event id"),
|
|
497
|
-
type: "native-session-lifecycle",
|
|
498
|
-
receivedAt: requireTimestamp(value.receivedAt),
|
|
499
|
-
...normalized
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
function parsePromptAcceptedEvent(value) {
|
|
330
|
+
function parseRuntimeObservationEvent(value) {
|
|
503
331
|
const expected = [
|
|
504
|
-
"schemaVersion", "id", "type", "receivedAt", "scope", "
|
|
505
|
-
|
|
332
|
+
"schemaVersion", "id", "type", "receivedAt", "scope", "observation",
|
|
333
|
+
...(value.taskId === undefined ? [] : ["taskId"])
|
|
506
334
|
];
|
|
507
335
|
if (value.schemaVersion !== 1 || !hasExactKeys(value, expected))
|
|
508
336
|
throw invalidEvent();
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
function parseProviderProgressEvent(value) {
|
|
519
|
-
const expected = [
|
|
520
|
-
"schemaVersion", "id", "type", "receivedAt", "scope", "taskId", "roleName",
|
|
521
|
-
"agentId", "adapterId", "launchId", "nativeSessionId", "runId", "progressId",
|
|
522
|
-
...(value.sequence === undefined ? [] : ["sequence"])
|
|
523
|
-
];
|
|
524
|
-
if (value.schemaVersion !== 1 || !hasExactKeys(value, expected))
|
|
525
|
-
throw invalidEvent();
|
|
526
|
-
const normalized = normalizeProviderProgressInput(value);
|
|
337
|
+
const observation = createRuntimeObservation(value.observation);
|
|
338
|
+
const scope = observation.fence.taskId === undefined ? "global" : "task";
|
|
339
|
+
if (value.scope !== scope
|
|
340
|
+
|| (scope === "task" && value.taskId !== observation.fence.taskId)
|
|
341
|
+
|| value.receivedAt !== observation.receivedAt) {
|
|
342
|
+
throw invalidEvent("Runtime observation envelope does not match its canonical fence.");
|
|
343
|
+
}
|
|
527
344
|
return Object.freeze({
|
|
528
345
|
schemaVersion: 1,
|
|
529
346
|
id: requireIdentityText(value.id, "Event id"),
|
|
530
|
-
type: "
|
|
531
|
-
receivedAt:
|
|
532
|
-
|
|
347
|
+
type: "runtime-observation",
|
|
348
|
+
receivedAt: observation.receivedAt,
|
|
349
|
+
scope,
|
|
350
|
+
...(scope === "task" ? { taskId: observation.fence.taskId } : {}),
|
|
351
|
+
observation
|
|
533
352
|
});
|
|
534
353
|
}
|
|
535
354
|
function parseDurableJobTerminalEvent(value) {
|
|
@@ -591,24 +410,6 @@ function parseCodexEvent(value) {
|
|
|
591
410
|
...normalized
|
|
592
411
|
});
|
|
593
412
|
}
|
|
594
|
-
function parseClaudeStopFailureEvent(value) {
|
|
595
|
-
const expected = [
|
|
596
|
-
"schemaVersion", "id", "type", "receivedAt", "scope", "taskId", "roleName",
|
|
597
|
-
"agentId", "adapterId", "launchId", "nativeSessionId", "runId", "error",
|
|
598
|
-
...(value.errorDetails === undefined ? [] : ["errorDetails"]),
|
|
599
|
-
...(value.lastAssistantMessage === undefined ? [] : ["lastAssistantMessage"])
|
|
600
|
-
];
|
|
601
|
-
if (value.schemaVersion !== 1 || !hasExactKeys(value, expected))
|
|
602
|
-
throw invalidEvent();
|
|
603
|
-
const normalized = normalizeClaudeStopFailureInput(value);
|
|
604
|
-
return Object.freeze({
|
|
605
|
-
schemaVersion: 1,
|
|
606
|
-
id: requireIdentityText(value.id, "Event id"),
|
|
607
|
-
type: "claude-stop-failure",
|
|
608
|
-
receivedAt: requireTimestamp(value.receivedAt),
|
|
609
|
-
...normalized
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
413
|
function requireIdentityText(value, label) {
|
|
613
414
|
if (typeof value !== "string" || value.includes("\0"))
|
|
614
415
|
throw invalidEvent();
|
|
@@ -618,15 +419,6 @@ function requireIdentityText(value, label) {
|
|
|
618
419
|
}
|
|
619
420
|
return text;
|
|
620
421
|
}
|
|
621
|
-
function requireLongText(value, label) {
|
|
622
|
-
if (typeof value !== "string" || value.includes("\0") || value.trim().length === 0) {
|
|
623
|
-
throw invalidEvent(`${label} is required.`);
|
|
624
|
-
}
|
|
625
|
-
if (Buffer.byteLength(value, "utf8") > MAX_CLAUDE_HOOK_TEXT_BYTES) {
|
|
626
|
-
throw new RuntimeEventInboxError("RUNTIME_EVENT_TOO_LARGE", `${label} exceeds the durable inbox limit.`);
|
|
627
|
-
}
|
|
628
|
-
return value;
|
|
629
|
-
}
|
|
630
422
|
function requireTimestamp(value) {
|
|
631
423
|
const timestamp = requireIdentityText(value, "Received at");
|
|
632
424
|
if (!Number.isFinite(Date.parse(timestamp)))
|
|
@@ -672,57 +464,19 @@ function hasSameIdentity(left, right) {
|
|
|
672
464
|
|| left.nativeSessionId === right.nativeSessionId)
|
|
673
465
|
&& (!("runId" in left) || !("runId" in right) || left.runId === right.runId)
|
|
674
466
|
&& (!("turnId" in left) || !("turnId" in right) || left.turnId === right.turnId)
|
|
675
|
-
&& (!("progressId" in left)
|
|
676
|
-
|| !("progressId" in right)
|
|
677
|
-
|| left.progressId === right.progressId)
|
|
678
|
-
&& (!("sequence" in left) || !("sequence" in right) || left.sequence === right.sequence)
|
|
679
467
|
&& (!("jobId" in left) || !("jobId" in right) || left.jobId === right.jobId);
|
|
680
468
|
}
|
|
681
469
|
function compareRuntimeEvents(left, right) {
|
|
682
|
-
return left.receivedAt.localeCompare(right.receivedAt)
|
|
683
|
-
|| left.id.localeCompare(right.id);
|
|
684
|
-
}
|
|
685
|
-
/** Chooses one latest fact inside an already-isolated exact progress stream. */
|
|
686
|
-
export function compareRuntimeProgressRecency(left, right) {
|
|
687
470
|
const receivedAt = left.receivedAt.localeCompare(right.receivedAt);
|
|
688
471
|
if (receivedAt !== 0)
|
|
689
472
|
return receivedAt;
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
return 0;
|
|
698
|
-
}
|
|
699
|
-
function semanticSegment(events, current) {
|
|
700
|
-
let before = {
|
|
701
|
-
receivedAt: "",
|
|
702
|
-
id: ""
|
|
703
|
-
};
|
|
704
|
-
let after;
|
|
705
|
-
for (const event of events) {
|
|
706
|
-
if (event.type === "native-turn-progress")
|
|
707
|
-
continue;
|
|
708
|
-
const order = compareRuntimeEvents(event, current);
|
|
709
|
-
if (order < 0 && compareRuntimeEvents(event, before) > 0) {
|
|
710
|
-
before = event;
|
|
711
|
-
}
|
|
712
|
-
else if (order > 0 && (after === undefined || compareRuntimeEvents(event, after) < 0)) {
|
|
713
|
-
after = event;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
return { before, ...(after === undefined ? {} : { after }) };
|
|
717
|
-
}
|
|
718
|
-
function sameProgressStream(left, right) {
|
|
719
|
-
return left.taskId === right.taskId
|
|
720
|
-
&& left.roleName === right.roleName
|
|
721
|
-
&& left.agentId === right.agentId
|
|
722
|
-
&& left.adapterId === right.adapterId
|
|
723
|
-
&& left.launchId === right.launchId
|
|
724
|
-
&& left.nativeSessionId === right.nativeSessionId
|
|
725
|
-
&& left.runId === right.runId;
|
|
473
|
+
const sequence = (left.observation?.sequence ?? -1) - (right.observation?.sequence ?? -1);
|
|
474
|
+
if (sequence !== 0)
|
|
475
|
+
return sequence;
|
|
476
|
+
const ordinal = (left.observation?.ordinal ?? -1) - (right.observation?.ordinal ?? -1);
|
|
477
|
+
if (ordinal !== 0)
|
|
478
|
+
return ordinal;
|
|
479
|
+
return left.id.localeCompare(right.id);
|
|
726
480
|
}
|
|
727
481
|
function hasExactKeys(value, expected) {
|
|
728
482
|
const actual = Object.keys(value).sort();
|