@evomap/evolver-proxy 2.0.0-beta.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.
Files changed (82) hide show
  1. package/README.md +49 -0
  2. package/dist/bin/envFile.d.ts +10 -0
  3. package/dist/bin/envFile.js +68 -0
  4. package/dist/bin/evolver-llm-proxy.d.ts +2 -0
  5. package/dist/bin/evolver-llm-proxy.js +111 -0
  6. package/dist/bin/evolver-proxy.d.ts +83 -0
  7. package/dist/bin/evolver-proxy.js +511 -0
  8. package/dist/bin/proxySettings.d.ts +15 -0
  9. package/dist/bin/proxySettings.js +84 -0
  10. package/dist/bin/proxyStorePath.d.ts +1 -0
  11. package/dist/bin/proxyStorePath.js +16 -0
  12. package/dist/daemon/atpConsent.d.ts +13 -0
  13. package/dist/daemon/atpConsent.js +60 -0
  14. package/dist/daemon/ipcConfig.d.ts +1 -0
  15. package/dist/daemon/ipcConfig.js +13 -0
  16. package/dist/daemon/proxyDaemon.d.ts +191 -0
  17. package/dist/daemon/proxyDaemon.js +1015 -0
  18. package/dist/daemon/selectHub.d.ts +16 -0
  19. package/dist/daemon/selectHub.js +30 -0
  20. package/dist/index.d.ts +8 -0
  21. package/dist/index.js +8 -0
  22. package/dist/lifecycle/deployGuard.d.ts +46 -0
  23. package/dist/lifecycle/deployGuard.js +53 -0
  24. package/dist/lifecycle/legacyNodeId.d.ts +96 -0
  25. package/dist/lifecycle/legacyNodeId.js +163 -0
  26. package/dist/lifecycle/manager.d.ts +106 -0
  27. package/dist/lifecycle/manager.js +390 -0
  28. package/dist/llm/bodyCapture.d.ts +31 -0
  29. package/dist/llm/bodyCapture.js +293 -0
  30. package/dist/llm/index.d.ts +3 -0
  31. package/dist/llm/index.js +3 -0
  32. package/dist/llm/server.d.ts +35 -0
  33. package/dist/llm/server.js +359 -0
  34. package/dist/llm/traceBackfill.d.ts +53 -0
  35. package/dist/llm/traceBackfill.js +525 -0
  36. package/dist/llm/traceConfig.d.ts +7 -0
  37. package/dist/llm/traceConfig.js +44 -0
  38. package/dist/llm/traceControl.d.ts +16 -0
  39. package/dist/llm/traceControl.js +85 -0
  40. package/dist/llm/traceEnvelope.d.ts +75 -0
  41. package/dist/llm/traceEnvelope.js +286 -0
  42. package/dist/llm/traceSink.d.ts +61 -0
  43. package/dist/llm/traceSink.js +278 -0
  44. package/dist/llm/traceUploadPayload.d.ts +14 -0
  45. package/dist/llm/traceUploadPayload.js +27 -0
  46. package/dist/llm/upstream.d.ts +68 -0
  47. package/dist/llm/upstream.js +491 -0
  48. package/dist/private/adapterLoader.d.ts +46 -0
  49. package/dist/private/adapterLoader.js +62 -0
  50. package/dist/private/privateRuntimeSmokeOptions.d.ts +15 -0
  51. package/dist/private/privateRuntimeSmokeOptions.js +132 -0
  52. package/dist/router/cachePassthrough.d.ts +3 -0
  53. package/dist/router/cachePassthrough.js +13 -0
  54. package/dist/router/features.d.ts +9 -0
  55. package/dist/router/features.js +52 -0
  56. package/dist/router/index.d.ts +6 -0
  57. package/dist/router/index.js +6 -0
  58. package/dist/router/messagesRoute.d.ts +138 -0
  59. package/dist/router/messagesRoute.js +753 -0
  60. package/dist/router/modelRouter.d.ts +49 -0
  61. package/dist/router/modelRouter.js +66 -0
  62. package/dist/router/providerRoutes.d.ts +42 -0
  63. package/dist/router/providerRoutes.js +1579 -0
  64. package/dist/router/sseScan.d.ts +56 -0
  65. package/dist/router/sseScan.js +543 -0
  66. package/dist/selfUpdate/executor.d.ts +90 -0
  67. package/dist/selfUpdate/executor.js +179 -0
  68. package/dist/selfUpdate/failureCodes.d.ts +33 -0
  69. package/dist/selfUpdate/failureCodes.js +84 -0
  70. package/dist/selfUpdate/index.d.ts +5 -0
  71. package/dist/selfUpdate/index.js +5 -0
  72. package/dist/selfUpdate/lastUpdate.d.ts +43 -0
  73. package/dist/selfUpdate/lastUpdate.js +195 -0
  74. package/dist/selfUpdate/policy.d.ts +3 -0
  75. package/dist/selfUpdate/policy.js +9 -0
  76. package/dist/selfUpdate/releaseBinary.d.ts +56 -0
  77. package/dist/selfUpdate/releaseBinary.js +498 -0
  78. package/dist/selfUpdate/version.d.ts +2 -0
  79. package/dist/selfUpdate/version.js +14 -0
  80. package/dist/sync/engine.d.ts +65 -0
  81. package/dist/sync/engine.js +461 -0
  82. package/package.json +41 -0
