@evomap/evolver-proxy 2.0.0-beta.2 → 2.0.0-beta.22
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/bin/evolver-llm-proxy.js +0 -0
- package/dist/bin/evolver-proxy.d.ts +105 -7
- package/dist/bin/evolver-proxy.js +877 -121
- package/dist/daemon/atpConsent.js +5 -2
- package/dist/daemon/collaborationFacade.js +26 -16
- package/dist/daemon/proxyDaemon.d.ts +65 -0
- package/dist/daemon/proxyDaemon.js +1384 -29
- package/dist/daemon/publishRecallVerifier.d.ts +114 -0
- package/dist/daemon/publishRecallVerifier.js +495 -0
- package/dist/daemon/selectHub.js +5 -3
- package/dist/daemon/systemdNotifier.d.ts +48 -0
- package/dist/daemon/systemdNotifier.js +163 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/lifecycle/claimNudge.d.ts +20 -0
- package/dist/lifecycle/claimNudge.js +124 -0
- package/dist/lifecycle/legacyNodeId.d.ts +11 -13
- package/dist/lifecycle/legacyNodeId.js +35 -20
- package/dist/lifecycle/manager.d.ts +4 -0
- package/dist/lifecycle/manager.js +15 -2
- package/dist/llm/server.js +24 -4
- package/dist/llm/traceControl.js +1 -1
- package/dist/llm/upstream.d.ts +5 -1
- package/dist/llm/upstream.js +72 -2
- package/dist/private/accountAssetCompatibility.d.ts +29 -0
- package/dist/private/accountAssetCompatibility.js +196 -0
- package/dist/private/adapterLoader.d.ts +21 -1
- package/dist/private/adapterLoader.js +242 -7
- package/dist/private/nodeCredentialStore.d.ts +23 -0
- package/dist/private/nodeCredentialStore.js +210 -0
- package/dist/router/messagesRoute.js +9 -3
- package/dist/router/providerRoutes.js +7 -3
- package/dist/selfUpdate/bootstrap.d.ts +162 -0
- package/dist/selfUpdate/bootstrap.js +3524 -0
- package/dist/selfUpdate/bootstrapReadiness.d.ts +9 -0
- package/dist/selfUpdate/bootstrapReadiness.js +153 -0
- package/dist/selfUpdate/builtinKey.d.ts +4 -0
- package/dist/selfUpdate/builtinKey.js +16 -0
- package/dist/selfUpdate/controllerLifecycleAuthority.d.ts +45 -0
- package/dist/selfUpdate/controllerLifecycleAuthority.js +61 -0
- package/dist/selfUpdate/executor.d.ts +27 -11
- package/dist/selfUpdate/executor.js +233 -58
- package/dist/selfUpdate/failureCodes.d.ts +10 -0
- package/dist/selfUpdate/failureCodes.js +13 -0
- package/dist/selfUpdate/index.d.ts +5 -1
- package/dist/selfUpdate/index.js +5 -1
- package/dist/selfUpdate/lastUpdate.d.ts +3 -1
- package/dist/selfUpdate/lastUpdate.js +37 -6
- package/dist/selfUpdate/migration.d.ts +158 -0
- package/dist/selfUpdate/migration.js +2672 -0
- package/dist/selfUpdate/policy.d.ts +19 -2
- package/dist/selfUpdate/policy.js +76 -2
- package/dist/selfUpdate/recoveryChildStartGate.d.ts +29 -0
- package/dist/selfUpdate/recoveryChildStartGate.js +319 -0
- package/dist/selfUpdate/releaseBinary.d.ts +13 -0
- package/dist/selfUpdate/releaseBinary.js +93 -10
- package/dist/selfUpdate/transaction.d.ts +117 -0
- package/dist/selfUpdate/transaction.js +1322 -0
- package/dist/selfUpdate/unixController.d.ts +23 -0
- package/dist/selfUpdate/unixController.js +514 -0
- package/dist/selfUpdate/version.d.ts +6 -2
- package/dist/selfUpdate/version.js +5 -3
- package/dist/selfUpdate/windowsController.d.ts +35 -0
- package/dist/selfUpdate/windowsController.js +655 -0
- package/dist/selfUpdate/windowsUpdater.d.ts +104 -0
- package/dist/selfUpdate/windowsUpdater.js +882 -0
- package/dist/sync/engine.d.ts +12 -0
- package/dist/sync/engine.js +255 -64
- package/package.json +10 -3
package/dist/sync/engine.d.ts
CHANGED
|
@@ -23,6 +23,13 @@ export interface SyncEngineDeps {
|
|
|
23
23
|
onOutboundSucceeded?: (envelope: Envelope, result: unknown) => void | Promise<void>;
|
|
24
24
|
/** Cache a durable facade terminal result after SyncEngine moved the envelope to DLQ. */
|
|
25
25
|
onOutboundTerminal?: (envelope: Envelope, error: unknown) => void | Promise<void>;
|
|
26
|
+
/** Return the durable marker that proves a racing facade request already reached external acceptance. */
|
|
27
|
+
acceptedOutcomeKey?: (envelope: Envelope) => string | undefined;
|
|
28
|
+
/** Return a replayable terminal result that must be committed atomically with the mailbox DLQ transition. */
|
|
29
|
+
terminalOutcome?: (envelope: Envelope, error: unknown) => {
|
|
30
|
+
key: string;
|
|
31
|
+
result: unknown;
|
|
32
|
+
} | undefined;
|
|
26
33
|
/** Canonicalize an inbound envelope before durable insertion. */
|
|
27
34
|
normalizeInboundEnvelope?: (envelope: Envelope) => Envelope;
|
|
28
35
|
onOutboundFlushed?: (result: OutboundResult) => void | Promise<void>;
|
|
@@ -44,6 +51,7 @@ export interface InboundResult {
|
|
|
44
51
|
}
|
|
45
52
|
export declare class SyncEngine {
|
|
46
53
|
private readonly deps;
|
|
54
|
+
private readonly acceptedTaskOutboundMemory;
|
|
47
55
|
private lastActivityAt;
|
|
48
56
|
constructor(deps: SyncEngineDeps);
|
|
49
57
|
/** 出站: claim proxy 消息 → 经 proxyHandler 推 hub → complete; 终态(publish reject)直进 DLQ 不重试(money-safety). */
|
|
@@ -59,6 +67,10 @@ export declare class SyncEngine {
|
|
|
59
67
|
private notifyOutboundFlushed;
|
|
60
68
|
private normalizeOutbound;
|
|
61
69
|
private outboundDedupKey;
|
|
70
|
+
private acceptedTaskOutbound;
|
|
71
|
+
private durableAcceptedTaskOutbound;
|
|
72
|
+
private completeAcceptedOutcome;
|
|
73
|
+
private rememberAcceptedTaskOutbound;
|
|
62
74
|
private safeAck;
|
|
63
75
|
/** AgentEvent → core Envelope; 未知类型(不在目录)跳过. */
|
|
64
76
|
private toEnvelope;
|
package/dist/sync/engine.js
CHANGED
|
@@ -9,6 +9,9 @@ export const MAX_BATCH = 50;
|
|
|
9
9
|
export const IDLE_THRESHOLD_MS = 5 * 60_000;
|
|
10
10
|
const MAX_SYNC_ERROR_LENGTH = 2_000;
|
|
11
11
|
const CURSOR_KEY = 'sync:inbound_cursor';
|
|
12
|
+
const ACCEPTED_TASK_OUTBOUND_PREFIX = 'sync:accepted_task_outbound:';
|
|
13
|
+
const ACCEPTED_TASK_JOURNAL_WRITE_ATTEMPTS = 2;
|
|
14
|
+
const LOCAL_FINALIZE_RETRY_MS = 1_000;
|
|
12
15
|
const BATCHABLE_PROXY_TYPES = new Set(['proxy_trace']);
|
|
13
16
|
const TRACE_CONFIG_TYPES = new Set(['trace_collection_config', 'proxy_trace_config']);
|
|
14
17
|
const STATE = {
|
|
@@ -95,6 +98,12 @@ function envelopeToAgentEvent(e) {
|
|
|
95
98
|
...(e.type === 'proxy_trace' && e.idempotencyKey ? { refId: e.idempotencyKey } : {}),
|
|
96
99
|
};
|
|
97
100
|
}
|
|
101
|
+
function requiredClaimOwner(envelope) {
|
|
102
|
+
const claimOwner = mailbox.mailboxClaimOwner(envelope);
|
|
103
|
+
if (!claimOwner)
|
|
104
|
+
throw new Error(`mailbox_claim_owner_missing:${envelope.id}`);
|
|
105
|
+
return claimOwner;
|
|
106
|
+
}
|
|
98
107
|
/**
|
|
99
108
|
* SyncEngine(M6-2): proxy↔hub 双向同步. 移植 v1 sync/{engine,outbound,inbound} 到 TS,
|
|
100
109
|
* hub I/O 全走 HubCapability.mailbox(非裸 hubFetch). tick 方法纯逻辑(注入 now), 可对 FakeHubCapability 确定性测;
|
|
@@ -104,7 +113,13 @@ function applyMailboxPushManyOutcomes(group, outcomes, deps) {
|
|
|
104
113
|
const byId = new Map(outcomes.map((outcome) => [outcome.id, outcome]));
|
|
105
114
|
const result = { sent: 0, failed: 0, terminal: 0, deferred: 0, completedDuplicates: 0 };
|
|
106
115
|
for (const pushed of group) {
|
|
107
|
-
const outcome = byId.get(pushed.event.id) ?? byId.get(pushed.original.id)
|
|
116
|
+
const outcome = byId.get(pushed.event.id) ?? byId.get(pushed.original.id) ?? {
|
|
117
|
+
id: pushed.event.id,
|
|
118
|
+
status: 'failed',
|
|
119
|
+
reason: 'mailbox_response_incomplete',
|
|
120
|
+
retryable: true,
|
|
121
|
+
terminal: false,
|
|
122
|
+
};
|
|
108
123
|
if (outcome?.status === 'failed') {
|
|
109
124
|
const msg = redactAndTruncate(outcome.reason ?? 'mailbox_push_rejected');
|
|
110
125
|
const authLike = isAuthError(msg);
|
|
@@ -119,36 +134,47 @@ function applyMailboxPushManyOutcomes(group, outcomes, deps) {
|
|
|
119
134
|
result.authFailed = true;
|
|
120
135
|
result.authErrorMessage ??= msg;
|
|
121
136
|
}
|
|
122
|
-
deps.store.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
137
|
+
if (deps.store.deferClaimed(pushed.original.id, msg, nowAfterFailure, retry, requiredClaimOwner(pushed.original)))
|
|
138
|
+
result.deferred += 1;
|
|
139
|
+
for (const duplicate of pushed.duplicates) {
|
|
140
|
+
if (deps.store.deferClaimed(duplicate.id, msg, nowAfterFailure, retry, requiredClaimOwner(duplicate))) {
|
|
141
|
+
result.deferred += 1;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
126
144
|
}
|
|
127
145
|
else {
|
|
128
146
|
const maxAttempts = outcome.terminal ? 1 : undefined;
|
|
129
|
-
deps.store.
|
|
130
|
-
for (const duplicate of pushed.duplicates)
|
|
131
|
-
deps.store.
|
|
147
|
+
let transitioned = deps.store.failClaimed(pushed.original.id, msg, deps.now(), requiredClaimOwner(pushed.original), maxAttempts) ? 1 : 0;
|
|
148
|
+
for (const duplicate of pushed.duplicates) {
|
|
149
|
+
if (deps.store.failClaimed(duplicate.id, msg, deps.now(), requiredClaimOwner(duplicate), maxAttempts)) {
|
|
150
|
+
transitioned += 1;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
132
153
|
if (outcome.terminal)
|
|
133
|
-
result.terminal +=
|
|
154
|
+
result.terminal += transitioned;
|
|
134
155
|
else
|
|
135
|
-
result.failed +=
|
|
156
|
+
result.failed += transitioned;
|
|
136
157
|
}
|
|
137
158
|
}
|
|
138
159
|
else {
|
|
139
|
-
deps.store.
|
|
140
|
-
|
|
141
|
-
|
|
160
|
+
const completedOriginal = deps.store.completeClaimed(pushed.original.id, deps.now(), requiredClaimOwner(pushed.original));
|
|
161
|
+
let completedDuplicates = 0;
|
|
162
|
+
for (const duplicate of pushed.duplicates) {
|
|
163
|
+
if (deps.store.completeClaimed(duplicate.id, deps.now(), requiredClaimOwner(duplicate)))
|
|
164
|
+
completedDuplicates += 1;
|
|
165
|
+
}
|
|
142
166
|
if (pushed.dedupKey)
|
|
143
167
|
deps.store.markProcessed(pushed.dedupKey, { type: pushed.original.type }, deps.now());
|
|
144
|
-
|
|
145
|
-
|
|
168
|
+
if (completedOriginal)
|
|
169
|
+
result.sent += 1;
|
|
170
|
+
result.completedDuplicates += completedDuplicates;
|
|
146
171
|
}
|
|
147
172
|
}
|
|
148
173
|
return result;
|
|
149
174
|
}
|
|
150
175
|
export class SyncEngine {
|
|
151
176
|
deps;
|
|
177
|
+
acceptedTaskOutboundMemory = new Map();
|
|
152
178
|
lastActivityAt;
|
|
153
179
|
constructor(deps) {
|
|
154
180
|
this.deps = deps;
|
|
@@ -160,6 +186,7 @@ export class SyncEngine {
|
|
|
160
186
|
const batch = this.deps.store.claim('proxy', limit, this.deps.leaseMs ?? 30_000, now, this.deps.runtimeNamespace);
|
|
161
187
|
let sent = 0, failed = 0, terminal = 0, deferred = 0, completedDuplicates = 0;
|
|
162
188
|
let authFailed = false;
|
|
189
|
+
let localPersistenceFailed = false;
|
|
163
190
|
let authErrorMessage; // first auth-failure detail, carried up so the daemon surfaces the real code (#314)
|
|
164
191
|
for (let i = 0; i < batch.length; i += 1) {
|
|
165
192
|
const e = batch[i];
|
|
@@ -173,8 +200,9 @@ export class SyncEngine {
|
|
|
173
200
|
break;
|
|
174
201
|
const outboundDedupKey = this.outboundDedupKey(current);
|
|
175
202
|
if (outboundDedupKey && this.deps.store.isProcessed(outboundDedupKey)) {
|
|
176
|
-
this.deps.store.
|
|
177
|
-
|
|
203
|
+
if (this.deps.store.completeClaimed(current.id, this.deps.now(), requiredClaimOwner(current))) {
|
|
204
|
+
completedDuplicates += 1;
|
|
205
|
+
}
|
|
178
206
|
continue;
|
|
179
207
|
}
|
|
180
208
|
const existing = outboundDedupKey ? groupByDedupKey.get(outboundDedupKey) : undefined;
|
|
@@ -184,8 +212,9 @@ export class SyncEngine {
|
|
|
184
212
|
}
|
|
185
213
|
const guard = this.normalizeOutbound(current);
|
|
186
214
|
if (!guard.ok) {
|
|
187
|
-
this.deps.store.
|
|
188
|
-
|
|
215
|
+
if (this.deps.store.failClaimed(current.id, guard.reason, this.deps.now(), requiredClaimOwner(current), 1)) {
|
|
216
|
+
terminal += 1;
|
|
217
|
+
}
|
|
189
218
|
continue;
|
|
190
219
|
}
|
|
191
220
|
const entry = {
|
|
@@ -220,22 +249,24 @@ export class SyncEngine {
|
|
|
220
249
|
if (applied.deferredFailure) {
|
|
221
250
|
const nowAfterFailure = this.deps.now();
|
|
222
251
|
for (const pending of batch.slice(j)) {
|
|
223
|
-
this.deps.store.
|
|
224
|
-
|
|
252
|
+
if (this.deps.store.deferClaimed(pending.id, applied.deferredFailure.msg, nowAfterFailure, applied.deferredFailure.retryAfterMs, requiredClaimOwner(pending)))
|
|
253
|
+
deferred += 1;
|
|
225
254
|
}
|
|
226
255
|
break;
|
|
227
256
|
}
|
|
228
257
|
}
|
|
229
258
|
else {
|
|
230
259
|
for (const pushed of group) {
|
|
231
|
-
this.deps.store.
|
|
232
|
-
|
|
233
|
-
|
|
260
|
+
if (this.deps.store.completeClaimed(pushed.original.id, this.deps.now(), requiredClaimOwner(pushed.original)))
|
|
261
|
+
sent += 1;
|
|
262
|
+
for (const duplicate of pushed.duplicates) {
|
|
263
|
+
if (this.deps.store.completeClaimed(duplicate.id, this.deps.now(), requiredClaimOwner(duplicate))) {
|
|
264
|
+
completedDuplicates += 1;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
234
267
|
if (pushed.dedupKey)
|
|
235
268
|
this.deps.store.markProcessed(pushed.dedupKey, { type: pushed.original.type }, this.deps.now());
|
|
236
|
-
completedDuplicates += pushed.duplicates.length;
|
|
237
269
|
}
|
|
238
|
-
sent += group.length;
|
|
239
270
|
}
|
|
240
271
|
i = j - 1;
|
|
241
272
|
continue;
|
|
@@ -248,14 +279,17 @@ export class SyncEngine {
|
|
|
248
279
|
const nowAfterFailure = this.deps.now();
|
|
249
280
|
const retry = retryAfterMs(err);
|
|
250
281
|
for (const pushed of group) {
|
|
251
|
-
this.deps.store.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
282
|
+
if (this.deps.store.deferClaimed(pushed.original.id, msg, nowAfterFailure, retry, requiredClaimOwner(pushed.original)))
|
|
283
|
+
deferred += 1;
|
|
284
|
+
for (const duplicate of pushed.duplicates) {
|
|
285
|
+
if (this.deps.store.deferClaimed(duplicate.id, msg, nowAfterFailure, retry, requiredClaimOwner(duplicate)))
|
|
286
|
+
deferred += 1;
|
|
287
|
+
}
|
|
255
288
|
}
|
|
256
289
|
for (const pending of batch.slice(j)) {
|
|
257
|
-
this.deps.store.
|
|
258
|
-
|
|
290
|
+
if (this.deps.store.deferClaimed(pending.id, msg, nowAfterFailure, retry, requiredClaimOwner(pending))) {
|
|
291
|
+
deferred += 1;
|
|
292
|
+
}
|
|
259
293
|
}
|
|
260
294
|
authFailed = true;
|
|
261
295
|
authErrorMessage ??= msg;
|
|
@@ -263,10 +297,12 @@ export class SyncEngine {
|
|
|
263
297
|
}
|
|
264
298
|
else if (isTerminal(err, group[0].original.type)) {
|
|
265
299
|
for (const pushed of group) {
|
|
266
|
-
this.deps.store.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
300
|
+
if (this.deps.store.failClaimed(pushed.original.id, msg, this.deps.now(), requiredClaimOwner(pushed.original), 1))
|
|
301
|
+
terminal += 1;
|
|
302
|
+
for (const duplicate of pushed.duplicates) {
|
|
303
|
+
if (this.deps.store.failClaimed(duplicate.id, msg, this.deps.now(), requiredClaimOwner(duplicate), 1))
|
|
304
|
+
terminal += 1;
|
|
305
|
+
}
|
|
270
306
|
}
|
|
271
307
|
i = j - 1;
|
|
272
308
|
}
|
|
@@ -274,23 +310,27 @@ export class SyncEngine {
|
|
|
274
310
|
const nowAfterFailure = this.deps.now();
|
|
275
311
|
const retry = retryAfterMs(err);
|
|
276
312
|
for (const pushed of group) {
|
|
277
|
-
this.deps.store.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
313
|
+
if (this.deps.store.deferClaimed(pushed.original.id, msg, nowAfterFailure, retry, requiredClaimOwner(pushed.original)))
|
|
314
|
+
deferred += 1;
|
|
315
|
+
for (const duplicate of pushed.duplicates) {
|
|
316
|
+
if (this.deps.store.deferClaimed(duplicate.id, msg, nowAfterFailure, retry, requiredClaimOwner(duplicate)))
|
|
317
|
+
deferred += 1;
|
|
318
|
+
}
|
|
281
319
|
}
|
|
282
320
|
for (const pending of batch.slice(j)) {
|
|
283
|
-
this.deps.store.
|
|
284
|
-
|
|
321
|
+
if (this.deps.store.deferClaimed(pending.id, msg, nowAfterFailure, retry, requiredClaimOwner(pending)))
|
|
322
|
+
deferred += 1;
|
|
285
323
|
}
|
|
286
324
|
break;
|
|
287
325
|
}
|
|
288
326
|
else {
|
|
289
327
|
for (const pushed of group) {
|
|
290
|
-
this.deps.store.
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
328
|
+
if (this.deps.store.failClaimed(pushed.original.id, msg, this.deps.now(), requiredClaimOwner(pushed.original)))
|
|
329
|
+
failed += 1;
|
|
330
|
+
for (const duplicate of pushed.duplicates) {
|
|
331
|
+
if (this.deps.store.failClaimed(duplicate.id, msg, this.deps.now(), requiredClaimOwner(duplicate)))
|
|
332
|
+
failed += 1;
|
|
333
|
+
}
|
|
294
334
|
}
|
|
295
335
|
i = j - 1;
|
|
296
336
|
}
|
|
@@ -299,25 +339,99 @@ export class SyncEngine {
|
|
|
299
339
|
}
|
|
300
340
|
const outboundDedupKey = this.outboundDedupKey(e);
|
|
301
341
|
if (outboundDedupKey && this.deps.store.isProcessed(outboundDedupKey)) {
|
|
302
|
-
this.deps.store.
|
|
303
|
-
|
|
342
|
+
if (this.deps.store.completeClaimed(e.id, this.deps.now(), requiredClaimOwner(e)))
|
|
343
|
+
completedDuplicates += 1;
|
|
304
344
|
continue;
|
|
305
345
|
}
|
|
306
346
|
const guard = this.normalizeOutbound(e);
|
|
307
347
|
if (!guard.ok) {
|
|
308
|
-
this.deps.store.
|
|
309
|
-
|
|
348
|
+
if (this.deps.store.failClaimed(e.id, redactAndTruncate(guard.reason), this.deps.now(), requiredClaimOwner(e), 1))
|
|
349
|
+
terminal += 1;
|
|
310
350
|
continue;
|
|
311
351
|
}
|
|
352
|
+
const claimOwner = requiredClaimOwner(e);
|
|
353
|
+
let handlerResult;
|
|
354
|
+
let hubAccepted = false;
|
|
312
355
|
try {
|
|
313
|
-
const
|
|
314
|
-
await this.deps.
|
|
315
|
-
|
|
356
|
+
const accepted = this.acceptedTaskOutbound(e);
|
|
357
|
+
handlerResult = accepted ? accepted.result : await this.deps.proxyHandler(guard.envelope);
|
|
358
|
+
hubAccepted = true;
|
|
359
|
+
let journalError;
|
|
360
|
+
try {
|
|
361
|
+
this.rememberAcceptedTaskOutbound(e, handlerResult);
|
|
362
|
+
}
|
|
363
|
+
catch (err) {
|
|
364
|
+
journalError = `accepted_journal: ${safeErrorMessage(err)}`;
|
|
365
|
+
}
|
|
366
|
+
if (!this.deps.store.renewClaim(e.id, this.deps.now(), this.deps.leaseMs ?? 30_000, claimOwner))
|
|
367
|
+
continue;
|
|
368
|
+
try {
|
|
369
|
+
await this.deps.onOutboundSucceeded?.(e, handlerResult);
|
|
370
|
+
}
|
|
371
|
+
catch (err) {
|
|
372
|
+
const msg = `local_finalize: ${safeErrorMessage(err)}`;
|
|
373
|
+
localPersistenceFailed = true;
|
|
374
|
+
if (journalError && !this.durableAcceptedTaskOutbound(e)) {
|
|
375
|
+
try {
|
|
376
|
+
this.rememberAcceptedTaskOutbound(e, handlerResult);
|
|
377
|
+
journalError = undefined;
|
|
378
|
+
}
|
|
379
|
+
catch (retryError) {
|
|
380
|
+
journalError = `accepted_journal_retry: ${safeErrorMessage(retryError)}`;
|
|
381
|
+
this.markError(journalError, false);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
this.markError(msg, false);
|
|
385
|
+
if (this.durableAcceptedTaskOutbound(e)) {
|
|
386
|
+
if (this.deps.store.deferClaimed(e.id, msg, this.deps.now(), LOCAL_FINALIZE_RETRY_MS, claimOwner)) {
|
|
387
|
+
deferred += 1;
|
|
388
|
+
}
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
// The Hub has already accepted this envelope. A local observer must not cause a second external action.
|
|
392
|
+
}
|
|
393
|
+
if (!this.deps.store.completeClaimed(e.id, this.deps.now(), claimOwner))
|
|
394
|
+
continue;
|
|
395
|
+
this.acceptedTaskOutboundMemory.delete(e.id);
|
|
316
396
|
if (outboundDedupKey)
|
|
317
397
|
this.deps.store.markProcessed(outboundDedupKey, { type: e.type }, this.deps.now());
|
|
398
|
+
if (journalError)
|
|
399
|
+
this.markError(journalError, false);
|
|
400
|
+
if (journalError)
|
|
401
|
+
localPersistenceFailed = true;
|
|
318
402
|
sent += 1;
|
|
319
403
|
}
|
|
320
404
|
catch (err) {
|
|
405
|
+
if (hubAccepted) {
|
|
406
|
+
if (this.deps.store.getById(e.id)?.status === 'done') {
|
|
407
|
+
this.acceptedTaskOutboundMemory.delete(e.id);
|
|
408
|
+
sent += 1;
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
const msg = `local_finalize: ${safeErrorMessage(err)}`;
|
|
412
|
+
localPersistenceFailed = true;
|
|
413
|
+
if (!this.durableAcceptedTaskOutbound(e)) {
|
|
414
|
+
try {
|
|
415
|
+
this.rememberAcceptedTaskOutbound(e, handlerResult);
|
|
416
|
+
}
|
|
417
|
+
catch (journalRetryError) {
|
|
418
|
+
this.markError(`accepted_journal_retry: ${safeErrorMessage(journalRetryError)}`, false);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
this.markError(msg, false);
|
|
422
|
+
if (this.durableAcceptedTaskOutbound(e)) {
|
|
423
|
+
if (this.deps.store.deferClaimed(e.id, msg, this.deps.now(), LOCAL_FINALIZE_RETRY_MS, claimOwner)) {
|
|
424
|
+
deferred += 1;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
// No durable local replay is possible for this callback, but the external action is already complete.
|
|
429
|
+
if (this.deps.store.completeClaimed(e.id, this.deps.now(), claimOwner))
|
|
430
|
+
sent += 1;
|
|
431
|
+
this.acceptedTaskOutboundMemory.delete(e.id);
|
|
432
|
+
}
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
321
435
|
const msg = safeErrorMessage(err);
|
|
322
436
|
const authLike = isAuthError(err);
|
|
323
437
|
this.markError(`outbound: ${msg}`, authLike);
|
|
@@ -325,18 +439,34 @@ export class SyncEngine {
|
|
|
325
439
|
const nowAfterFailure = this.deps.now();
|
|
326
440
|
const retry = retryAfterMs(err);
|
|
327
441
|
for (const pending of batch.slice(i)) {
|
|
328
|
-
this.deps.
|
|
329
|
-
|
|
442
|
+
const acceptedKey = this.deps.acceptedOutcomeKey?.(pending);
|
|
443
|
+
const pendingClaimOwner = requiredClaimOwner(pending);
|
|
444
|
+
const transitioned = acceptedKey
|
|
445
|
+
? this.deps.store.deferClaimedUnlessProcessed(pending.id, acceptedKey, msg, nowAfterFailure, retry, pendingClaimOwner)
|
|
446
|
+
: this.deps.store.deferClaimed(pending.id, msg, nowAfterFailure, retry, pendingClaimOwner);
|
|
447
|
+
if (transitioned)
|
|
448
|
+
deferred += 1;
|
|
449
|
+
else if (this.completeAcceptedOutcome(pending))
|
|
450
|
+
sent += 1;
|
|
330
451
|
}
|
|
331
452
|
authFailed = true;
|
|
332
453
|
authErrorMessage ??= msg;
|
|
333
454
|
break;
|
|
334
455
|
}
|
|
335
456
|
else if (isTerminal(err, e.type)) {
|
|
336
|
-
|
|
337
|
-
|
|
457
|
+
const transitionNow = this.deps.now();
|
|
458
|
+
const acceptedKey = this.deps.acceptedOutcomeKey?.(e);
|
|
459
|
+
const terminalOutcome = this.deps.terminalOutcome?.(e, err);
|
|
460
|
+
const transitioned = acceptedKey && terminalOutcome
|
|
461
|
+
? this.deps.store.failClaimedAndMarkProcessedUnlessProcessed(e.id, [acceptedKey], terminalOutcome.key, terminalOutcome.result, msg, transitionNow, claimOwner, 1)
|
|
462
|
+
: acceptedKey
|
|
463
|
+
? this.deps.store.failClaimedUnlessProcessed(e.id, acceptedKey, msg, transitionNow, claimOwner, 1)
|
|
464
|
+
: this.deps.store.failClaimed(e.id, msg, transitionNow, claimOwner, 1);
|
|
465
|
+
if (!transitioned) {
|
|
466
|
+
if (this.completeAcceptedOutcome(e))
|
|
467
|
+
sent += 1;
|
|
338
468
|
continue;
|
|
339
|
-
|
|
469
|
+
}
|
|
340
470
|
await this.deps.onOutboundTerminal?.(e, err);
|
|
341
471
|
terminal += 1;
|
|
342
472
|
}
|
|
@@ -344,20 +474,34 @@ export class SyncEngine {
|
|
|
344
474
|
const nowAfterFailure = this.deps.now();
|
|
345
475
|
const retry = retryAfterMs(err);
|
|
346
476
|
for (const pending of batch.slice(i)) {
|
|
347
|
-
this.deps.
|
|
348
|
-
|
|
477
|
+
const acceptedKey = this.deps.acceptedOutcomeKey?.(pending);
|
|
478
|
+
const pendingClaimOwner = requiredClaimOwner(pending);
|
|
479
|
+
const transitioned = acceptedKey
|
|
480
|
+
? this.deps.store.deferClaimedUnlessProcessed(pending.id, acceptedKey, msg, nowAfterFailure, retry, pendingClaimOwner)
|
|
481
|
+
: this.deps.store.deferClaimed(pending.id, msg, nowAfterFailure, retry, pendingClaimOwner);
|
|
482
|
+
if (transitioned)
|
|
483
|
+
deferred += 1;
|
|
484
|
+
else if (this.completeAcceptedOutcome(pending))
|
|
485
|
+
sent += 1;
|
|
349
486
|
}
|
|
350
487
|
break;
|
|
351
488
|
}
|
|
352
489
|
else {
|
|
353
|
-
|
|
354
|
-
|
|
490
|
+
const transitionNow = this.deps.now();
|
|
491
|
+
const acceptedKey = this.deps.acceptedOutcomeKey?.(e);
|
|
492
|
+
const transitioned = acceptedKey
|
|
493
|
+
? this.deps.store.failClaimedUnlessProcessed(e.id, acceptedKey, msg, transitionNow, claimOwner)
|
|
494
|
+
: this.deps.store.failClaimed(e.id, msg, transitionNow, claimOwner);
|
|
495
|
+
if (transitioned)
|
|
496
|
+
failed += 1;
|
|
497
|
+
else if (this.completeAcceptedOutcome(e))
|
|
498
|
+
sent += 1;
|
|
355
499
|
}
|
|
356
500
|
}
|
|
357
501
|
}
|
|
358
502
|
if (sent > 0)
|
|
359
503
|
this.lastActivityAt = now;
|
|
360
|
-
if (failed === 0 && terminal === 0 && deferred === 0)
|
|
504
|
+
if (failed === 0 && terminal === 0 && deferred === 0 && !localPersistenceFailed)
|
|
361
505
|
this.markOk();
|
|
362
506
|
if (sent > 0 || terminal > 0 || completedDuplicates > 0) {
|
|
363
507
|
await this.notifyOutboundFlushed({ sent, failed, terminal, deferred });
|
|
@@ -409,7 +553,7 @@ export class SyncEngine {
|
|
|
409
553
|
};
|
|
410
554
|
}
|
|
411
555
|
catch (err) {
|
|
412
|
-
if (isHubUnreachable(err)) {
|
|
556
|
+
if (isHubUnreachable(err) || (err instanceof HubClientError && err.status === 429)) {
|
|
413
557
|
const msg = `inbound: ${safeErrorMessage(err)}`;
|
|
414
558
|
this.markError(msg, false);
|
|
415
559
|
return { received: 0, enqueued: 0, hasMore: false, nextPollAfterMs: retryAfterMs(err) };
|
|
@@ -482,6 +626,53 @@ export class SyncEngine {
|
|
|
482
626
|
return null;
|
|
483
627
|
return e.idempotencyKey ? `outbound:${e.idempotencyKey}` : null;
|
|
484
628
|
}
|
|
629
|
+
acceptedTaskOutbound(e) {
|
|
630
|
+
if (e.type !== 'task_claim' && e.type !== 'task_complete')
|
|
631
|
+
return undefined;
|
|
632
|
+
return this.durableAcceptedTaskOutbound(e) ?? this.acceptedTaskOutboundMemory.get(e.id);
|
|
633
|
+
}
|
|
634
|
+
durableAcceptedTaskOutbound(e) {
|
|
635
|
+
if (e.type !== 'task_claim' && e.type !== 'task_complete')
|
|
636
|
+
return undefined;
|
|
637
|
+
const value = this.deps.store.getProcessed(`${ACCEPTED_TASK_OUTBOUND_PREFIX}${e.id}`);
|
|
638
|
+
if (value && typeof value === 'object' && value.accepted === true) {
|
|
639
|
+
return value;
|
|
640
|
+
}
|
|
641
|
+
const checkpoint = this.deps.store.getClaimedCheckpoint(e.id);
|
|
642
|
+
if (checkpoint && typeof checkpoint === 'object'
|
|
643
|
+
&& checkpoint.accepted === true) {
|
|
644
|
+
return checkpoint;
|
|
645
|
+
}
|
|
646
|
+
return undefined;
|
|
647
|
+
}
|
|
648
|
+
completeAcceptedOutcome(e) {
|
|
649
|
+
const acceptedKey = this.deps.acceptedOutcomeKey?.(e);
|
|
650
|
+
if (!acceptedKey || !this.deps.store.isProcessed(acceptedKey))
|
|
651
|
+
return false;
|
|
652
|
+
return this.deps.store.completeClaimed(e.id, this.deps.now(), requiredClaimOwner(e));
|
|
653
|
+
}
|
|
654
|
+
rememberAcceptedTaskOutbound(e, result) {
|
|
655
|
+
if (e.type !== 'task_claim' && e.type !== 'task_complete')
|
|
656
|
+
return;
|
|
657
|
+
const accepted = { accepted: true, result };
|
|
658
|
+
this.acceptedTaskOutboundMemory.set(e.id, accepted);
|
|
659
|
+
let lastError;
|
|
660
|
+
for (let attempt = 0; attempt < ACCEPTED_TASK_JOURNAL_WRITE_ATTEMPTS; attempt += 1) {
|
|
661
|
+
try {
|
|
662
|
+
this.deps.store.markProcessed(`${ACCEPTED_TASK_OUTBOUND_PREFIX}${e.id}`, accepted, this.deps.now());
|
|
663
|
+
this.acceptedTaskOutboundMemory.delete(e.id);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
catch (error) {
|
|
667
|
+
lastError = error;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (this.deps.store.setClaimedCheckpoint(e.id, accepted, this.deps.now(), requiredClaimOwner(e))) {
|
|
671
|
+
this.acceptedTaskOutboundMemory.delete(e.id);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
throw lastError;
|
|
675
|
+
}
|
|
485
676
|
async safeAck(id) {
|
|
486
677
|
try {
|
|
487
678
|
await this.deps.hub.mailbox.ack(id);
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evomap/evolver-proxy",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.22",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": "^22.13.0 || >=23.4.0"
|
|
8
|
+
},
|
|
6
9
|
"description": "系统级 mailbox/hub 同步 daemon (Node)",
|
|
7
10
|
"main": "./dist/index.js",
|
|
8
11
|
"types": "./dist/index.d.ts",
|
|
@@ -26,8 +29,12 @@
|
|
|
26
29
|
},
|
|
27
30
|
"dependencies": {
|
|
28
31
|
"@aws-sdk/client-bedrock-runtime": "^3.1053.0",
|
|
29
|
-
"@evomap/evolver-adapter-public": "2.0.0-beta.
|
|
30
|
-
"@evomap/evolver-core": "2.0.0-beta.
|
|
32
|
+
"@evomap/evolver-adapter-public": "2.0.0-beta.22",
|
|
33
|
+
"@evomap/evolver-core": "2.0.0-beta.22"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/EvoMap/evolver.git"
|
|
31
38
|
},
|
|
32
39
|
"publishConfig": {
|
|
33
40
|
"access": "public",
|