@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.
Files changed (63) hide show
  1. package/ARCHITECTURE.md +28 -4
  2. package/README.md +60 -97
  3. package/dist/agent/argumentPolicy.js +1 -1
  4. package/dist/agent/managedRuntimeEnvironment.js +1 -0
  5. package/dist/cli/commandCatalog.js +19 -9
  6. package/dist/cli/interactionPolicy.js +4 -2
  7. package/dist/cli.js +77 -32
  8. package/dist/commands/taskCommands.js +46 -11
  9. package/dist/commands/taskContextCommand.js +1 -1
  10. package/dist/commands/taskRoleRuntimeStatus.js +170 -10
  11. package/dist/controller/agentRuntimeObserver.js +210 -0
  12. package/dist/controller/clientRuntime.js +3 -21
  13. package/dist/controller/controller.js +47 -7
  14. package/dist/controller/fileSchedulerStoreAdapter.js +522 -388
  15. package/dist/controller/runtime.js +9 -3
  16. package/dist/controller/runtimeEventInbox.js +49 -295
  17. package/dist/controller/runtimeEventProcessor.js +184 -321
  18. package/dist/controller/runtimeHookRunFence.js +226 -0
  19. package/dist/controller/runtimeLaunchCoordinator.js +91 -26
  20. package/dist/controller/runtimeObservationHook.js +112 -0
  21. package/dist/core/controllerServer.js +5 -0
  22. package/dist/executor/agentAdapter.js +18 -3
  23. package/dist/executor/fileRoleLaunchPlanner.js +64 -15
  24. package/dist/executor/managedClaudeRunner.js +121 -0
  25. package/dist/observability/executionAudit.js +6 -3
  26. package/dist/repository/taskWorkspacePreparer.js +1 -4
  27. package/dist/run/providerRetryConfig.js +8 -3
  28. package/dist/runtime/agentDriver.js +229 -0
  29. package/dist/runtime/agentDriverObservation.js +57 -0
  30. package/dist/runtime/builtinAgentDrivers.js +235 -0
  31. package/dist/runtime/builtinTranscriptObserver.js +290 -0
  32. package/dist/runtime/builtinTranscriptUsage.js +97 -0
  33. package/dist/runtime/exactControlPlane.js +2 -2
  34. package/dist/runtime/index.js +1 -1
  35. package/dist/runtime/ports.js +12 -1
  36. package/dist/runtime/runtimeObservation.js +297 -0
  37. package/dist/runtime/runtimeProjection.js +277 -0
  38. package/dist/runtime/sessionTerminationGuard.js +78 -22
  39. package/dist/runtime/tmuxAdapters.js +35 -0
  40. package/dist/scheduler/activeRoleRunDelivery.js +28 -13
  41. package/dist/scheduler/leaderWakeupProcessor.js +21 -2
  42. package/dist/scheduler/roleRunLiveness.js +2 -2
  43. package/dist/scheduler/roleRunStall.js +62 -114
  44. package/dist/storage/migration/productionRegistry.js +85 -0
  45. package/dist/storage/sqliteStore.js +3 -3
  46. package/dist/storage/storageVersions.js +1 -1
  47. package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +2 -1
  48. package/dist/storage/upgrade/sqliteStateMigration.js +123 -0
  49. package/dist/telemetry/sqliteTelemetryStore.js +0 -28
  50. package/dist/telemetry/telemetryCompaction.js +1 -0
  51. package/dist/telemetry/telemetryConfig.js +4 -5
  52. package/dist/tmux/tmuxManager.js +136 -22
  53. package/dist/web/assets/client/view.js +1 -1
  54. package/dist/web/tmuxWebTerminal.js +17 -12
  55. package/dist/web/webSnapshot.js +1 -1
  56. package/dist/worktree/managedWorkspace.js +14 -0
  57. package/i18n/README.zh-CN.md +12 -7
  58. package/package.json +1 -1
  59. package/dist/controller/claudeLifecycleHook.js +0 -203
  60. package/dist/controller/codexLifecycleHook.js +0 -108
  61. package/dist/controller/providerHookRunFence.js +0 -156
  62. package/dist/lifecycle/providerLifecycleMapping.js +0 -190
  63. 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 (!isDeepStrictEqual(expectedWorkspace, request.managedWorkspace)) {
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 parseSessionLifecycleEvent(value) {
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", "taskId", "roleName",
505
- "agentId", "adapterId", "launchId", "nativeSessionId", "runId", "receiptId"
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 normalized = normalizePromptAcceptedInput(value);
510
- return Object.freeze({
511
- schemaVersion: 1,
512
- id: requireIdentityText(value.id, "Event id"),
513
- type: "native-prompt-accepted",
514
- receivedAt: requireTimestamp(value.receivedAt),
515
- ...normalized
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: "native-turn-progress",
531
- receivedAt: requireTimestamp(value.receivedAt),
532
- ...normalized
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
- if (left.sequence !== undefined
691
- && right.sequence !== undefined
692
- && left.sequence !== right.sequence) {
693
- return left.sequence - right.sequence;
694
- }
695
- // A content-derived event id is identity, not arrival evidence. Without two
696
- // comparable provider sequences, same-timestamp facts remain incomparable.
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();