@sema-agent/server 7.93.3 → 7.93.5

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.
@@ -84,6 +84,9 @@ export const PERSIST_RULE_EDITED_FLAG_ERROR = "persistRule.edited must be a bool
84
84
  export const PERSIST_RULE_BATCH_INDEX_ERROR = "persistRule.batchOfferIndex must be a non-negative integer when present";
85
85
  export const PERSIST_RULE_BATCH_EXCLUSIVE_ERROR = "persistRule.batchOfferIndex is mutually exclusive with persistRule.rule / persistRule.edited — send exactly one arm";
86
86
  export const SETTLED_BY_MUST_BE_STRING = "settledBy must be a string — the settlement source is one word from the engine's closed set, and this layer judges only the SHAPE (which word it is, the engine decides)";
87
+ function respondSettledBy(parsed) {
88
+ return parsed.settledBy;
89
+ }
87
90
  export const SETTLED_BY_DENY_ONLY = 'settledBy is only meaningful with decision "deny" — only a person approves an action that EXECUTES, so a self-reported settlement source on an allow is a contradiction the engine refuses; the refusal would arrive too late to undo this door\'s own side effects (a session allow-all grant, a persisted rule, a DECIDED row that replays as a plain approval), so it is refused here, before the decision';
88
91
  export const PERSIST_RULE_SCOPE_REFUSED = "this endpoint does not accept a rule scope — the server mints it from where the approval happened (send neither `scope` nor `persistRule.scope`)";
