@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.
- package/dist/boot/exit-boundary.d.ts +22 -0
- package/dist/boot/exit-boundary.js +73 -0
- package/dist/boot/shutdown.d.ts +2 -2
- package/dist/boot/shutdown.js +5 -62
- package/dist/host-decision.d.ts +28 -1
- package/dist/http/routes/runs.js +5 -5
- package/dist/plugins/approval-ask-store-memory.js +5 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +57 -1
- package/dist/plugins/approval-ask-store-sql.js +64 -23
- package/dist/plugins/device-store-sql.d.ts +10 -0
- package/dist/plugins/device-store-sql.js +12 -6
- package/dist/plugins/host-platform.d.ts +4 -2
- package/dist/plugins/one-time-migrations.d.ts +3 -0
- package/dist/plugins/one-time-migrations.js +16 -4
- package/dist/plugins/permission-rule-store-sql.d.ts +1 -0
- package/dist/plugins/permission-rule-store-sql.js +37 -28
- package/dist/plugins/session-policy-store-sql.d.ts +24 -0
- package/dist/plugins/session-policy-store-sql.js +12 -17
- package/dist/plugins/sql-json-column.d.ts +128 -0
- package/dist/plugins/sql-json-column.js +64 -0
- package/dist/plugins/task-list-store-sql.d.ts +13 -1
- package/dist/plugins/task-list-store-sql.js +23 -10
- package/dist/run-local.d.ts +15 -0
- package/dist/run-local.js +32 -0
- package/dist/tool-approval.d.ts +30 -24
- package/dist/tool-approval.js +57 -44
- package/package.json +2 -2
package/dist/tool-approval.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
235
|
-
if (
|
|
236
|
-
return
|
|
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 {
|
|
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
|
|
620
|
-
if (
|
|
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: "
|
|
634
|
+
return { kind: "decided", outcome: replayedOutcome };
|
|
624
635
|
if (replayed.updatedInput !== undefined)
|
|
625
|
-
return { kind: "
|
|
636
|
+
return { kind: "decided", outcome: replayedOutcome };
|
|
626
637
|
if (this.editedDecisionInFlight(askId))
|
|
627
638
|
return { kind: "edit-in-flight" };
|
|
628
|
-
return { kind: "
|
|
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,
|
|
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 "
|
|
1095
|
-
this.notifyExternalDecision(askId,
|
|
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 "
|
|
1115
|
-
|
|
1116
|
-
|
|
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
|
|
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
|
|
1899
|
-
let
|
|
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,
|
|
1916
|
+
this.notifyExternalDecision(askId, outcomeOfDecidedRow(late.row));
|
|
1909
1917
|
});
|
|
1910
|
-
|
|
1911
|
-
|
|
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
|
-
|
|
1924
|
-
|
|
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 (
|
|
1935
|
-
this.notifyExternalDecision(askId,
|
|
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 =
|
|
1943
|
-
approvalPayloadFingerprint({ 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
|
-
...(
|
|
1956
|
+
...(winnerRow !== undefined ? { settleOutcome: outcomeOfDecidedRow(winnerRow) } : {}),
|
|
1957
|
+
...(ambiguousConfirmedRow !== undefined ? { updatedInputDelivered: payloadIsMine } : {}),
|
|
1947
1958
|
});
|
|
1948
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
+
"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.
|
|
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",
|