@zq-silk/yui 0.15.3 → 0.15.6

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 (41) hide show
  1. package/dist/agent/managedRuntimeEnvironment.js +3 -0
  2. package/dist/cli.js +35 -127
  3. package/dist/commands/executionAuditCommands.js +6 -0
  4. package/dist/commands/taskContextCommand.js +4 -2
  5. package/dist/context/sessionBootstrapManifest.js +12 -21
  6. package/dist/controller/clientRuntime.js +1 -1
  7. package/dist/controller/fileSchedulerStoreAdapter.js +270 -162
  8. package/dist/controller/runtimeEventInbox.js +8 -0
  9. package/dist/controller/runtimeEventProcessor.js +31 -4
  10. package/dist/controller/runtimeHookTurnFence.js +101 -62
  11. package/dist/controller/runtimeLaunchCoordinator.js +38 -12
  12. package/dist/controller/runtimeObservationHook.js +17 -1
  13. package/dist/controller/structuredProviderObservation.js +39 -27
  14. package/dist/core/controllerClient.js +5 -0
  15. package/dist/core/controllerServer.js +7 -4
  16. package/dist/domain/agentResultTransport.js +2 -2
  17. package/dist/executor/agentExecutor.js +22 -38
  18. package/dist/executor/executorRegistry.js +16 -5
  19. package/dist/executor/fileRoleLaunchPlanner.js +22 -28
  20. package/dist/lifecycle/exactTurnTerminalization.js +3 -3
  21. package/dist/observability/executionAudit.js +12 -0
  22. package/dist/repository/executionLaneGitSnapshot.js +4 -3
  23. package/dist/repository/taskWorkspacePreparer.js +12 -4
  24. package/dist/review/taskFinalReviewContract.js +13 -32
  25. package/dist/runtime/agentError.js +299 -12
  26. package/dist/runtime/agentHost.js +419 -41
  27. package/dist/runtime/builtinAgentDrivers.js +5 -0
  28. package/dist/runtime/index.js +1 -1
  29. package/dist/runtime/ports.js +16 -2
  30. package/dist/runtime/providerRuntimeIdentity.js +34 -28
  31. package/dist/runtime/runtimeCoherence.js +91 -0
  32. package/dist/runtime/runtimeObservation.js +8 -5
  33. package/dist/runtime/structuredProviderHost.js +53 -44
  34. package/dist/runtime/tmuxAdapters.js +72 -43
  35. package/dist/scheduler/activeRoleTurnDelivery.js +59 -11
  36. package/dist/scheduler/leaderWakeupProcessor.js +61 -7
  37. package/dist/storage/sqliteSchema.js +9 -0
  38. package/dist/storage/storageVersions.js +1 -1
  39. package/dist/turn/turn.js +7 -1
  40. package/package.json +1 -1
  41. package/dist/runtime/exactControlPlane.js +0 -232
@@ -2,7 +2,6 @@ import { createHash } from "node:crypto";
2
2
  import { isDeepStrictEqual } from "node:util";
3
3
  import { activeLiveRoleAgentSession, bindTaskRoleProviderRuntime, createRoleSessionSet, recordRoleAgentSession, replaceTaskRoleAgentSession, recordTaskRoleTurnBoundary, rememberRoleAgentCompletedTurn, detachRoleAgentSessionHost, updateRoleAgentSessionStatus, updateTaskRoleProviderRuntime } from "../executor/agentExecutor.js";
4
4
  import { acceptProviderTurn, beginProviderTurn, createProviderRuntimeBinding, endProviderActivation, currentProviderActivation, currentProviderConversation, managedProviderTurnId, clearProviderGoal, providerGoalContinues, settleProviderTurnSubmission, settleProviderTurn, startProviderActivation, supersedeProviderConversation, updateProviderConversationRecoverability, updateProviderGoal } from "../runtime/providerRuntimeIdentity.js";
5
- import { hasRecentTurnId } from "../runtime/recentTurnIds.js";
6
5
  import { createTaskEvent } from "../event/taskEvent.js";
7
6
  import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
8
7
  import { buildTaskWakeEnvelope } from "../context/wakeNotification.js";