89
92
  export function parseToolApprovalResponse(body) {
@@ -222,21 +225,28 @@ function effectiveAskWindowMs(ttlMs, windowMarginMs, legDeadlineMonotonic, nowMo
222
225
  return Math.min(ttlMs, Math.max(0, legRemainingMs - windowMarginMs));
223
226
  }
224
227
  function replayTerminalAskRow(row) {
225
- if (row.state === "DECIDED") {
226
- if (row.decision !== "approve")
227
- return false;
228
- return row.updatedInput !== undefined ? { allow: true, updatedInput: row.updatedInput } : true;
229
- }
228
+ if (row.state === "DECIDED")
229
+ return outcomeOfDecidedRow(row);
230
230
  if (row.state === "PARKING" || row.state === "PARKED")
231
231
  return "unavailable";
232
232
  return false;
233
233
  }
234
- function settleArgsOf(outcome) {
235
- if (outcome === "unavailable")
236
- return "unavailable";
234
+ export function outcomeOfDecidedRow(row) {
235
+ if (row.decision !== "approve")
236
+ return row.settledBy !== null ? { allow: false, settledBy: row.settledBy } : false;
237
+ return row.updatedInput !== undefined ? { allow: true, updatedInput: row.updatedInput } : true;
238
+ }
239
+ function settleArgsOfDecided(outcome) {
237
240
  if (typeof outcome === "boolean")
238
241
  return { allowed: outcome };
239
- return { allowed: outcome.allow, ...(outcome.updatedInput !== undefined ? { updatedInput: outcome.updatedInput } : {}) };
242
+ return {
243
+ allowed: outcome.allow,
244
+ ...(outcome.updatedInput !== undefined ? { updatedInput: outcome.updatedInput } : {}),
245
+ ...(outcome.settledBy !== undefined ? { settledBy: outcome.settledBy } : {}),
246
+ };
247
+ }
248
+ function settleArgsOf(outcome) {
249
+ return outcome === "unavailable" ? "unavailable" : settleArgsOfDecided(outcome);
240
250
  }
241
251
  export class ToolApprovalCoordinator {
242
252
  als = new AsyncLocalStorage();
@@ -616,16 +626,17 @@ export class ToolApprovalCoordinator {
616
626
  classifyClaimLoss(askId, current, _where) {
617
627
  if (current.state === "absent")
618
628
  return { kind: "absent" };
619
- const replayed = settleArgsOf(replayTerminalAskRow(current));
620
- if (replayed === "unavailable")
629
+ const replayedOutcome = replayTerminalAskRow(current);
630
+ if (replayedOutcome === "unavailable")
621
631
  return { kind: "parked" };
632
+ const replayed = settleArgsOfDecided(replayedOutcome);
622
633
  if (!replayed.allowed)
623
- return { kind: "denied" };
634
+ return { kind: "decided", outcome: replayedOutcome };
624
635
  if (replayed.updatedInput !== undefined)
625
- return { kind: "approved", updatedInput: replayed.updatedInput };
636
+ return { kind: "decided", outcome: replayedOutcome };
626
637
  if (this.editedDecisionInFlight(askId))
627
638
  return { kind: "edit-in-flight" };
628
- return { kind: "approved" };
639
+ return { kind: "decided", outcome: replayedOutcome };
629
640
  }
630
641
  hasOtherSuspendedOwner(askId, self) {
631
642
  if (askId === undefined)
@@ -713,13 +724,14 @@ export class ToolApprovalCoordinator {
713
724
  }
714
725
  return tomb;
715
726
  }
716
- notifyExternalDecision(askId, allowed, updatedInput) {
727
+ notifyExternalDecision(askId, outcome) {
717
728
  const entries = this.pendingByAskId.get(askId);
718
729
  if (!entries)
719
730
  return { settled: 0 };
731
+ const args = settleArgsOfDecided(outcome);
720
732
  let settled = 0;
721
733
  for (const entry of [...entries]) {
722
- entry.settle(allowed, allowed ? "allowed" : "denied", updatedInput);
734
+ entry.settle(args.allowed, args.allowed ? "allowed" : "denied", args.updatedInput, args.settledBy);
723
735
  settled += 1;
724
736
  }
725
737
  return { settled };
@@ -1091,11 +1103,8 @@ export class ToolApprovalCoordinator {
1091
1103
  if (askId === undefined)
1092
1104
  return;
1093
1105
  switch (read.kind) {
1094
- case "approved":
1095
- this.notifyExternalDecision(askId, true, read.updatedInput);
1096
- return;
1097
- case "denied":
1098
- this.notifyExternalDecision(askId, false);
1106
+ case "decided":
1107
+ this.notifyExternalDecision(askId, read.outcome);
1099
1108
  return;
1100
1109
  case "parked":
1101
1110
  this.settleSameAskIdParkRoute(askId);
@@ -1111,14 +1120,12 @@ export class ToolApprovalCoordinator {
1111
1120
  };
1112
1121
  const settleFromClaimLoss = (read) => {
1113
1122
  switch (read.kind) {
1114
- case "approved":
1115
- settle(true, "allowed", read.updatedInput);
1116
- broadcastClaimTruth(read);
1117
- return true;
1118
- case "denied":
1119
- settle(false, "denied");
1123
+ case "decided": {
1124
+ const args = settleArgsOfDecided(read.outcome);
1125
+ settle(args.allowed, args.allowed ? "allowed" : "denied", args.updatedInput, args.settledBy);
1120
1126
  broadcastClaimTruth(read);
1121
1127
  return true;
1128
+ }
1122
1129
  case "parked":
1123
1130
  windowRouteUnavailable = true;
1124
1131
  settle(false, "expired");
@@ -1622,7 +1629,7 @@ export class ToolApprovalCoordinator {
1622
1629
  });
1623
1630
  return this.notPending();
1624
1631
  }
1625
- this.notifyExternalDecision(row.askId, row.decision === "approve", row.updatedInput);
1632
+ this.notifyExternalDecision(row.askId, outcomeOfDecidedRow(row));
1626
1633
  return {
1627
1634
  status: 200,
1628
1635
  body: {
@@ -1895,21 +1902,23 @@ export class ToolApprovalCoordinator {
1895
1902
  }
1896
1903
  }
1897
1904
  async respondWithCas(askStore, askId, batchId, id, entry, parsed, ruleEligibility) {
1898
- let decidedWon = false;
1899
- let confirmedRowPayload;
1905
+ let winnerRow;
1906
+ let ambiguousConfirmedRow;
1900
1907
  let noteLanded = false;
1901
1908
  try {
1902
1909
  const decided = await this.withStoreDeadline(askStore.decideAsk(askId, batchId, {
1903
1910
  decision: parsed.value === "deny" ? "deny" : "approve",
1904
1911
  ...(parsed.note !== undefined ? { decisionNote: parsed.note } : {}),
1905
1912
  ...(parsed.value !== "deny" && parsed.updatedInput !== undefined ? { updatedInput: parsed.updatedInput } : {}),
1913
+ ...(parsed.settledBy !== undefined ? { settledBy: respondSettledBy(parsed) } : {}),
1906
1914
  }), "decideAsk", DURABLE_CALL_TIMEOUT_MS, (late) => {
1907
1915
  if (late.ok)
1908
- this.notifyExternalDecision(askId, parsed.value !== "deny", parsed.updatedInput);
1916
+ this.notifyExternalDecision(askId, outcomeOfDecidedRow(late.row));
1909
1917
  });
1910
- decidedWon = decided.ok;
1911
- if (decided.ok)
1918
+ if (decided.ok) {
1919
+ winnerRow = decided.row;
1912
1920
  noteLanded = true;
1921
+ }
1913
1922
  if (!decided.ok) {
1914
1923
  return this.notPending();
1915
1924
  }
@@ -1920,8 +1929,8 @@ export class ToolApprovalCoordinator {
1920
1929
  try {
1921
1930
  const row = await this.withStoreDeadline(askStore.getAsk(askId), "getAsk(decideAsk-confirm)", DURABLE_CONVERGE_READ_TIMEOUT_MS);
1922
1931
  if (row?.state === "DECIDED" && row.decision === (parsed.value === "deny" ? "deny" : "approve")) {
1923
- decidedWon = true;
1924
- confirmedRowPayload = { updatedInput: row.updatedInput };
1932
+ winnerRow = row;
1933
+ ambiguousConfirmedRow = row;
1925
1934
  noteLanded = parsed.note === undefined || row.decisionNote === parsed.note;
1926
1935
  }
1927
1936
  }
@@ -1930,22 +1939,25 @@ export class ToolApprovalCoordinator {
1930
1939
  }
1931
1940
  }
1932
1941
  const wantAllowed = parsed.value !== "deny";
1942
+ const decidedWon = winnerRow !== undefined;
1933
1943
  if (this.pending.get(id) !== entry) {
1934
- if (decidedWon)
1935
- this.notifyExternalDecision(askId, wantAllowed, confirmedRowPayload !== undefined ? confirmedRowPayload.updatedInput : parsed.updatedInput);
1944
+ if (winnerRow !== undefined)
1945
+ this.notifyExternalDecision(askId, outcomeOfDecidedRow(winnerRow));
1936
1946
  const converged = decidedWon && entry.settledOutcome === (wantAllowed ? "allowed" : "denied");
1937
1947
  if (!converged) {
1938
1948
  return this.notPending();
1939
1949
  }
1940
1950
  return this.finishRespond(id, entry, parsed, { updatedInputDelivered: false, noteRecorded: noteLanded });
1941
1951
  }
1942
- const payloadIsMine = confirmedRowPayload === undefined ||
1943
- approvalPayloadFingerprint({ updatedInput: confirmedRowPayload.updatedInput }) === approvalPayloadFingerprint({ updatedInput: parsed.updatedInput });
1952
+ const payloadIsMine = ambiguousConfirmedRow === undefined ||
1953
+ approvalPayloadFingerprint({ updatedInput: ambiguousConfirmedRow.updatedInput }) === approvalPayloadFingerprint({ updatedInput: parsed.updatedInput });
1944
1954
  const result = this.finishRespond(id, entry, parsed, {
1945
1955
  noteRecorded: noteLanded,
1946
- ...(confirmedRowPayload !== undefined ? { settlePayload: confirmedRowPayload, updatedInputDelivered: payloadIsMine } : {}),
1956
+ ...(winnerRow !== undefined ? { settleOutcome: outcomeOfDecidedRow(winnerRow) } : {}),
1957
+ ...(ambiguousConfirmedRow !== undefined ? { updatedInputDelivered: payloadIsMine } : {}),
1947
1958
  });
1948
- this.notifyExternalDecision(askId, wantAllowed, confirmedRowPayload !== undefined ? confirmedRowPayload.updatedInput : parsed.updatedInput);
1959
+ if (winnerRow !== undefined)
1960
+ this.notifyExternalDecision(askId, outcomeOfDecidedRow(winnerRow));
1949
1961
  return result;
1950
1962
  }
1951
1963
  finishRespond(id, entry, parsed, opts) {
@@ -1962,7 +1974,8 @@ export class ToolApprovalCoordinator {
1962
1974
  this.allowAllSessions.add(sessionAllowKey(entry.owner, entry.sessionId, sessionAllowCategory(entry.toolName)));
1963
1975
  }
1964
1976
  const allowed = parsed.value !== "deny";
1965
- entry.settle(allowed, allowed ? "allowed" : "denied", opts?.settlePayload !== undefined ? opts.settlePayload.updatedInput : parsed.updatedInput, parsed.settledBy, allowed ? undefined : parsed.note);
1977
+ const settleArgs = opts?.settleOutcome !== undefined ? settleArgsOfDecided(opts.settleOutcome) : { allowed, updatedInput: parsed.updatedInput, settledBy: respondSettledBy(parsed) };
1978
+ entry.settle(settleArgs.allowed, settleArgs.allowed ? "allowed" : "denied", settleArgs.updatedInput, settleArgs.settledBy, settleArgs.allowed ? undefined : parsed.note);
1966
1979
  const updatedInputDelivered = opts?.updatedInputDelivered ?? true;
1967
1980
  const noteRecorded = opts?.noteRecorded ?? false;
1968
1981
  return {
@@ -2035,14 +2048,14 @@ export class ToolApprovalCoordinator {
2035
2048
  continue;
2036
2049
  if (this.editedDecisionInFlight(askId))
2037
2050
  continue;
2038
- const outcome = settleArgsOf(replayTerminalAskRow(row));
2051
+ const outcome = replayTerminalAskRow(row);
2039
2052
  if (outcome === "unavailable") {
2040
2053
  const before = this.pendingByAskId.get(askId)?.size ?? 0;
2041
2054
  this.settleSameAskIdParkRoute(askId);
2042
2055
  settled += before;
2043
2056
  continue;
2044
2057
  }
2045
- const delivered = this.notifyExternalDecision(askId, outcome.allowed, outcome.updatedInput).settled;
2058
+ const delivered = this.notifyExternalDecision(askId, outcome).settled;
2046
2059
  settled += delivered;
2047
2060
  }
2048
2061
  const backlog = Math.max(0, candidates.length - polled);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.93.3",
3
+ "version": "7.93.5",
4
4
  "description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -74,7 +74,7 @@
74
74
  "sharp": "^0.35.3"
75
75
  },
76
76
  "devDependencies": {
77
- "@sema-agent/sdk": "11.2.1",
77
+ "@sema-agent/sdk": "11.3.0",
78
78
  "@types/libsodium-wrappers": "^0.7.14",
79
79
  "@types/node": "22.10.2",
80
80
  "@types/pg": "^8.20.0",