@evomap/evolver 1.89.17 → 1.89.19

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 (86) hide show
  1. package/README.ja-JP.md +1 -1
  2. package/README.ko-KR.md +1 -1
  3. package/README.md +1 -1
  4. package/README.zh-CN.md +1 -1
  5. package/index.js +272 -18
  6. package/package.json +7 -2
  7. package/src/adapters/claudeCode.js +16 -18
  8. package/src/adapters/hookAdapter.js +92 -5
  9. package/src/adapters/scripts/evolver-session-start.js +98 -0
  10. package/src/atp/atpExecute.js +3 -1
  11. package/src/atp/hubClient.js +5 -3
  12. package/src/atp/serviceHelper.js +3 -2
  13. package/src/evolve/guards.js +1 -1
  14. package/src/evolve/pipeline/collect.js +1 -1
  15. package/src/evolve/pipeline/dispatch.js +1 -1
  16. package/src/evolve/pipeline/enrich.js +1 -1
  17. package/src/evolve/pipeline/hub.js +1 -1
  18. package/src/evolve/pipeline/select.js +1 -1
  19. package/src/evolve/pipeline/signals.js +1 -1
  20. package/src/evolve/utils.js +1 -1
  21. package/src/evolve.js +1 -1
  22. package/src/gep/a2aProtocol.js +1 -1
  23. package/src/gep/antiAbuseTelemetry.js +1 -1
  24. package/src/gep/autoDistillConv.js +1 -1
  25. package/src/gep/autoDistillLlm.js +1 -1
  26. package/src/gep/candidateEval.js +1 -1
  27. package/src/gep/candidates.js +1 -1
  28. package/src/gep/cliContracts.js +37 -1
  29. package/src/gep/contentHash.js +1 -1
  30. package/src/gep/conversationDistiller.js +1 -1
  31. package/src/gep/conversationSniffer.js +1 -1
  32. package/src/gep/crypto.js +1 -1
  33. package/src/gep/curriculum.js +1 -1
  34. package/src/gep/deviceId.js +1 -1
  35. package/src/gep/directoryClient.js +7 -3
  36. package/src/gep/envFingerprint.js +1 -1
  37. package/src/gep/epigenetics.js +1 -1
  38. package/src/gep/execBridge.js +1 -1
  39. package/src/gep/explore.js +1 -1
  40. package/src/gep/hash.js +1 -1
  41. package/src/gep/hubFetch.js +1 -1
  42. package/src/gep/hubReview.js +1 -1
  43. package/src/gep/hubSearch.js +1 -1
  44. package/src/gep/hubVerify.js +1 -1
  45. package/src/gep/learningSignals.js +1 -1
  46. package/src/gep/memoryGraph.js +1 -1
  47. package/src/gep/memoryGraphAdapter.js +1 -1
  48. package/src/gep/mutation.js +1 -1
  49. package/src/gep/narrativeMemory.js +1 -1
  50. package/src/gep/oauthLogin.js +3 -5
  51. package/src/gep/openPRRegistry.js +1 -1
  52. package/src/gep/personality.js +1 -1
  53. package/src/gep/policyCheck.js +1 -1
  54. package/src/gep/privacyClient.js +28 -10
  55. package/src/gep/prompt.js +1 -1
  56. package/src/gep/recallInject.js +1 -1
  57. package/src/gep/recallVerifier.js +1 -1
  58. package/src/gep/reflection.js +1 -1
  59. package/src/gep/savingsCore.js +1 -1
  60. package/src/gep/selector.js +1 -1
  61. package/src/gep/skill2gep.js +329 -43
  62. package/src/gep/skill2gepAudit.js +303 -0
  63. package/src/gep/skillDistiller.js +1 -1
  64. package/src/gep/skillPublisher.js +8 -3
  65. package/src/gep/solidify.js +1 -1
  66. package/src/gep/strategy.js +1 -1
  67. package/src/gep/taskReceiver.js +3 -2
  68. package/src/gep/tokenSavings.js +1 -1
  69. package/src/gep/trajectoryExport.js +1 -0
  70. package/src/gep/validator/index.js +7 -2
  71. package/src/gep/validator/reporter.js +7 -2
  72. package/src/gep/validator/stakeBootstrap.js +7 -2
  73. package/src/gep/workspaceKeychain.js +1 -1
  74. package/src/proxy/clientSettings.js +405 -0
  75. package/src/proxy/extensions/traceControl.js +1 -1
  76. package/src/proxy/index.js +13 -5
  77. package/src/proxy/inject.js +1 -1
  78. package/src/proxy/lifecycle/manager.js +82 -16
  79. package/src/proxy/mailbox/state.js +207 -0
  80. package/src/proxy/mailbox/store.js +164 -64
  81. package/src/proxy/router/messages_route.js +56 -4
  82. package/src/proxy/server/http.js +40 -6
  83. package/src/proxy/sync/inbound.js +31 -12
  84. package/src/proxy/sync/outbound.js +164 -24
  85. package/src/proxy/trace/extractor.js +1 -1
  86. package/src/proxy/trace/usage.js +1 -1