@@ -87,7 +86,12 @@ export class FileSchedulerStoreAdapter {
87
86
  if (adapterId === null) {
88
87
  return this.recordObsoleteCanonicalObservation(input, "driver-or-turn-mismatch", now);
89
88
  }
90
- if (hasPersistedRuntimeObservation(this.store.listEvents(taskId), input))
89
+ // Receipt dedupe is not business-result dedupe. Terminal folds revalidate
90
+ // their exact binding and commit result + notification + observation
91
+ // together, including a retry after a previously interrupted fold.
92
+ if (!isTurnTerminalObservation(input)
93
+ && input.kind !== "turn.accepted" && input.kind !== "input.accepted"
94
+ && hasPersistedRuntimeObservation(this.store.listEvents(taskId), input))
91
95
  return "applied";
92
96
  let outcome;
93
97
  switch (input.kind) {
@@ -146,8 +150,9 @@ export class FileSchedulerStoreAdapter {
146
150
  default:
147
151
  outcome = "obsolete";
148
152
  }
149
- if (outcome === "applied")
153
+ if (outcome === "applied" && !isTurnTerminalObservation(input)) {
150
154
  this.persistRuntimeObservation(input, now);
155
+ }
151
156
  return outcome;
152
157
  }
153
158
  adapterForRuntimeObservation(input) {
@@ -225,7 +230,7 @@ export class FileSchedulerStoreAdapter {
225
230
  : {
226
231
  status: "failed",
227
232
  diagnostic: input.payload.resultTransportDiagnostic,
228
- failureReason: "missing-result"
233
+ failureReason: "runtime-failed"
229
234
  }
230
235
  : {
231
236
  status: "failed",
@@ -242,13 +247,16 @@ export class FileSchedulerStoreAdapter {
242
247
  agentId: input.fence.agentId,
243
248
  adapterId,
244
249
  runtimeGenerationId: input.fence.runtimeGenerationId,
250
+ activationId: input.fence.activationId,
251
+ conversationId: input.fence.conversationId,
245
252
  nativeSessionId: input.fence.nativeSessionId,
246
253
  nativeTurnId: input.fence.nativeTurnId,
247
254
  attemptId: input.fence.receiptId,
248
255
  turnId: input.fence.turnId,
249
256
  ...(input.payload.input === undefined ? {} : { input: input.payload.input }),
250
257
  providerStatus,
251
- outcome
258
+ outcome,
259
+ observation: input
252
260
  };
253
261
  const classification = this.classifyRuntimeTurnTerminal(completed);
254
262
  if (classification !== "apply")
@@ -882,9 +890,14 @@ export class FileSchedulerStoreAdapter {
882
890
  agentId: run?.effective.agentId ?? sessions.activeAgentId,
883
891
  adapterId: run?.effective.adapterId ?? session?.adapterId ?? "unknown",
884
892
  driverId: driver?.id ?? "unknown",
885
- runtimeGenerationId: session?.runtimeGenerationId ?? "",
886
- nativeSessionId: session?.nativeSessionId ?? "",
887
- nativeTurnId: "",
893
+ // No native Turn exists at submission resolution, and the Session
894
+ // facts may be absent. An unknown fact is an absent key: an empty
895
+ // string reads back as a real value and cannot be told apart from
896
+ // one the Provider genuinely reported.
897
+ ...optionalEventFields({
898
+ runtimeGenerationId: session?.runtimeGenerationId,
899
+ nativeSessionId: session?.nativeSessionId
900
+ }),
888
901
  source: error.source,
889
902
  phase: error.phase,
890
903
  category: error.category,
@@ -948,12 +961,6 @@ export class FileSchedulerStoreAdapter {
948
961
  }
949
962
  recordAgentError(input, now) {
950
963
  return this.store.transaction((store) => {
951
- const duplicate = [...store.listEvents(input.taskId)].reverse().find((event) => (event.type === "runtime.agent-error"
952
- && event.payload.turnId === input.turnId
953
- && event.payload.phase === input.phase
954
- && event.payload.raw === input.raw));
955
- if (duplicate !== undefined)
956
- return duplicate.id;
957
964
  const run = store.getTurn(input.taskId, input.turnId);
958
965
  const sessionSet = store.getTaskRoleSessionSet(input.taskId, input.roleName);
959
966
  const sessionAgentId = run?.effective.agentId ?? sessionSet?.activeAgentId;
@@ -979,16 +986,35 @@ export class FileSchedulerStoreAdapter {
979
986
  ? {}
980
987
  : { sessionDisposition: input.sessionDisposition })
981
988
  });
989
+ const duplicate = [...store.listEvents(input.taskId)].reverse().find((event) => (event.type === "runtime.agent-error"
990
+ && event.payload.turnId === input.turnId
991
+ && event.payload.roleName === input.roleName
992
+ && event.payload.phase === input.phase
993
+ && event.payload.attemptId === input.attemptId
994
+ && event.payload.source === error.source
995
+ && event.payload.inputDisposition === error.inputDisposition
996
+ && event.payload.sessionDisposition === error.sessionDisposition
997
+ && event.payload.registrationDisposition === input.registrationDisposition
998
+ && event.payload.hostState === input.hostState
999
+ && event.payload.expectedRuntimeGenerationId === input.expectedRuntimeGenerationId
1000
+ && event.payload.observedRuntimeGenerationId === input.observedRuntimeGenerationId
1001
+ && (input.attemptId === undefined
1002
+ ? event.payload.raw === error.raw
1003
+ : event.payload.message === error.message
1004
+ && event.payload.errorName === input.errorName
1005
+ && event.payload.causeName === input.causeName)));
1006
+ // Re-reading one failed attempt may add a different caller stack, not a
1007
+ // new execution fact. Preserve its first full cause without multiplying
1008
+ // notifications; changed disposition/identity/diagnostic remains visible.
1009
+ if (duplicate !== undefined)
1010
+ return duplicate.id;
982
1011
  const event = createTaskEvent(store.nextEventId(input.taskId), input.taskId, "runtime.agent-error", {
983
- sourceEventId: `${input.turnId}:${input.phase}`,
1012
+ sourceEventId: `${input.turnId}:${input.phase}${input.attemptId === undefined ? "" : `:${input.attemptId}`}`,
984
1013
  turnId: input.turnId,
985
1014
  roleName: input.roleName,
986
1015
  agentId: run?.effective.agentId ?? session?.agentId ?? "unknown",
987
1016
  adapterId: run?.effective.adapterId ?? session?.adapterId ?? "unknown",
988
1017
  driverId: driver?.id ?? "unknown",
989
- runtimeGenerationId: session?.runtimeGenerationId ?? "",
990
- nativeSessionId: session?.nativeSessionId ?? "",
991
- nativeTurnId: "",
992
1018
  source: error.source,
993
1019
  phase: error.phase,
994
1020
  category: error.category,
@@ -996,7 +1022,24 @@ export class FileSchedulerStoreAdapter {
996
1022
  message: error.message,
997
1023
  raw: error.raw,
998
1024
  inputDisposition: error.inputDisposition,
999
- sessionDisposition: error.sessionDisposition
1025
+ sessionDisposition: error.sessionDisposition,
1026
+ // Structured facts from the failing operation. Absent keys stay
1027
+ // absent rather than becoming an empty string, so a reader can tell
1028
+ // "the Host did not report this" from "the Host reported nothing".
1029
+ // The Session identities follow the same rule: this failure can
1030
+ // happen before any Session record exists, and there is no native
1031
+ // Turn to name at all.
1032
+ ...optionalEventFields({
1033
+ runtimeGenerationId: session?.runtimeGenerationId,
1034
+ nativeSessionId: session?.nativeSessionId,
1035
+ errorName: input.errorName,
1036
+ causeName: input.causeName,
1037
+ hostState: input.hostState,
1038
+ expectedRuntimeGenerationId: input.expectedRuntimeGenerationId,
1039
+ observedRuntimeGenerationId: input.observedRuntimeGenerationId,
1040
+ attemptId: input.attemptId,
1041
+ registrationDisposition: input.registrationDisposition
1042
+ })
1000
1043
  }, now);
1001
1044
  store.saveEvent(input.taskId, event);
1002
1045
  const leaderCannotReceive = input.roleName === "leader"
@@ -1558,129 +1601,98 @@ export class FileSchedulerStoreAdapter {
1558
1601
  * for later mailbox input. It never performs tmux, workspace, or Controller I/O.
1559
1602
  */
1560
1603
  classifyRuntimeTurnTerminal(input) {
1561
- const task = this.store.getTask(input.taskId);
1562
- const role = this.store.getRole(input.taskId, input.roleName);
1563
- if (task === null || task.status === "archived" || role === null)
1564
- return "obsolete";
1565
- const sessions = this.store.getTaskRoleSessionSet(input.taskId, input.roleName);
1566
- const existing = sessions?.sessions[input.agentId];
1567
- const owner = {
1568
- scope: "task",
1569
- taskId: input.taskId,
1570
- roleName: input.roleName
1571
- };
1572
- if (existing === undefined
1573
- && !runtimeHookMatchesReservation(this.store, owner, input.runtimeGenerationId))
1574
- return "obsolete";
1575
- try {
1576
- const effectiveExisting = nativeTransitionExisting(this.store, owner, existing, input.nativeSessionId, input.runtimeGenerationId, "Runtime turn completion conflicts with the fixed Role session.");
1577
- const effective = taskSessionEffective(this.store, input.taskId, input.roleName, input.agentId, effectiveExisting);
1578
- if (effective.agentId !== input.agentId || effective.adapterId !== input.adapterId) {
1579
- return "obsolete";
1580
- }
1581
- }
1582
- catch {
1583
- return "obsolete";
1584
- }
1585
- const providerTurn = sessions?.providerBinding?.turn;
1586
- const canonicalTurnId = sessions === null || sessions === undefined
1587
- ? input.nativeTurnId
1588
- : canonicalStructuredProviderTurnId(sessions, input.nativeTurnId, input.attemptId);
1589
- const recordedProviderTurn = providerTurn !== null
1590
- && providerTurn !== undefined
1591
- && providerTurn.nativeTurnId === input.nativeTurnId
1592
- && providerTurn.nativeTurnId === canonicalTurnId;
1593
- if (recordedProviderTurn) {
1594
- if (input.turnId === undefined)
1595
- return "apply";
1596
- const run = this.store.getTurn(input.taskId, input.turnId);
1597
- return run === null ? "obsolete" : "apply";
1598
- }
1599
- const active = this.store.getActiveTurn(input.taskId, input.roleName);
1600
- if (input.turnId === undefined)
1601
- return active === null ? "apply" : "obsolete";
1602
- return active?.id === input.turnId ? "apply" : "obsolete";
1604
+ return resolveTerminalExecution(this.store, input) === null ? "obsolete" : "apply";
1603
1605
  }
1604
1606
  observeRuntimeTurnTerminal(input, now = new Date()) {
1607
+ // Resource inspection is bounded but external to SQLite. Revalidate the
1608
+ // immutable Turn workspace after acquiring the write transaction.
1609
+ const preparedTurn = resolveTerminalExecution(this.store, input)?.turn;
1610
+ const workspace = preparedTurn?.status === "active"
1611
+ && preparedTurn.executionGroupId !== undefined
1612
+ && preparedTurn.executionLaneId !== undefined
1613
+ && preparedTurn.workspace !== undefined
1614
+ && input.outcome.status === "completed"
1615
+ ? this.snapshotExecutionLaneWorkspace(this.store, preparedTurn.workspace)
1616
+ : undefined;
1605
1617
  return this.store.transaction((store) => {
1606
1618
  const task = store.getTask(input.taskId);
1607
1619
  if (task === null)
1608
1620
  throw new Error(`Task not found: ${input.taskId}.`);
1609
- if (task.status !== "active" && task.status !== "completed") {
1621
+ if (task.status === "archived") {
1610
1622
  throw new Error(`Cannot complete a runtime turn for unavailable Task: ${input.taskId}.`);
1611
1623
  }
1612
- const role = requireRole(store, input.taskId, input.roleName);
1624
+ const resolved = resolveTerminalExecution(store, input);
1625
+ if (resolved === null)
1626
+ throw new Error("Runtime terminal has no exact execution binding.");
1627
+ const { turn: observedTurn, current: recordedProviderTurn, attemptId } = resolved;
1628
+ if (!isDeepStrictEqual(preparedTurn?.workspace, observedTurn?.workspace)) {
1629
+ throw new Error("Runtime terminal workspace changed during result preparation.");
1630
+ }
1631
+ if (workspace !== undefined && observedTurn?.workspace !== undefined
1632
+ && !isDeepStrictEqual(store.getManagedWorkspace(observedTurn.workspace.owner), observedTurn.workspace)) {
1633
+ throw new Error("Runtime terminal workspace ownership changed during result preparation.");
1634
+ }
1613
1635
  let sessions = store.getTaskRoleSessionSet(input.taskId, input.roleName)
1614
1636
  ?? createRoleSessionSet({ scope: "task", taskId: input.taskId, roleName: input.roleName }, input.agentId, now);
1615
- const canonicalTurnId = canonicalStructuredProviderTurnId(sessions, input.nativeTurnId, input.attemptId);
1616
- const providerTurn = sessions.providerBinding?.turn;
1617
- const recordedProviderTurn = providerTurn !== null
1618
- && providerTurn !== undefined
1619
- && providerTurn.nativeTurnId === input.nativeTurnId
1620
- && providerTurn.nativeTurnId === canonicalTurnId;
1621
- const observedTurn = input.turnId === undefined
1622
- ? store.listTurns(input.taskId).find((turn) => (turn.result?.provider?.nativeTurnId === canonicalTurnId)) ?? null
1623
- : store.getTurn(input.taskId, input.turnId);
1637
+ const canonicalTurnId = input.nativeTurnId ?? resolved.nativeTurnId;
1624
1638
  const existing = sessions.sessions[input.agentId];
1625
1639
  const owner = {
1626
1640
  scope: "task",
1627
1641
  taskId: input.taskId,
1628
1642
  roleName: input.roleName
1629
1643
  };
1630
- if (existing === undefined
1631
- && !runtimeHookMatchesReservation(store, owner, input.runtimeGenerationId)) {
1632
- throw new Error("Runtime turn completion does not match the launch reservation.");
1633
- }
1634
- const effectiveExisting = nativeTransitionExisting(store, owner, existing, input.nativeSessionId, input.runtimeGenerationId, "Runtime turn completion conflicts with the fixed Role session.");
1635
- const effective = taskSessionEffective(store, task.id, role.name, input.agentId, effectiveExisting);
1636
- if (effective.agentId !== input.agentId || effective.adapterId !== input.adapterId) {
1637
- throw new Error("Runtime turn completion does not match the effective runtime generation identity.");
1638
- }
1639
- if (effectiveExisting !== undefined
1640
- && hasRecentTurnId(effectiveExisting.recentCompletedTurnIds, canonicalTurnId)) {
1644
+ assertConsistentTerminal(store, input, observedTurn);
1645
+ // A historical operation failure stays failed. The immutable terminal
1646
+ // observation retains the late report for explicit Leader adoption.
1647
+ // Never touch a successor Session, active pointer, mailbox or Review.
1648
+ if (!recordedProviderTurn || (observedTurn !== null && observedTurn.status !== "active")) {
1649
+ // A failed operation and a subsequently confirmed Provider outcome are
1650
+ // distinct facts. Settle only the still-owned original attempt, never
1651
+ // rewriting the historical Task result or a successor's binding.
1652
+ if (recordedProviderTurn && sessions.providerBinding?.turn !== null
1653
+ && sessions.providerBinding?.turn !== undefined
1654
+ && ["submitting", "accepted", "delivery-unknown"].includes(sessions.providerBinding.turn.status)) {
1655
+ if (sessions.providerBinding.turn.status !== "accepted") {
1656
+ sessions = updateTaskRoleProviderRuntime(sessions, acceptProviderTurn(sessions.providerBinding, {
1657
+ attemptId,
1658
+ ...(canonicalTurnId === undefined ? {} : { nativeTurnId: canonicalTurnId }),
1659
+ acceptedAt: now.toISOString()
1660
+ }), now);
1661
+ }
1662
+ sessions = settleStructuredProviderTurn(sessions, canonicalTurnId, input.providerStatus, now, attemptId);
1663
+ store.saveTaskRoleSessionSet(sessions);
1664
+ }
1665
+ if (input.observation !== undefined)
1666
+ this.persistRuntimeObservation(input.observation, now);
1641
1667
  return {
1642
- session: effectiveExisting,
1668
+ ...(sessions.sessions[input.agentId] === undefined
1669
+ ? {} : { session: sessions.sessions[input.agentId] }),
1643
1670
  duplicate: true,
1644
1671
  ...(observedTurn === null ? {} : { turn: observedTurn })
1645
1672
  };
1646
1673
  }
1647
- if (!recordedProviderTurn
1648
- && existing !== undefined
1649
- && (hasRuntimeCleanupObligation(store.getWorkMailbox(runtimeLifecycleTarget(owner)))
1650
- || isObsoleteTerminalRuntimeTurn(store, input))) {
1651
- return { session: existing, duplicate: false, disposition: "obsolete" };
1652
- }
1653
- if (!recordedProviderTurn && input.turnId !== undefined
1654
- && observedTurn?.id !== input.turnId) {
1655
- return { session: effectiveExisting, duplicate: false, disposition: "obsolete" };
1674
+ const pending = sessions.providerBinding;
1675
+ if (pending.turn?.status === "submitting" || pending.turn?.status === "delivery-unknown") {
1676
+ sessions = updateTaskRoleProviderRuntime(sessions, acceptProviderTurn(pending, {
1677
+ attemptId,
1678
+ ...(canonicalTurnId === undefined ? {} : { nativeTurnId: canonicalTurnId }),
1679
+ acceptedAt: now.toISOString()
1680
+ }), now);
1656
1681
  }
1657
- const sessionStatus = effectiveExisting?.status ?? "active";
1658
- sessions = recordRoleAgentSession(sessions, {
1659
- agentId: input.agentId,
1660
- adapterId: input.adapterId,
1661
- nativeSessionId: input.nativeSessionId,
1662
- ...(input.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: input.runtimeGenerationId }),
1663
- policy: "fixed",
1664
- status: sessionStatus,
1665
- ...(effectiveExisting?.endReason === undefined
1666
- ? {}
1667
- : { endReason: effectiveExisting.endReason }),
1668
- effective
1669
- }, now);
1670
- sessions = settleStructuredProviderTurn(sessions, canonicalTurnId, input.providerStatus, now);
1671
- sessions = recordTaskRoleTurnBoundary(sessions, {
1672
- agentId: input.agentId,
1673
- nativeSessionId: input.nativeSessionId,
1674
- turnId: canonicalTurnId
1675
- }, now);
1682
+ sessions = settleStructuredProviderTurn(sessions, canonicalTurnId, input.providerStatus, now, attemptId);
1683
+ if (canonicalTurnId !== undefined)
1684
+ sessions = recordTaskRoleTurnBoundary(sessions, {
1685
+ agentId: input.agentId,
1686
+ nativeSessionId: input.nativeSessionId,
1687
+ turnId: canonicalTurnId
1688
+ }, now);
1676
1689
  store.saveTaskRoleSessionSet(sessions);
1677
1690
  completeRuntimeHookReservation(store, owner, input.runtimeGenerationId);
1678
1691
  let terminalTurn;
1679
1692
  if (recordedProviderTurn && observedTurn?.status === "active") {
1680
1693
  const binding = sessions.providerBinding;
1681
- const conversation = currentProviderConversation(binding);
1682
- const activation = binding.activations.find((entry) => (entry.activationId === input.runtimeGenerationId
1683
- || (entry.conversationId === conversation.conversationId && entry.status === "active")));
1694
+ const conversation = binding.conversations.find((entry) => entry.conversationId === input.nativeSessionId);
1695
+ const activation = binding.activations.find((entry) => (entry.activationId === resolved.activationId));
1684
1696
  if (activation === undefined) {
1685
1697
  throw new Error("Provider Turn result has no matching Activation.");
1686
1698
  }
@@ -1692,7 +1704,7 @@ export class FileSchedulerStoreAdapter {
1692
1704
  && observedTurn.executionLaneId !== undefined
1693
1705
  && observedTurn.workspace !== undefined
1694
1706
  && input.outcome.status === "completed") {
1695
- const gitSnapshot = this.snapshotExecutionLaneWorkspace(store, observedTurn.workspace);
1707
+ const gitSnapshot = workspace;
1696
1708
  if (gitSnapshot.status === "captured") {
1697
1709
  systemEvidence = { workspaceSnapshot: gitSnapshot.snapshot };
1698
1710
  }
@@ -1721,7 +1733,8 @@ export class FileSchedulerStoreAdapter {
1721
1733
  accountScope: binding.accountScope,
1722
1734
  conversationId: conversation.conversationId,
1723
1735
  activationId: activation.activationId,
1724
- nativeTurnId: canonicalTurnId,
1736
+ ...(canonicalTurnId === undefined ? {} : { nativeTurnId: canonicalTurnId }),
1737
+ attemptId,
1725
1738
  status: providerStatus
1726
1739
  }
1727
1740
  },
@@ -1735,7 +1748,7 @@ export class FileSchedulerStoreAdapter {
1735
1748
  const event = createTaskEvent(store.nextEventId(input.taskId), input.taskId, terminalTurn.status === "completed" ? "turn.completed" : "turn.failed", {
1736
1749
  turnId: terminalTurn.id,
1737
1750
  roleName: terminalTurn.roleName,
1738
- providerTurnId: canonicalTurnId,
1751
+ ...(canonicalTurnId === undefined ? {} : { providerTurnId: canonicalTurnId }),
1739
1752
  providerStatus
1740
1753
  }, now);
1741
1754
  store.saveEvent(input.taskId, event);
@@ -1744,6 +1757,8 @@ export class FileSchedulerStoreAdapter {
1744
1757
  routeRoleEvent(store, event, terminalTurn.roleName, terminalTurn.purpose === "review" ? "review-result" : "role-turn-result", now);
1745
1758
  }
1746
1759
  }
1760
+ if (input.observation !== undefined)
1761
+ this.persistRuntimeObservation(input.observation, now);
1747
1762
  return {
1748
1763
  session: sessions.sessions[input.agentId],
1749
1764
  duplicate: false,
@@ -1976,6 +1991,27 @@ export class FileSchedulerStoreAdapter {
1976
1991
  return this.store.transaction((store) => {
1977
1992
  const continuation = input.fence.receiptId?.startsWith("turn-input:") === true;
1978
1993
  const ordinaryAttemptId = input.fence.receiptId;
1994
+ if (!continuation && input.fence.turnId !== undefined) {
1995
+ const exact = resolveTerminalExecution(store, {
1996
+ taskId: input.fence.taskId,
1997
+ roleName: input.fence.roleName,
1998
+ agentId: input.fence.agentId,
1999
+ adapterId,
2000
+ runtimeGenerationId: input.fence.runtimeGenerationId,
2001
+ activationId: input.fence.activationId,
2002
+ conversationId: input.fence.conversationId,
2003
+ nativeSessionId: input.fence.nativeSessionId,
2004
+ nativeTurnId: input.fence.nativeTurnId,
2005
+ attemptId: ordinaryAttemptId,
2006
+ turnId: input.fence.turnId
2007
+ });
2008
+ if (exact === null)
2009
+ return "obsolete";
2010
+ // A terminal can prove acceptance before the delayed receipt. Retain
2011
+ // the receipt without recreating lifecycle state or touching a successor.
2012
+ if (!exact.current || exact.turn?.status !== "active")
2013
+ return "applied";
2014
+ }
1979
2015
  if (input.fence.turnId === undefined
1980
2016
  && ordinaryAttemptId?.startsWith("direct:") === true) {
1981
2017
  const taskId = input.fence.taskId;
@@ -2377,34 +2413,6 @@ function recordObsoleteRuntimeEvent(store, input, reason, now) {
2377
2413
  reason
2378
2414
  }, now));
2379
2415
  }
2380
- function isLeaderDisposedWorkItemTurn(store, input) {
2381
- if (input.turnId === undefined)
2382
- return false;
2383
- const run = store.getTurn(input.taskId, input.turnId);
2384
- if (run === null
2385
- || run.status !== "failed"
2386
- || run.roleName !== input.roleName
2387
- || run.effective.agentId !== input.agentId
2388
- || run.workItemId === undefined) {
2389
- return false;
2390
- }
2391
- return store.getWorkItem(input.taskId, run.workItemId)?.disposition !== undefined;
2392
- }
2393
- function isObsoleteTerminalRuntimeTurn(store, input) {
2394
- if (isLeaderDisposedWorkItemTurn(store, input))
2395
- return true;
2396
- if (input.turnId === undefined)
2397
- return false;
2398
- const run = store.getTurn(input.taskId, input.turnId);
2399
- if (run === null
2400
- || run.status !== "failed"
2401
- || run.roleName !== input.roleName
2402
- || run.effective.agentId !== input.agentId) {
2403
- return false;
2404
- }
2405
- return store.listEvents(input.taskId).some((event) => (event.type === "runtime.role-delivery-failed"
2406
- && event.payload.turnId === input.turnId));
2407
- }
2408
2416
  function sessionPreview(value) {
2409
2417
  const normalized = value.trim().replaceAll(/\s+/g, " ");
2410
2418
  const truncated = normalized.slice(0, 1_024);
@@ -2811,6 +2819,14 @@ function turnLaunchEventPayload(turn) {
2811
2819
  writeProjectIds: turn.effective.writeProjectIds.join(",") || "none"
2812
2820
  };
2813
2821
  }
2822
+ /**
2823
+ * Keeps only the fields the caller actually knew. Event payloads are a
2824
+ * string map, so an unknown fact has to be an absent key: writing `""` would
2825
+ * claim the Host reported an empty value.
2826
+ */
2827
+ function optionalEventFields(fields) {
2828
+ return Object.fromEntries(Object.entries(fields).filter((entry) => typeof entry[1] === "string" && entry[1].trim().length > 0));
2829
+ }
2814
2830
  function runtimeObservationTelemetryEntry(input) {
2815
2831
  return {
2816
2832
  taskId: input.fence.taskId,
@@ -2907,7 +2923,7 @@ function recordStructuredProviderAcceptance(sessions, input, now) {
2907
2923
  const current = sessions.providerBinding;
2908
2924
  const attemptId = input.fence.receiptId;
2909
2925
  const turnId = input.fence.nativeTurnId;
2910
- if (current === null || attemptId === undefined || turnId === undefined)
2926
+ if (current === null || attemptId === undefined)
2911
2927
  return sessions;
2912
2928
  if (current.turn === null
2913
2929
  || current.turn.turnId !== input.fence.turnId)
@@ -2922,19 +2938,109 @@ function recordStructuredProviderAcceptance(sessions, input, now) {
2922
2938
  });
2923
2939
  return updateTaskRoleProviderRuntime(sessions, binding, now);
2924
2940
  }
2925
- /**
2926
- * A Provider may expose the same accepted input through more than one exact
2927
- * observation surface. Preserve the first native Turn identity bound to the
2928
- * durable attempt; a later transport alias may settle that same attempt but
2929
- * must neither replace nor strand the canonical identity.
2930
- */
2931
- function canonicalStructuredProviderTurnId(sessions, observedTurnId, attemptId) {
2932
- const turn = sessions.providerBinding?.turn;
2933
- return attemptId !== undefined
2934
- && turn?.attemptId === attemptId
2935
- && turn.nativeTurnId !== undefined
2936
- ? turn.nativeTurnId
2937
- : observedTurnId;
2941
+ /** One correlation boundary shared by classification and the committing fold. */
2942
+ function resolveTerminalExecution(store, input) {
2943
+ const task = store.getTask(input.taskId);
2944
+ if (task === null || task.status === "archived")
2945
+ return null;
2946
+ if (input.conversationId !== undefined && input.conversationId !== input.nativeSessionId)
2947
+ return null;
2948
+ const sessions = store.getTaskRoleSessionSet(input.taskId, input.roleName);
2949
+ const session = sessions?.sessions[input.agentId];
2950
+ const binding = sessions?.providerBinding;
2951
+ const pending = binding?.turn;
2952
+ const evidence = store.listEvents(input.taskId).map(runtimeObservationFromTaskEvent)
2953
+ .filter((event) => event !== null
2954
+ && (event.kind === "turn.accepted" || isTurnTerminalObservation(event)));
2955
+ if (input.attemptId !== undefined && evidence.some((event) => (event.fence.roleName === input.roleName
2956
+ && event.fence.agentId === input.agentId
2957
+ && event.fence.nativeSessionId === input.nativeSessionId
2958
+ && event.fence.receiptId === input.attemptId
2959
+ && (event.fence.runtimeGenerationId !== input.runtimeGenerationId
2960
+ && input.runtimeGenerationId !== undefined
2961
+ || input.nativeTurnId !== undefined && event.fence.nativeTurnId !== undefined
2962
+ && event.fence.nativeTurnId !== input.nativeTurnId))))
2963
+ return null;
2964
+ const identityMatches = (attemptId, nativeTurnId) => ((input.attemptId === undefined
2965
+ ? input.nativeTurnId !== undefined && input.nativeTurnId === nativeTurnId
2966
+ : input.attemptId === attemptId)
2967
+ && !(input.nativeTurnId !== undefined && nativeTurnId !== undefined
2968
+ && input.nativeTurnId !== nativeTurnId));
2969
+ if (binding !== null && binding !== undefined && pending !== null && pending !== undefined
2970
+ && session?.adapterId === input.adapterId
2971
+ && session.nativeSessionId === input.nativeSessionId
2972
+ && (input.runtimeGenerationId === undefined
2973
+ || (pending.activationId ?? session.runtimeGenerationId) === input.runtimeGenerationId)
2974
+ && currentProviderConversation(binding).conversationId === input.nativeSessionId
2975
+ && identityMatches(pending.attemptId, pending.nativeTurnId)
2976
+ && (input.turnId === undefined || input.turnId === pending.turnId)
2977
+ && pending.status !== "rejected") {
2978
+ const activation = binding.activations.find((entry) => (entry.activationId === (input.runtimeGenerationId ?? pending.activationId ?? session.runtimeGenerationId)
2979
+ && (input.activationId === undefined || entry.activationId === input.activationId)
2980
+ && entry.conversationId === input.nativeSessionId));
2981
+ const turn = pending.turnId === undefined ? null : store.getTurn(input.taskId, pending.turnId);
2982
+ if (activation !== undefined && (pending.turnId === undefined || (turn !== null && turn.roleName === input.roleName
2983
+ && turn.effective.agentId === input.agentId && turn.effective.adapterId === input.adapterId)))
2984
+ return {
2985
+ turn, current: true, attemptId: pending.attemptId,
2986
+ nativeTurnId: pending.nativeTurnId, activationId: activation.activationId
2987
+ };
2988
+ }
2989
+ // Old results use immutable acceptance/terminal evidence, not today's Role
2990
+ // config, Session status, current generation or active Turn pointer.
2991
+ const matches = evidence.filter((event) => event.fence.roleName === input.roleName
2992
+ && event.fence.agentId === input.agentId
2993
+ && event.fence.nativeSessionId === input.nativeSessionId
2994
+ && input.runtimeGenerationId !== undefined
2995
+ && event.fence.runtimeGenerationId === input.runtimeGenerationId
2996
+ && (input.activationId === undefined
2997
+ || (event.fence.activationId ?? event.fence.runtimeGenerationId) === input.activationId)
2998
+ && identityMatches(event.fence.receiptId, event.fence.nativeTurnId)
2999
+ && event.fence.turnId !== undefined
3000
+ && (input.turnId === undefined || event.fence.turnId === input.turnId));
3001
+ const accepted = matches[0];
3002
+ if (accepted === undefined || accepted.fence.receiptId === undefined)
3003
+ return null;
3004
+ if (matches.some((event) => event.fence.turnId !== accepted.fence.turnId
3005
+ || event.fence.receiptId !== accepted.fence.receiptId)) {
3006
+ throw new Error("Runtime terminal has conflicting durable execution bindings.");
3007
+ }
3008
+ const turn = store.getTurn(input.taskId, accepted.fence.turnId);
3009
+ if (turn === null || turn.effective.adapterId !== input.adapterId
3010
+ || turn.effective.agentId !== input.agentId || turn.roleName !== input.roleName)
3011
+ return null;
3012
+ return {
3013
+ turn, current: false, attemptId: accepted.fence.receiptId,
3014
+ nativeTurnId: accepted.fence.nativeTurnId,
3015
+ activationId: accepted.fence.activationId ?? accepted.fence.runtimeGenerationId
3016
+ };
3017
+ }
3018
+ function isTurnTerminalObservation(input) {
3019
+ return ["turn.completed", "turn.failed", "turn.cancelled"].includes(input.kind);
3020
+ }
3021
+ function assertConsistentTerminal(store, input, turn) {
3022
+ const previous = store.listEvents(input.taskId).map(runtimeObservationFromTaskEvent)
3023
+ .filter((event) => event !== null
3024
+ && isTurnTerminalObservation(event)
3025
+ && event.fence.roleName === input.roleName
3026
+ && event.fence.agentId === input.agentId
3027
+ && event.fence.nativeSessionId === input.nativeSessionId
3028
+ && event.fence.runtimeGenerationId === input.runtimeGenerationId
3029
+ && (input.attemptId === undefined
3030
+ ? input.nativeTurnId !== undefined && event.fence.nativeTurnId === input.nativeTurnId
3031
+ : event.fence.receiptId === input.attemptId));
3032
+ if (input.observation !== undefined && previous.some((event) => (event.kind !== input.observation.kind
3033
+ || !isDeepStrictEqual(event.payload, input.observation.payload)
3034
+ || event.fence.turnId !== input.observation.fence.turnId)))
3035
+ throw new Error("Conflicting terminal content for the same execution.");
3036
+ if (turn?.result?.provider !== undefined) {
3037
+ if (turn.result.provider.status !== input.providerStatus
3038
+ || (input.outcome.status === "completed" && turn.result.output !== input.outcome.output)
3039
+ || (previous.length === 0 && input.outcome.status === "failed"
3040
+ && turn.result.diagnostic !== input.outcome.diagnostic)) {
3041
+ throw new Error("Conflicting result for the same execution.");
3042
+ }
3043
+ }
2938
3044
  }
2939
3045
  function terminalSessionReplacementBasis(sessions, input, run) {
2940
3046
  const binding = sessions.providerBinding;
@@ -3012,15 +3118,17 @@ function terminalProviderReplacementBasis(sessions, input, mode) {
3012
3118
  && session.nativeSessionId !== current.nativeSessionId));
3013
3119
  return replaced === undefined ? undefined : "terminal-session";
3014
3120
  }
3015
- function settleStructuredProviderTurn(sessions, turnId, status, now) {
3121
+ function settleStructuredProviderTurn(sessions, turnId, status, now, attemptId) {
3016
3122
  const binding = sessions.providerBinding;
3017
- if (binding === null || binding.turn === null || binding.turn.nativeTurnId !== turnId) {
3123
+ if (binding === null || binding.turn === null
3124
+ || (attemptId === undefined ? binding.turn.nativeTurnId !== turnId : binding.turn.attemptId !== attemptId)) {
3018
3125
  return sessions;
3019
3126
  }
3020
3127
  if (["completed", "failed", "cancelled"].includes(binding.turn.status))
3021
3128
  return sessions;
3022
3129
  return updateTaskRoleProviderRuntime(sessions, settleProviderTurn(binding, {
3023
3130
  nativeTurnId: turnId,
3131
+ ...(attemptId === undefined ? {} : { attemptId }),
3024
3132
  status,
3025
3133
  settledAt: now.toISOString()
3026
3134
  }), now);
@@ -1,4 +1,5 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
+ import { isDeepStrictEqual } from "node:util";
2
3
  import { closeSync, constants, existsSync, fchmodSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
3
4
  import { join } from "node:path";
4
5
  import { createRuntimeObservation, isRuntimeTokenEvidence } from "../runtime/runtimeObservation.js";
@@ -488,6 +489,13 @@ function assertEventId(id) {
488
489
  throw invalidEvent("Runtime event id is invalid.");
489
490
  }
490
491
  function hasSameIdentity(left, right) {
492
+ if (left.type === "runtime-observation" && right.type === "runtime-observation"
493
+ && ["turn.completed", "turn.failed", "turn.cancelled"].includes(left.observation.kind)) {
494
+ return left.id === right.id
495
+ && left.observation.kind === right.observation.kind
496
+ && isDeepStrictEqual(left.observation.fence, right.observation.fence)
497
+ && isDeepStrictEqual(left.observation.payload, right.observation.payload);
498
+ }
491
499
  return left.id === right.id
492
500
  && left.type === right.type
493
501
  && left.scope === right.scope