@sema-agent/core 7.56.0 → 7.57.0
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/CHANGELOG.md +541 -0
- package/LICENSE +1 -1
- package/dist/agents/org-delivery-gate.js +3 -4
- package/dist/agents/peer-held-queue.d.ts +69 -49
- package/dist/agents/peer-held-queue.js +255 -106
- package/dist/agents/peer-session-drain.js +63 -43
- package/dist/agents/send-message-tool.js +3 -2
- package/dist/agents/subagent.js +8 -8
- package/dist/brain/anthropic.js +16 -8
- package/dist/brain/open-responses.js +10 -8
- package/dist/brain/openai.js +11 -9
- package/dist/brain/provider-dialect.d.ts +3 -2
- package/dist/brain/provider-dialect.js +2 -2
- package/dist/brain/request-params.d.ts +6 -0
- package/dist/brain/request-params.js +14 -1
- package/dist/brain/stream-engine.d.ts +9 -0
- package/dist/brain/stream-engine.js +24 -10
- package/dist/brain/stream-shared.d.ts +5 -4
- package/dist/brain/stream-shared.js +4 -4
- package/dist/core/allow-entry.d.ts +3 -10
- package/dist/core/allow-entry.js +17 -25
- package/dist/core/approval-object.d.ts +21 -5
- package/dist/core/approval-object.js +44 -17
- package/dist/core/ask-origin.d.ts +2 -2
- package/dist/core/auto-compaction.d.ts +71 -6
- package/dist/core/auto-compaction.js +42 -26
- package/dist/core/background-agent-store.d.ts +6 -2
- package/dist/core/background-agent-store.js +1 -1
- package/dist/core/checkpoint-requirements.d.ts +7 -1
- package/dist/core/checkpoint-requirements.js +2 -0
- package/dist/core/checkpoint-store.d.ts +17 -29
- package/dist/core/checkpoint-store.js +19 -11
- package/dist/core/cross-boundary-taint.js +4 -2
- package/dist/core/engine-notice.d.ts +3 -2
- package/dist/core/engine-notice.js +6 -0
- package/dist/core/file-history-store.d.ts +14 -8
- package/dist/core/file-history-store.js +14 -4
- package/dist/core/gate-lanes.js +2 -3
- package/dist/core/hooks.d.ts +2 -2
- package/dist/core/mailbox-store.d.ts +62 -1
- package/dist/core/mailbox-store.js +59 -0
- package/dist/core/mcp-name-judge.d.ts +46 -0
- package/dist/core/mcp-name-judge.js +61 -0
- package/dist/core/mcp-probe.d.ts +1 -1
- package/dist/core/mcp-probe.js +2 -5
- package/dist/core/mcp.js +10 -30
- package/dist/core/memory-engine/consolidation-driver.js +4 -3
- package/dist/core/memory-engine/distiller.js +26 -17
- package/dist/core/memory-engine/layout.js +5 -2
- package/dist/core/park-selfcheck.js +2 -0
- package/dist/core/permission-rule-consent.d.ts +5 -3
- package/dist/core/permission-rule-consent.js +20 -15
- package/dist/core/permission-rule-model.d.ts +43 -2
- package/dist/core/permission-rule-model.js +52 -8
- package/dist/core/permission-rule-org.js +1 -1
- package/dist/core/protocol-http-auth.d.ts +16 -2
- package/dist/core/protocol-http-auth.js +42 -11
- package/dist/core/protocol-naming.d.ts +20 -0
- package/dist/core/protocol-naming.js +20 -3
- package/dist/core/read-only-shell.d.ts +20 -5
- package/dist/core/read-only-shell.js +29 -14
- package/dist/core/remote-env.d.ts +37 -1
- package/dist/core/remote-env.js +11 -0
- package/dist/core/resume-startup-cause.d.ts +24 -2
- package/dist/core/resume-startup-cause.js +20 -1
- package/dist/core/roles.js +50 -7
- package/dist/core/runner/compact-session.d.ts +10 -0
- package/dist/core/runner/compact-session.js +202 -0
- package/dist/core/runner/compaction-call-options.d.ts +2 -2
- package/dist/core/runner/compaction-seams.d.ts +21 -1
- package/dist/core/runner/compaction-seams.js +11 -8
- package/dist/core/runner/contracts.d.ts +23 -16
- package/dist/core/runner/derived-route-fallback.d.ts +13 -0
- package/dist/core/runner/derived-route-fallback.js +6 -0
- package/dist/core/runner/env-capability-census.d.ts +3 -2
- package/dist/core/runner/env-capability-census.js +9 -3
- package/dist/core/runner/permission-rule-lanes.d.ts +7 -7
- package/dist/core/runner/permission-rule-lanes.js +17 -11
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +2 -1
- package/dist/core/runner/prepare-config-doors.d.ts +6 -0
- package/dist/core/runner/prepare-config-doors.js +4 -3
- package/dist/core/runner/prepare-hands-readface.d.ts +3 -1
- package/dist/core/runner/prepare-hands-readface.js +2 -0
- package/dist/core/runner/prepare-park-ask.d.ts +2 -0
- package/dist/core/runner/prepare-park-ask.js +2 -2
- package/dist/core/runner/prepare-policy-chain.js +2 -4
- package/dist/core/runner/prepare-task.d.ts +4 -1
- package/dist/core/runner/prepare-task.js +23 -27
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +13 -0
- package/dist/core/runner/prepare-tool-disclosure-mount.js +13 -7
- package/dist/core/runner/rewind-files.d.ts +2 -5
- package/dist/core/runner/rewind-files.js +9 -51
- package/dist/core/runner/run-leg.js +10 -21
- package/dist/core/runner/run-notification-lane.d.ts +2 -0
- package/dist/core/runner/run-notification-lane.js +2 -2
- package/dist/core/runner/run-recovery-lanes.js +3 -4
- package/dist/core/runner/run-settle-and-teardown.js +5 -11
- package/dist/core/runner/run-terminal-adoption.js +3 -4
- package/dist/core/runner/run-turn-boundary.js +3 -5
- package/dist/core/runner/runtask.d.ts +8 -1
- package/dist/core/runner/runtask.js +28 -34
- package/dist/core/runner/send-message-continuation.js +1 -1
- package/dist/core/runner/session-verb-doors.d.ts +28 -0
- package/dist/core/runner/session-verb-doors.js +48 -0
- package/dist/core/runner/session-write-ledger.d.ts +30 -0
- package/dist/core/runner/session-write-ledger.js +101 -0
- package/dist/core/runner/stream-settle-backstop.js +11 -14
- package/dist/core/runner/sub-agent-deny-lists.d.ts +25 -3
- package/dist/core/runner/sub-agent-deny-lists.js +21 -10
- package/dist/core/runner/teardown-bounded.d.ts +1 -0
- package/dist/core/runner/teardown-bounded.js +1 -0
- package/dist/core/runner-deps.d.ts +2 -2
- package/dist/core/secret-env.d.ts +4 -0
- package/dist/core/secret-env.js +3 -0
- package/dist/core/session-policy-store.d.ts +7 -2
- package/dist/core/session-policy-store.js +1 -1
- package/dist/core/shell-wrapper-projection.d.ts +1 -1
- package/dist/core/store-contracts/background-agent-store-contract.js +1 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -1
- package/dist/core/store-contracts/file-history-store-contract.js +9 -0
- package/dist/core/store-contracts/mailbox-store-contract.js +49 -1
- package/dist/core/task-registry-agent.d.ts +8 -4
- package/dist/core/task-registry-agent.js +14 -8
- package/dist/core/task-registry-shared.d.ts +35 -6
- package/dist/core/task-registry-shared.js +11 -0
- package/dist/core/task-registry.d.ts +1 -1
- package/dist/core/task-registry.js +1 -1
- package/dist/core/tool-catalog-entries.d.ts +6 -0
- package/dist/core/tool-catalog-entries.js +1 -0
- package/dist/core/tool-name-aliases.d.ts +32 -2
- package/dist/core/tool-name-aliases.js +23 -0
- package/dist/core/tool-name-readings.d.ts +4 -0
- package/dist/core/tool-name-readings.js +1 -0
- package/dist/core/tool-policy.d.ts +10 -6
- package/dist/core/tool-policy.js +4 -7
- package/dist/core/trace.d.ts +4 -1
- package/dist/core/trace.js +2 -1
- package/dist/core/untrusted-egress.d.ts +20 -0
- package/dist/core/untrusted-egress.js +77 -3
- package/dist/engine/harness/agent-harness.d.ts +7 -5
- package/dist/engine/harness/agent-harness.js +11 -2
- package/dist/engine/loop/agent-loop.d.ts +1 -4
- package/dist/engine/loop/agent-loop.js +26 -4
- package/dist/index.d.ts +12 -7
- package/dist/index.js +7 -4
- package/dist/internal/harness.d.ts +2 -1
- package/dist/internal/harness.js +1 -0
- package/dist/primitives/credential-ref.d.ts +89 -0
- package/dist/primitives/credential-ref.js +125 -0
- package/dist/primitives/index.d.ts +2 -0
- package/dist/primitives/index.js +1 -0
- package/dist/stores/file/checkpoint-store.d.ts +0 -4
- package/dist/stores/file/checkpoint-store.js +0 -1
- package/dist/stores/file/file-history-store.js +12 -4
- package/dist/stores/file/mailbox-store.d.ts +2 -1
- package/dist/stores/file/mailbox-store.js +26 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +15 -19
- package/dist/tools/fs/bash-readonly-classifier.js +5 -10
- package/dist/tools/fs/fs-bash.js +10 -3
- package/dist/tools/fs/pattern-kill-guard.d.ts +3 -2
- package/dist/tools/fs/pattern-kill-guard.js +68 -36
- package/dist/tools/fs/shell-read-check.d.ts +10 -0
- package/dist/tools/fs/shell-read-check.js +7 -1
- package/dist/tools/fs/shell-read-identity.d.ts +12 -9
- package/dist/tools/fs/shell-read-identity.js +9 -4
- package/dist/tools/web.js +9 -7
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +79 -3
- package/wire-changes/core-stores-sql@7.57.0.json +29 -0
- package/wire-changes/core@7.57.0.json +1487 -0
- package/wire-changes/mods-kernel@7.57.0.json +29 -0
- package/wire-changes/primitives@7.57.0.json +245 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isMailboxDecisionDeliverable, isMailboxDecisionFinal } from "../core/mailbox-store.js";
|
|
1
2
|
import { PEER_HELD_QUEUE_CAP, PEER_HELD_REVIEW_CAUSES } from "./peer-notices.js";
|
|
2
3
|
import { peerSessionBoxHandle } from "./peer-directory.js";
|
|
3
4
|
export const realPeerClock = {
|
|
@@ -10,14 +11,23 @@ export const realPeerClock = {
|
|
|
10
11
|
clearTimeout: (h) => clearTimeout(h),
|
|
11
12
|
};
|
|
12
13
|
const crashHandlers = new WeakMap();
|
|
13
|
-
export const PEER_HELD_DECISION_MEMORY = PEER_HELD_QUEUE_CAP * 4;
|
|
14
14
|
export function createPeerHeldQueue(key, opts = {}) {
|
|
15
15
|
const cap = opts.cap ?? PEER_HELD_QUEUE_CAP;
|
|
16
16
|
const clock = opts.clock ?? realPeerClock;
|
|
17
17
|
const entries = [];
|
|
18
18
|
const deadlines = new Map();
|
|
19
|
-
const decisions = new Map();
|
|
20
19
|
let cfg;
|
|
20
|
+
const cardChains = new Map();
|
|
21
|
+
const onCard = (seq, f) => {
|
|
22
|
+
const run = (cardChains.get(seq) ?? Promise.resolve()).then(f);
|
|
23
|
+
const tail = run.then(() => undefined, () => undefined);
|
|
24
|
+
cardChains.set(seq, tail);
|
|
25
|
+
void tail.then(() => {
|
|
26
|
+
if (cardChains.get(seq) === tail)
|
|
27
|
+
cardChains.delete(seq);
|
|
28
|
+
});
|
|
29
|
+
return run;
|
|
30
|
+
};
|
|
21
31
|
const sinkOrThrow = () => {
|
|
22
32
|
if (cfg === undefined)
|
|
23
33
|
throw new Error("PeerHeldQueue: not configured — bind a run (configure) before holding messages");
|
|
@@ -43,103 +53,199 @@ export function createPeerHeldQueue(key, opts = {}) {
|
|
|
43
53
|
entries.splice(i, 1);
|
|
44
54
|
clearDeadline(entry.seq);
|
|
45
55
|
};
|
|
46
|
-
const
|
|
56
|
+
const leave = (entry, settlement, reason, receipt) => {
|
|
47
57
|
const sink = sinkOrThrow();
|
|
48
58
|
remove(entry);
|
|
49
|
-
if (settlement !== "delivered") {
|
|
50
|
-
decisions.set(entry.seq, settlement);
|
|
51
|
-
while (decisions.size > PEER_HELD_DECISION_MEMORY) {
|
|
52
|
-
const oldest = decisions.keys().next();
|
|
53
|
-
if (oldest.done === true)
|
|
54
|
-
break;
|
|
55
|
-
decisions.delete(oldest.value);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
59
|
if (receipt !== undefined)
|
|
59
60
|
safe(() => sink.receipt(entry, receipt));
|
|
60
61
|
safe(() => sink.onSettled?.(entry, settlement, reason, entries.length));
|
|
61
62
|
};
|
|
63
|
+
const move = async (entry, next) => {
|
|
64
|
+
const sink = sinkOrThrow();
|
|
65
|
+
let outcome;
|
|
66
|
+
try {
|
|
67
|
+
outcome = await sink.decide(entry, entry.decision, next);
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
safe(() => sink.onFault?.(entry, e));
|
|
71
|
+
return "fault";
|
|
72
|
+
}
|
|
73
|
+
if (outcome.ok)
|
|
74
|
+
return "won";
|
|
75
|
+
adoptCurrent(entry, outcome.current);
|
|
76
|
+
return "lost";
|
|
77
|
+
};
|
|
78
|
+
const adoptCurrent = (entry, current) => {
|
|
79
|
+
if (!entries.includes(entry))
|
|
80
|
+
return;
|
|
81
|
+
if (current === "gone" || current === "unset")
|
|
82
|
+
return leave(entry, "retired", "superseded", undefined);
|
|
83
|
+
if (isMailboxDecisionFinal(current))
|
|
84
|
+
return leave(entry, current, "decided_elsewhere", undefined);
|
|
85
|
+
if (isMailboxDecisionDeliverable(current) && entry.decision !== current) {
|
|
86
|
+
entry.decision = current;
|
|
87
|
+
clearDeadline(entry.seq);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const settleLocal = async (entry, settlement, reason) => {
|
|
91
|
+
const sink = sinkOrThrow();
|
|
92
|
+
let tells = false;
|
|
93
|
+
try {
|
|
94
|
+
tells = sink.tellsSender(entry);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
tells = false;
|
|
98
|
+
}
|
|
99
|
+
const from = settlement === "expired" ? "held" : "delivered";
|
|
100
|
+
if (!tells || entry.decision !== from)
|
|
101
|
+
return leave(entry, "retired", reason, undefined);
|
|
102
|
+
const r = await move(entry, settlement);
|
|
103
|
+
if (r === "won")
|
|
104
|
+
leave(entry, settlement, reason, settlement);
|
|
105
|
+
else if (r === "fault")
|
|
106
|
+
leave(entry, "retired", reason, undefined);
|
|
107
|
+
};
|
|
62
108
|
const armDeadline = (entry) => {
|
|
63
109
|
if (cfg === undefined || cfg.dialogExpiryMs === null || deadlines.has(entry.seq))
|
|
64
110
|
return;
|
|
65
|
-
if (entry.
|
|
111
|
+
if (entry.decision !== "held")
|
|
66
112
|
return;
|
|
67
113
|
if (!PEER_HELD_REVIEW_CAUSES.includes(entry.cause))
|
|
68
114
|
return;
|
|
69
115
|
const h = clock.setTimeout(() => {
|
|
70
116
|
deadlines.delete(entry.seq);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
117
|
+
void (async () => {
|
|
118
|
+
if (!entries.includes(entry))
|
|
119
|
+
return;
|
|
120
|
+
await rejudge();
|
|
121
|
+
await onCard(entry.seq, async () => {
|
|
122
|
+
if (!entries.includes(entry) || entry.decision !== "held")
|
|
123
|
+
return;
|
|
124
|
+
if ((await move(entry, "expired")) === "won")
|
|
125
|
+
leave(entry, "expired", "deadline", "expired");
|
|
126
|
+
});
|
|
127
|
+
})().catch(() => undefined);
|
|
77
128
|
}, cfg.dialogExpiryMs);
|
|
78
129
|
deadlines.set(entry.seq, h);
|
|
79
130
|
};
|
|
80
|
-
const
|
|
131
|
+
const mayHandOver = (entry) => {
|
|
81
132
|
const sink = sinkOrThrow();
|
|
82
133
|
if (sink.inject === undefined)
|
|
134
|
+
return false;
|
|
135
|
+
try {
|
|
136
|
+
return sink.canInject === undefined || sink.canInject(entry);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const tryDeliver = async (entry, reason) => {
|
|
143
|
+
const sink = sinkOrThrow();
|
|
144
|
+
if (sink.inject === undefined || !mayHandOver(entry))
|
|
83
145
|
return "no_leg";
|
|
146
|
+
if (entry.decision !== "delivered") {
|
|
147
|
+
const r = await move(entry, "delivered");
|
|
148
|
+
if (r === "lost")
|
|
149
|
+
return "lost";
|
|
150
|
+
if (r === "fault")
|
|
151
|
+
return "no_leg";
|
|
152
|
+
entry.decision = "delivered";
|
|
153
|
+
clearDeadline(entry.seq);
|
|
154
|
+
}
|
|
84
155
|
try {
|
|
85
156
|
if (sink.inject(entry) === "deferred")
|
|
86
157
|
return "no_leg";
|
|
87
158
|
}
|
|
88
159
|
catch {
|
|
89
|
-
|
|
160
|
+
await settleLocal(entry, "dropped", "inject_failed");
|
|
90
161
|
return "dropped";
|
|
91
162
|
}
|
|
92
|
-
|
|
163
|
+
leave(entry, "delivered", reason, "delivered");
|
|
93
164
|
return "delivered";
|
|
94
165
|
};
|
|
95
|
-
const
|
|
166
|
+
const refuseCard = async (entry, reason) => {
|
|
167
|
+
const owed = entry.decision === "delivered";
|
|
168
|
+
if (owed && !mayHandOver(entry))
|
|
169
|
+
return "waits";
|
|
170
|
+
const word = owed ? "dropped" : "refused";
|
|
171
|
+
const r = await move(entry, word);
|
|
172
|
+
if (r === "won")
|
|
173
|
+
leave(entry, word, reason, word);
|
|
174
|
+
return r;
|
|
175
|
+
};
|
|
176
|
+
const rejudgeOne = async (entry, reannounce) => {
|
|
177
|
+
const sink = sinkOrThrow();
|
|
178
|
+
let verdict;
|
|
179
|
+
try {
|
|
180
|
+
verdict = sink.judge(entry);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return "held";
|
|
184
|
+
}
|
|
185
|
+
if (verdict.verdict === "refuse")
|
|
186
|
+
return (await refuseCard(entry, "rejudge")) === "won" ? "refused" : "held";
|
|
187
|
+
if (verdict.verdict === "deliver" || isMailboxDecisionDeliverable(entry.decision)) {
|
|
188
|
+
const r = await tryDeliver(entry, entry.decision === "approved" ? "approved" : "rejudge");
|
|
189
|
+
return r === "delivered" ? "released" : "held";
|
|
190
|
+
}
|
|
191
|
+
if (verdict.verdict === "hold" && verdict.cause !== entry.cause) {
|
|
192
|
+
entry.cause = verdict.cause;
|
|
193
|
+
reannounce.push([entry, verdict.cause]);
|
|
194
|
+
clearDeadline(entry.seq);
|
|
195
|
+
armDeadline(entry);
|
|
196
|
+
}
|
|
197
|
+
return "held";
|
|
198
|
+
};
|
|
199
|
+
const rejudge = async () => {
|
|
200
|
+
sinkOrThrow();
|
|
201
|
+
let released = 0;
|
|
202
|
+
let refused = 0;
|
|
203
|
+
const reannounce = [];
|
|
204
|
+
for (const entry of [...entries]) {
|
|
205
|
+
const r = await onCard(entry.seq, async () => (entries.includes(entry) ? rejudgeOne(entry, reannounce) : "held"));
|
|
206
|
+
if (r === "released")
|
|
207
|
+
released += 1;
|
|
208
|
+
else if (r === "refused")
|
|
209
|
+
refused += 1;
|
|
210
|
+
}
|
|
96
211
|
const sink = sinkOrThrow();
|
|
212
|
+
for (const [entry, cause] of reannounce)
|
|
213
|
+
if (entries.includes(entry))
|
|
214
|
+
safe(() => sink.onHeld?.(entry, entries.length, cause, true));
|
|
215
|
+
return { released, refused, held: entries.length };
|
|
216
|
+
};
|
|
217
|
+
const reconcile = (snapshot) => {
|
|
218
|
+
sinkOrThrow();
|
|
97
219
|
let retired = 0;
|
|
98
220
|
for (const entry of [...entries]) {
|
|
99
|
-
if (
|
|
221
|
+
if (cardChains.has(entry.seq))
|
|
100
222
|
continue;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
223
|
+
const present = snapshot.has(entry.seq);
|
|
224
|
+
const before = entries.length;
|
|
225
|
+
adoptCurrent(entry, present ? (snapshot.get(entry.seq) ?? "unset") : "gone");
|
|
226
|
+
if (entries.length < before)
|
|
227
|
+
retired += 1;
|
|
104
228
|
}
|
|
105
229
|
return retired;
|
|
106
230
|
};
|
|
107
|
-
const
|
|
231
|
+
const makeRoom = async () => {
|
|
232
|
+
if (entries.length < cap)
|
|
233
|
+
return true;
|
|
108
234
|
const sink = sinkOrThrow();
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
for (const entry of [...entries]) {
|
|
113
|
-
let verdict;
|
|
235
|
+
const victim = entries.find((e) => {
|
|
236
|
+
if (e.decision !== "held")
|
|
237
|
+
return false;
|
|
114
238
|
try {
|
|
115
|
-
|
|
239
|
+
return sink.tellsSender(e);
|
|
116
240
|
}
|
|
117
241
|
catch {
|
|
118
|
-
|
|
242
|
+
return false;
|
|
119
243
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (verdict.verdict === "deliver" || entry.released === true) {
|
|
126
|
-
const r = tryDeliver(entry, entry.released === true ? "approved" : "rejudge");
|
|
127
|
-
if (r === "no_leg")
|
|
128
|
-
entry.released = true;
|
|
129
|
-
else if (r === "delivered")
|
|
130
|
-
released += 1;
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
if (verdict.cause !== entry.cause) {
|
|
134
|
-
entry.cause = verdict.cause;
|
|
135
|
-
reannounce.push([entry, verdict.cause]);
|
|
136
|
-
clearDeadline(entry.seq);
|
|
137
|
-
armDeadline(entry);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
for (const [entry, cause] of reannounce)
|
|
141
|
-
safe(() => sink.onHeld?.(entry, entries.length, cause, true));
|
|
142
|
-
return { released, refused, held: entries.length };
|
|
244
|
+
});
|
|
245
|
+
if (victim === undefined)
|
|
246
|
+
return false;
|
|
247
|
+
await onCard(victim.seq, async () => (entries.includes(victim) ? settleLocal(victim, "expired", "evicted") : undefined));
|
|
248
|
+
return entries.length < cap;
|
|
143
249
|
};
|
|
144
250
|
const queue = {
|
|
145
251
|
key,
|
|
@@ -160,63 +266,55 @@ export function createPeerHeldQueue(key, opts = {}) {
|
|
|
160
266
|
cfg = { ...cfg, sink: rest };
|
|
161
267
|
},
|
|
162
268
|
configured: () => cfg !== undefined,
|
|
163
|
-
hold(input) {
|
|
269
|
+
async hold(input, known) {
|
|
164
270
|
const sink = sinkOrThrow();
|
|
165
271
|
if (entries.some((e) => e.box === input.box && e.seq === input.seq))
|
|
166
|
-
return;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
272
|
+
return "adopted";
|
|
273
|
+
let state = known ?? "held";
|
|
274
|
+
let fresh = false;
|
|
275
|
+
if (known === undefined) {
|
|
276
|
+
let outcome;
|
|
277
|
+
try {
|
|
278
|
+
outcome = await sink.decide({ ...input, heldAt: clock.now(), decision: "held" }, undefined, "held");
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
safe(() => sink.onFault?.({ ...input, heldAt: clock.now(), decision: "held" }, e));
|
|
282
|
+
return "fault";
|
|
283
|
+
}
|
|
284
|
+
if (outcome.ok)
|
|
285
|
+
fresh = true;
|
|
286
|
+
else if (outcome.current === "gone" || outcome.current === "unset")
|
|
287
|
+
return "gone";
|
|
288
|
+
else if (isMailboxDecisionFinal(outcome.current))
|
|
289
|
+
return outcome.current;
|
|
290
|
+
else
|
|
291
|
+
state = outcome.current;
|
|
292
|
+
}
|
|
293
|
+
if (entries.some((e) => e.box === input.box && e.seq === input.seq))
|
|
294
|
+
return fresh ? "held" : "adopted";
|
|
295
|
+
if (!(await makeRoom())) {
|
|
296
|
+
if (fresh)
|
|
297
|
+
safe(() => sink.receipt({ ...input, heldAt: clock.now(), decision: state }, "held"));
|
|
298
|
+
return "uncarded";
|
|
171
299
|
}
|
|
300
|
+
const entry = { ...input, heldAt: clock.now(), decision: state };
|
|
172
301
|
entries.push(entry);
|
|
173
302
|
armDeadline(entry);
|
|
174
|
-
|
|
175
|
-
|
|
303
|
+
if (state === "held")
|
|
304
|
+
safe(() => sink.onHeld?.(entry, entries.length, entry.cause, false));
|
|
305
|
+
if (fresh)
|
|
306
|
+
safe(() => sink.receipt(entry, "held"));
|
|
307
|
+
return fresh ? "held" : "adopted";
|
|
176
308
|
},
|
|
177
309
|
rejudge,
|
|
178
310
|
reconcile,
|
|
179
311
|
settle(seq, decision) {
|
|
180
|
-
|
|
181
|
-
const entry = entries.find((e) => e.seq === seq);
|
|
182
|
-
if (entry === undefined)
|
|
183
|
-
return "gone";
|
|
184
|
-
if (decision === "deny") {
|
|
185
|
-
settleOut(entry, "denied", "denied", "denied");
|
|
186
|
-
return "denied";
|
|
187
|
-
}
|
|
188
|
-
if (decision === "cancel") {
|
|
189
|
-
settleOut(entry, "expired", "cancelled", "expired");
|
|
190
|
-
return "expired";
|
|
191
|
-
}
|
|
192
|
-
let verdict;
|
|
193
|
-
try {
|
|
194
|
-
verdict = sink.judge(entry);
|
|
195
|
-
}
|
|
196
|
-
catch {
|
|
197
|
-
entry.released = true;
|
|
198
|
-
return "released_pending";
|
|
199
|
-
}
|
|
200
|
-
if (verdict.verdict === "refuse") {
|
|
201
|
-
settleOut(entry, "refused", "approved", "refused");
|
|
202
|
-
return "dropped_by_policy";
|
|
203
|
-
}
|
|
204
|
-
const r = tryDeliver(entry, "approved");
|
|
205
|
-
if (r === "no_leg") {
|
|
206
|
-
entry.released = true;
|
|
207
|
-
clearDeadline(entry.seq);
|
|
208
|
-
return "released_pending";
|
|
209
|
-
}
|
|
210
|
-
return r;
|
|
312
|
+
return onCard(seq, () => settleCard(seq, decision));
|
|
211
313
|
},
|
|
314
|
+
has: (seq) => entries.some((e) => e.seq === seq),
|
|
212
315
|
list: () => entries.map((e) => ({ ...e, ...(e.hopChain !== undefined ? { hopChain: [...e.hopChain] } : {}), ...(e.peerMeta !== undefined ? { peerMeta: { ...e.peerMeta } } : {}) })),
|
|
213
316
|
size: () => entries.length,
|
|
214
|
-
|
|
215
|
-
forgetDecisions(seqs) {
|
|
216
|
-
for (const seq of seqs)
|
|
217
|
-
decisions.delete(seq);
|
|
218
|
-
},
|
|
219
|
-
shutdown() {
|
|
317
|
+
async shutdown() {
|
|
220
318
|
if (cfg === undefined) {
|
|
221
319
|
entries.length = 0;
|
|
222
320
|
for (const h of deadlines.values())
|
|
@@ -224,12 +322,63 @@ export function createPeerHeldQueue(key, opts = {}) {
|
|
|
224
322
|
deadlines.clear();
|
|
225
323
|
return;
|
|
226
324
|
}
|
|
227
|
-
for (const entry of [...entries])
|
|
228
|
-
|
|
325
|
+
for (const entry of [...entries]) {
|
|
326
|
+
await onCard(entry.seq, async () => {
|
|
327
|
+
if (entries.includes(entry))
|
|
328
|
+
await settleLocal(entry, "expired", "shutdown");
|
|
329
|
+
});
|
|
330
|
+
}
|
|
229
331
|
},
|
|
230
332
|
};
|
|
333
|
+
async function settleCard(seq, decision) {
|
|
334
|
+
const sink = sinkOrThrow();
|
|
335
|
+
const entry = entries.find((e) => e.seq === seq);
|
|
336
|
+
if (entry === undefined)
|
|
337
|
+
return "gone";
|
|
338
|
+
const handedOver = entry.decision === "delivered";
|
|
339
|
+
if (decision === "deny" || decision === "cancel") {
|
|
340
|
+
if (handedOver)
|
|
341
|
+
return "delivered";
|
|
342
|
+
const word = decision === "deny" ? "denied" : "expired";
|
|
343
|
+
const r = await move(entry, word);
|
|
344
|
+
if (r === "fault")
|
|
345
|
+
throw new Error(`PeerHeldQueue.settle: the ${decision} of seq ${seq} could not be recorded (the store refused the move); nothing was decided`);
|
|
346
|
+
if (r === "lost")
|
|
347
|
+
return "decided_elsewhere";
|
|
348
|
+
leave(entry, word, decision === "deny" ? "denied" : "cancelled", word);
|
|
349
|
+
return word;
|
|
350
|
+
}
|
|
351
|
+
let verdict;
|
|
352
|
+
try {
|
|
353
|
+
verdict = sink.judge(entry);
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
verdict = undefined;
|
|
357
|
+
}
|
|
358
|
+
if (verdict?.verdict === "refuse") {
|
|
359
|
+
const r = await refuseCard(entry, "approved");
|
|
360
|
+
if (r === "fault")
|
|
361
|
+
throw new Error(`PeerHeldQueue.settle: the refusal of seq ${seq} could not be recorded (the store refused the move)`);
|
|
362
|
+
if (r !== "waits")
|
|
363
|
+
return r === "lost" ? "decided_elsewhere" : "dropped_by_policy";
|
|
364
|
+
}
|
|
365
|
+
if (handedOver)
|
|
366
|
+
return "delivered";
|
|
367
|
+
if (entry.decision === "held") {
|
|
368
|
+
const r = await move(entry, "approved");
|
|
369
|
+
if (r === "fault")
|
|
370
|
+
throw new Error(`PeerHeldQueue.settle: the approval of seq ${seq} could not be recorded (the store refused the move); nothing was decided`);
|
|
371
|
+
if (r === "lost")
|
|
372
|
+
return "decided_elsewhere";
|
|
373
|
+
entry.decision = "approved";
|
|
374
|
+
clearDeadline(entry.seq);
|
|
375
|
+
}
|
|
376
|
+
if (verdict === undefined)
|
|
377
|
+
return "released_pending";
|
|
378
|
+
const d = await tryDeliver(entry, "approved");
|
|
379
|
+
return d === "no_leg" ? "released_pending" : d === "lost" ? "decided_elsewhere" : d;
|
|
380
|
+
}
|
|
231
381
|
crashHandlers.set(queue, () => {
|
|
232
|
-
decisions.clear();
|
|
233
382
|
entries.length = 0;
|
|
234
383
|
for (const h of deadlines.values())
|
|
235
384
|
clock.clearTimeout(h);
|