@@ -3,6 +3,11 @@
3
3
  const crypto = require('crypto');
4
4
  const http = require('http');
5
5
  const { writeSettings, readSettings, clearSettings, clearIfStale } = require('./settings');
6
+ const {
7
+ isValidReusableProxyToken,
8
+ readReusableClientProxyToken,
9
+ syncClaudeProxySettings,
10
+ } = require('../clientSettings');
6
11
 
7
12
  const MAX_PORT_ATTEMPTS = 100;
8
13
  const DEFAULT_PORT = 19820;
@@ -93,13 +98,14 @@ function tryListen(server, port) {
93
98
  }
94
99
 
95
100
  class ProxyHttpServer {
96
- constructor(routes, { port, logger } = {}) {
101
+ constructor(routes, { port, logger, clientSettings } = {}) {
97
102
  this.routes = routes;
98
103
  this.basePort = port || Number(process.env.EVOMAP_PROXY_PORT) || DEFAULT_PORT;
99
104
  this.actualPort = null;
100
105
  this.logger = logger || console;
101
106
  this.server = null;
102
107
  this.token = null;
108
+ this.clientSettings = clientSettings || null;
103
109
  }
104
110
 
105
111
  async start() {
@@ -109,16 +115,22 @@ class ProxyHttpServer {
109
115
  // already exported into long-lived shells (the .bashrc auto-source only
110
116
  // runs once per terminal).
111
117
  const priorProxy = readSettings().proxy || {};
112
- const previous = typeof priorProxy.token === 'string' ? priorProxy.token : null;
118
+ const previous = isValidReusableProxyToken(priorProxy.token) ? priorProxy.token.trim() : null;
113
119
  // settings.json is operator-edited; previous_tokens may contain non-strings
114
120
  // (numbers, booleans, objects) that would later crash Buffer.from(cand, 'utf8')
115
121
  // in _handleRequest as ERR_INVALID_ARG_TYPE — an unhandled rejection that
116
122
  // takes the daemon down under default --unhandled-rejections=throw.
117
123
  const priorPreviousTokens = Array.isArray(priorProxy.previous_tokens)
118
- ? priorProxy.previous_tokens.filter((t) => typeof t === 'string' && t.length > 0)
124
+ ? priorProxy.previous_tokens
125
+ .map((t) => (typeof t === 'string' ? t.trim() : ''))
126
+ .filter((t) => isValidReusableProxyToken(t))
119
127
  : [];
120
128
  clearIfStale();
121
- this.token = previous || crypto.randomBytes(32).toString('hex');
129
+ const clientSettingsOpts = this.clientSettings || {};
130
+ const clientToken = this.clientSettings
131
+ ? readReusableClientProxyToken({ ...clientSettingsOpts, port: this.basePort })
132
+ : null;
133
+ this.token = previous || clientToken || crypto.randomBytes(32).toString('hex');
122
134
  this._priorPreviousTokens = priorPreviousTokens;
123
135
  this.server = http.createServer((req, res) => this._handleRequest(req, res));
124
136
 
@@ -138,6 +150,24 @@ class ProxyHttpServer {
138
150
  proxyBlock.previous_tokens = this._priorPreviousTokens;
139
151
  }
140
152
  writeSettings({ proxy: proxyBlock });
153
+ if (this.clientSettings) {
154
+ try {
155
+ const syncResult = syncClaudeProxySettings({
156
+ ...clientSettingsOpts,
157
+ url,
158
+ port: this.basePort,
159
+ token: this.token,
160
+ runtimeEnv: process.env,
161
+ });
162
+ if (syncResult.synced && syncResult.changed) {
163
+ this.logger.log(`[proxy] Synced Claude client settings at ${syncResult.file}`);
164
+ } else if (syncResult.reason === 'invalid_settings_json') {
165
+ this.logger.warn?.(`[proxy] Skipped Claude client settings sync because ${syncResult.file} is not valid JSON`);
166
+ }
167
+ } catch (err) {
168
+ this.logger.warn?.('[proxy] Claude client settings sync failed: ' + (err && err.message ? err.message : err));
169
+ }
170
+ }
141
171
  this.logger.log(`[proxy] HTTP server listening on ${url}`);
142
172
  return { port, url, token: this.token };
143
173
  }
@@ -171,9 +201,13 @@ class ProxyHttpServer {
171
201
  // and unhandled-reject through the auth path.
172
202
  const previous = readSettings().proxy?.previous_tokens;
173
203
  const extras = Array.isArray(previous)
174
- ? previous.filter((t) => typeof t === 'string' && t.length > 0)
204
+ ? previous
205
+ .map((t) => (typeof t === 'string' ? t.trim() : ''))
206
+ .filter((t) => isValidReusableProxyToken(t))
175
207
  : [];
176
- const candidates = [this.token, ...extras].filter((t) => typeof t === 'string' && t.length > 0);
208
+ const candidates = [this.token, ...extras]
209
+ .filter((t) => isValidReusableProxyToken(t))
210
+ .map((t) => t.trim());
177
211
  let valid = false;
178
212
  for (const cand of candidates) {
179
213
  const expBuf = Buffer.from(cand, 'utf8');
@@ -16,6 +16,18 @@ const {
16
16
  const DEFAULT_POLL_INTERVAL_ACTIVE = 10_000;
17
17
  const DEFAULT_POLL_INTERVAL_IDLE = 60_000;
18
18
 
19
+ function toInboundStoreMessage(m, channel) {
20
+ return {
21
+ id: m.id,
22
+ type: m.type,
23
+ payload: m.payload,
24
+ channel,
25
+ priority: m.priority || 'normal',
26
+ refId: m.ref_id,
27
+ expiresAt: m.expires_at,
28
+ };
29
+ }
30
+
19
31
  class InboundSync {
20
32
  constructor({ store, hubUrl, getHeaders, logger }) {
21
33
  this.store = store;
@@ -86,26 +98,33 @@ class InboundSync {
86
98
 
87
99
  const data = await readHubResponseJson(res);
88
100
  const messages = data.messages || [];
101
+ let stored = 0;
102
+ let dropped = 0;
89
103
 
90
104
  if (messages.length > 0) {
91
- this.store.writeInboundBatch(
92
- messages.map(m => ({
93
- id: m.id,
94
- type: m.type,
95
- payload: m.payload,
96
- channel,
97
- priority: m.priority || 'normal',
98
- refId: m.ref_id,
99
- expiresAt: m.expires_at,
100
- }))
101
- );
105
+ for (const m of messages) {
106
+ try {
107
+ this.store.writeInbound(toInboundStoreMessage(m, channel));
108
+ stored++;
109
+ } catch (err) {
110
+ if (err && err.code === 'MAILBOX_JSONL_LINE_TOO_LARGE') {
111
+ dropped++;
112
+ const msgId = m && m.id ? String(m.id) : '(missing id)';
113
+ this.logger.warn?.(`[inbound] dropped oversized inbound message ${msgId}: ${err.message}`);
114
+ continue;
115
+ }
116
+ throw err;
117
+ }
118
+ }
102
119
  }
103
120
 
104
121
  if (data.next_cursor) {
105
122
  this.store.setCursor(cursorKey, data.next_cursor);
106
123
  }
107
124
 
108
- return { received: messages.length, cursor: data.next_cursor || cursor };
125
+ const result = { received: stored, cursor: data.next_cursor || cursor };
126
+ if (dropped > 0) result.dropped = dropped;
127
+ return result;
109
128
  } catch (err) {
110
129
  if (err instanceof AuthError) throw err;
111
130
  if (isHubUnreachableError(err)) {
@@ -12,9 +12,113 @@ const {
12
12
  sanitizeHubResponseForLog,
13
13
  throwIfHubUnreachableResponse,
14
14
  } = require('../../gep/hubFetch');
15
+ const { redactString } = require('../../gep/sanitize');
15
16
 
16
17
  const MAX_BATCH = 50;
17
18
  const MAX_RETRIES = 10;
19
+ const DEFAULT_MAX_OUTBOUND_BODY_BYTES = 4 * 1024 * 1024;
20
+ const MIN_ADAPTIVE_OUTBOUND_BODY_BYTES = 16 * 1024;
21
+ const OUTBOUND_BODY_BYTES_STATE_KEY = 'outbound_sync_max_body_bytes';
22
+
23
+ function sanitizeHubErrorMessage(value) {
24
+ if (typeof sanitizeHubResponseForLog === 'function') {
25
+ return sanitizeHubResponseForLog(value);
26
+ }
27
+
28
+ let text;
29
+ if (typeof value === 'string') {
30
+ text = value;
31
+ } else if (value && typeof value.message === 'string') {
32
+ text = value.message;
33
+ } else {
34
+ try { text = JSON.stringify(value); } catch { text = String(value || ''); }
35
+ }
36
+ return redactString(String(text || ''));
37
+ }
38
+
39
+ function resolveHardOutboundBodyBytes(env = process.env) {
40
+ const raw = Number(env.EVOMAP_OUTBOUND_SYNC_MAX_BODY_BYTES || env.EVOMAP_MAILBOX_OUTBOUND_MAX_BODY_BYTES);
41
+ const max = Number.isFinite(raw) && raw > 0
42
+ ? Math.floor(raw)
43
+ : DEFAULT_MAX_OUTBOUND_BODY_BYTES;
44
+ return Math.max(1, max);
45
+ }
46
+
47
+ function resolveMaxOutboundBodyBytes(env = process.env, store = null) {
48
+ let max = resolveHardOutboundBodyBytes(env);
49
+ try {
50
+ const adaptive = Number(store && typeof store.getState === 'function'
51
+ ? store.getState(OUTBOUND_BODY_BYTES_STATE_KEY)
52
+ : null);
53
+ if (Number.isFinite(adaptive) && adaptive > 0) max = Math.min(max, Math.floor(adaptive));
54
+ } catch { /* adaptive state is best-effort */ }
55
+ return Math.max(1, max);
56
+ }
57
+
58
+ function hubMessage(m) {
59
+ return {
60
+ id: m.id,
61
+ type: m.type,
62
+ payload: m.payload,
63
+ priority: m.priority,
64
+ ref_id: m.ref_id,
65
+ created_at: m.created_at,
66
+ };
67
+ }
68
+
69
+ function serializeOutboundBody(senderId, messages) {
70
+ return JSON.stringify({
71
+ sender_id: senderId,
72
+ proxy_protocol_version: PROXY_PROTOCOL_VERSION,
73
+ messages: messages.map(hubMessage),
74
+ });
75
+ }
76
+
77
+ function outboundEndpoint(hubUrl, senderId) {
78
+ const endpoint = new URL(`${String(hubUrl || '').replace(/\/+$/, '')}/a2a/mailbox/outbound`);
79
+ if (senderId) endpoint.searchParams.set('sender_id', senderId);
80
+ return endpoint.toString();
81
+ }
82
+
83
+ function outboundBodyBytes(senderId, messages) {
84
+ return Buffer.byteLength(serializeOutboundBody(senderId, messages), 'utf8');
85
+ }
86
+
87
+ function buildSizedOutboundBatch(senderId, messages, maxBodyBytes, hardMaxBodyBytes = maxBodyBytes) {
88
+ const selected = [];
89
+ const rejected = [];
90
+ let body = serializeOutboundBody(senderId, selected);
91
+ let bodyBytes = Buffer.byteLength(body, 'utf8');
92
+
93
+ for (const m of messages) {
94
+ const singleBytes = outboundBodyBytes(senderId, [m]);
95
+ if (singleBytes > hardMaxBodyBytes) {
96
+ rejected.push({
97
+ id: m.id,
98
+ status: 'rejected',
99
+ error: `outbound message exceeds max body bytes (${singleBytes} > ${hardMaxBodyBytes})`,
100
+ });
101
+ continue;
102
+ }
103
+ if (selected.length === 0 && singleBytes > maxBodyBytes) {
104
+ selected.push(m);
105
+ body = serializeOutboundBody(senderId, selected);
106
+ bodyBytes = Buffer.byteLength(body, 'utf8');
107
+ break;
108
+ }
109
+
110
+ const candidate = selected.concat(m);
111
+ const candidateBody = serializeOutboundBody(senderId, candidate);
112
+ const candidateBytes = Buffer.byteLength(candidateBody, 'utf8');
113
+ if (candidateBytes > maxBodyBytes) break;
114
+
115
+ selected.push(m);
116
+ body = candidateBody;
117
+ bodyBytes = candidateBytes;
118
+ }
119
+
120
+ return { selected, rejected, body, bodyBytes, maxBodyBytes };
121
+ }
18
122
 
19
123
  class OutboundSync {
20
124
  constructor({ store, hubUrl, getHeaders, logger }) {
@@ -81,41 +185,75 @@ class OutboundSync {
81
185
  pending = pendingBatch.filter(m => !rejectedIds.has(m.id));
82
186
  if (pending.length === 0) return { sent: 0, dropped: rejectedTraceUploads.length };
83
187
  }
84
- const dropped = rejectedTraceUploads.length;
85
-
86
- const endpoint = `${this.hubUrl}/a2a/mailbox/outbound`;
188
+ let dropped = rejectedTraceUploads.length;
87
189
 
88
190
  try {
89
191
  const senderId = this.store.getState('node_id');
192
+ const endpoint = outboundEndpoint(this.hubUrl, senderId);
193
+ const prepared = buildSizedOutboundBatch(
194
+ senderId,
195
+ pending,
196
+ resolveMaxOutboundBodyBytes(process.env, this.store),
197
+ resolveHardOutboundBodyBytes(process.env)
198
+ );
199
+ if (prepared.rejected.length > 0) {
200
+ this.store.updateStatusBatch(prepared.rejected);
201
+ dropped += prepared.rejected.length;
202
+ }
203
+ pending = prepared.selected;
204
+ if (pending.length === 0) return { sent: 0, dropped };
205
+
90
206
  const res = await hubFetch(endpoint, {
91
207
  method: 'POST',
92
208
  headers: this.getHeaders(),
93
- body: JSON.stringify({
94
- sender_id: senderId,
95
- proxy_protocol_version: PROXY_PROTOCOL_VERSION,
96
- messages: pending.map(m => ({
97
- id: m.id,
98
- type: m.type,
99
- payload: m.payload,
100
- priority: m.priority,
101
- ref_id: m.ref_id,
102
- created_at: m.created_at,
103
- })),
104
- }),
209
+ body: prepared.body,
105
210
  signal: AbortSignal.timeout(30_000),
106
211
  });
107
212
 
213
+ if (res.status === 413) {
214
+ const errText = sanitizeHubErrorMessage(
215
+ await readHubResponseText(res).catch(() => 'request entity too large')
216
+ );
217
+ const error = `Hub 413 outbound payload too large: ${errText}`;
218
+ if (pending.length <= 1) {
219
+ this.store.updateStatusBatch(pending.map(m => ({
220
+ id: m.id,
221
+ status: 'rejected',
222
+ error,
223
+ })));
224
+ const result = {
225
+ sent: 0,
226
+ error: 'hub_payload_too_large',
227
+ payloadTooLarge: true,
228
+ dropped: dropped + pending.length,
229
+ };
230
+ return result;
231
+ }
232
+ const currentCeiling = Math.min(prepared.maxBodyBytes, Math.max(1, prepared.bodyBytes - 1));
233
+ const nextMax = currentCeiling > MIN_ADAPTIVE_OUTBOUND_BODY_BYTES
234
+ ? Math.max(MIN_ADAPTIVE_OUTBOUND_BODY_BYTES, Math.floor(currentCeiling / 2))
235
+ : Math.max(1, Math.floor(currentCeiling / 2));
236
+ try { this.store.setState(OUTBOUND_BODY_BYTES_STATE_KEY, nextMax); } catch { /* best effort */ }
237
+ this.logger.warn?.(
238
+ `[outbound] Hub 413 for ${pending.length} messages (${prepared.bodyBytes} bytes); ` +
239
+ `reducing outbound batch budget to ${nextMax} bytes`
240
+ );
241
+ const result = { sent: 0, error: 'hub_payload_too_large', payloadTooLarge: true };
242
+ if (dropped > 0) result.dropped = dropped;
243
+ return result;
244
+ }
245
+
108
246
  await throwIfHubUnreachableResponse(res, 'outbound flush');
109
247
  this._recordHubReachable();
110
248
 
111
249
  if (res.status === 403 || res.status === 401) {
112
- const errText = await readHubResponseText(res).catch(() => 'unknown');
113
- throw new AuthError(`Hub ${res.status}: ${sanitizeHubResponseForLog(errText)}`, res.status);
250
+ const errText = sanitizeHubErrorMessage(await readHubResponseText(res).catch(() => 'unknown'));
251
+ throw new AuthError(`Hub ${res.status}: ${errText}`, res.status);
114
252
  }
115
253
 
116
254
  if (!res.ok) {
117
- const errText = await readHubResponseText(res).catch(() => 'unknown');
118
- throw new Error(`Hub returned ${res.status}: ${sanitizeHubResponseForLog(errText)}`);
255
+ const errText = sanitizeHubErrorMessage(await readHubResponseText(res).catch(() => 'unknown'));
256
+ throw new Error(`Hub returned ${res.status}: ${errText}`);
119
257
  }
120
258
 
121
259
  const data = await readHubResponseJson(res);
@@ -129,10 +267,11 @@ class OutboundSync {
129
267
  updates.push({ id: r.id, status: 'synced' });
130
268
  } else if (r.status === 'failed' || r.status === 'rejected') {
131
269
  const msg = pending.find(m => m.id === r.id);
270
+ const error = sanitizeHubErrorMessage(r.error || 'rejected by hub');
132
271
  if (msg && msg.retry_count < MAX_RETRIES) {
133
- this.store.incrementRetry(r.id, r.error || 'rejected by hub');
272
+ this.store.incrementRetry(r.id, error);
134
273
  } else {
135
- updates.push({ id: r.id, status: 'failed', error: r.error || 'max retries' });
274
+ updates.push({ id: r.id, status: 'failed', error: sanitizeHubErrorMessage(r.error || 'max retries') });
136
275
  }
137
276
  }
138
277
 
@@ -166,11 +305,12 @@ class OutboundSync {
166
305
  if (dropped > 0) result.dropped = dropped;
167
306
  return result;
168
307
  }
169
- this.logger.error(`[outbound] flush failed: ${err.message}`);
308
+ const errMessage = sanitizeHubErrorMessage(err);
309
+ this.logger.error(`[outbound] flush failed: ${errMessage}`);
170
310
  for (const m of pending) {
171
- this.store.incrementRetry(m.id, err.message);
311
+ this.store.incrementRetry(m.id, errMessage);
172
312
  }
173
- const result = { sent: 0, error: err.message };
313
+ const result = { sent: 0, error: errMessage };
174
314
  if (dropped > 0) result.dropped = dropped;
175
315
  return result;
176
316
  }