@@ -0,0 +1,461 @@
1
+ import { mailbox, hub as hubNs } from '@evomap/evolver-core';
2
+ import { normalizeProxyTraceOutboundPayload } from '../llm/traceBackfill.js';
3
+ import { applyTraceCollectionConfig } from '../llm/traceControl.js';
4
+ import { hubAuthFailureHint } from '../daemon/selectHub.js';
5
+ /** 双线程轮询节奏(沿用 v1 常量). */
6
+ export const SYNC_INTERVALS = { outboundIdle: 5_000, outboundPending: 1_000, inboundActive: 10_000, inboundIdle: 60_000 };
7
+ export const MAX_BATCH = 50;
8
+ export const IDLE_THRESHOLD_MS = 5 * 60_000;
9
+ const MAX_SYNC_ERROR_LENGTH = 2_000;
10
+ const CURSOR_KEY = 'sync:inbound_cursor';
11
+ const BATCHABLE_PROXY_TYPES = new Set(['proxy_trace']);
12
+ const TRACE_CONFIG_TYPES = new Set(['trace_collection_config', 'proxy_trace_config']);
13
+ const STATE = {
14
+ lastSyncAt: 'sync:last_sync_at',
15
+ lastError: 'sync:last_error',
16
+ authStatus: 'hub:auth_status',
17
+ };
18
+ function isTerminal(err) {
19
+ return err instanceof hubNs.PublishRejectedError && err.terminal === true;
20
+ }
21
+ function isRetryableRejection(err) {
22
+ return err instanceof hubNs.PublishRejectedError
23
+ && err.terminal === false
24
+ && (err.retryable === true || (typeof err.retryAfterMs === 'number' && Number.isFinite(err.retryAfterMs)));
25
+ }
26
+ function isHubUnreachable(err) {
27
+ const e = err;
28
+ return e?.name === 'HubUnreachableError' || e?.code === 'HUB_UNREACHABLE';
29
+ }
30
+ function retryAfterMs(err) {
31
+ const retry = err?.retryAfterMs
32
+ ?? err?.details?.retryAfterMs;
33
+ return Math.max(1_000, typeof retry === 'number' && Number.isFinite(retry) ? retry : 60_000);
34
+ }
35
+ function errorMessage(err) {
36
+ return err instanceof Error ? err.message : String(err);
37
+ }
38
+ function redactAndTruncate(message) {
39
+ try {
40
+ return hubNs.redactString(message).slice(0, MAX_SYNC_ERROR_LENGTH);
41
+ }
42
+ catch {
43
+ return '[REDACTED]';
44
+ }
45
+ }
46
+ function safeErrorMessage(err) {
47
+ return redactAndTruncate(errorMessage(err));
48
+ }
49
+ function isAuthError(err) {
50
+ // 与 proxyDaemon 的 isAuthLikeError 对齐: 先认 typed AuthError(name), 再退到 message 正则。
51
+ // 否则带通用 message 的 typed AuthError(如 'credential rejected') 会漏判, 令 hub:auth_status telemetry 与 daemon 层不一致。
52
+ return err?.name === 'AuthError'
53
+ || /\b(401|403|unauthorized|forbidden|auth)\b/i.test(errorMessage(err));
54
+ }
55
+ function canBatchMailboxPush(e) {
56
+ return BATCHABLE_PROXY_TYPES.has(e.type);
57
+ }
58
+ function isMailboxPushManyResult(value) {
59
+ return Boolean(value && typeof value === 'object' && Array.isArray(value.outcomes));
60
+ }
61
+ function envelopeToAgentEvent(e) {
62
+ const stableId = e.type === 'proxy_trace' && e.idempotencyKey ? e.idempotencyKey : e.id;
63
+ return {
64
+ id: stableId,
65
+ type: e.type,
66
+ payload: e.payload,
67
+ priority: 'medium',
68
+ createdAt: e.createdAt,
69
+ ...(e.type === 'proxy_trace' && e.idempotencyKey ? { refId: e.idempotencyKey } : {}),
70
+ };
71
+ }
72
+ /**
73
+ * SyncEngine(M6-2): proxy↔hub 双向同步. 移植 v1 sync/{engine,outbound,inbound} 到 TS,
74
+ * hub I/O 全走 HubCapability.mailbox(非裸 hubFetch). tick 方法纯逻辑(注入 now), 可对 FakeHubCapability 确定性测;
75
+ * 定时器循环(start/stop)是薄包装, 由 M6-4 daemon 装配驱动.
76
+ */
77
+ export class SyncEngine {
78
+ deps;
79
+ lastActivityAt;
80
+ constructor(deps) {
81
+ this.deps = deps;
82
+ this.lastActivityAt = deps.now();
83
+ }
84
+ /** 出站: claim proxy 消息 → 经 proxyHandler 推 hub → complete; 终态(publish reject)直进 DLQ 不重试(money-safety). */
85
+ async syncOutbound(limit = MAX_BATCH) {
86
+ const now = this.deps.now();
87
+ const batch = this.deps.store.claim('proxy', limit, this.deps.leaseMs ?? 30_000, now, this.deps.runtimeNamespace);
88
+ let sent = 0, failed = 0, terminal = 0, deferred = 0, completedDuplicates = 0;
89
+ let authFailed = false;
90
+ let authErrorMessage; // first auth-failure detail, carried up so the daemon surfaces the real code (#314)
91
+ for (let i = 0; i < batch.length; i += 1) {
92
+ const e = batch[i];
93
+ if (this.deps.hub.mailbox.pushMany && canBatchMailboxPush(e)) {
94
+ const group = [];
95
+ const groupByDedupKey = new Map();
96
+ let j = i;
97
+ for (; j < batch.length; j += 1) {
98
+ const current = batch[j];
99
+ if (!canBatchMailboxPush(current))
100
+ break;
101
+ const outboundDedupKey = this.outboundDedupKey(current);
102
+ if (outboundDedupKey && this.deps.store.isProcessed(outboundDedupKey)) {
103
+ this.deps.store.complete(current.id, this.deps.now());
104
+ completedDuplicates += 1;
105
+ continue;
106
+ }
107
+ const existing = outboundDedupKey ? groupByDedupKey.get(outboundDedupKey) : undefined;
108
+ if (existing) {
109
+ existing.duplicates.push(current);
110
+ continue;
111
+ }
112
+ const guard = this.normalizeOutbound(current);
113
+ if (!guard.ok) {
114
+ this.deps.store.fail(current.id, guard.reason, this.deps.now(), 1);
115
+ terminal += 1;
116
+ continue;
117
+ }
118
+ const entry = {
119
+ original: current,
120
+ envelope: guard.envelope,
121
+ event: envelopeToAgentEvent(guard.envelope),
122
+ dedupKey: outboundDedupKey,
123
+ duplicates: [],
124
+ };
125
+ group.push(entry);
126
+ if (outboundDedupKey)
127
+ groupByDedupKey.set(outboundDedupKey, entry);
128
+ }
129
+ if (group.length === 0) {
130
+ i = j - 1;
131
+ continue;
132
+ }
133
+ try {
134
+ const result = await this.deps.hub.mailbox.pushMany(group.map((entry) => entry.event));
135
+ if (isMailboxPushManyResult(result)) {
136
+ const byId = new Map(result.outcomes.map((outcome) => [outcome.id, outcome]));
137
+ let firstFailure;
138
+ let deferredFailure;
139
+ for (const pushed of group) {
140
+ const outcome = byId.get(pushed.event.id) ?? byId.get(pushed.original.id);
141
+ if (outcome?.status === 'failed') {
142
+ const msg = redactAndTruncate(outcome.reason ?? 'mailbox_push_rejected');
143
+ const authLike = isAuthError(msg);
144
+ firstFailure ??= msg;
145
+ const retryable = outcome.terminal !== true
146
+ && (outcome.retryable === true || (typeof outcome.retryAfterMs === 'number' && Number.isFinite(outcome.retryAfterMs)));
147
+ if (authLike || retryable) {
148
+ const nowAfterFailure = this.deps.now();
149
+ const retry = Math.max(1_000, typeof outcome.retryAfterMs === 'number' && Number.isFinite(outcome.retryAfterMs) ? outcome.retryAfterMs : 60_000);
150
+ deferredFailure ??= { msg, retryAfterMs: retry };
151
+ if (authLike) {
152
+ authFailed = true;
153
+ authErrorMessage ??= msg;
154
+ }
155
+ this.deps.store.defer(pushed.original.id, msg, nowAfterFailure, retry);
156
+ for (const duplicate of pushed.duplicates)
157
+ this.deps.store.defer(duplicate.id, msg, nowAfterFailure, retry);
158
+ deferred += 1 + pushed.duplicates.length;
159
+ }
160
+ else {
161
+ const maxAttempts = outcome.terminal ? 1 : undefined;
162
+ this.deps.store.fail(pushed.original.id, msg, this.deps.now(), maxAttempts);
163
+ for (const duplicate of pushed.duplicates)
164
+ this.deps.store.fail(duplicate.id, msg, this.deps.now(), maxAttempts);
165
+ if (outcome.terminal)
166
+ terminal += 1 + pushed.duplicates.length;
167
+ else
168
+ failed += 1 + pushed.duplicates.length;
169
+ }
170
+ }
171
+ else {
172
+ this.deps.store.complete(pushed.original.id, this.deps.now());
173
+ for (const duplicate of pushed.duplicates)
174
+ this.deps.store.complete(duplicate.id, this.deps.now());
175
+ if (pushed.dedupKey)
176
+ this.deps.store.markProcessed(pushed.dedupKey, { type: pushed.original.type }, this.deps.now());
177
+ sent += 1;
178
+ completedDuplicates += pushed.duplicates.length;
179
+ }
180
+ }
181
+ if (firstFailure)
182
+ this.markError(`outbound: ${firstFailure}`, isAuthError(firstFailure));
183
+ if (deferredFailure) {
184
+ const nowAfterFailure = this.deps.now();
185
+ for (const pending of batch.slice(j)) {
186
+ this.deps.store.defer(pending.id, deferredFailure.msg, nowAfterFailure, deferredFailure.retryAfterMs);
187
+ deferred += 1;
188
+ }
189
+ break;
190
+ }
191
+ }
192
+ else {
193
+ for (const pushed of group) {
194
+ this.deps.store.complete(pushed.original.id, this.deps.now());
195
+ for (const duplicate of pushed.duplicates)
196
+ this.deps.store.complete(duplicate.id, this.deps.now());
197
+ if (pushed.dedupKey)
198
+ this.deps.store.markProcessed(pushed.dedupKey, { type: pushed.original.type }, this.deps.now());
199
+ completedDuplicates += pushed.duplicates.length;
200
+ }
201
+ sent += group.length;
202
+ }
203
+ i = j - 1;
204
+ continue;
205
+ }
206
+ catch (err) {
207
+ const msg = safeErrorMessage(err);
208
+ const authLike = isAuthError(err);
209
+ this.markError(`outbound: ${msg}`, authLike);
210
+ if (authLike) {
211
+ const nowAfterFailure = this.deps.now();
212
+ const retry = retryAfterMs(err);
213
+ for (const pushed of group) {
214
+ this.deps.store.defer(pushed.original.id, msg, nowAfterFailure, retry);
215
+ for (const duplicate of pushed.duplicates)
216
+ this.deps.store.defer(duplicate.id, msg, nowAfterFailure, retry);
217
+ deferred += 1 + pushed.duplicates.length;
218
+ }
219
+ for (const pending of batch.slice(j)) {
220
+ this.deps.store.defer(pending.id, msg, nowAfterFailure, retry);
221
+ deferred += 1;
222
+ }
223
+ authFailed = true;
224
+ authErrorMessage ??= msg;
225
+ break;
226
+ }
227
+ else if (isTerminal(err)) {
228
+ for (const pushed of group) {
229
+ this.deps.store.fail(pushed.original.id, msg, this.deps.now(), 1);
230
+ for (const duplicate of pushed.duplicates)
231
+ this.deps.store.fail(duplicate.id, msg, this.deps.now(), 1);
232
+ terminal += 1 + pushed.duplicates.length;
233
+ }
234
+ i = j - 1;
235
+ }
236
+ else if (isRetryableRejection(err) || isHubUnreachable(err)) {
237
+ const nowAfterFailure = this.deps.now();
238
+ const retry = retryAfterMs(err);
239
+ for (const pushed of group) {
240
+ this.deps.store.defer(pushed.original.id, msg, nowAfterFailure, retry);
241
+ for (const duplicate of pushed.duplicates)
242
+ this.deps.store.defer(duplicate.id, msg, nowAfterFailure, retry);
243
+ deferred += 1 + pushed.duplicates.length;
244
+ }
245
+ for (const pending of batch.slice(j)) {
246
+ this.deps.store.defer(pending.id, msg, nowAfterFailure, retry);
247
+ deferred += 1;
248
+ }
249
+ break;
250
+ }
251
+ else {
252
+ for (const pushed of group) {
253
+ this.deps.store.fail(pushed.original.id, msg, this.deps.now());
254
+ for (const duplicate of pushed.duplicates)
255
+ this.deps.store.fail(duplicate.id, msg, this.deps.now());
256
+ failed += 1 + pushed.duplicates.length;
257
+ }
258
+ i = j - 1;
259
+ }
260
+ continue;
261
+ }
262
+ }
263
+ const outboundDedupKey = this.outboundDedupKey(e);
264
+ if (outboundDedupKey && this.deps.store.isProcessed(outboundDedupKey)) {
265
+ this.deps.store.complete(e.id, this.deps.now());
266
+ completedDuplicates += 1;
267
+ continue;
268
+ }
269
+ const guard = this.normalizeOutbound(e);
270
+ if (!guard.ok) {
271
+ this.deps.store.fail(e.id, redactAndTruncate(guard.reason), this.deps.now(), 1);
272
+ terminal += 1;
273
+ continue;
274
+ }
275
+ try {
276
+ await this.deps.proxyHandler(guard.envelope);
277
+ this.deps.store.complete(e.id, this.deps.now());
278
+ if (outboundDedupKey)
279
+ this.deps.store.markProcessed(outboundDedupKey, { type: e.type }, this.deps.now());
280
+ sent += 1;
281
+ }
282
+ catch (err) {
283
+ const msg = safeErrorMessage(err);
284
+ const authLike = isAuthError(err);
285
+ this.markError(`outbound: ${msg}`, authLike);
286
+ if (authLike) {
287
+ const nowAfterFailure = this.deps.now();
288
+ const retry = retryAfterMs(err);
289
+ for (const pending of batch.slice(i)) {
290
+ this.deps.store.defer(pending.id, msg, nowAfterFailure, retry); // Auth recovery owns retries; mailbox attempts stay intact.
291
+ deferred += 1;
292
+ }
293
+ authFailed = true;
294
+ authErrorMessage ??= msg;
295
+ break;
296
+ }
297
+ else if (isTerminal(err)) {
298
+ this.deps.store.fail(e.id, msg, this.deps.now(), 1); // maxAttempts=1 → 直进 DLQ, 不反复打经济端点
299
+ terminal += 1;
300
+ }
301
+ else if (isRetryableRejection(err) || isHubUnreachable(err)) {
302
+ const nowAfterFailure = this.deps.now();
303
+ const retry = retryAfterMs(err);
304
+ for (const pending of batch.slice(i)) {
305
+ this.deps.store.defer(pending.id, msg, nowAfterFailure, retry); // Hub outage 不消耗 attempts
306
+ deferred += 1;
307
+ }
308
+ break;
309
+ }
310
+ else {
311
+ this.deps.store.fail(e.id, msg, this.deps.now()); // 退避重试
312
+ failed += 1;
313
+ }
314
+ }
315
+ }
316
+ if (sent > 0)
317
+ this.lastActivityAt = now;
318
+ if (failed === 0 && terminal === 0 && deferred === 0)
319
+ this.markOk();
320
+ if (sent > 0 || terminal > 0 || completedDuplicates > 0) {
321
+ await this.notifyOutboundFlushed({ sent, failed, terminal, deferred });
322
+ }
323
+ return {
324
+ sent,
325
+ failed,
326
+ terminal,
327
+ deferred,
328
+ ...(authFailed ? { authFailed: true, ...(authErrorMessage ? { authErrorMessage } : {}) } : {}),
329
+ };
330
+ }
331
+ /** 入站: poll hub → 去重 enqueue 到 MailboxStore → ack; 游标存 kv. 遵守 #1195 nextPollAfterMs. */
332
+ async syncInbound() {
333
+ try {
334
+ const res = await this.deps.hub.mailbox.poll();
335
+ let enqueued = 0;
336
+ for (const ev of res.events) {
337
+ const dedupKey = `inbound:${ev.id}`;
338
+ if (this.deps.store.isProcessed(dedupKey)) {
339
+ await this.safeAck(ev.id);
340
+ continue;
341
+ }
342
+ if (TRACE_CONFIG_TYPES.has(ev.type)) {
343
+ applyTraceCollectionConfig(ev.payload, this.deps.store, this.deps.env ?? process.env);
344
+ this.deps.store.markProcessed(dedupKey, { type: ev.type }, this.deps.now());
345
+ await this.safeAck(ev.id);
346
+ if (ev.cursor)
347
+ this.deps.store.setState(CURSOR_KEY, ev.cursor);
348
+ continue;
349
+ }
350
+ const env = this.toEnvelope(ev);
351
+ if (env) {
352
+ this.deps.store.send(env);
353
+ this.deps.store.markProcessed(dedupKey, { type: ev.type }, this.deps.now());
354
+ enqueued += 1;
355
+ }
356
+ await this.safeAck(ev.id);
357
+ if (ev.cursor)
358
+ this.deps.store.setState(CURSOR_KEY, ev.cursor);
359
+ }
360
+ if (res.events.length > 0)
361
+ this.lastActivityAt = this.deps.now();
362
+ this.markOk();
363
+ return {
364
+ received: res.events.length, enqueued, hasMore: res.hasMore ?? false,
365
+ ...(res.nextPollAfterMs !== undefined ? { nextPollAfterMs: res.nextPollAfterMs } : {}),
366
+ };
367
+ }
368
+ catch (err) {
369
+ if (isHubUnreachable(err)) {
370
+ const msg = `inbound: ${safeErrorMessage(err)}`;
371
+ this.markError(msg, false);
372
+ return { received: 0, enqueued: 0, hasMore: false, nextPollAfterMs: retryAfterMs(err) };
373
+ }
374
+ this.markError(`inbound: ${safeErrorMessage(err)}`, isAuthError(err));
375
+ throw err;
376
+ }
377
+ }
378
+ /** #1195 背压: hub 给 nextPollAfterMs 优先(有 backlog→drain 立即, 否则遵守); 缺省回退 idle/active. */
379
+ nextInboundDelay(last) {
380
+ if (last.hasMore)
381
+ return 0; // drain backlog, 别 idle
382
+ if (last.nextPollAfterMs !== undefined)
383
+ return Math.max(0, last.nextPollAfterMs);
384
+ try {
385
+ return this.isIdle() ? SYNC_INTERVALS.inboundIdle : SYNC_INTERVALS.inboundActive;
386
+ }
387
+ catch (err) {
388
+ this.markError(`inbound_delay: ${safeErrorMessage(err)}`, false);
389
+ return SYNC_INTERVALS.inboundActive;
390
+ }
391
+ }
392
+ nextOutboundDelay() {
393
+ try {
394
+ return this.deps.store.countClaimable('proxy', this.deps.now(), this.deps.runtimeNamespace) > 0
395
+ ? SYNC_INTERVALS.outboundPending : SYNC_INTERVALS.outboundIdle;
396
+ }
397
+ catch (err) {
398
+ this.markError(`outbound_delay: ${safeErrorMessage(err)}`, false);
399
+ return SYNC_INTERVALS.outboundIdle;
400
+ }
401
+ }
402
+ isIdle() { return this.deps.now() - this.lastActivityAt > IDLE_THRESHOLD_MS; }
403
+ markOk() {
404
+ this.deps.store.setState(STATE.lastSyncAt, String(this.deps.now()));
405
+ this.deps.store.setState(STATE.lastError, '');
406
+ this.deps.store.setState(STATE.authStatus, 'ok');
407
+ }
408
+ markError(message, authError) {
409
+ // #314: on an auth failure append an actionable hint keyed off the hub error code in `message` (which carries
410
+ // the typed AuthError text, e.g. "a2a_auth_required") so a public-mode 401 is not silent and not misdirected.
411
+ let full = message;
412
+ if (authError) {
413
+ const hint = hubAuthFailureHint(this.deps.env ?? process.env, message);
414
+ if (hint)
415
+ full = `${message}. ${hint}`;
416
+ }
417
+ this.deps.store.setState(STATE.lastError, redactAndTruncate(full));
418
+ if (authError)
419
+ this.deps.store.setState(STATE.authStatus, 'auth_failed');
420
+ }
421
+ async notifyOutboundFlushed(result) {
422
+ try {
423
+ await this.deps.onOutboundFlushed?.(result);
424
+ }
425
+ catch (err) {
426
+ this.markError(`outbound_flush_callback: ${safeErrorMessage(err)}`, false);
427
+ }
428
+ }
429
+ normalizeOutbound(e) {
430
+ if (e.type !== 'proxy_trace')
431
+ return { ok: true, envelope: e };
432
+ const normalized = normalizeProxyTraceOutboundPayload(e.payload, this.deps.env ?? process.env, this.deps.store);
433
+ if (!normalized.ok)
434
+ return normalized;
435
+ return { ok: true, envelope: { ...e, payload: normalized.payload } };
436
+ }
437
+ outboundDedupKey(e) {
438
+ if (e.type !== 'proxy_trace')
439
+ return null;
440
+ return e.idempotencyKey ? `outbound:${e.idempotencyKey}` : null;
441
+ }
442
+ async safeAck(id) {
443
+ try {
444
+ await this.deps.hub.mailbox.ack(id);
445
+ }
446
+ catch { /* ack 失败不阻塞 poll(分离, #1195 review 点) */ }
447
+ }
448
+ /** AgentEvent → core Envelope; 未知类型(不在目录)跳过. */
449
+ toEnvelope(ev) {
450
+ try {
451
+ return mailbox.createEnvelope({
452
+ type: ev.type, payload: ev.payload, idempotencyKey: `inbound:${ev.id}`,
453
+ ...(this.deps.runtimeNamespace ? { runtimeNamespace: this.deps.runtimeNamespace } : {}),
454
+ now: this.deps.now(),
455
+ });
456
+ }
457
+ catch {
458
+ return null;
459
+ } // UnknownMessageTypeError → 跳过
460
+ }
461
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@evomap/evolver-proxy",
3
+ "version": "2.0.0-beta.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "系统级 mailbox/hub 同步 daemon (Node)",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "bin": {
10
+ "evolver-llm-proxy": "./dist/bin/evolver-llm-proxy.js",
11
+ "evolver-proxy": "./dist/bin/evolver-proxy.js"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./bin/evolver-proxy": {
19
+ "types": "./dist/bin/evolver-proxy.d.ts",
20
+ "default": "./dist/bin/evolver-proxy.js"
21
+ },
22
+ "./bin/evolver-llm-proxy": {
23
+ "types": "./dist/bin/evolver-llm-proxy.d.ts",
24
+ "default": "./dist/bin/evolver-llm-proxy.js"
25
+ }
26
+ },
27
+ "dependencies": {
28
+ "@aws-sdk/client-bedrock-runtime": "^3.1053.0",
29
+ "@evomap/evolver-adapter-public": "2.0.0-beta.0",
30
+ "@evomap/evolver-core": "2.0.0-beta.0"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public",
34
+ "tag": "v2-beta"
35
+ },
36
+ "files": [
37
+ "dist/",
38
+ "README.md",
39
+ "package.json"
40
+ ]
41
+ }